diff --git a/CMakeLists.txt b/CMakeLists.txt index 7477607..8f6d597 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,28 @@ add_compile_options( -Wno-unused-function ) +# Check if the required environment variables MIDASSYS, EPICSSYS and EPICS_HOST_ARCH are available +if (NOT DEFINED ENV{MIDASSYS}) + message(SEND_ERROR "MIDASSYS environment variable not defined.") +endif() +if (NOT DEFINED ENV{EPICSSYS}) + message(SEND_ERROR "EPICSSYS environment variable not defined.") +endif() +if (NOT DEFINED ENV{EPICS_HOST_ARCH}) + message(SEND_ERROR "EPICS_HOST_ARCH environment variable not defined.") +endif() + +set(MIDASSYS $ENV{MIDASSYS}) +set(EPICSSYS $ENV{EPICSSYS}) + +# Select the correct EPICS library depending on the OS (currently Linux is available) +if (${CMAKE_SYSTEM_NAME} MATCHES Linux) + link_directories(${EPICSSYS}/lib/linux-x86_64) + set(LIBS ${LIBS} -lpthread -lutil -lrt -lbsd -ldl) +endif() + option( - BUILD_FRONTEND + BUILD_FRONTENDS "If ON, build the default frontend executable" OFF ) @@ -27,11 +47,6 @@ set( "path to tcpip driver that should be used" ) -set( - DRIVERS - $ENV{MIDASSYS}/drivers/class/generic.cxx -) - set(SYS_LIBS pthread util @@ -45,7 +60,14 @@ set(MIDAS_LIBS midas::mscb ) +set(TCPIP_DRIVER) + set(LIBS ${SYS_LIBS} ${MIDAS_LIBS}) +set(EPICS_LIBS + ${LIBS} + ${EPICSSYS}/lib/$ENV{EPICS_HOST_ARCH}/libca.so + ${EPICSSYS}/lib/$ENV{EPICS_HOST_ARCH}/libCom.so +) add_subdirectory(midas_lan_gpib) @@ -67,28 +89,28 @@ if(IS_DIRECTORY ${TCPIP_DRIVER_DIR}) ) else() set( - DRIVERS - ${DRIVERS} + TCPIP_DRIVER ${TCPIP_DRIVER_DIR} ) endif() ################################################################################ -## Device Library +## Device Libraries ################################################################################ add_library( expmag device/expmag.cxx device/spline.cxx - ${DRIVERS} + ${TCPIP_DRIVER} + $ENV{MIDASSYS}/drivers/class/generic.cxx ) set_property( TARGET expmag PROPERTY - CXX_STANDARD 11 + CXX_STANDARD 17 ) target_include_directories( @@ -106,33 +128,86 @@ target_link_libraries( ${LIBS} ) +######## EPICS library + +add_library( + expmag_epics + device/expmag.cxx + device/epics_ca.cxx + device/spline.cxx + ${TCPIP_DRIVER} + $ENV{MIDASSYS}/drivers/class/generic.cxx +) + +set_property( + TARGET + expmag_epics + PROPERTY + CXX_STANDARD 17 +) + +target_include_directories( + expmag_epics + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + $ENV{MIDASSYS}/drivers + ${EPICSSYS}/include + ${EPICSSYS}/include/os/Linux + ${EPICSSYS}/include/compiler/gcc + ${EPICSSYS}/include/compiler/clang +) + +target_link_libraries( + expmag_epics + PUBLIC + midas_lan_gpib + ${EPICS_LIBS} +) + +target_compile_definitions( + expmag_epics + PRIVATE + EPICSGFA + GFA_SPECIFIC +) + ################################################################################ ## Test Frontend ################################################################################ -if(${BUILD_FRONTEND}) +if(${BUILD_FRONTENDS}) - add_executable( - expmag_fe - frontend/expmag_scfe.cxx - ) + foreach(target IN ITEMS expmag_fe expmag_epics_fe) - set_property( - TARGET - expmag_fe - PROPERTY - CXX_STANDARD 11 - ) + add_executable( + ${target} + frontend/expmag_scfe.cxx + ) - target_include_directories( - expmag_fe - PRIVATE - $ENV{MIDASSYS}/drivers - ) + set_property( + TARGET + ${target} + PROPERTY + CXX_STANDARD 17 + ) + + target_include_directories( + ${target} + PRIVATE + $ENV{MIDASSYS}/drivers + ) + + endforeach() target_link_libraries( expmag_fe expmag ) + target_link_libraries( + expmag_epics_fe + expmag_epics + ) + endif() diff --git a/README.md b/README.md index f5b4f57..a79b5ec 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,11 @@ # Main experimental magnet PS This repository contains the driver code and Midas frontend for the main -experimental magnet PS. - -> **Important Note** -> -> The EPICSGFA flag is currently not used, it was created for using a prototype -> EPICS driver for MIDAS. It is not clear whether this prototype driver would -> work. If all instruments work without this flag in late 2026, it is heavily -> recommended to remove it entirely. +experimental magnet PS. There are two different versions of this driver - one +using LAN-GPIB to run the magnets and a second one which uses the EPICS +interface provided by GFA. The respective CMake targets are: +- `expmag` (LAN-GPIB) +- `expmag_epics` (EPICS interface) ## Requirements @@ -20,7 +17,7 @@ to the built Midas artifacts and headers. Clone this repository, enter the cloned directory, and then build via CMake. ```bash -cmake -S "$(pwd)" -B -DBUILD_FRONTEND=ON +cmake -S "$(pwd)" -B -DBUILD_FRONTENDS=ON cmake --build --clean-first -- -j8 ``` diff --git a/device/epics_ca.cxx b/device/epics_ca.cxx new file mode 100644 index 0000000..7d32062 --- /dev/null +++ b/device/epics_ca.cxx @@ -0,0 +1,6067 @@ + +/******************************************************************* *\ + + Name: epics_ca.c + Created by: Stefan Ritt (Modified -> V3.12 -> V3.14 + GFA RA36) + + Contents: Epics channel access device driver for EPICS Version 3.14 + (incomplete prototype version) + + class driver is generic with Demand and Measured Variables + DF_PRIO_DEVICE device has priority over initial ODB value during startup + DF_NOUPDALL Demand[] channels are not updated when Demand key is updated + but no Demand[] value changed. + generic.c has to be compiled without updating mirrored demand. + compilation flag is -DOMIT_GENERIC_MIRROR_UPDATE + + + device driver list + DEVICE_DRIVER epics_driver[] = { + // initial demand value taken from instrument + { "EPICSGFA", epics_ca, , null, DF_PRIO_DEVICE | DF_NOUPDALL}, + { "" } + }; + + EPICS base package must be installed (caRepeater, libca.so libCom.so) + and EPICS environment (EPICS_CA_ADDR_LIST, EPICS_CA_AUTO_ADDR_LIST and + EPICS_CA_SERVER_PORT) must be defined when the EPICS gateway is used. + + $Id: epics_ca.c,v 1.11 2024/06/30 10:09:48 raselli Exp $ + + 25-AUG-2015 RA36 Adapted to new type names + 25-APR-2021 RA36 FENTE has now a second type of channel set for mm instead of steps + steps: COM:2, STA:1, SOL:2, IST1:2 or POSA:2 and + mm: COM:2, STA:2, SOL , POSA NOTE: STA:2 is not used + solved ambiguity STA:1 <> STA:2 when searching corresponding + G-group of COM:2 + solved ambiguity IST1:2 <> POSA:2 when searching corresponding + G-group of SOL:2 + 26-APR-2021 RA36 Adapted for current changes MA_SEP was CS_PS + 13-JUL-2021 RA36 Fixed problem with sending 4 = "MOVE_TO_SOL" + Removed FENTE COM:2 information to avoid wrong log message when ODB + value is updated due to the different assignment between command + number and corresponding command string between the old slit + devices and the new slit devices. + 12-APR-2022 RA36 Added IF flags to :COM:2 for new slit systems and blocked sending + negative commands to :COM:2. + 04-APR-2023 RA36 New DESC:TYP CS_KV will be handled as BX + 24-APR-2024 RA36 New DESC:TYP DI_MESTRA will be handled as DIAG + was also necessary to add MHC4:IST:2 as SCALchan for DIAG + +\********************************************************************/ + +/* these compilation options are now set as -D compilation option in Makefile */ + +/* #define GFA_SPECIFIC */ /* include GFA_SPECIFIC code in private */ + /* header and source */ + +/* #define OMIT_ODBUPDATE */ /* define OMIT_ODBUPDATE to omit update of */ + /* /Equipment//Variables/Demand and */ + /* other variables .../Settings/Names ... */ + /* when used as sub device driver for EXPMAG */ + /* NOTE: do not set F or G Chn_Flags at all! */ + +/* #define RESET_DEMAND_VALUE_TO_RANGE */ /* Reset demand value to range limits */ + /* when out of range */ + /* Was abandoned due to problems with */ + /* Channel Access gateway to avoid */ + /* magnets are reset due to wrong */ + /* reading */ +#include +#include +#include +#include + +#ifdef FORTIFY +#include "fortify.h" +#endif + +#include "epics_ca_private.h" /* include private header */ +#include "epicsMutex.h" +#include "epicsVersion.h" + +//#define MIDEBUG // write debug information */ +//#define MIDEBUG1 // debug information about functions (except _get()) */ +//#define MIDEBUG2 // exhaustive debug information */ +//#define MIDEBUGP // status return of ca_pend_io() and ca_pend_event() */ +/*#define MIDEBUGE // event handler debug using cm_msg(MLOG) */ + /* *** THIS WILL BLOCK NORMAL EXECUTION *** */ +/*#define MIDEBUGE1 // event handler func */ + /* *** THIS WILL BLOCK NORMAL EXECUTION *** */ +//#define MIDEBUGF // printf ss_time()'ed info to file */ +//#define MIDEBUGFE // printf ss_time()'ed info of event handler to file */ +//#define MIDEBUGFX // printf ss_time()'ed info of excep handler to file */ +//#define MIDEBUGFC // printf ss_time()'ed info of connect handler to file */ + +#define MIDEBUGCL /* write debug information ca_client_status(CLLEVEL) */ + /* after important or unique e.g. ca_pend_io() */ +/*#define MIDEBUGCLA // write debug information ca_client_status(CLLEVEL) */ +/*#define MIDEBUGCL1 // write debug information ca_client_status(CLLEVEL) */ + /* before important or unique e.g. ca_pend_io()*/ +/*#define MIDEBUGCL2 // write debug information ca_client_status(CLLEVEL) */ + /* before/after unimportant e.g ca_flush_io()*/ +#define CLLEVEL 10 /* specify interest level for ca_client_status() calls */ + /* 0=basic info, 10=channel info */ +#define PREEMPTIVE /* additional thread doing ca_pend_event() */ + /* NOTE: to lower cpu usage of thread compile this + * code using -DSLOW_THREAD option + */ + +/* the first option is monitoring where USE_CONNECTION_HANDLER, SUBSCRIBE_ENABLED and + * DO_MONITORING have to be defined. + * NOTE: DO_GET and DO_GET_CALLBACK should not be defined!! + * + * the second option is doing callbacks where DO_GET and DO_GET_CALLBACK + * have to be defined. + * NOTE: USE_CONNECTION_HANDLER, SUBSCRIBE_ENABLED and DO_MONITORING should not be defined!! + * */ + + /* 30-JUN-2019 RA36 switching back to first option because second option does not work with the + * current version of the EPICS gateway + */ + +#define USE_CONNECTION_HANDLER // use connection handler +#define SUBSCRIBE_ENABLED // do subscription in connection handler callback */ +#define DO_MONITORING // subscription of connected channels in epics_ca_init */ +//#define DO_GET // ca_get() of channels in epics_ca_init +//#define DO_GET_CALLBACK // ca_get_callback() of channels in epics_ca_get() + +/*---- globals -----------------------------------------------------*/ + +static BOOL process_exception; // flag to process exception in exception handler +static BOOL process_connection; // flag to process connection info in connection handler +static BOOL process_event; // flag to process events in event handler + +static BOOL in_ca_process; // flag already in epics_ca_process() + +static BOOL do_callback; // flag to get values by callback to event handler + +#ifdef PREEMPTIVE +#ifdef OS_UNIX +static pthread_t pollthreadid; // thread ID for poll thread +#else + TDC threading not (yet) supported for this OS +#endif +struct ca_client_context *cacontext; +int pollthreadkeeprunning; +epicsMutexId process_mutex = NULL; +epicsMutexId exception_mutex = NULL; +epicsMutexId connection_mutex = NULL; +epicsMutexId event_mutex = NULL; +#endif /* PREEMPTIVE */ + +static char poll_thread_error[256]; + +static char xhandler_info[256], xhandler_info_m[256]; // exception handler info + +static char chandler_info[256], chandler_info_m[256]; // connection handler info +static char chandler_error[256], chandler_error_m[256]; // error +static char chandler_found[256], chandler_found_m[256]; // found + +static char ehandler_error[256], ehandler_error_m[256]; // event handler error +static char ehandler_info[256], ehandler_info_m[256]; // event handler info + +#define EPICS_EH_MAX_ERROR 3 //!< maximum number of error messages for ehandler +#define EPICS_EH_DELTA_TIME_ERROR 3600 //!< reset error counter after + // EPICS_DELTA_TIME_ERROR seconds +static INT *ehandler_nerr; //!< event handler error count +static DWORD *ehandler_lasterr; //!< event handler start of interval + +static char smsg[128]; + +#ifdef MIDEBUGF +static FILE *fp = NULL; +#endif +#ifdef MIDEBUGFX +static FILE *fpx = NULL; +#endif +#ifdef MIDEBUGFC +static FILE *fpc = NULL; +#endif +#ifdef MIDEBUGFE +static FILE *fpe = NULL; +#endif + +#ifdef GFA_SPECIFIC +typedef struct _GFA_msg { /* message corresponding to value */ + long value; + char *msg; +} GFA_msg; + +typedef struct _GFA_mask {/* mask and pointer to list of value/message pairs */ + long mask; + char *desc; + GFA_msg *sm; + int nm; +} GFA_mask; + +typedef struct _GFA_channel {/* channel ification and pointer to list of masks */ + char *spec; + char *desc; + GFA_mask *cm; + int ns; + BOOL iscmd; +} GFA_channel; + +typedef struct _GFA_type { /* device type and pointer to list of channel */ + char *type; + char *desc; + GFA_channel *tc; + int nc; +} GFA_type; + +/* ------------------------- beam stopper (KV = Kanalverschluss) ---------- */ + +/* ------------------------ KV-STA :STA:1 messages --------------------- */ +GFA_msg KVstamsg0003[] = { + {0x0000, "Moving" }, + {0x0001, "Open" }, + {0x0002, "Closed" }, + {0x0003, "Error" } +}; + +GFA_msg KVstamsg0004[] = { + {0x0000, "Pass Bew.NOK" }, + {0x0004, "Pass Bew.OK" } +}; + +GFA_msg KVstamsg0008[] = { + {0x0000, "RPS Bew.NOK" }, + {0x0008, "RPS Bew.OK" } +}; + +GFA_mask KVsta[] = { + { 0x0003, "Status", KVstamsg0003, sizeof(KVstamsg0003)/sizeof(GFA_msg)} +#ifdef MIDEBUG // RA36 02-MAY_2016 + ,{ 0x0004, "Status", KVstamsg0004, sizeof(KVstamsg0004)/sizeof(GFA_msg)} + ,{ 0x0008, "Status", KVstamsg0008, sizeof(KVstamsg0008)/sizeof(GFA_msg)} +#endif +}; + +/* ------------------------ KV-COM :COM:2 commands --------------------- */ +GFA_msg KVcommsg0001[] = { + {0x0000, "OPEN" }, + {0x0001, "CLOSE" } +}; + +GFA_mask KVcom[] = { + { 0x0001, "Commands", KVcommsg0001, sizeof(KVcommsg0001)/sizeof(GFA_msg)} +}; + +GFA_channel KVchan[] = { + { ":STA:1", "Status", KVsta, sizeof(KVsta)/sizeof(GFA_mask), FALSE}, + { ":COM:2", "Commmand", KVcom, sizeof(KVcom)/sizeof(GFA_mask), TRUE } +}; + +/* ------------ KombiPS1 and MultiIO (BEND,QUAD,SEPT,STER,PS,SOLE,TS,KICKER) -------- */ + +/* ------------------------ KombiPS1 :STA:1 messages --------------------- */ +/* NOTE: MultiIO :STA:1 messages are identical */ + /* 8 4 2 1 */ +GFA_msg KPS1stamsg000f[] = { /* status bits 3+2+1+0 */ + /* bit 0 : set : Soll power on */ + /* bit 1 : set : Soll power off */ + /* bit 2 : set : Ist power on */ + /* bit 3 : set : Ist power off */ + {0x0000, "Soll and Ist NOT ON and NOT OFF" }, /* 0 0 0 0 */ + {0x0001, "Soll ON - Ist NOT ON and NOT OFF" }, /* 0 0 0 1 */ + {0x0002, "Soll OFF - Ist NOT ON and NOT OFF" }, /* 0 0 1 0 */ + {0x0003, "Soll ON and OFF - Ist NOT ON and NOT OFF" }, /* 0 0 1 1 */ + {0x0004, "Soll NOT ON and NOT OFF - Ist ON" }, /* 0 1 0 0 */ + {0x0005, "On" }, /* 0 1 0 1 */ + {0x0006, "Soll OFF - Ist ON" }, /* 0 1 1 0 */ + {0x0007, "Soll ON and OFF, Ist ON" }, /* 0 1 1 1 */ + {0x0008, "Soll NOT ON and NOT OFF - Ist OFF" }, /* 1 0 0 0 */ + {0x0009, "Soll On - Ist OFF" }, /* 1 0 0 1 */ + {0x000a, "Off" }, /* 1 0 1 0 */ + {0x000b, "Soll ON and OFF - Ist OFF" }, /* 1 0 1 1 */ + {0x000c, "Soll NOT ON and NOT OFF - Ist ON and OFF" }, /* 1 1 0 0 */ + {0x000d, "Soll ON - Ist ON and OFF" }, /* 1 1 0 1 */ + {0x000e, "Soll OFF - Ist ON and OFF" }, /* 1 1 1 0 */ + {0x000f, "Soll ON and OFF - Ist ON and OFF" } /* 1 1 1 1 */ +}; + +GFA_msg KPS1stamsg0010[] = { /* status bit 4 */ +#ifdef MIDEBUG + {0x0000, "In Remote Mode" }, +#endif + {0x0010, "In Local Mode" } +}; + +GFA_msg KPS1stamsg0020[] = { /* status bit 5 */ +#ifdef MIDEBUG + {0x0020, "SG Status OK" }, +#endif + {0x0000, "SG Status NOK" } +}; + +GFA_msg KPS1stamsg0040[] = { /* status bit 6 */ +#ifdef MIDEBUG + {0x0040, "PSC Status OK" }, +#endif + {0x0000, "PSC Status NOK" } +}; + +GFA_msg KPS1stamsg0080[] = { /* status bit 7 */ +#ifdef MIDEBUG + {0x0080, "Config OK" }, +#endif + {0x0000, "Config Error" } +}; + /* status bit 8 NOT USED */ + /* status bit 9 NOT USED */ +#ifdef MIDEBUG +GFA_msg KPS1stamsg0400[] = { /* status bit 10 */ + {0x0400, "IST == SOLA" }, + {0x0000, "IST <> SOLA" } +}; +#endif + +#ifdef MIDEBUG +GFA_msg KPS1stamsg0800[] = { /* status bit 11 */ + {0x0800, "IST >= Low Limit" }, + {0x0000, "IST < Low Limit" } +}; +#endif + +#ifdef MIDEBUG +GFA_msg KPS1stamsg1000[] = { /* status bit 12 */ + {0x1000, "IST <= High Limit"}, + {0x0000, "IST > High Limit" } +}; +#endif + +#ifdef MIDEBUG +GFA_msg KPS1stamsg2000[] = { /* status bit 13 */ + {0x2000, "SOL==SOLA" }, + {0x0000, "SOL<>SOLA" } +}; +#endif + /* status bit 14 NOT USED */ +GFA_msg KPS1stamsg8000[] = { /* status bit 15 */ +#ifdef MIDEBUG + {0x8000, "PSC Link OK" }, +#endif + {0x0000, "PSC Link Error" } +}; + +GFA_mask KPS1sta[] = { + { 0x000F, "Status", KPS1stamsg000f, sizeof(KPS1stamsg000f)/sizeof(GFA_msg)}, + { 0x0010, "Status", KPS1stamsg0010, sizeof(KPS1stamsg0010)/sizeof(GFA_msg)}, + { 0x0020, "Status", KPS1stamsg0020, sizeof(KPS1stamsg0020)/sizeof(GFA_msg)}, + { 0x0040, "Status", KPS1stamsg0040, sizeof(KPS1stamsg0040)/sizeof(GFA_msg)}, + { 0x0080, "Status", KPS1stamsg0080, sizeof(KPS1stamsg0080)/sizeof(GFA_msg)}, + /* bit 8 & 9 not used */ +#ifdef MIDEBUG + { 0x0400, "Status", KPS1stamsg0400, sizeof(KPS1stamsg0400)/sizeof(GFA_msg)}, + { 0x0800, "Status", KPS1stamsg0800, sizeof(KPS1stamsg0800)/sizeof(GFA_msg)}, + { 0x1000, "Status", KPS1stamsg1000, sizeof(KPS1stamsg1000)/sizeof(GFA_msg)}, + { 0x2000, "Status", KPS1stamsg2000, sizeof(KPS1stamsg2000)/sizeof(GFA_msg)}, +#endif + /* bit 14 not used */ + { 0x8000, "Status", KPS1stamsg8000, sizeof(KPS1stamsg8000)/sizeof(GFA_msg)} +}; + +/* ------------------------ KombiPS1 :COM:2 commands --------------------- */ +/* NOTE: MultiIO :COM:2 commands are identical */ +GFA_msg KPS1cmdmsg[] = { + {0x0000, "ON" }, + {0x0001, "OFF" }, + {0x0002, "RESET"} +}; + +GFA_mask KPS1cmd[] = { + { 0x0003, "Command", KPS1cmdmsg, sizeof(KPS1cmdmsg)/sizeof(GFA_msg)} +}; + +/* ------------------------ KombiPS1 list of channels -------------------- */ +GFA_channel KPS1chan[] = { + { ":STA:1", "Status", KPS1sta, sizeof(KPS1sta)/sizeof(GFA_mask), FALSE}, + { ":COM:2", "Command", KPS1cmd, sizeof(KPS1cmd)/sizeof(GFA_mask), TRUE }, + { ":IST:2", "Istwert", NULL, 0, FALSE}, + { ":SOL:2", "Sollwert", NULL, 0, TRUE }, + { ":SPA:2", "Spannung", NULL, 0, FALSE}, + { ":SCALE:2:factor", "Scalefactor", NULL, 0, TRUE } +}; + +/* ------------------------ FENTE (slit system) ----------------------------------- */ + +/* ------------------------ Slit system :STA:1 messages --------------------- */ + +GFA_msg Slitsta1msg307[] = { /* 00. */ + {0x0000, "UNDEFINED" }, + {0x0001, "in CW limit switch" }, + {0x0002, "in CCW limit switch" }, + {0x0003, "not connected/command error" }, + {0x0004, "stopped" }, + {0x0005, "in CW limit switch and stopped" }, + {0x0006, "in CCW limit switch and stopped" }, + {0x0007, "?EMERGENCY STOP (reset using key)" } +}; // NOTE: 0x0001 and 0x0002 will only be reset when a move order finished successfully and + // is in state stopped + +GFA_msg Slitsta1msg300[] = { /* .00 */ +#ifdef MIDEBUG + {0x0000, "moving direction UNDEFINED" }, +#endif + {0x0100, "moving CW" }, + {0x0200, "moving CCW" } +}; + +GFA_mask Slitsta1[] = { + { 0x0307, "Status", Slitsta1msg307, sizeof(Slitsta1msg307)/sizeof(GFA_msg)} +#ifdef MIDEBUG // RA36 02-MAY_2016 + ,{ 0x0300, "Status", Slitsta1msg300, sizeof(Slitsta1msg300)/sizeof(GFA_msg)} +#endif +}; + +/* ------------------------ Slit system :COM:2 commands --------------------- */ + +GFA_msg Slitcmdmsg[] = { // NOTE: old slit devices have commands 0-6 or strings + {0x0000, "MOVE_TO_CW" }, + {0x0001, "MOVE_TO_CCW" }, + {0x0002, "STOP" }, + {0x0003, "MOVE_TO_SOL" }, + {0x0004, "SOL=POSA" }, + {0x0005, "RESET_CW" }, + {0x0006, "RESET_CCW" } +}; + +GFA_msg NewSlitcmdmsg[] = { // NOTE: new slit devices setting [mm] and [steps] only have 1-4 with different numbers + {0x0001, "MOVE_TO_CW" }, + {0x0002, "MOVE_TO_CCW" }, + {0x0003, "STOP" }, + {0x0004, "MOVE_TO_SOL" } +}; + +GFA_mask Slitcmd[] = { + { 0x0007, "Command", Slitcmdmsg, sizeof(Slitcmdmsg)/sizeof(GFA_msg)} +}; + +/* ------------------------ Slit system list of channels -------------------- */ +GFA_channel Slitchan[] = { + {":STA:1", "Status", Slitsta1,sizeof(Slitsta1)/sizeof(GFA_mask),FALSE}, + // {":STA:2", "Status", Slitsta2,sizeof(Slitsta2)/sizeof(GFA_mask),FALSE}, + // NOT POSSIBLE (string) + {":STA:2", "Status", NULL, 0, FALSE}, + // {":COM:2", "Command", Slitcmd,sizeof(Slitcmd)/sizeof(GFA_mask),TRUE }, + // RA36 13-JUL-2021 removed due to different assignment number vs. corresponding command + {":COM:2", "Command", NULL, 0, TRUE }, + /* NOTE for new slit system: No valid :STA:2 channel - (dummy) Feedback in FSxxxx:COM:2 -> Use F flag */ + {":IST1:2","Position ADC rohwert", NULL, 0, FALSE}, + {":SOL:2", "Sollwert [Steps]", NULL, 0, TRUE }, + {":POSA:2","Position ADC gefiltert", NULL, 0, FALSE}, + // New 25-APR-2021 + {":SOL", "Sollwert [mm]", NULL, 0, TRUE }, + {":POSA", "Position [mm]", NULL, 0, FALSE} +}; + +/* ------------------------ Valve system ------------------------------------------ */ + +/* ------------------------ Valve system :STA:1 messages -------------------- */ +GFA_msg Valvstamsg003[] = { +#ifdef MIDEBUG + {0x0000, "Bypass Valve UNDEFINED" }, +#endif + {0x0001, "Bypass Valve closed" }, + {0x0002, "Bypass Valve open" } +}; + +GFA_msg Valvstamsg01C[] = { +#ifdef MIDEBUG + {0x0000, "Valve UNDEFINED" }, +#endif + {0x0004, "Valve armed - will open" }, + {0x0008, "Valve closed" }, + {0x0010, "Valve open" } +}; + +GFA_mask Valvsta[] = { + { 0x0003, "BP Valve Status",Valvstamsg003, sizeof(Valvstamsg003)/sizeof(GFA_msg)}, + { 0x001C, "Valve Status", Valvstamsg01C, sizeof(Valvstamsg01C)/sizeof(GFA_msg)} +}; + +/* ------------------------ Valve system :CSV:2 commands --------------------- */ +GFA_msg Valvcmdmsg[] = { + {0x0000, "AUF" }, + {0x0001, "ZU" } +}; + +GFA_mask Valvcmd[] = { + { 0x0007, "Command", Valvcmdmsg, sizeof(Valvcmdmsg)/sizeof(GFA_msg)} +}; + +/* ------------------------ Valve system list of channels -------------------- */ +GFA_channel Valvechan[] = { + {":STA:1", "Status", Valvsta,sizeof(Valvsta)/sizeof(GFA_mask),FALSE}, + {":CSV:2", "Command", Valvcmd,sizeof(Valvcmd)/sizeof(GFA_mask),TRUE } +}; + +/* ------------------------ Pump system ------------------------------------------- */ + +/* NOTE: Not tested yet derived from ACS --> check channel names */ + +/* ------------------------ Pump system :STAX:1 messages --------------------- */ +GFA_msg Pumpxmsg0001[] = { +/*#ifdef MIDEBUG */ + {0x0000, "Pump station OK" }, +/*#endif */ + {0x0001, "Pump station failure" } +}; + +GFA_msg Pumpxmsg0008[] = { +/*#ifdef MIDEBUG */ + {0x0000, "TCS OK" }, +/*#endif */ + {0x0008, "TCS failure" } +}; + +GFA_msg Pumpxmsg0010[] = { +/*#ifdef MIDEBUG */ + {0x0000, "Turbo pump OK" }, +/*#endif */ + {0x0010, "Turbo pump failure" } +}; + +GFA_msg Pumpxmsg0040[] = { +/*#ifdef MIDEBUG */ + {0x0000, "Start time NOT exceeded" }, +/*#endif */ + {0x0040, "Start time exceeded" } +}; + +GFA_msg Pumpxmsg0080[] = { +/*#ifdef MIDEBUG */ + {0x0000, "Recovery time NOT exceeded" }, +/*#endif */ + {0x0080, "Recovery time exceeded" } +}; + +GFA_msg Pumpxmsg0100[] = { +/*#ifdef MIDEBUG */ + {0x0000, "NO inrush of air" }, +/*#endif */ + {0x0100, "Inrush of air" } +}; + +GFA_msg Pumpxmsg0200[] = { +/*#ifdef MIDEBUG */ + {0x0000, "TPG300 OK" }, +/*#endif */ + {0x0200, "TPG300 failure" } +}; + +GFA_msg Pumpxmsg0800[] = { +/*#ifdef MIDEBUG */ + {0x0000, "GP measuring cell OK" }, +/*#endif */ + {0x0800, "GP measuring cell failure" } +}; + +GFA_msg Pumpxmsg1000[] = { +/*#ifdef MIDEBUG */ + {0x0000, "GXt measuring cell OK" }, +/*#endif */ + {0x0001, "GXt measuring cell failure" } +}; + +GFA_mask Pumpstax[] = { + { 0x0001, "Pump station status", Pumpxmsg0001, sizeof(Pumpxmsg0001)/sizeof(GFA_msg)}, +/* 0x0002, 0x0004 not specified */ + { 0x0008, "TCS status", Pumpxmsg0008, sizeof(Pumpxmsg0008)/sizeof(GFA_msg)}, + { 0x0010, "Turbopump status", Pumpxmsg0010, sizeof(Pumpxmsg0010)/sizeof(GFA_msg)}, +/* 0x0020 not specified */ + { 0x0040, "Start time", Pumpxmsg0040, sizeof(Pumpxmsg0040)/sizeof(GFA_msg)}, + { 0x0080, "Recovery time", Pumpxmsg0080, sizeof(Pumpxmsg0080)/sizeof(GFA_msg)}, + { 0x0100, "Vacuum status", Pumpxmsg0100, sizeof(Pumpxmsg0100)/sizeof(GFA_msg)}, + { 0x0200, "Pump station", Pumpxmsg0200, sizeof(Pumpxmsg0200)/sizeof(GFA_msg)}, +/* 0x0400 not specified */ + { 0x0800, "GP status", Pumpxmsg0800, sizeof(Pumpxmsg0800)/sizeof(GFA_msg)}, + { 0x1000, "GXt status", Pumpxmsg1000, sizeof(Pumpxmsg1000)/sizeof(GFA_msg)}, +}; + +/* ------------------------ Pump system :STA:1 messages ---------------------- */ +GFA_msg Pumpmsg0003[] = { + {0x0000, "HP UNDEFINED" }, + {0x0001, "HP On" }, + {0x0002, "HP Off" } +}; + +GFA_msg Pumpmsg0004[] = { +/*#ifdef MIDEBUG */ + {0x0000, "NOT HP starting period" }, +/*#endif */ + {0x0004, "HP starting period" } +}; + +GFA_msg Pumpmsg0008[] = { +/*#ifdef MIDEBUG*/ + {0x0000, "HP revolution speed < 80%" }, +/*#endif*/ + {0x0008, "HP revolution speed >= 80%" } +}; + +GFA_msg Pumpmsg0010[] = { +/*#ifdef MIDEBUG */ + {0x0000, "HV NOT locked" }, +/*#endif */ + {0x0010, "HV locked" } +}; + +GFA_msg Pumpmsg0060[] = { + {0x0000, "HV state UNDEFINED" }, + {0x0020, "HV closed" }, + {0x0040, "HV open" } +}; + +GFA_msg Pumpmsg0180[] = { + {0x0000, "Buffer valve state UNDEFINED" }, + {0x0080, "Buffer valve closed" }, + {0x0100, "Buffer valve open" } +}; + +GFA_msg Pumpmsg0600[] = { + {0x0000, "VP state UNDEFINED" }, + {0x0200, "VP closed" }, + {0x0400, "VP open" } +}; + +GFA_msg PumpmsgB800[] = { + {0x0000, "GXt Status UNDEFINED" }, + {0x0800, "GXt > 1mbar (Water)" }, + {0x1000, "GXt > 10e-3 mbar (Pre)" }, + {0x2000, "GXt < 10e-3 mbar (hoch)" }, + /* 0x4000 Not specified */ + {0x8000, "GXt < 10e-2 mbar (OK)" } +}; + +GFA_mask Pumpsta[] = { + { 0x0003, "HP State", Pumpmsg0003, sizeof(Pumpmsg0003)/sizeof(GFA_msg)}, + { 0x000C, "HP Status1", Pumpmsg0004, sizeof(Pumpmsg0004)/sizeof(GFA_msg)}, + { 0x000C, "HP Status2", Pumpmsg0008, sizeof(Pumpmsg0008)/sizeof(GFA_msg)}, + { 0x0010, "HV Status", Pumpmsg0010, sizeof(Pumpmsg0010)/sizeof(GFA_msg)}, + { 0x0060, "HV State", Pumpmsg0060, sizeof(Pumpmsg0060)/sizeof(GFA_msg)}, + { 0x0180, "Buffer valve", Pumpmsg0180, sizeof(Pumpmsg0180)/sizeof(GFA_msg)}, + { 0x0600, "VP State", Pumpmsg0600, sizeof(Pumpmsg0600)/sizeof(GFA_msg)}, + { 0xB800, "GXt Status", PumpmsgB800, sizeof(PumpmsgB800)/sizeof(GFA_msg)} +}; + +/* ------------------------ Pump system list of channels --------------------- */ +GFA_channel Pumpchan[] = { +/*NIY{":CPUM:2", "Pump station Command",Pstcmd, sizeof(Pstcmd )/sizeof(GFA_mask),TRUE }, */ +/*NIY{":CHV:2", "Pump Valve Command", Pvcmd, sizeof(Pvcmd )/sizeof(GFA_mask),TRUE }, */ + {":IST:1", "Actual main vacuum", NULL, 0, FALSE}, + {":STA:1", "Status", Pumpsta, sizeof(Pumpsta )/sizeof(GFA_mask),FALSE}, + {":STAX:1","Extended status", Pumpstax,sizeof(Pumpstax)/sizeof(GFA_mask),FALSE} +}; + +/* ------------------------ Gauge system ------------------------------------------ */ + +/* NOTE: Not tested yet derived from ACS --> check channel names */ + +/* ------------------------ Gauge system :STA:1 messages -------------------- */ +GFA_msg Gstamsg0FF[] = { + {0x0000, "Pressure > 1mbar or UNDEFINED" }, + {0x0001, "Pressure < 1 mbar" }, + {0x0002, "Pressure < 10e-1 mbar" }, + {0x0004, "Pressure < 10e-2 mbar" }, + {0x0008, "Pressure < 10e-3 mbar" }, + {0x0010, "Pressure < 10e-4 mbar" }, + {0x0020, "Pressure < 10e-5 mbar" }, + {0x0040, "Pressure < 10e-6 mbar" }, + {0x0080, "Pressure < 10e-7 mbar" } +}; + +GFA_msg Gstamsg100[] = { + {0x0000, "Interlock" }, + {0x0100, "OK, No Interlock" } +}; + +GFA_mask Gaugsta[] = { + { 0x00FF, "Gauge pressure", Gstamsg0FF, sizeof(Gstamsg0FF)/sizeof(GFA_msg)}, + { 0x0100, "Interlock Status", Gstamsg100, sizeof(Gstamsg100)/sizeof(GFA_msg)} +}; + +/* ------------------------ Gauge system list of channels -------------------- */ +GFA_channel Gaugechan[] = { + {":IST:1", "Actual main vacuum", NULL, 0, FALSE}, + {":ISTH:1","Actual high-vacuum", NULL, 0, FALSE}, + {":ISTV:1","Actual fore-vacuum", NULL, 0, FALSE}, +/* {":STAX:1","Extended status", Gaugstax, sizeof(Gaugstax)/sizeof(GFA_mask),FALSE}, */ + {":STA:1", "Status", Gaugsta, sizeof(Gaugsta )/sizeof(GFA_mask),FALSE} +}; + +/* ------------------------ SR/Sep HV system :STA:2 messages ---------------- */ +GFA_msg HVPSstamsg[] = { + {0x0000, "OFF" }, + {0x0001, "ON" } +}; + +GFA_mask HVPSsta[] = { + { 0x0001, "Status", HVPSstamsg, sizeof(HVPSstamsg)/sizeof(GFA_msg)} +}; + +/* ------------------------ Spin rotator/Separator HV list of channels ------ */ +GFA_channel HVPSchan[] = { + /* NOTE: HV:, HVN: and HVP: are end part of channel name */ + {"HVN:STA:2", "Neg. HV Status", HVPSsta, sizeof(HVPSsta)/sizeof(GFA_mask), FALSE}, + {"HVN:ISTI:2","Neg. HV Current Measured [uA]", NULL, 0, FALSE}, + {"HVN:ISTV:2","Neg. HV Voltage Measured [kV]", NULL, 0, FALSE}, + {"HVN:SOLI:2","Neg. HV Current Demand [uA]", NULL, 0, TRUE }, + {"HVN:SOLV:2","Neg. HV Voltage Demand [kV]", NULL, 0, TRUE }, + {"HVP:STA:2", "Pos. HV Status", HVPSsta, sizeof(HVPSsta)/sizeof(GFA_mask), FALSE}, + {"HVP:ISTI:2","Pos. HV Current Measured [uA]", NULL, 0, FALSE}, + {"HVP:ISTV:2","Pos. HV Voltage Measured [kV]", NULL, 0, FALSE}, + {"HVP:SOLI:2","Pos. HV Current Demand [uA]", NULL, 0, TRUE }, + {"HVP:SOLV:2","Pos. HV Voltage Demand [kV]", NULL, 0, TRUE }, + {"HV:COM:2", "HV IDLE=0/ON=1/OFF=2/SET=3", NULL, 0, TRUE }, + /* NOTE: No HV:STA:2 channel - (dummy) Feedback in HV:COM:2 -> Use F flag */ + {"HV:XRAY:2", "Xray ", NULL, 0, FALSE}, + {"HV:VAC:2", "Vacuum", NULL, 0, FALSE}, + {"HV:VAC2:2", "Vacuum", NULL, 0, FALSE}, + {"HV:TIME:2", "TimeStamp of SoftIOC", NULL, 0, FALSE} +}; + +/* ------------------------ Single scaler channel -------------------- */ +GFA_channel SCALchan[] = { + {"CNT:INP", "Single scaler", NULL, 0, FALSE}, + {"MHC4:IST:2", "Single scaler", NULL, 0, FALSE} +}; + +/* ------------------------ List of all (used) GFA types ------------------------ */ + /* + * Following types are replaced by GFA types specified below + * DEGR -> FENTE + * CS_DEGR -> FENTE + * CS_FENT -> FENTE + * CS_BX -> BX + * CS_KV -> BX since 04-APR-2023 + * CS_PS -> PS + * MA_BEND -> BEND + * MA_QUAD -> QUAD + * MA_STER -> STER + * MA_SEP -> SEPT or PS? + * MA_SEXT -> SEXT + * MA_SOLE -> SOLE + * MA_SPIN -> PS + * MA_KICK -> KICKER + * MA_TR -> TS + * TR -> TS + * VA_VALVE -> VALVE + * VA_PUMP -> PUMP + * VA_GAUGE -> GAUGE + * DI_DIAG -> DIAG + * DI_MESTRA -> DIAG + */ +GFA_type gfatype[] = { + /* for these GFA types KombiPS1 or MultiIO devices are used */ + { "BEND", "Bending magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "QUAD", "Quadrupol magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "SEPT", "Septum magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "STER", "Steering magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "PS", "Spinrot magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + /* NOTE: PS is also used for beamline controls power supplies */ + { "SOLE", "Solenoid", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "TS", "Shim magnet", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + { "KICKER", "Kicker HV", KPS1chan, sizeof(KPS1chan )/sizeof(GFA_channel)}, + + { "BX", "Beam Blocker", KVchan, sizeof(KVchan )/sizeof(GFA_channel)}, + { "FENTE", "Slit system", Slitchan, sizeof(Slitchan )/sizeof(GFA_channel)}, +/* NIY "SEXT", "Sextupol magnet", currently not used */ + { "VALVE", "Valve", Valvechan, sizeof(Valvechan)/sizeof(GFA_channel)}, + { "PUMP", "Pump", Pumpchan, sizeof(Pumpchan )/sizeof(GFA_channel)}, + { "GAUGE", "Gauge", Gaugechan, sizeof(Gaugechan)/sizeof(GFA_channel)}, + { "HVPS", "Spinrot/Sep HV", HVPSchan, sizeof(HVPSchan )/sizeof(GFA_channel)}, + { "DIAG", "Scaler Channel", SCALchan, sizeof(SCALchan )/sizeof(GFA_channel)}, +/* NIY other devices */ + { "", "Terminator", NULL, 0}, +}; + +#define N_GFA_TYPES (sizeof(gfatype)/sizeof(GFA_type)) -1 + +#endif /* ifdef GFA_SPECIFIC */ + +/*---- functions ---------------------------------------------------*/ + +INT epics_ca (INT cmd, ...); +INT epics_ca_init (HNDLE , CA_INFO **, INT); +INT epics_ca_exit (CA_INFO *); +INT epics_ca_set (CA_INFO *, INT, float, BOOL, BOOL); +INT epics_ca_set_all (CA_INFO *, INT, float); +INT epics_ca_set_label (CA_INFO *, INT, char *); +INT epics_ca_get (CA_INFO *, INT, float *); +INT epics_ca_get_demand(CA_INFO *, INT, float *); +INT epics_ca_get_all (CA_INFO *, INT, float *); + +INT epics_ca_set_pending (CA_INFO *, BOOL); + +INT epics_ca_get_default_name (CA_INFO *, INT, char *); +INT epics_ca_get_default_threshold(CA_INFO *, INT, float *); + +#ifdef GFA_SPECIFIC +INT show_gfa_changes(CA_INFO *, INT, BOOL, float, float , char *); +#endif + +char *castatestr( enum channel_state); +char *caoperstr( long ); +void epics_ca_exception_handler ( struct exception_handler_args ); +void epics_ca_event_handler ( struct event_handler_args ); +void epics_ca_connection_handler( struct connection_handler_args ); + +#ifdef PREEMPTIVE +void *poll_thread(void *param); +#endif + +/*----------------------------------------------------------------------------*/ +// return pointer to global message string of CA state +char *castatestr(enum channel_state castate) { + + switch(castate) { + case cs_never_conn: + strcpy(&smsg[0],"Server not found or unavailable"); + break; + + case cs_prev_conn: + strcpy(&smsg[0],"Previously connected to server"); + break; + + case cs_conn: + strcpy(&smsg[0],"Connected to server"); + break; + + case cs_closed: + strcpy(&smsg[0],"Channel deleted by user"); + break; + + default: + strcpy(&smsg[0],"**UNKNOWN STATE**"); + } + return &smsg[0]; +} + +/*----------------------------------------------------------------------------*/ + + // return pointer to global message string of CA operation +char *caoperstr(long op) { + + switch(op) { + case CA_OP_GET: + strcpy(&smsg[0],"CA_OP_GET"); + break; + + case CA_OP_PUT: + strcpy(&smsg[0],"CA_OP_PUT"); + break; + + case CA_OP_CREATE_CHANNEL: + strcpy(&smsg[0],"CA_OP_CREATE_CHANNEL"); + break; + + case CA_OP_ADD_EVENT: + strcpy(&smsg[0],"CA_OP_ADD_EVENT"); + break; + + case CA_OP_CLEAR_EVENT: + strcpy(&smsg[0],"CA_OP_CLEAR_EVENT"); + break; + + case CA_OP_OTHER: + strcpy(&smsg[0],"CA_OP_OTHER"); + break; + + case CA_OP_CONN_UP: + strcpy(&smsg[0],"CA_OP_CONN_UP"); + break; + + case CA_OP_CONN_DOWN: + strcpy(&smsg[0],"CA_OP_CONN_DOWN"); + break; + + default: + strcpy(&smsg[0],"**UNKNOWN OPERATION**"); + } + return &smsg[0]; +} + +/*----------------------------------------------------------------------------*/ + // process epics channel information and channel readout + // previously handled in exception, connection or event handler callback routine +void epics_ca_process(CA_INFO *info) { + +#ifndef PREEMPTIVE + ca_poll(); +#endif + +#ifdef PREEMPTIVE + epicsMutexLock(process_mutex); +#endif + if (in_ca_process) { // avoid reentrant call +#ifdef PREEMPTIVE + epicsMutexUnlock(process_mutex); +#endif + return; + } else + in_ca_process = TRUE; +#ifdef PREEMPTIVE + epicsMutexUnlock(process_mutex); +#endif + + // copy critical section objects to main thread only variables +#ifdef PREEMPTIVE + epicsMutexLock(exception_mutex); +#endif + if (xhandler_info[0] != '\0') { + strncpy(xhandler_info_m, xhandler_info,sizeof(xhandler_info_m)-1); + xhandler_info[0] = '\0'; + xhandler_info_m[sizeof(xhandler_info_m)-1] = '\0'; + } +#ifdef PREEMPTIVE + epicsMutexUnlock(exception_mutex); +#endif + +#ifdef PREEMPTIVE + epicsMutexLock(connection_mutex); +#endif + if (chandler_info[0] != '\0') { + strncpy(chandler_info_m, chandler_info, sizeof(chandler_info_m)-1); + chandler_info[0] = '\0'; + chandler_info_m[sizeof(chandler_info_m)-1] = '\0'; + } + if (chandler_error[0] != '\0') { + strncpy(chandler_error_m, chandler_error,sizeof(chandler_error_m)-1); + chandler_error[0] = '\0'; + chandler_error_m[sizeof(chandler_error_m)-1] = '\0'; + } + if (chandler_found[0] != '\0') { + strncpy(chandler_found_m, chandler_found,sizeof(chandler_found_m)-1); + chandler_found[0] = '\0'; + chandler_found_m[sizeof(chandler_found_m)-1] = '\0'; + } +#ifdef PREEMPTIVE + epicsMutexUnlock(connection_mutex); +#endif + + // start up error of poll thread + if (poll_thread_error[0] != '\0') { + cm_msg(MERROR, "poll_thread", poll_thread_error); + cm_msg_flush_buffer(); + poll_thread_error[0] = '\0'; + } + + // exception handler info + if (xhandler_info_m[0] != '\0') { + cm_msg(MLOG,"",xhandler_info_m); + cm_msg_flush_buffer(); + xhandler_info_m[0] = '\0'; + } + + // channel handler info, not found, error + if (chandler_info_m[0] != '\0') { + cm_msg(MLOG,"",chandler_info_m); + cm_msg_flush_buffer(); + chandler_info_m[0] = '\0'; + } + + if (chandler_error_m[0] != '\0') { + cm_msg(MERROR,"epics_ca_connection_handler",chandler_error_m); + cm_msg_flush_buffer(); + chandler_error_m[0] = '\0'; + } + + if (chandler_found_m[0] != '\0') { + cm_msg(MLOG,"",chandler_found_m); + cm_msg_flush_buffer(); + chandler_found_m[0] = '\0'; + } + + if (process_event) { + int i; + BOOL found; + + found = FALSE; + + // handle critical section objects +#ifdef PREEMPTIVE + epicsMutexLock(event_mutex); +#endif + if (ehandler_info[0] != '\0') { + strncpy(ehandler_info_m, ehandler_info,sizeof(ehandler_info_m)-1); + ehandler_info[0] = '\0'; + ehandler_info_m[sizeof(ehandler_info_m)-1] = '\0'; + } + if (ehandler_error[0] != '\0') { + strncpy(ehandler_error_m, ehandler_error,sizeof(ehandler_error_m)-1); + ehandler_error[0] = '\0'; + ehandler_error_m[sizeof(ehandler_error_m)-1] = '\0'; + } + + for (i=0;inum_channels;i++) { // process all channels + if (*(info->found_new+i)) { + *(info->found_new_m+i) = TRUE; // set found flag and copy information to mirror + *(info->array_new_m+i) = *(info->array_new+i); + *(info->status_new_m+i) = *(info->status_new+i); + *(info->found_new+i) = FALSE; // reset found flag + found = TRUE; + } + } +#ifdef PREEMPTIVE + epicsMutexUnlock(event_mutex); +#endif + + // channel handler error or info + if (ehandler_error_m[0] != '\0') { + cm_msg(MERROR,"epics_ca_event_handler",ehandler_error_m); + cm_msg_flush_buffer(); + ehandler_error_m[0] = '\0'; + } + + if (ehandler_info_m[0] != '\0') { + cm_msg(MLOG,"",ehandler_info_m); + cm_msg_flush_buffer(); + ehandler_info_m[0] = '\0'; + } + + if (found) { // at least one channel found + for (i=0;inum_channels;i++) { // process all channels + if (*(info->found_new_m+i)) { // channel found by event handler + if (*(info->subscribed+i) != TRUE) { + *(info->subscribed+i) = TRUE; + cm_msg(MLOG,"","epics_ca_event_handler: Subscribed flag not yet set for " + "channel %d(%s)",i,info->settings.channel_names+CHN_NAME_LENGTH*i); + cm_msg_flush_buffer(); + } + + if (*(info->down+i)) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","epics_ca_event_handler: channel %d(%s) Marked DOWN. Now " + "set to NOT DOWN",i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = FALSE; /* mark channel to be NOT down */ + cm_msg(MLOG,"","Channel %d(%s,%s) ID (%p) RECONNECTED",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(*(info->pchid+i)), + *(info->pchid+i)); + cm_msg_flush_buffer(); + } + + if ((*(info->status_new_m+i) != ECA_NORMAL) && + (*(info->status+i) != *(info->status_new_m+i))) { + cm_msg(MLOG,"","epics_ca_event_handler: WARNING Status %d = \"%s\" for " + "channel %d(%s) ID (%p)", *(info->status_new_m+i), + ca_message(*(info->status_new_m+i)), i, + info->settings.channel_names+CHN_NAME_LENGTH*i, *(info->pchid+i)); + cm_msg_flush_buffer(); + } + *(info->status+i) = *(info->status_new_m+i); + + if (info->array) { + *(info->lastget+i) = ss_time(); + if (*(info->array+i) != *(info->array_new_m+i)) { /* value changed */ +#ifdef GFA_SPECIFIC + // RA36 01-DEC-2020 omit annoying messages WEDL:STA:1 0x000a <> 0x0002 + if ((strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "WEDL:STA:1", CHN_NAME_LENGTH) == 0) && + (((int)*(info->array+i) & 0xfff0) == + ((int)*(info->array_new_m+i) & 0xfff0) ) && + (((int)*(info->array+i) & 0x000f) == 0x000a) && + (((int)*(info->array_new_m+i) & 0x000f) == 0x0002)) { + // do update to previous value + *(info->array_new_m+i) = *(info->array+i); + } else { + show_gfa_changes(info,i,*(info->first+i),*(info->array+i), + *(info->array_new_m+i),"EH"); + } +#endif /* #ifdef GFA_SPECIFIC */ + /* First reading or after connection was down */ + if (*(info->first+i) || *(info->firstread+i)) { + + if (*(info->first+i)) { + cm_msg(MLOG,"","EH: Channel %d(%s) first reading %f",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->array_new_m+i)); + cm_msg_flush_buffer(); + *(info->first+i) = FALSE; + } + + if (*(info->firstread+i)) { + if (*(info->array+i) != *(info->array_new_m+i)) { + cm_msg(MLOG,"","EH: Channel %d(%s) %f now %f",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->array+i),*(info->array_new_m+i)); + cm_msg_flush_buffer(); + } + + /* R-flag is set */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'R')) { + /* demand value and readout of this channel are different + * send demand again */ + if (*(info->demand+i) != *(info->array_new_m+i)) { + cm_msg(MLOG,"","Channel %d(%s) UP again. Demand may not have " + "been set and R(eset) flag set -> Setting demand %f again", + i, info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->demand+i)); + cm_msg_flush_buffer(); + /* for this channel and dependent channels when PNZV flags set + * force setting demand value (again) */ + epics_ca_set(info,i,*(info->demand+i),FALSE,TRUE); + } + } + *(info->firstread+i) = FALSE; + } + } + *(info->array+i) = *(info->array_new_m+i); + } else { + *(info->first+i) = FALSE; + *(info->firstread+i) = FALSE; + } + } + *(info->found_new_m+i) = FALSE; // reset + } + } + } + } +#ifdef PREEMPTIVE + epicsMutexLock(process_mutex); +#endif + in_ca_process = FALSE; +#ifdef PREEMPTIVE + epicsMutexUnlock(process_mutex); +#endif + +} + +/*----------------------------------------------------------------------------*/ + +#ifdef PREEMPTIVE +void *poll_thread(void *param) { + + if (cacontext) { + int status; + + if ((status=ca_attach_context(cacontext))==ECA_NORMAL) { + do { + //ca_poll(); // when using ca_poll thread will consume 100% CPU time + //ca_pend_event(0.000001); // this does not reduce CPU time +#ifdef SLOW_THREAD + ca_pend_event( 0.1); // this reduces CPU time drastically to < 0.3% +#else + ca_pend_event( 0.00001); // this reduces CPU time to 13% +#endif +#ifdef SINGLE_PROC + // nothing to do, give processor to others - maybe main thread +#ifdef OS_UNIX + sched_yield(); +#else + NIY for OS +#endif +#endif + } while (pollthreadkeeprunning == 1); + // ca_detach_context(); DOES NOT EXIST! + } else { + snprintf(poll_thread_error,256,"ERROR ca_attach_context() returned status %d (%s)", + status, ca_message(status)); + } + } else { + snprintf(poll_thread_error,256,"ERROR NIL pointer to CA context"); + } + + // exit thread + pthread_exit(NULL); +} +#endif /* PREEMPTIVE */ + +/*----------------------------------------------------------------------------*/ +/* + * epics_ca_set_pending : + * BOOL pendig + * TRUE increment info->pending blocking in range (tolerance) checks + * until all pending operations accomplished + * FALSE decrement info->pending. in range (tolerance) checks will + * be evaluated when all dependecies are released + */ +INT epics_ca_set_pending (CA_INFO *info, BOOL pending) { + INT status; + + status = FE_SUCCESS; + if (info) { + if (pending) { + if (info->pending == 0) { + cm_msg(MLOG,"","*** Blocking Tolerance InRange checking"); + cm_msg_flush_buffer(); + } + info->pending++; + } else { + if (info->pending > 0) { + info->pending--; + if (info->pending == 0) { + cm_msg(MLOG,"","*** Tolerance InRange checking not blocked anymore"); + cm_msg_flush_buffer(); + } + } else { + cm_msg(MERROR,"epics_ca_set_pending","PROGRAMMING ERROR releasing pending " + "AND info->pending == 0"); + } + } + } + return status; +} +/*----------------------------------------------------------------------------*/ + +/* exception handler callback */ + +void epics_ca_exception_handler( struct exception_handler_args eargs) { + char buf[512]; + char *pname; + + if (!process_exception) return; /* do not process exception information */ + +#ifdef PREEMPTIVE + epicsMutexLock(exception_mutex); +#endif + +#ifdef MIDEBUGFX + if (fpx) fprintf(fpx,"%u : X ++epics_ca_exception_handler()\n", ss_time()); +#endif + + if (eargs.chid) { + snprintf(buf,512,"%s - chan=%s op=%ld (\"%s\"), data_type = \"%s\", count=%ld", + eargs.ctx, ca_name(eargs.chid), eargs.op, caoperstr(eargs.op), + dbr_type_to_text(eargs.type), eargs.count); + } else { + snprintf(buf,512,"%s - eargs.chid=0 op=%ld (\"%s\")", eargs.ctx, eargs.op, + caoperstr(eargs.op)); + } + + ca_signal(eargs.stat, buf); + + snprintf(xhandler_info,256,"epics_ca_exception_handler: Status: %ld EXCEPTION: %s", + eargs.stat, buf); +#ifdef MIDEBUGFX + if (fpx) { + fprintf(fpx,"Status: %d EXCEPTION: %s\n", eargs.stat, buf); + fprintf(fpx,"%u : X --epics_ca_exception_handler()\n", ss_time()); + fflush(fpx); + } +#endif + +#ifdef PREEMPTIVE + epicsMutexUnlock(exception_mutex); +#endif + +} + +/*----------------------------------------------------------------------------*/ + +/* connection handler callback */ + +void epics_ca_connection_handler( struct connection_handler_args cargs) { + + if (!process_connection) return; /* do not process connection information */ + +#ifdef PREEMPTIVE + epicsMutexLock(connection_mutex); +#endif + +#ifdef MIDEBUGFC + if (fpc) fprintf(fpc,"%u : C ++epics_ca_connection_handler()\n", ss_time()); +#endif + + if (cargs.chid) { + CA_INFO *pinfo; + int i,status; + + pinfo = (CA_INFO *) ca_puser(cargs.chid); + if (pinfo) { + BOOL found; + + /* search channel index */ + for (found = FALSE, i = 0; i < pinfo->num_channels; i++) { + if (*(pinfo->pchid+i) == cargs.chid) { /* matching channel ID? */ + + if (cargs.op == CA_OP_CONN_UP) { + if (*(pinfo->subscribed+i)) { +#ifdef MIDEBUG + snprintf(chandler_info,256,"Channel %d(%s,%s) ID (%p) RECONNECTED",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(cargs.chid), + cargs.chid); +#endif +#ifdef MIDEBUGFC + if (fpc) fprintf(fpc,"%u : C Channel %d(%s,%s) ID (%p) RECONNECTED\n", + ss_time(),i,pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(cargs.chid),cargs.chid); +#endif + if (!*(pinfo->found+i)) { + *(pinfo->found+i) = TRUE; + snprintf(chandler_found,256,"epics_ca_connection_handler: found flag " + "of channel %d(%s) was not set yet",i,ca_name(cargs.chid)); + } + } else { +#ifdef MIDEBUG1 + snprintf(chandler_info,256,"Channel %d(%s,%s) ID %p CONNECTED",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(cargs.chid), + cargs.chid); +#endif +#ifdef MIDEBUGFC + if (fpc) fprintf(fpc,"%u : C Channel %d(%s,%s) ID (%p) CONNECTED\n", + ss_time(), i, pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(cargs.chid), cargs.chid); +#endif + /* chan specific: omit channels with S-flag e.g.: returning strings + * and :COM:2 and :CSV:2 channels + */ + if ((strchr(pinfo->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL)&& + (strstr(pinfo->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2")==NULL)&& + (strstr(pinfo->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2")==NULL)){ + if (!pinfo->subscribe_disabled) { +#ifdef MIDEBUG + snprintf(chandler_info,256,"Channel %d(%s,%s) ID (%p) CONNECTED",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(cargs.chid), + cargs.chid); +#endif + /* subscribe event handler callback for this channel when not created + * subscribed value is converted to float + */ +#ifdef SUBSCRIBE_ENABLED + status = ca_create_subscription(DBR_FLOAT,1,*(pinfo->pchid+i),DBE_LOG| + DBE_VALUE, epics_ca_event_handler, pinfo, NULL); + if (!(status & ECA_NORMAL)) { + snprintf(chandler_error,256,"Channel %d(%s): " + "ca_create_subscription() returned status 0x%X = \"%s\"",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,status, + ca_message(status)); + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d(%s): CA event handler (float) will be " + "installed",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,status); + cm_msg_flush_buffer(); +#endif + *(pinfo->subscribed+i) = TRUE; + /* NOTE: ca_pend_io(0.01); may not be called in this event handler */ + status = ca_flush_io(); + if (status != ECA_NORMAL) + snprintf(chandler_error, 256, "ca_flush_io() returned %d",status); + } +#else + snprintf(chandler_info,256,"Channel %d(%s,%s) ID (0X%lx) CONNECTED " + "(subscription disabled)", i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(cargs.chid),(unsigned long int)cargs.chid); +#endif /* SUBSCRIBE_ENABLED */ + } else { +#ifdef MIDEBUG + snprintf(chandler_info,256,"Channel %d(%s,%s) ID (%p) CONNECTED " + "(subscription currently disabled)", i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(cargs.chid), + cargs.chid); +#endif + } + } else { + if (strchr(pinfo->settings.chnflags+CHNFLAGS_LENGTH*i,'S')) + snprintf(chandler_info,256,"Channel %d(%s): S flag: CA event handler " + "will NOT be installed",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i); +#ifdef MIDEBUG + else if (strstr(pinfo->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2")) + snprintf(chandler_info,256,"Channel %d(%s): COM Channel : CA event " + "handler will NOT be installed",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i); + else if (strstr(pinfo->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2")) + snprintf(chandler_info,256,"Channel %d(%s): CSV Channel : CA event " + "handler will NOT be installed",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(pinfo->subscribed+i) = TRUE; /* mark as handled */ + } + *(pinfo->found+i) = TRUE; /* mark channel as found */ + } + /* if (strchr(pinfo->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(pinfo->down+i) = FALSE; /* mark channel to be NOT down */ + } else { + if (!*(pinfo->down+i)) { + snprintf(chandler_info,256,"Channel %d(%s,%s) ID (%p) is DOWN",i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(cargs.chid), cargs.chid); + /* if (strchr(pinfo->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(pinfo->down+i) = TRUE; /* mark channel to be down */ + } + *(pinfo->firstread+i) = TRUE; /* mark channel for first read after DOWN */ + } + found = TRUE; + break; + } + } /* for all channels */ + + if (!found) + snprintf(chandler_error,256,"Channel %s NOT found in list!",ca_name(cargs.chid)); + } else { + snprintf(chandler_error, 256, "NULL pointer returned by ca_puser()"); + } + } else { + snprintf(chandler_error, 256, "channel id nil returned as argument"); + } + +#ifdef MIDEBUGFC + if (fpc) { + fprintf(fpc,"%u : C --epics_ca_connection_handler()\n", ss_time()); + fflush(fpc); + } +#endif + +#ifdef PREEMPTIVE + epicsMutexUnlock(connection_mutex); +#endif + +} + +/*----------------------------------------------------------------------------*/ +#ifdef GFA_SPECIFIC +// show gfa specific status and command changes +// channel: channel number -> channel name +// firstflag : TRUE present curval as initial value +// FALSE show difference of curval and prevval +// prevval: previously set value +// curval: currently set value +// prefix: prefix prepended to message (e.g. INI or EH) +INT show_gfa_changes(CA_INFO *pinfo, INT channel, BOOL firstflag, float prevval, + float curval, char *prefix) { + INT retval; + + retval = FALSE; + if (pinfo) { + if ((channel >= 0) && (channel < pinfo->num_channels)) { + char lprefix[10]; + char *tpos; + + if (prefix && (strlen(prefix) > 0)) + strncpy(lprefix,prefix,9); + else + strcpy(lprefix,"??"); + lprefix[9] = '\0'; + + /* channel has : */ + if ((tpos=strchr(pinfo->settings.channel_names+CHN_NAME_LENGTH*channel,':')) + != NULL) { + int j; + + for (j = 0; j < N_GFA_TYPES; j++) { + /* find type */ + if (strcmp(gfatype[j].type,pinfo->settings.dtype+DTYPE_LENGTH*channel)==0){ + GFA_channel *tc; + int k; +#ifdef MIDEBUGE + cm_msg(MLOG,"","%s: Found type %s", lprefix, gfatype[j].type); +#endif + tc = gfatype[j].tc; + for (k=0; k < gfatype[j].nc; k++) { /* channels of this type */ + + if (!(tc+k)) continue; + + if (strcmp(tpos,(tc+k)->spec ) == 0) { /* matching : */ + int l; + GFA_mask *cm; +#ifdef MIDEBUGE + cm_msg(MLOG,"","%s: Found spec %s", lprefix, (tc+k)->spec); +#endif + cm = (tc+k)->cm; + retval = TRUE; + for (l = 0; l < (tc+k)->ns; l++) { /* for all masks */ + int m; + int ttval, tarr; + GFA_msg *sm; + + if (!(cm+l)) continue; +#ifdef MIDEBUGE + cm_msg(MLOG,"","%s: Checking Mask 0x%4.4X",lprefix,(cm+l)->mask); +#endif + ttval = (int)curval & (cm+l)->mask; + if (firstflag) + tarr = 0; /* hide setup flag */ + else + tarr = (int) prevval & (cm+l)->mask; + if (( ttval != tarr)||firstflag) { + char *tpost,*tposa; + tpost = tposa = NULL; + + sm = (cm+l)->sm; + for (m = 0; m < (cm+l)->nm; m++) { + if (!(sm+m)) continue; + /* cm_msg(MLOG,"","check Value 0x%4.4X : \"%s\"", */ + /* (sm+m)->value, (sm+m)->msg); */ + if ((sm+m)->value == ttval) { + if (tpost) cm_msg(MLOG,"","%s: New value: %s overwrit" + "ten by %s", lprefix,tpost, (sm+m)->msg); + tpost = (sm+m)->msg; + } + if ((sm+m)->value == tarr) { + if (tposa) cm_msg(MLOG,"","%s: Prev value: %s overwri" + "tten by %s", lprefix, tposa, (sm+m)->msg); + tposa = (sm+m)->msg; + } + } /* for all values */ + + if (tpost) { + if (firstflag) tposa = NULL;/*hide setup */ + if (tposa) { + cm_msg(MLOG,"","%s: %d %s: \"%s\" changed to \"%s\"", lprefix, + channel,pinfo->settings.channel_names+CHN_NAME_LENGTH*channel, + tposa, tpost); + cm_msg_flush_buffer(); + } else { + if (strstr(pinfo->settings.channel_names+ + CHN_NAME_LENGTH*channel,":STA:")) { + cm_msg(MLOG,"","%s: %s: Current status is \"%s\"", lprefix, + pinfo->settings.channel_names+CHN_NAME_LENGTH*channel, + tpost); + cm_msg_flush_buffer(); + } else if (strstr(pinfo->settings.channel_names+ + CHN_NAME_LENGTH*channel,":COM:")) { +#ifdef MIDEBUG + cm_msg(MLOG,"","%s: %s: Current RAW command is \"%s\"", + lprefix, pinfo->settings.channel_names + +CHN_NAME_LENGTH*channel, tpost); +#else + /* DO NOTHING */ +#endif + } else { + cm_msg(MLOG,"","%s: %s: \"%s\" is currently set", lprefix, + pinfo->settings.channel_names+CHN_NAME_LENGTH*channel, + tpost); + cm_msg_flush_buffer(); + } + } + } else { + /* NIY nothing assigned to mask */ + } + } + } /* for all masks */ + break; + } + } /* for all channels of this type */ + break; + } + } /* for all GFA types */ + } + } + } + return retval; + } +#endif /* #ifdef GFA_SPECIFIC */ + +/*----------------------------------------------------------------------------*/ +/* event handler callback called by event subscription or ca_get_callback() */ + +void epics_ca_event_handler(struct event_handler_args args) { + CA_INFO *pinfo; + int i; + BOOL found; + + if (!process_event) return; + +#ifdef PREEMPTIVE + epicsMutexLock(event_mutex); +#endif + +#ifdef MIDEBUGFE + if (fpe) fprintf(fpe,"%u : E ++epics_ca_event_handler(address = %x)\n", ss_time(), + &args); +#endif + + if (args.status != ECA_NORMAL) { + if (args.chid) { + BOOL writemsg; + + writemsg = TRUE; + pinfo = (CA_INFO *) args.usr; + + if (pinfo) { + /* find channel index */ + for (i = 0; i < pinfo->num_channels; i++) { + if (*(pinfo->pchid+i) == args.chid) { /* matching channel ID? */ + if (ehandler_nerr && ehandler_lasterr) { + DWORD nowtime,difftime; + + nowtime = ss_time(); + if (*(ehandler_lasterr+i) > nowtime) { // last error time out of order + *(ehandler_nerr+i) = 0; // reset start of last err interval + *(ehandler_lasterr+i) = nowtime; + } + difftime = nowtime - *(ehandler_lasterr+i); + if (difftime > EPICS_EH_DELTA_TIME_ERROR) { // interval expired? + *(ehandler_nerr+i) = 0; // reset start of last err interval + *(ehandler_lasterr+i) = nowtime; + } + // increment error count for returned status + if (args.status == 152) + *(ehandler_nerr+i) += 1; + + // suppress error message + if (*(ehandler_nerr+i) > EPICS_EH_MAX_ERROR) writemsg = FALSE; + } + break; + } + } + } + + if (writemsg) + snprintf(ehandler_error,256,"EH: Channel %s ID (%p) WARNING Status %d = " + "\"%s\"", ca_name(args.chid),args.chid,args.status, + ca_message(args.status)); + } else + snprintf(ehandler_error,256,"EH: WARNING Status %d = \"%s\"", + args.status, ca_message(args.status)); +#ifdef PREEMPTIVE + epicsMutexUnlock(event_mutex); +#endif + return; + } + + found = FALSE; + + pinfo = (CA_INFO *) args.usr; + + if (pinfo && args.chid) { + /* find channel index */ + for (i = 0; i < pinfo->num_channels; i++) { + if (*(pinfo->pchid+i) == args.chid) { /* matching channel ID? */ + if (args.type == DBR_FLOAT) { /* data type is pure float? */ + float tval; + + if (args.dbr) { + tval = *((float *)args.dbr); +#ifdef MIDEBUGE + snprintf(ehandler_info,256,"callback for channel %d(%s,%s): value %f", i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(args.chid),tval); +#endif +#ifdef MIDEBUGFE + if (fpe) fprintf(fpe,"%u : E callback for channel %d(%s,%s): value %f\n", + ss_time(),i,pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(args.chid),tval); +#endif + *(pinfo->found_new+i) = TRUE; + *(pinfo->status_new+i) = args.status; + *(pinfo->array_new+i) = tval; + + if (strcmp(pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(args.chid)) != 0) { + snprintf(ehandler_info,256,"EH: CHANNEL MISMATCH: Channel %d name %s " + "<> %s by ca_name()",i, pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(args.chid)); + } + + } else { + snprintf(ehandler_error,256,"Channel %d(%s): NULL pointer to " + "data returned as dbr argument!", i, + pinfo->settings.channel_names+CHN_NAME_LENGTH*i); + } + } else { +/*#ifdef MIDEBUG */ + snprintf(ehandler_info,256,"epics_ca_event_handler: channel %d(%s): Unsup" + "ported data type %s", i, pinfo->settings.channel_names+CHN_NAME_LENGTH*i, + dbr_type_to_text(args.type)); +/*#endif*/ + } + found = TRUE; + break; + } + } /* for */ + } else if (!args.chid) { + snprintf(ehandler_info,256,"epics_ca_event_handler: WARNING args.chid is 0!"); + } else if (!pinfo) { + snprintf(ehandler_info,256,"epics_ca_event_handler: WARNING args.usr NULL pointer " + "to pinfo!"); + } + + if (!found) { + if (args.status != ECA_NORMAL) + snprintf(ehandler_error,256,"WARNING Status %d = \"%s\"", args.status, + ca_message(args.status)); + else + snprintf(ehandler_info,256,"epics_ca_event_handler: WARNING args.chid (%p) " + "NOT FOUND!", args.chid); + } + +#ifdef MIDEBUGFE + if (fpe) {fprintf(fpe,"%u : E --epics_ca_event_handler()\n", ss_time()); + fflush(fpe);} +#endif + +#ifdef PREEMPTIVE + epicsMutexUnlock(event_mutex); +#endif +} + +/*---- device driver routines --------------------------------------*/ + +INT epics_ca_init(HNDLE hKey, CA_INFO **pinfo, INT channels) { + int status, i; + HNDLE hDB, subkey; + CA_INFO *info; + enum channel_state castate; + char tpath[MAX_ODB_PATH]; + char *pnames; + double iotimeout; + DWORD wtimeout,wtimeoutn; + BOOL wflag,loaded; + +#ifdef FORTIFY + Fortify_CheckAllMemory(); + Fortify_OutputStatistics(); +#endif + process_exception = FALSE; + process_connection = FALSE; + process_event = FALSE; + in_ca_process = FALSE; + do_callback = FALSE; + +#ifdef PREEMPTIVE + cacontext = NULL; + pollthreadkeeprunning = 0; + process_mutex = NULL; + exception_mutex = NULL; + connection_mutex = NULL; + event_mutex = NULL; +#endif + + poll_thread_error[0] = '\0'; + + xhandler_info[0] = xhandler_info_m[0] = '\0'; + + chandler_info[0] = chandler_info_m[0] = '\0'; + chandler_error[0] = chandler_error_m[0] = '\0'; + chandler_found[0] = chandler_found_m[0] = '\0'; + + ehandler_error[0] = ehandler_error_m[0] = '\0'; + ehandler_info[0] = ehandler_info_m[0] = '\0'; + +#ifdef MIDEBUGF + sprintf(tpath,"EPICS_ca_%d.log",ss_time()); + fp = fopen(tpath,"w"); + if (fp) { + cm_msg(MLOG,"","EPICS debug information will be written to %s",tpath); + cm_msg_flush_buffer(); + fprintf(fp,"%u : ++epics_ca_init(%d)\n",ss_time(),channels); + } +#endif +#ifdef MIDEBUGFX + sprintf(tpath,"EPICS_ca_X_%d.log",ss_time()); + fpx = fopen(tpath,"w"); + if (fpx) { + cm_msg(MLOG,"","EPICS Exception handler debug information will be written to %s", + tpath); + cm_msg_flush_buffer(); + } +#endif +#ifdef MIDEBUGFC + sprintf(tpath,"EPICS_ca_C_%d.log",ss_time()); + fpc = fopen(tpath,"w"); + if (fpc) { + cm_msg(MLOG,"","EPICS Connection handler debug information will be written to %s", + tpath); + cm_msg_flush_buffer(); + } +#endif +#ifdef MIDEBUGFE + sprintf(tpath,"EPICS_ca_E_%d.log",ss_time()); + fpe = fopen(tpath,"w"); + if (fpe) { + cm_msg(MLOG,"","EPICS Event handler debug information will be written to %s",tpath); + cm_msg_flush_buffer(); + } +#endif + +#ifdef MIDEBUG1 + cm_msg(MLOG,"","++epics_ca_init(init %d channels)",channels); +#ifdef MIDEBUGF + cm_msg(MLOG,"","ss_time()'ed Info written to %s",tpath); +#endif + cm_msg_flush_buffer(); +#endif + + pnames = NULL; + loaded = FALSE; + + cm_get_experiment_database(&hDB, NULL); + + /* allocate info structure */ + info = (CA_INFO*)calloc(1, sizeof(CA_INFO)); + *pinfo = info; /* must be done first else core dump on error return */ + + /* check if necessary event variables are defined */ + if (!getenv("EPICS_CA_AUTO_ADDR_LIST") || !getenv("EPICS_CA_ADDR_LIST") || + !getenv("EPICS_CA_SERVER_PORT")) { + if (!getenv("EPICS_CA_AUTO_ADDR_LIST")) + cm_msg(MERROR, "epics_ca_init", + "Please define environment variable 'EPICS_CA_AUTO_ADDR_LIST'"); + if (!getenv("EPICS_CA_ADDR_LIST")) + cm_msg(MERROR, "epics_ca_init", + "Please define environment variable 'EPICS_CA_ADDR_LIST'"); + if (!getenv("EPICS_CA_SERVER_PORT")) + cm_msg(MERROR, "epics_ca_init", + "Please define environment variable 'EPICS_CA_SERVER_PORT'"); + cm_msg_flush_buffer(); + return FE_ERR_HW; + } else { +#ifdef epicsReleaseVersion + // "EPICS R3.14.12.7-DEV-2020-09-15_PSI-3.14.12.7-56-g07c5ad5" + // "EPICS R7.0.6-2021-12-16" + cm_msg(MLOG,"","%s", epicsReleaseVersion); +#endif + cm_msg(MLOG,"","EPICS_CA_AUTO_ADDR_LIST is %s",getenv("EPICS_CA_AUTO_ADDR_LIST")); + cm_msg(MLOG,"","EPICS_CA_ADDR_LIST is %s", getenv("EPICS_CA_ADDR_LIST")); + cm_msg(MLOG,"","EPICS_CA_SERVER_PORT is %s", getenv("EPICS_CA_SERVER_PORT")); + cm_msg_flush_buffer(); + } + + /* NIY check if caRepeater program is available */ + + info->num_channels = channels; + + /* initially disable all commands */ + info->cmd_disabled = 1; + + /* initialize EPICS CA driver */ +#ifdef PREEMPTIVE + status = ca_context_create(ca_enable_preemptive_callback); +#else + status = ca_context_create(ca_disable_preemptive_callback); +#endif + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR, "epics_ca_init", "Unable to initialize! ca_context_create() " + "returned status 0X%x = \"%s\"",status,ca_message(status)); + cm_msg_flush_buffer(); + return FE_ERR_HW; +#ifdef MIDEBUG + } else { + cm_msg(MLOG,"","EPICS ca_context_create(ca_disable_preemptive_callback) called"); + cm_msg_flush_buffer(); +#endif + } + +#ifdef PREEMPTIVE + cacontext = ca_current_context(); + + process_mutex = epicsMutexCreate(); + exception_mutex = epicsMutexCreate(); + connection_mutex = epicsMutexCreate(); + event_mutex = epicsMutexCreate(); + + pollthreadkeeprunning = 1; +#ifdef OS_UNIX + if (pthread_create(&pollthreadid, NULL, (void* (*)(void*))poll_thread, NULL) != 0) { +#else + NIY for OS +#endif + pollthreadid = 0; + cm_msg(MERROR,"epics_ca_init","ERROR not able to start poll_thread"); + } else { + cm_msg(MLOG,"","Poll thread id is %lu", pollthreadid); + cm_msg(MLOG,"","Main thread process ID is %d", getpid()); + } + cm_msg_flush_buffer(); + +#endif /* PREEMPTIVE */ + + status = ca_add_exception_event(epics_ca_exception_handler,0); + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR, "epics_ca_init", "Unable to install global exception handler! " + "ca_add_exception_event() returned status 0X%x = \"%s\"", + status,ca_message(status)); +#ifdef MIDEBUG + } else { + cm_msg(MLOG,"","EPICS ca_add_exception_event() installed CA exception handler"); +#ifdef MIDEBUGCLE + printf("+++ CA client status after ca_add_exception_event() ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_add_exception_event() ******\n"); +#endif +#endif + } + process_exception = TRUE; + +#ifdef RESET_DEMAND_VALUE_TO_RANGE + cm_msg(MLOG,"","Compilation flag RESET_DEMAND_VALUE_TO_RANGE is #define'd"); +#else + cm_msg(MLOG,"","Compilation flag RESET_DEMAND_VALUE_TO_RANGE is NOT #define'd"); +#endif + cm_msg_flush_buffer(); + + /* allocate arrays */ + info->settings.channel_names = (char*)calloc(channels, CHN_NAME_LENGTH); +#ifdef GFA_SPECIFIC + info->settings.dtype = (char*)calloc(channels, DTYPE_LENGTH); + info->settings.dsec = (char*)calloc(channels, DSEC_LENGTH); +#endif + info->settings.chnflags = (char*)calloc(channels, CHNFLAGS_LENGTH); + info->settings.minval = (float*)calloc(channels, sizeof(float)); + info->settings.maxval = (float*)calloc(channels, sizeof(float)); + info->settings.tolerance = (float*)calloc(channels, sizeof(float)); + info->settings.used = (BOOL*)calloc(channels, sizeof(BOOL)); + info->settings.readonly = (BOOL*)calloc(channels, sizeof(BOOL)); + + info->array_new = (float*)calloc(channels, sizeof(float)); + info->array_new_m = (float*)calloc(channels, sizeof(float)); + info->array = (float*)calloc(channels, sizeof(float)); + info->array_m = (float*)calloc(channels, sizeof(float)); + info->demand = (float*)calloc(channels, sizeof(float)); + info->threshold = NULL; + info->lastset = (DWORD*)calloc(channels, sizeof(DWORD)); + info->lastget = (DWORD*)calloc(channels, sizeof(DWORD)); + info->get = (BOOL*)calloc(channels, sizeof(BOOL)); + info->getcalled = (BOOL*)calloc(channels, sizeof(BOOL)); + info->status_new = (int*)calloc(channels, sizeof(int)); + info->status_new_m = (int*)calloc(channels, sizeof(int)); + info->status = (int*)calloc(channels, sizeof(int)); + info->pchid = (chid*)calloc(channels, sizeof(chid)); + info->found_new = (BOOL*)calloc(channels, sizeof(BOOL)); + info->found_new_m = (BOOL*)calloc(channels, sizeof(BOOL)); + info->found = (BOOL*)calloc(channels, sizeof(BOOL)); + info->iscmd = (BOOL*)calloc(channels, sizeof(BOOL)); + info->subscribed = (BOOL*)calloc(channels, sizeof(BOOL)); + info->down = (BOOL*)calloc(channels, sizeof(BOOL)); + info->first = (BOOL*)calloc(channels, sizeof(BOOL)); + info->firstread = (BOOL*)calloc(channels, sizeof(BOOL)); + info->nerr = (INT*)calloc(channels, sizeof(INT)); + info->nmsg = (INT*)calloc(channels, sizeof(INT)); +#ifdef GFA_SPECIFIC + info->tolchan = (INT*)calloc(channels, sizeof(INT)); + info->gchan = (INT*)calloc(channels, sizeof(INT)); + info->intol = (BOOL*)calloc(channels, sizeof(BOOL)); +#endif + info->silentreadonly = FALSE; + + ehandler_nerr = (INT*)calloc(channels, sizeof(INT)); + ehandler_lasterr = (DWORD*)calloc(channels, sizeof(DWORD)); + + /* init arrays */ + for (i = 0; i < channels; i++) { + sprintf(info->settings.channel_names+CHN_NAME_LENGTH*i, "NONE_%d", i); +#ifdef GFA_SPECIFIC + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "**TYPE_NOT_INIT**"); + strcpy(info->settings.dsec+DSEC_LENGTH*i, "**SEC_NOT_INIT**"); +#endif + strcpy(info->settings.chnflags+CHNFLAGS_LENGTH*i, "US"); + *(info->settings.minval+i) = 0.f; + *(info->settings.maxval+i) = 0.f; + *(info->settings.tolerance+i)= 0.f; + /* *(info->settings.used+i) = FALSE; already init'd */ + *(info->settings.readonly+i) = TRUE; + + *(info->array_new+i) = -2.f; + *(info->array_new_m+i) = -2.f; + *(info->array+i) = -2.f; + *(info->array_m+i) = -2.f; + *(info->demand+i) = -2.f; + /* *(info->found_new+i) = FALSE; already init'd */ + /* *(info->found_new_m+i) = FALSE; already init'd */ + /* *(info->found+i) = FALSE; already init'd */ + /* *(info->iscmd+i) = FALSE; already init'd */ + /* *(info->subscribed+i) = FALSE; already init'd */ + *(info->down+i) = TRUE; + *(info->first+i) = TRUE; + /* *(info->firstread+i) = FALSE; already init'd */ + /* *(info->nerr+i) = 0; already init'd */ + /* *(info->nmsg+i) = 0; already init'd */ +#ifdef GFA_SPECIFIC + *(info->tolchan+i) = -1; + *(info->gchan+i) = -1; + *(info->intol+i) = TRUE; +#endif + /* *(info->pchid+i) = 0; already init'd */ + *(info->lastset+i) = ss_time(); + /* *(info->lastget+i) = 0; already init'd */ + /* *(info->get+i) = FALSE; already init'd */ + /* *(info->getcalled+i) = FALSE; already init'd */ + *(info->status_new+i) = ECA_NORMAL; + *(info->status_new_m+i)= ECA_NORMAL; + *(info->status+i) = ECA_NORMAL; + /* *(ehandler_nerr+i) = 0; already init'd */ + *(ehandler_lasterr+i) = ss_time(); + } + + /* remember if DD/Loadfile is already present */ + status = db_find_key(hDB, hKey, "DD/Loadfile", &subkey); + + strcpy(info->settings.loadfile,"NONE"); + db_merge_data(hDB, hKey, "DD/Loadfile", info->settings.loadfile, + NAME_LENGTH, 1, TID_STRING); + /* DD/Loadfile was not in ODB -> stop here user may now set loadfile name */ + if (status == DB_NO_KEY) { + cm_msg(MLOG,"","Created \"DD/Loadfile\" in ODB -> set name of loadfile and restart" + " to load initial setup from local loadfile"); + cm_msg(MLOG,"","Or just restart and do manual setup in ODB"); + cm_msg_flush_buffer(); + return FE_ERR_HW; + } + + if (strlen(info->settings.loadfile) >= NAME_LENGTH) { + cm_msg(MLOG,"","EPICSGFA/DD/Loadfile Name length is equals or larger than %d! " + "Name will be truncated!",NAME_LENGTH); + cm_msg_flush_buffer(); + info->settings.loadfile[NAME_LENGTH-1] = '\0'; + } + + info->settings.Sum_NOT_Connected = 0; + db_merge_data(hDB, hKey, "DD/Sum_NOT_Connected", + &info->settings.Sum_NOT_Connected, sizeof(int), 1, TID_INT); + status = db_find_key(hDB, hKey, "DD/Sum_NOT_Connected", &info->hkeySNC); + if (status == DB_SUCCESS) { + if (info->settings.Sum_NOT_Connected != 0) { + info->settings.Sum_NOT_Connected = 0; + db_set_data(hDB, info->hkeySNC, &info->settings.Sum_NOT_Connected, + sizeof(int), 1, TID_INT); + } + } else { + info->hkeySNC = 0; + } + + info->settings.Sum_NOT_in_Tolerance = 0; + db_merge_data(hDB, hKey, "DD/Sum_NOT_in_Tolerance", + &info->settings.Sum_NOT_in_Tolerance, sizeof(int), 1, TID_INT); + status = db_find_key(hDB, hKey, "DD/Sum_NOT_in_Tolerance", &info->hkeySNT); + if (status == DB_SUCCESS) { + if (info->settings.Sum_NOT_in_Tolerance != 0) { + info->settings.Sum_NOT_in_Tolerance = 0; + db_set_data(hDB,info->hkeySNT,&info->settings.Sum_NOT_in_Tolerance, + sizeof(int),1,TID_INT); + } + } else { + info->hkeySNT = 0; + } + + strcpy(info->settings.AlarmWhenNOTConnected,"NONE"); + db_merge_data(hDB, hKey, "DD/AlarmWhenNOTConnected", + info->settings.AlarmWhenNOTConnected, NAME_LENGTH, 1, TID_STRING); + + strcpy(info->settings.AlarmWhenNOTinTolerance,"NONE"); + db_merge_data(hDB, hKey, "DD/AlarmWhenNOTinTolerance", + info->settings.AlarmWhenNOTinTolerance, NAME_LENGTH, 1, TID_STRING); + + /* read info about EPICS Channel Name, Used, Readonly, Chn flags, Name, Min, Max, + * Threshold from a local file and write to DD if not created yet */ + if (strcmp(info->settings.loadfile,"NONE") != 0){ + + if (db_find_key(hDB, hKey, "DD/EPICS Channel name", &subkey) == DB_NO_KEY) { + FILE *stream; + + i = 0; + if ((stream = fopen(info->settings.loadfile,"r")) != NULL) { + char tline[255]; + char *p,*pn,*po; + int n,k; + + pnames = (char*)calloc(channels, NAME_LENGTH); + info->threshold = (float*)calloc(channels, sizeof(float)); + n = 0; + while (!feof(stream)) { + /* 1 2 3 4 5 6 7 8 + * EPICSCAname[,Y/N,Y/N,flags,MidasName,Min,Max,Threshold,Toler] # comments */ + if (fgets(tline, sizeof(tline), stream) != NULL) { + n++; + if ((p = strchr(tline,'#'))) *p = '\0'; /* remove comments */ + + if (strlen(tline) > 0) { /* make sure last field on line ends with , */ + int j; + for (j=strlen(tline)-1; j >= 0; j--) { + if (!isspace(tline[j])) { /* remove trailing blanks */ + if (tline[j] != ',') {tline[j+1] = ','; tline[j+2] = '\0';} + break; + } + } + } + if ((strlen(tline) > 0)&&(p = strchr(tline,','))) { /* EPICS channel name */ + if (i < channels) { + cm_msg(MLOG,"","Channel %d: Processing Line %d: %s", i, n, tline); + cm_msg_flush_buffer(); + pn = p+1; *p = '\0'; + po = &tline[0]; + + for (k=strlen(po)-1; k >= 0; k--) /* remove trailing blanks */ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + while (isspace(*po)) po++; /* remove leading blanks */ + + if (strlen(po) >= CHN_NAME_LENGTH) *(po+CHN_NAME_LENGTH-1) = '\0'; +#ifdef GFA_SPECIFIC + /* NIY check GFA convention ? */ +#endif + strcpy(info->settings.channel_names+ CHN_NAME_LENGTH*i,po); + + /* take EPICS Channel as Midas Name default (maybe overwritten below) */ + if (strlen(po) >= NAME_LENGTH) *(po+NAME_LENGTH-1) = '\0'; + strcpy(pnames+NAME_LENGTH*i,po); + + if (info->threshold) *(info->threshold+i) = 0.1f; + + if ((p = strchr(pn,','))) { /* used */ + po = pn; pn = p+1; *p = '\0'; + if (strchr(po,'y')||strchr(po,'Y')) *(info->settings.used+i) = TRUE; + + if ((p = strchr(pn,','))) { /* readonly */ + po = pn; pn = p+1; *p = '\0'; + if (strchr(po,'n')||strchr(po,'N')) + *(info->settings.readonly+i)=FALSE; + + if ((p = strchr(pn,','))) { /* channel flags */ + po = pn; pn = p+1; *p = '\0'; + for (k=strlen(po)-1; k >= 0; k--) /* remove trailing blanks */ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + if (strlen(po) >= CHNFLAGS_LENGTH) *(po+CHNFLAGS_LENGTH-1) = '\0'; + if (strlen(po) > 0) { + int k1; + + /* remove blanks */ + for (k=0, k1=0; k < strlen(po); k++) { + *(info->settings.chnflags+ CHNFLAGS_LENGTH*i+k) = '\0'; + if (!isspace(*(po+k))) { +#ifdef GFA_SPECIFIC + /* NIY check flags also with/without GFA_SPECIFIC + * NIY T should only be set for SOL:2 and IST:2 or + * SOL:2 and IST1:2 or POSA:2 or SOL and POSA for FENTE or + * :STA:1 for VALVE or BX or + * HVN and HVP :SOLV:2 and :ISTV:2 for HVPS */ +#else +#endif + /* NIY R should only be set for :SOL: or maybe :COM: */ + *(info->settings.chnflags+ CHNFLAGS_LENGTH*i+k1) = *(po+k); + k1++; + *(info->settings.chnflags+ CHNFLAGS_LENGTH*i+k1) = '\0'; + } + } + } + + if ((p = strchr(pn,','))) { /* Midas Name */ + po = pn; pn = p+1; *p = '\0'; + for (k=strlen(po)-1; k >= 0; k--) /* remove trailing blanks */ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + while (isspace(*po)) po++; /* remove leading blanks */ + if (strlen(po) >= NAME_LENGTH) *(po+NAME_LENGTH-1) = '\0'; + if (strlen(po) > 0) { + strcpy(pnames+NAME_LENGTH*i,po); + } + + if ((p = strchr(pn,','))) { /* Minval */ + po = pn; pn = p+1; *p = '\0'; + for (k=strlen(po)-1; k >= 0; k--) /* remove trailing blanks */ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + if (strlen(po) > 0) { + float tval; + if (sscanf(po,"%f",&tval) == 1) + *(info->settings.minval+i) = tval; + } + + if ((p = strchr(pn,','))) { /* Maxval */ + po = pn; pn = p+1; *p = '\0'; + for (k=strlen(po)-1; k >= 0; k--)/* remove trailing blanks*/ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + if (strlen(po) > 0) { + float tval; + if (sscanf(po,"%f",&tval) == 1) + *(info->settings.maxval+i) = tval; + } + + if ((p = strchr(pn,','))) { /* threshold */ + po = pn; pn = p+1; *p = '\0'; + for (k=strlen(po)-1; k >= 0; k--)/* remove trailng blnks*/ + if (isspace(*(po+k))) *(po+k)='\0'; else break; + if (strlen(po) > 0) { + float tval; + if ((sscanf(po,"%f",&tval) == 1) && info->threshold) + *(info->threshold+i) = tval; + } + + for (k=strlen(pn)-1; k >= 0; k--)/* remove trailng blnks*/ + if (isspace(*(pn+k))) *(pn+k)='\0'; else break; + if (strlen(pn) > 0) { /* rest is tolerance */ + float tval; + if (sscanf(pn,"%f",&tval) == 1) + *(info->settings.tolerance+i) = tval; + } + } + } + } + } + } + } + } + /* NIY check min < max or both 0 */ + /* NIY check threshold > 0 */ +#ifdef GFA_SPECIFIC + /* NIY check tolerance >= 0 when T flag , + * except VALVE && :STA:1 or BX && :STA:1 */ +#endif + } else { + cm_msg(MLOG,"","Line %d not handled! %s", n, tline); + cm_msg_flush_buffer(); + } + i++; + } + } + } /* while not EOF */ + + if (i < channels) { + cm_msg(MLOG,"","Less channels (%d) loaded from %s than expected (%d) " + "-> recompile with %d or add more channel lines", i, + info->settings.loadfile, channels, i); + for (; i < channels; i++) { /* init rest to defaults */ + sprintf(pnames+NAME_LENGTH*i,"NONE_%d",i); + if (info->threshold) *(info->threshold+i) = 0.1f; + } + } else if (i > channels) { + cm_msg(MLOG,"","More channels (%d) read from %s than expected (%d) " + "-> recompile with %d or remove channel lines", i, + info->settings.loadfile, channels, i); + } else { + cm_msg(MLOG,"","Expected number of channels (%d) read ", channels); + } + if (i > 0) loaded = TRUE; + fclose(stream); + cm_msg_flush_buffer(); + } else { + cm_msg(MLOG,"","ERROR %d returned when trying to open %s for read access!", + errno, info->settings.loadfile); + cm_msg_flush_buffer(); + return FE_ERR_HW; /* RA36 25-SEP-2012 */ + } + } + } + + /* init or get channel names, Used and Readonly DD settings */ + db_merge_data(hDB, hKey, "DD/EPICS Channel name", info->settings.channel_names, + CHN_NAME_LENGTH * channels, channels, TID_STRING); + db_merge_data(hDB, hKey, "DD/Used", info->settings.used, + sizeof(BOOL)* channels, channels, TID_BOOL); + db_merge_data(hDB, hKey, "DD/Readonly", info->settings.readonly, + sizeof(BOOL)* channels, channels, TID_BOOL); + db_merge_data(hDB, hKey, "DD/Chn_Flags", info->settings.chnflags, + CHNFLAGS_LENGTH * channels, channels, TID_STRING); +#ifdef GFA_SPECIFIC + db_merge_data(hDB, hKey, "DD/DESC:TYP", info->settings.dtype, + DTYPE_LENGTH * channels, channels, TID_STRING); + db_merge_data(hDB, hKey, "DD/DESC:SEC", info->settings.dsec, + DSEC_LENGTH * channels, channels, TID_STRING); +#endif + if (loaded) { /* min and max were loaded write them now */ + db_merge_data(hDB, hKey, "DD/Minval", info->settings.minval, + sizeof(float)* channels, channels, TID_FLOAT); + db_merge_data(hDB, hKey, "DD/Maxval", info->settings.maxval, + sizeof(float)* channels, channels, TID_FLOAT); + } + + db_merge_data(hDB, hKey, "DD/Tolerance", info->settings.tolerance, + sizeof(float)* channels, channels, TID_FLOAT); + +#ifdef MIDEBUGCLE + printf("+++ CA client status before doing GFA_SPECIFIC ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before doing GFA_SPECIFIC ******\n"); +#endif + + /* -- search for channels */ + + /* get current watchdog settings */ + cm_get_watchdog_params(&wflag, &wtimeout); + wtimeoutn = 0; /* setting zero timeout will turn off watchdog */ + +#ifdef GFA_SPECIFIC + /* search for :DESC:TYP and :DESC:SEC to + * get device type and section of channel */ + { + chid *tchidt; + chid *tchids; + char tname[2*CHN_NAME_LENGTH]; + HNDLE tkey; + + tchidt = (chid*)calloc(channels, sizeof(chid)); + tchids = (chid*)calloc(channels, sizeof(chid)); + + if (tchidt && tchids) { + INT j,k; + BOOL found; + + cm_msg(MLOG,"","Searching for GFA specific :DESC:TYP channels"); + cm_msg_flush_buffer(); + /* post :DESC:TYP channel search to IO buffer */ + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)) { + char *tpos; + + strcpy(tname,info->settings.channel_names+CHN_NAME_LENGTH*i); + tpos = strchr(tname,':'); + /* + * GFA specific: devices of the same type have the same channel names + * channel name is : + * e.g.: BMB1:COM:2 to set/read demand or BMB1:STA:1 to read status + * :DESC:TYP contains info about GFA device type + * :DESC:SEC contains info about GFA device section + */ + if (tpos) { + *(tpos+1) = '\0'; + /*cm_msg(MLOG,"","Channel %d Looking for previous %s channels",i,tname); */ + for (found=FALSE, k=0; ksettings.channel_names+CHN_NAME_LENGTH*k); */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*k,tname, + strlen(tname)) == 0) { + if (*(info->settings.used+k)&&(*(tchidt+k)!=0)) { +#ifndef ALLOW_MULTIPLE + found = TRUE; +#endif + break; + } + } + } /* for */ + + *tpos = '\0'; + strcat(tname,":DESC:TYP"); + if (found) { +#ifdef MIDEBUG + cm_msg(MLOG,"","** Channel %d(%s) channel previously handled",i,tname); +#endif + continue; + } + status = ca_create_channel(tname,NULL,NULL,CA_PRIORITY_DEFAULT,tchidt+i); + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR, "epics_ca_init", "ERROR status 0X%x = \"%s\" returned by " + "ca_create_channel(%s)", status, ca_message(status), tname); + } else { +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_flush_io() returned %d",status); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif + /* flush send buffer and block for outstanding queries */ + iotimeout = 2.0; // 06-NOV-2012 was 0.7 then 1.0 +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for %d (%s) " + "channel ID (%p) create before timeout", + iotimeout,i,tname,*(tchidt+i)); +#endif + if (i==0) iotimeout *= 2.f; + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_create_channel(%s,NOCB) ******\n", + iotimeout, ca_message(status), tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_create_channel(%s,NOCB) ******\n", + iotimeout, ca_message(status), tname); +#endif + if (status == ECA_TIMEOUT) { // timeout -> wait + for (j=0;j<4;j++) { + ca_pend_event(iotimeout); + if (ca_state(*(tchidt+i)) == cs_conn) break; + } + if (ca_state(*(tchidt+i)) != cs_conn) + cm_msg(MERROR, "epics_ca_init", "ECA_TIMEOUT (%d*%f) creating " + "channel %d (%s)",j,iotimeout,i,tname); + } else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)", + status, ca_message(status)); + } + } + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after creating :DESC:TYP " + "channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after creating :DESC:TYP " + "channels ******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after creating :DESC:TYP " + "channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after creating :DESC:TYP " + "channels ******\n"); +#endif + +#ifdef OBSOLETE +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_create_channel(:DESC:TYP,NOCB) ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_create_channel(:DESC:TYP,NOCB) ******\n"); +#endif + /* flush send buffer and block for outstanding queries */ + iotimeout = channels*0.5 + 5.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for :DESC:TYP " + "channels create before timeout",iotimeout); + cm_msg_flush_buffer(); + cm_set_watchdog_params(wflag,wtimeoutn); + status = ca_pend_io(iotimeout); + /* restore watchdog params */ + cm_set_watchdog_params(wflag, wtimeout); + + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)",status, + ca_message(status)); + cm_msg_flush_buffer(); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after ca_create_channel" + "(:DESC:TYP,NOCB) ******\n", iotimeout, ca_message(status)); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after ca_create_channel" + "(:DESC:TYP,NOCB) ******\n", iotimeout, ca_message(status)); +#endif +#endif /* OBSOLETE */ + + /* post :DESC:SEC channel search to IO buffer */ + cm_msg(MLOG,"","Searching for GFA specific :DESC:SEC channels"); + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)) { + char *tpos; + + strcpy(tname,info->settings.channel_names+CHN_NAME_LENGTH*i); + tpos = strchr(tname,':'); + if (tpos) { + *(tpos+1) = '\0'; + /*cm_msg(MLOG,"","Channel %d Looking for previous %s channels",i,tname); */ + for (found=FALSE, k=0; ksettings.channel_names+CHN_NAME_LENGTH*k); */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*k,tname, + strlen(tname)) == 0) { + if (*(info->settings.used+k)&&(*(tchids+k)!=0)) { +#ifndef ALLOW_MULTIPLE + found = TRUE; +#endif + break; + } + } + } + *tpos = '\0'; + strcat(tname,":DESC:SEC"); + if (found) { +#ifdef MIDEBUG + cm_msg(MLOG,"","** Channel %d(%s) channel previously handled",i,tname); +#endif + continue; + } + status = ca_create_channel(tname,NULL,NULL,CA_PRIORITY_DEFAULT,tchids+i); + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR, "epics_ca_init", "ERROR status 0X%x = \"%s\" returned by " + "ca_create_channel(%s)", status, ca_message(status), tname); + } else { +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_flush_io() returned %d",status); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_create_channel(%s,NOCB) ******\n",tname); +#endif + /* flush send buffer and block for outstanding queries */ + iotimeout = 2.0; // 06-NOV-2012 was 0.7 then 1.0 +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for %d (%s) " + "channel ID (%p) create before timeout",iotimeout,i, + tname, *(tchids+i)); +#endif + if (i==0) iotimeout *= 2.f; + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after ca_create" + "_channel(%s,NOCB) ******\n",iotimeout, ca_message(status), tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after ca_create" + "_channel(%s,NOCB) ******\n",iotimeout, ca_message(status), tname); +#endif + if (status == ECA_TIMEOUT) { // timeout -> wait + for (j=0;j<4;j++) { + ca_pend_event(iotimeout); + if (ca_state(*(tchids+i)) == cs_conn) break; + } + if (ca_state(*(tchids+i)) != cs_conn) + cm_msg(MERROR, "epics_ca_init", "ECA_TIMEOUT (%d*%f) creating " + "channel %d (%s)",j,iotimeout,i,tname); + } else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)", + status, ca_message(status)); + } + } + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after creating :DESC:SEC " + "channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after creating :DESC:SEC " + "channels ******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after creating :DESC:SEC " + "channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after creating :DESC:SEC " + "channels ******\n"); +#endif + +#ifdef OBSOLETE +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_create_channel(:DESC:SEC,NOCB) ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_create_channel(:DESC:SEC,NOCB) ******\n"); +#endif + /* flush send buffer and block for outstanding queries */ + iotimeout = channels*0.5 + 5.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for :DESC:SEC " + "channels create before timeout",iotimeout); + cm_msg_flush_buffer(); + cm_set_watchdog_params(wflag,wtimeoutn); + status = ca_pend_io(iotimeout); + /* restore watchdog params */ + cm_set_watchdog_params(wflag, wtimeout); + + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)",status, + ca_message(status)); + cm_msg_flush_buffer(); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after ca_create_channel" + "(:DESC:SEC,NOCB) ******\n", iotimeout, ca_message(status)); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after ca_create_channel" + "(:DESC:SEC,NOCB) ******\n", iotimeout, ca_message(status)); +#endif +#endif /* OBSOLETE */ + + /* if channel is connected post get channel info to IO buffer */ + cm_msg(MLOG,"","Reading GFA specific :DESC:TYP channels"); + cm_msg_flush_buffer(); + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)&&(*(tchidt+i)!=0)) { + j = 0; +gtype_again: + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "**TYPE_NOT_RETURNED**"); + if (*(tchidt+i) && (ca_state(*(tchidt+i)) == cs_conn)) { + status = ca_get(DBR_STRING,*(tchidt+i),info->settings.dtype+DTYPE_LENGTH*i); + if (status != ECA_NORMAL) { + cm_msg(MLOG,"","ca_get(dtype of channel %d(%s)): status 0X%x = \"%s\"",i, + ca_name(*(tchidt+i)),status,ca_message(status)); + } else { +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); +#endif + ca_flush_io(); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); +#endif + j++; + /* flush send buffer and block for outstanding queries */ + iotimeout = j*0.5; + if (j>1) cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for %d (%s) " + "channel get before timeout",iotimeout,i, + ca_name(*(tchidt+i))); +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after ca_get(%s) " + "******\n",ca_name(*(tchidt+i))); +#endif + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after ca_get(%s)" + " ******\n", iotimeout, ca_message(status), ca_name(*(tchidt+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after ca_get(%s)" + " ******\n", iotimeout, ca_message(status), ca_name(*(tchidt+i))); +#endif + if (status == ECA_TIMEOUT) { + cm_msg(MLOG, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + ca_pend_event(j*1.0); + if (j < 4) goto gtype_again; + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + } else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)", + status, ca_message(status)); + } + } else { + cm_msg(MLOG,"","ca_get(:DESC:TYP of channel %d(%s)): Channel not connected", + i, ca_name(*(tchidt+i))); + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "**TYPE_NOT_FOUND**"); + } +#ifndef ALLOW_MULTIPLE + } else if (*(info->settings.used+i)) { /* channel used but not created */ + char *tpos; /* check for previous channel */ + + strcpy(tname,info->settings.channel_names+CHN_NAME_LENGTH*i); + tpos = strchr(tname,':'); + if (tpos) { + *(tpos+1) = '\0'; + /*cm_msg(MLOG,"","Channel %d Looking for previous %s channels",i,tname); */ + for (found=FALSE, k=0; ksettings.channel_names+CHN_NAME_LENGTH*k); */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*k,tname, + strlen(tname)) == 0) { + /*cm_msg(MLOG,"","**Found DESC:TYP of channel %d is %s",k, + * info->settings.dtype+DTYPE_LENGTH*k); */ + strcpy(info->settings.dtype+DTYPE_LENGTH*i, + info->settings.dtype+DTYPE_LENGTH*k); + found = TRUE; + break; + } + } /* for */ + *tpos = '\0'; + strcat(tname,":DESC:TYP"); + if (found) { +#ifdef MIDEBUG + cm_msg(MLOG,"","** Channel %d(%s) channel previously handled, " + "type is %s",i,tname,info->settings.dtype+DTYPE_LENGTH*i); +#endif + continue; + } + } +#endif /* ALLOW_MULTIPLE */ + } else { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "**CHAN_NOT_USED**"); + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after ca_get() " + ":DESC:TYP channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after ca_get() " + ":DESC:TYP channels ******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after ca_get() " + ":DESC:TYP channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after ca_get() " + ":DESC:TYP channels ******\n"); +#endif + + cm_msg(MLOG,"","Reading GFA specific :DESC:SEC channels"); + cm_msg_flush_buffer(); + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)&&(*(tchids+i)!=0)) { + j = 0; +gdesc_again: + strcpy(info->settings.dsec+DSEC_LENGTH*i, "**SEC_NOT_RETURNED**"); + if (*(tchids+i) && (ca_state(*(tchids+i)) == cs_conn)) { + status = ca_get(DBR_STRING,*(tchids+i),info->settings.dsec+DSEC_LENGTH*i); + if (status != ECA_NORMAL) { + cm_msg(MLOG,"","ca_get(dsec of channel %d): status 0X%x = \"%s\"",i, + status,ca_message(status)); + } else { +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); +#endif + ca_flush_io(); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); +#endif + /* flush send buffer and block for outstanding queries */ + j++; + iotimeout = j*0.5; + if (j>1) cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for %d %s " + "channel get before timeout",iotimeout,i, + ca_name(*(tchids+i))); +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after ca_get(%s) " + "******\n",ca_name(*(tchids+i))); +#endif + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after ca_get(%s)" + " ******\n", iotimeout, ca_message(status), ca_name(*(tchids+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after ca_get(%s)" + " ******\n", iotimeout, ca_message(status), ca_name(*(tchids+i))); +#endif + if (status == ECA_TIMEOUT) { + cm_msg(MLOG, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + ca_pend_event(j*1.0); + if (j < 4) goto gdesc_again; + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + } else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned status %d (%s)", + status, ca_message(status)); + } + } else { + cm_msg(MLOG,"","ca_get(:DESC:SEC of channel %d(%s)): Channel not connected", + i, ca_name(*(tchids+i))); + strcpy(info->settings.dsec+DSEC_LENGTH*i, "**SEC_NOT_FOUND**"); + } +#ifndef ALLOW_MULTIPLE + } else if (*(info->settings.used+i)) { /* channel used but not created */ + char *tpos; /* check for previous channel */ + + strcpy(tname,info->settings.channel_names+CHN_NAME_LENGTH*i); + tpos = strchr(tname,':'); + if (tpos) { + *(tpos+1) = '\0'; + /*cm_msg(MLOG,"","Channel %d Looking for previous %s channels",i,tname); */ + for (found=FALSE, k=0; ksettings.channel_names+CHN_NAME_LENGTH*k); */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*k,tname, + strlen(tname)) == 0) { + /*cm_msg(MLOG,"","**Found DESC:SEC of channel %d is %s",k, + * info->settings.dsec+DSEC_LENGTH*k); */ + strcpy(info->settings.dsec+DSEC_LENGTH*i, + info->settings.dsec+DSEC_LENGTH*k); + found = TRUE; + break; + } + } + *tpos = '\0'; + strcat(tname,":DESC:TYP"); + if (found) { +#ifdef MIDEBUG + cm_msg(MLOG,"","** Channel %d(%s) channel previously handled, " + "section is %s",i,tname,info->settings.dsec+DSEC_LENGTH*i); +#endif + continue; + } + } +#endif /* ALLOW_MULTIPLE */ + } else { + strcpy(info->settings.dsec+DSEC_LENGTH*i, "**CHAN_NOT_USED**"); + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after ca_get() " + ":DESC:SEC channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after ca_get() " + ":DESC:SEC channels ******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after ca_get() " + ":DESC:SEC channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after ca_get() " + ":DESC:SEC channels ******\n"); +#endif + + /* set predefined info for :DESC:TYP channels not found * + * then release all channels */ + cm_msg(MLOG,"","Closing GFA specific :DESC:TYP channels"); + cm_msg_flush_buffer(); + for (i = 0; i < channels; i++) { + epics_ca_process(info); + /*if (*(tchidt+i)) { */ + if (*(info->settings.used+i)) { + if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"**",2)==0) { + /* some beamline channel are not fully included in EPICS GFA therefore + * supply missing DESC:TYP information from EPICS channel name */ + + /* KS: channel locks (Kanalverschluss) */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"KS",2)==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "BX"); + + /* FS: slits (french: fente) */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"FS",2) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "FENTE"); + + /* VSD: Beamline valves of secondary areas */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"VSD",3) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "VALVE"); + + /* ASPK31I: Current of positive kicker ASPK31 */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ASPK31I",7) ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "KICKER"); + + /* ASNK31I: Current of negative kicker ASNK31 */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ASNK31I",7) ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "KICKER"); + + /* QSK81R and QSK81L have currently no :DESC:TYP and DESC:SEC: */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"QSK81",5) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "QUAD"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"SSL51",5) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "STER"); + + /* WEUH and WEUL are not yet fully integrated */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"WEU",3) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "SOLE"); + + /* SPIN2-PIE1 and SPIN2-PIE3 are not yet fully integrated */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"SPIN2-",6) + ==0) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "PS"); // MA_SPIN -> PS + + /* Single scalers for Optima */ +/* else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,"CNT:INP") + * != NULL) + * strcpy(info->settings.dtype+DTYPE_LENGTH*i, "DIAG"); + */ + /* SEPHVN:, SEPHVP: and SEPHV: channels of separator + * HV and spinrotator HV are from a SoftIOC not reachable over EPICS + * gateway */ + /* NOTE: There are also SEP: channels of the SEP magnets */ + else if (((strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"SEP",3) + ==0)||(strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ROT",3) == 0)) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,"HVN:") || + strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,"HVP:") || + strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,"HV:"))) + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "HVPS"); + else + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "**TYPE_NOT_FOUND**"); + + } else { +/* degraders and slits */ + /* DEGR */ if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"DEGR",4)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "FENTE"); + cm_msg(MLOG,"","Degrader type DEGR %d(%s) will be treated as FENTE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +/* beamline controls CS_ */ + /* CS_FENT */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"CS_FENT",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "FENTE"); + cm_msg(MLOG,"","Slit type CS_FENT %d(%s) will be treated as FENTE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* CS_DEGR */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"CS_DEGR",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "FENTE"); + cm_msg(MLOG,"","Degrader type CS_DEGR %d(%s) will be treated as FENTE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* CS_BX */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"CS_BX",5)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "BX"); + cm_msg(MLOG,"","Beam blocker type CS_BX %d(%s) will be treated as BX", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* CS_KV */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"CS_KV",5)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "BX"); + cm_msg(MLOG,"","Beam blocker type CS_KV %d(%s) will be treated as BX", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* CS_PS */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"CS_PS",5)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "PS"); + cm_msg(MLOG,"","Controls power supply CS_PS %d(%s) will be treated as " + "PS", i,info->settings.channel_names+CHN_NAME_LENGTH*i); + +/* magnets MA_ */ + /* MA_BEND */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_BEND",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "BEND"); + cm_msg(MLOG,"","Magnet type MA_BEND %d(%s) will be treated as BEND", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_QUAD */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_QUAD",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "QUAD"); + cm_msg(MLOG,"","Magnet type MA_QUAD %d(%s) will be treated as QUAD", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_STER */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_STER",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "STER"); + cm_msg(MLOG,"","Magnet type MA_STER %d(%s) will be treated as STER", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_SEP */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_SEP",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "SEPT"); + cm_msg(MLOG,"","Magnet type MA_SEP %d(%s) will be treated as SEPT", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_SEXT */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_SEXT",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "SEXT"); + cm_msg(MLOG,"","Magnet type MA_SEXT %d(%s) will be treated as SEXT", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_SOLE */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_SOLE",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "SOLE"); + cm_msg(MLOG,"","Magnet type MA_SOLE %d(%s) will be treated as SOLE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_TR */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_TR",5)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "TS"); + cm_msg(MLOG,"","Magnet type MA_TR %d(%s) will be treated as TS", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* TR */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"TR",2)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "TS"); + cm_msg(MLOG,"","Magnet type TR %d(%s) will be treated as TS", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_SPIN */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_SPIN",8)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "PS"); + cm_msg(MLOG,"","SEP/SPINROT Magnet %d(%s) will be treated as PS", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* MA_KICK */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"MA_KICK",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "KICKER"); + cm_msg(MLOG,"","MA_KICK %d(%s) will be treated as KICKER", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +/* diagnostic */ + /* DI_DIAG */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"DI_DIAG",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "DIAG"); + cm_msg(MLOG,"","DI_DIAG %d(%s) will be treated as DIAG", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* DI_MESTRA */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"DI_MESTRA",9)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "DIAG"); + cm_msg(MLOG,"","DI_MESTRA %d(%s) will be treated as DIAG", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + +/* vacuum devices VA_*/ + /* VA_VALVE*/} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"VA_VALVE",8)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "VALVE"); + cm_msg(MLOG,"","Vacuum type VA_VALVE %d(%s) will be treated as VALVE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* VA_PUMP */} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"VA_PUMP",7)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "PUMP"); + cm_msg(MLOG,"","Vacuum type VA_PUMP %d(%s) will be treated as PUMP", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + /* VA_GAUGE*/} else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"VA_GAUGE",8)==0) { + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "GAUGE"); + cm_msg(MLOG,"","Vacuum type VA_GAUGE %d(%s) will be treated as GAUGE", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + + /* ANY */ } else if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"ANY",3)==0) { + if ((strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"SEP",3)==0) + ||(strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i,"SPIN",4) + ==0)) { + cm_msg(MLOG,"","Type ANY of %d(%s) will be treated as SOLEnoid", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + strcpy(info->settings.dtype+DTYPE_LENGTH*i, "SOLE"); + } + } + } + + if (*(tchidt+i)) { + strcpy(tname,ca_name(*(tchidt+i))); + status = ca_clear_channel(*(tchidt+i)); + if (!(status & ECA_NORMAL)) + cm_msg(MERROR, "epics_ca_init", "Channel %d(%s): " + "ca_clear_channel(%s) returned status 0X%x = \"%s\"",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, tname, + status, ca_message(status)); +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif + ca_flush_io(); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif + iotimeout = 0.1; + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_clear_channel(%s) ******\n",iotimeout,ca_message(status),tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_clear_channel(%s) ******\n",iotimeout,ca_message(status),tname); +#endif + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() ca_clear_channel(%s) " + "returned ECA_TIMEOUT",tname); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() ca_clear_channel(%s) " + "returned %d =\"%s\"", tname, status,ca_message(status)); + } + } + /*} */ + cm_msg_flush_buffer(); + } /* for */ + + /* set predefined info for :DESC:SEC channels not found * + * then release all channels */ + cm_msg(MLOG,"","Closing GFA specific :DESC:SEC channels"); + cm_msg_flush_buffer(); + for (i = 0; i < channels; i++) { + epics_ca_process(info); + /*if (*(tchids+i)) { */ + if (*(info->settings.used+i)) { + if (strncmp(info->settings.dsec+DSEC_LENGTH*i,"**",2)==0) { + /* spinrotator/separator HV is not fully implemented + * in EPICS therefore supply SEC information from channel name */ + if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ROT1HV",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIM31"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "VSD3",4)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIM3"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ASPK31I",7)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIM3"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ASNK31I",7)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIM3"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ROT2HV",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE1"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ROT3HV",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "ROT4HV",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); +/* else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + * "ZPIM3",5)==0) + * strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIM3"); + * else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + * "SPINROT3",8)==0) + * strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); + * else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + * "SPINROT4",8)==0) + * strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); + * else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + * "ZPIE3",5)==0) + * strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); + * else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + * "FS81-",5)==0) + * strcpy(info->settings.dsec+DSEC_LENGTH*i, "MUE1"); + */ else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "QSK81R",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "MUE1"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "SSL51",5)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE1"); + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "QSK81L",6)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "MUE1"); + /* WEU and WEV are currently not fully integrated */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "WEU",3)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE1"); + /* SPIN2-PIE1 is currently not fully integrated */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "SPIN2-PIE1",10)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE1"); + /* SPIN2-PIE3 is currently not fully integrated */ + else if (strncmp(info->settings.channel_names+CHN_NAME_LENGTH*i, + "SPIN2-PIE3",10)==0) + strcpy(info->settings.dsec+DSEC_LENGTH*i, "PIE3"); + else + strcpy(info->settings.dsec+DSEC_LENGTH*i, "**SEC_NOT_FOUND**"); + } + if (*(tchids+i)) { + strcpy(tname,ca_name(*(tchids+i))); + status = ca_clear_channel(*(tchids+i)); + if (!(status & ECA_NORMAL)) + cm_msg(MERROR, "epics_ca_init", "Channel %d(%s): " + "ca_clear_channel(%s) returned status 0X%x = \"%s\"",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,tname, + status, ca_message(status)); +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif + ca_flush_io(); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif +#ifdef MIDEBUGCL1 + printf("+++ CA client status before ca_pend_io() after " + "ca_clear_channel(%s) ******\n",tname); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io() after " + "ca_clear_channel(%s) ******\n",tname); +#endif + iotimeout = 0.1; + status = ca_pend_io(iotimeout); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_clear_channel(%s) ******\n",iotimeout,ca_message(status),tname); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_clear_channel(%s) ******\n",iotimeout,ca_message(status),tname); +#endif + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() ca_clear_channel(%s) " + "returned ECA_TIMEOUT",tname); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() ca_clear_channel(%s) " + "returned %d =\"%s\"", tname, status,ca_message(status)); + } + } + /*} */ + cm_msg_flush_buffer(); + } /* for */ + +#ifdef OBSOLETE + /* flush send buffer and block for outstanding queries */ + iotimeout = channels*0.1 + 1.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for :DESC:TYP and :DESC:SEC " + "channels clear before timeout",iotimeout); + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned %d =\"%s\"",status, + ca_message(status)); +#endif /* OBSOLETE */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after ca_clear_channel() " + "of :DESC:TYP and :DESC:SEC channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after ca_clear_channel() " + "of :DESC:TYP and :DESC:SEC channels ******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after ca_clear_channel() of" + " :DESC:TYP and :DESC:SEC channels ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after ca_clear_channel() of" + " :DESC:TYP and :DESC:SEC channels ******\n"); +#endif + } + + /* update ODB */ + status = db_find_key(hDB, hKey, "DD/DESC:TYP", &tkey); + if (status == DB_SUCCESS) + db_set_data(hDB, tkey, info->settings.dtype, DTYPE_LENGTH * channels, + channels, TID_STRING); + + status = db_find_key(hDB, hKey, "DD/DESC:SEC", &tkey); + if (status == DB_SUCCESS) + db_set_data(hDB, tkey, info->settings.dsec, DSEC_LENGTH * channels, + channels, TID_STRING); + + if (tchidt) free(tchidt); + if (tchids) free(tchids); + + } +#endif /* #ifdef GFA_SPECIFIC */ + + /* disable event handler callback subscription in the connection handler callback + * before everything is set-up */ + info->subscribe_disabled = TRUE; + +#ifdef USE_CONNECTION_HANDLER + cm_msg(MLOG,"","Using connection_handler() callback"); + cm_msg_flush_buffer(); +#endif + process_connection = TRUE; // process connection handler callback + + /* post create channel connection to IO buffer */ + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)) { + /* NOTE: connection handler used -> ca_pend_io() will not block - waiting for + * channel to enter connected state */ +#ifdef USE_CONNECTION_HANDLER + status = ca_create_channel(info->settings.channel_names+CHN_NAME_LENGTH*i, + epics_ca_connection_handler, info, CA_PRIORITY_DEFAULT, info->pchid+i); +#else + status = ca_create_channel(info->settings.channel_names+CHN_NAME_LENGTH*i, + NULL, NULL, CA_PRIORITY_DEFAULT, info->pchid+i); +#endif + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR, "epics_ca_init", "ERROR status 0X%x = \"%s\" returned by " + "ca_create_channel(%s)", status, ca_message(status), + info->settings.channel_names+CHN_NAME_LENGTH*i); + } else { + cm_msg(MLOG,"","Create channel %d (%s) ID (%p) Type: %s Sec:%s",i, + ca_name(*(info->pchid+i)), *(info->pchid+i), + info->settings.dtype+DTYPE_LENGTH*i, + info->settings.dsec+DSEC_LENGTH*i); +#ifdef MIDEBUGCL2 + printf("+++ CA client status before ca_flush_io() after " + "ca_create_channel(%s,hndl) ******\n",ca_name(*(info->pchid+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_flush_io() after " + "ca_create_channel(%s,hndl) ******\n",ca_name(*(info->pchid+i))); +#endif + ca_flush_io(); +#ifdef MIDEBUGCL2 + printf("+++ CA client status after ca_flush_io() after " + "ca_create_channel(%s,hndl) ******\n",ca_name(*(info->pchid+i))); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_flush_io() after " + "ca_create_channel(%s,hndl) ******\n",ca_name(*(info->pchid+i))); +#endif + // iotimeout = 0.2; //was too small then 0.7 now 2.0 06-NOV-2012 + iotimeout = 2.0; + if (i==0) iotimeout *= 4; // increased timeout *4 for first chan'l 12-APR-2018 + // as first channel needs more time to be connected + // if others are also affected, increase number of + // epics_ca_process() or ca_pend_event() calls +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds to create channel " + "connections before timeout",iotimeout); +#endif + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io(%f) returned ECA_TIMEOUT", + iotimeout); + else if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io(%f) returned %d =\"%s\"", + iotimeout, status, ca_message(status)); + } + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef MIDEBUGCLA + printf("+++ CA client status before waiting for connection callbacks ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before waiting for connection callbacks ******\n"); +#endif + +#ifdef PREEMPTIVE + epics_ca_process(info); + ss_sleep(4000); +#else + ca_pend_event(4.0); +#endif + if (channels > 50) { /* check again for new gateway */ +#ifdef PREEMPTIVE + epics_ca_process(info); + ss_sleep(4000); +#else + ca_pend_event(4.0); +#endif + } + if (channels > 100) { /* check again for new gateway */ +#ifdef PREEMPTIVE + epics_ca_process(info); + ss_sleep(4000); +#else + ca_pend_event(4.0); +#endif + } + if (channels > 150) { /* check again for new gateway */ +#ifdef PREEMPTIVE + epics_ca_process(info); + ss_sleep(4000); +#else + ca_pend_event(4.0); +#endif + } + + epics_ca_process(info); + +#ifdef MIDEBUGCLA + printf("+++ CA client status after waiting for connection callbacks ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after waiting for connection callbacks ******\n"); +#endif + + /* all channels must be connected now to get MIN/MAX if not already present in ODB */ + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)) { + if ((castate=ca_state(*(info->pchid+i))) != cs_conn) { + cm_msg(MERROR, "epics_ca_init", "Not connected to EPICS Channel %d(%s). " + "Channel state is %d = \"%s\"",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + castate, castatestr(castate)); + } else { + if (!*(info->found+i)) { +#ifdef MIDEBUG + cm_msg(MLOG, "epics_ca_init", "Connected to EPICS Channel %d(%s)", i, + info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->found+i) = TRUE; + } + } + } + cm_msg_flush_buffer(); + } /* for */ + + /* get MIN/MAX if not already present (ODB may have been deleted after loading to + * query MIN/MAX) */ + /* NOTE: some channels may still be missing */ + status = db_find_key(hDB, hKey, "DD/Minval", &subkey); + if (status != DB_NO_KEY) status = db_find_key(hDB, hKey, "DD/Maxval", &subkey); + + /* status = DB_NO_KEY; /* TESTTESTTEST */ + if (status == DB_NO_KEY) { + struct dbr_ctrl_float *pctrl; + + cm_msg(MLOG,"","Posting Control Structure Query to get LOW and HIGH of channels"); + cm_msg_flush_buffer(); + pctrl = (dbr_ctrl_float*)calloc(channels,sizeof(struct dbr_ctrl_float)); + if (pctrl) { + int nchans; + for (nchans = 0, i = 0; i < channels; i++) { + epics_ca_process(info); + if (*(info->settings.used+i)) { + if ((castate=ca_state(*(info->pchid+i))) == cs_conn) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'U') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'M') != NULL) { + status = ca_get(DBR_CTRL_FLOAT,*(info->pchid+i),pctrl+i); + if (status != ECA_NORMAL) + cm_msg(MLOG,"","ca_get(DBR_CTRL_FLOAT of channel %d): " + "status 0X%x = \"%s\"",i,status,ca_message(status)); + else + ca_flush_io(); + nchans++; + /* flush send buffer and block for outstanding queries */ + iotimeout = 0.5; +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for ca_get(DBR_CTRL" + "_FLOAT,%s) before timeout",iotimeout, + info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() for ca_get(DBR_CTRL" + "_FLOAT,%s) returned ECA_TIMEOUT after %.1f seconds", + info->settings.channel_names+CHN_NAME_LENGTH*i,iotimeout); + epics_ca_process(info); + } else { + cm_msg(MLOG,"","Channel %d M flag not set", i); + } + } else { + cm_msg(MLOG,"","Channel %d S flag set", i); + } + } else { + cm_msg(MLOG,"","Channel %d U flag set", i); + } + } else { + cm_msg(MLOG,"","Channel %d not connected", i); + } + } else { + cm_msg(MLOG,"","Channel %d not used", i); + } + cm_msg_flush_buffer(); + } /* for */ + + if (nchans > 0) { + cm_msg(MLOG,"","Posted Queries for LOW and HIGH of %d channels",nchans); + cm_msg_flush_buffer(); +#ifdef OBSOLETE + /* flush send buffer and block for outstanding queries */ + iotimeout = channels*0.1 + 1.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for ca_get(DBR_CTRL_FLOAT) " + "of all channels before timeout",iotimeout); + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() ca_get(DBR_CTRL_FLOAT) of all " + "channels returned ECA_TIMEOUT"); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(%f) status 0X%x =\"%s\"",iotimeout,status, + ca_message(status)); +#endif +#endif /* OBSOLETE */ + + epics_ca_process(info); + + for (i = 0; i < channels; i++) { + if (*(info->settings.used+i)) { + if ((castate=ca_state(*(info->pchid+i))) == cs_conn) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'U') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'M') != NULL) { + *(info->settings.minval+i) = (pctrl+i)->lower_ctrl_limit; + cm_msg(MLOG,"","Channel %d(%s): LOW is %f", + i,info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->settings.minval+i)); + *(info->settings.maxval+i) = (pctrl+i)->upper_ctrl_limit; + cm_msg(MLOG,"","Channel %d(%s): HIGH is %f", + i,info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->settings.maxval+i)); + } + } + } + } else { + cm_msg(MLOG,"","Channel %d(%s) is NOT connected! LOW and HIGH are not " + "determined", i,info->settings.channel_names+CHN_NAME_LENGTH*i); + } + } + cm_msg_flush_buffer(); + } /* for() */ + } + free(pctrl); + } else { + cm_msg(MERROR,"epics_ca_init","Memory not allocated for struct dbr_ctrl_float"); + cm_msg_flush_buffer(); + } + } + + epics_ca_process(info); + + /* create and init or if already existing read DD/Minval DD/Maxval */ + db_merge_data(hDB, hKey, "DD/Minval", info->settings.minval, + sizeof(float)* channels, channels, TID_FLOAT); + db_merge_data(hDB, hKey, "DD/Maxval", info->settings.maxval, + sizeof(float)* channels, channels, TID_FLOAT); + + epics_ca_process(info); + + process_event = TRUE; /* events may now be processed in event handler callback */ + + /* log information */ + for (i = 0; i < channels; i++) { + if (*(info->settings.used+i)) { +#ifdef GFA_SPECIFIC + snprintf(smsg,sizeof(smsg),"Channel %d(%s): %s(%s) Type: %s Section: %s " + "Chn_Flags: %s", i, info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->settings.readonly+i)?"R":"W", + ca_write_access(info->pchid[i])?"W":"R", + info->settings.dtype+DTYPE_LENGTH *i, + info->settings.dsec+DSEC_LENGTH*i, + info->settings.chnflags+CHNFLAGS_LENGTH*i); +#else + snprintf(smsg,sizeof(smsg),"Channel %d(%s): %s(%s) Chn_Flags: %s", i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->settings.readonly+i)?"R":"W", + ca_write_access(info->pchid[i])?"W":"R", + info->settings.chnflags+CHNFLAGS_LENGTH*i); +#endif /* #ifdef GFA_SPECIFIC */ + + if ((strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'U'))|| + (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S'))) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'I')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'F')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'M')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C')|| +#ifdef GFA_SPECIFIC + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'P')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'N')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'Z')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'V')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'T')|| + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'G')|| +#endif + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'R') ) + if (sizeof(smsg) -strlen(smsg) > 45) + strcat(smsg," ** Other flag(s) overruled by U and/or S **"); + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'M')) { + if (sizeof(smsg) -strlen(smsg) > 45) + sprintf(&smsg[strlen(smsg)]," Minval = %.2f, Maxval = %.2f", + *(info->settings.minval+i), *(info->settings.maxval+i)); + } +#ifdef GFA_SPECIFIC + /* NIY check and list additional info (tolerance when T, ... */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'T')) { + if ( *(info->settings.tolerance+i) >= 0.f) { + if (sizeof(smsg) -strlen(smsg) > 25) + sprintf(&smsg[strlen(smsg)]," Tolerance = %f", + *(info->settings.tolerance+i)); + } + } +#endif + } + cm_msg(MLOG,"",smsg); + } else { + snprintf(smsg,sizeof(smsg), "Channel %d(%s): NOT Used", i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + cm_msg(MLOG,"",smsg); + } + epics_ca_process(info); + cm_msg_flush_buffer(); + } /* for() */ + +#ifndef OMIT_ODBUPDATE + /* get equipment name and demand key */ + if ((status = db_get_path(hDB, hKey, tpath, sizeof(tpath))) == DB_SUCCESS) { + int i; + + for (i = strlen("/Equipment/"); i < strlen(tpath); i++) { + if (tpath[i] == '/') { + tpath[i] = '\0'; + strncpy(info->name, &tpath[strlen("/Equipment/")], NAME_LENGTH); + info->name[NAME_LENGTH - 1] = '\0'; + sprintf(tpath, "/Equipment/%s/Variables/Demand",info->name); + status = db_find_key(hDB, 0, tpath, &info->hkeyDemand); + if (status != DB_SUCCESS) info->hkeyDemand = 0; + break; + } + } + cm_msg(MINFO, "", "epics_ca_init: Equipment name is %s", info->name); + } else { + cm_msg(MERROR, "epics_ca_init","ERROR %d getting key path", status); + } + cm_msg_flush_buffer(); +#else + info->hkeyDemand = 0; +#endif + + epics_ca_process(info); + +#ifndef OMIT_ODBUPDATE + /* directly init ODB settings of Names */ + if (pnames != NULL) { + sprintf(tpath, "/Equipment/%s/Settings/Names",info->name); + db_merge_data(hDB, 0, tpath, pnames, NAME_LENGTH*channels, channels, TID_STRING); + free(pnames); + pnames = NULL; + } +#endif + + epics_ca_process(info); + +#ifndef OMIT_ODBUPDATE + /* directly init ODB settings of thresholds */ + if (info->threshold != NULL) { + /* NIY TODO does not work + *sprintf(tpath, "/Equipment/%s/Settings/Update Threshold Measured",info->name); + *db_merge_data(hDB, 0, tpath, info->threshold, sizeof(float)* channels, channels, + * TID_FLOAT); + *free(info->threshold); + *info->threshold = NULL; + */ + } +#endif + + epics_ca_process(info); + +#ifdef GFA_SPECIFIC + + /* list GFA types, their channels, channel masks, values and corresp. messages */ +#ifdef MIDEBUG + cm_msg(MLOG, "", "List of used GFA types"); + + for (i=0; i < N_GFA_TYPES; i++) { + int n,j; + BOOL found; + GFA_channel *tc; + + for (found=FALSE,n=0; n < info->num_channels; n++) { + if (strcmp(info->settings.dtype+DTYPE_LENGTH*n,gfatype[i].type) == 0) { + found = TRUE; + break; + } + } + + if (!found) continue; + + cm_msg(MLOG,"","TYPE: \"%s\" \"%s\" %d channels", gfatype[i].type, gfatype[i].desc, + gfatype[i].nc); + tc = gfatype[i].tc; + + for (j = 0; j < gfatype[i].nc; j++) { + int k; + GFA_mask *cm; + + if (!(tc+j)) continue; +#ifdef MIDEBUG1 + cm_msg(MLOG,"","**Channel \"%s\" \"%s\" %d masks",(tc+j)->spec,(tc+j)->desc, + (tc+j)->ns); +#else + cm_msg(MLOG,"","**Channel \"%s\" \"%s\"",(tc+j)->spec,(tc+j)->desc); +#endif + cm = (tc+j)->cm; + for (k = 0; k < (tc+j)->ns; k++) { + int l; + GFA_msg *sm; + + if (!(cm+k)) continue; + cm_msg(MLOG,"","****Mask 0x%4.4X",(cm+k)->mask); + sm = (cm+k)->sm; + for (l = 0; l < (cm+k)->nm; l++) { + if (!(sm+l)) continue; + cm_msg(MLOG,"","******Value 0x%4.4X : \"%s\"",(sm+l)->value, (sm+l)->msg); + } + + } + cm_msg_flush_buffer(); + } /* for */ + } +#endif + + epics_ca_process(info); + + /* check if GFA type is in list and channel is known + * when not write message NO GFA specific handling possible for TYP */ + for (i=0; i < info->num_channels; i++) { + int n; + BOOL found; + for (found=FALSE,n=0; n < N_GFA_TYPES; n++) { + if (strcmp(info->settings.dtype+DTYPE_LENGTH*i,gfatype[n].type) == 0) { + found = TRUE; + break; + } + } + + if (found) { + int m; + char *tpos; + + tpos = strchr(info->settings.channel_names+CHN_NAME_LENGTH*i,':'); + if (tpos) { + /* loop over channels of type matching channel specification string */ + for (found = FALSE, m=0; m < gfatype[n].nc; m++) { + if ((gfatype[n].tc+m) && (strcmp((gfatype[n].tc+m)->spec,tpos)==0)) { + found = TRUE; /* channel spec found */ + *(info->iscmd+i) = (gfatype[n].tc+m)->iscmd; /* copy iscmd flag of spec */ + break; + } + } + if (!found && ((strcmp(info->settings.dtype+DTYPE_LENGTH*i,"HVPS")==0)|| + (strcmp(info->settings.dtype+DTYPE_LENGTH*i,"DIAG")==0))) { + /*loop over chans of type containing channel specification string */ + for (found = FALSE, m=0; m < gfatype[n].nc; m++) { + if ((gfatype[n].tc+m) && + strstr(info->settings.channel_names+CHN_NAME_LENGTH*i, + (gfatype[n].tc+m)->spec)) { + found = TRUE; /* channel spec found */ + *(info->iscmd+i) = (gfatype[n].tc+m)->iscmd;/* copy iscmd flag of spec */ + break; + } + } + } + if (!found) { + cm_msg(MLOG,"","Channel %d(%s): Not found in list of handled channels for " + "GFA type \"%s\"", i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + info->settings.dtype+DTYPE_LENGTH*i); + if (!*(info->settings.readonly+i)) /* not found but write flag set */ + *(info->iscmd+i) = TRUE; /* assume command */ + } + } + } else { + /* omit channels with unknown types */ + if (strncmp(info->settings.dtype+DTYPE_LENGTH*i,"**",2) == 0) continue; + + cm_msg(MLOG,"","Channel %d(%s): GFA Type \"%s\" not found in list" + " of handled GFA types!", i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + info->settings.dtype+DTYPE_LENGTH*i); + } + cm_msg_flush_buffer(); + } /* for */ +#else + for (i=0; i < info->num_channels; i++) { + if (!*(info->settings.readonly+i)) /* write flag set -> assume command */ + *(info->iscmd+i) = TRUE; + } +#endif /* #ifdef GFA_SPECIFIC */ + + epics_ca_process(info); + + /* reset alarms */ + if (strncmp(info->settings.AlarmWhenNOTConnected,"NONE",4) != 0) + al_reset_alarm(info->settings.AlarmWhenNOTConnected); + + if (strncmp(info->settings.AlarmWhenNOTinTolerance,"NONE",4) != 0) + al_reset_alarm(info->settings.AlarmWhenNOTinTolerance); + +#ifdef DO_MONITORING + cm_msg(MLOG,"","Executing DO_MONITORING to create channel subscriptions"); + cm_msg_flush_buffer(); + /* buffer add change notifications */ + for (i = 0; i < channels; i++) { + epics_ca_process(info); + /* only for connected channels */ + if (*(info->settings.used+i) && ((castate=ca_state(*(info->pchid+i))) == cs_conn)) { + /* chan specific: omit channels with S-flag e.g.: returning strings + * and :COM:2 and :CSV:2 channels */ + if ((strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2") == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2") == NULL)){ + /* subscribe event handler callback for this channel converting value to float*/ + status = ca_create_subscription(DBR_FLOAT, 1, *(info->pchid+i), + DBE_LOG|DBE_VALUE, epics_ca_event_handler, info, NULL); + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR,"epics_ca_init","Channel %d(%s): ca_create_subscription() retur" + "ned status 0x%X = \"%s\"",i,info->settings.channel_names+CHN_NAME_LENGTH*i, + status, ca_message(status)); + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d(%s): CA event handler (float) will be installed", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->subscribed+i) = TRUE; + + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_flush_io() returned %d",status); + + /* flush send buffer and block for outstanding queries */ + iotimeout = 0.5; +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for ca_create_subscr" + "iption(%s) before timeout",iotimeout, + info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() for ca_create_subscr" + "iption(%s) returned ECA_TIMEOUT after %.1f seconds", + info->settings.channel_names+CHN_NAME_LENGTH*i,iotimeout); + } + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S')) + cm_msg(MLOG,"","Channel %d(%s): S flag: CA event handler will NOT be " + "installed", i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#ifdef MIDEBUG + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2")) + cm_msg(MLOG,"","Channel %d(%s): COM Channel : CA event handler will NOT " + "be installed",i,info->settings.channel_names+CHN_NAME_LENGTH*i); + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2")) + cm_msg(MLOG,"","Channel %d(%s): CSV Channel : CA event handler will NOT " + "be installed",i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->subscribed+i) = TRUE; /* mark as handled */ + } + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = FALSE; /* channel is currently up */ + } else { + if (!*(info->down+i)) { + if (ca_state(*(info->pchid+i)) != cs_conn) + cm_msg(MLOG,"","Channel %d(%s,%s) ID (%p) is DOWN",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(*(info->pchid+i)), *(info->pchid+i)); + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = TRUE; /* channel is currently down */ + } + /* channel already marked for first read after DOWN */ + } + cm_msg_flush_buffer(); + } /* for */ + + /* enable event handler callback subscription in connection handler */ + info->subscribe_disabled = FALSE; /* enable subscription now */ + +#ifdef OBSOLETE + /* flush send buffer and block for outstanding queries */ + iotimeout = channels*0.2 + 1.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for DBR_FLOAT event handler sub" + "scription of channels before timeout",iotimeout); + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(%f) status 0X%x = \"%s\"", iotimeout, status, + ca_message(status)); +#endif +#endif /* OBSOLETE */ + + /* allow some time before proceeding */ + epics_ca_process(info); + ca_pend_event(5.0); /* process receive buffer to handle events */ + if (channels > 50) ca_pend_event(4.0); /* check again for new gateway */ + if (channels > 100) ca_pend_event(4.0); /* check again for new gateway */ + if (channels > 150) ca_pend_event(4.0); /* check again for new gateway */ +#endif /* DO_MONITORING */ + + epics_ca_process(info); +#ifdef DO_GET + cm_msg(MLOG,"","Executing DO_GET to get initial readout of epics channels"); + cm_msg_flush_buffer(); + /* buffer add change notifications */ + for (i = 0; i < channels; i++) { + epics_ca_process(info); + *(info->array+i) = -2.f; + /* only for connected channels */ + if (*(info->settings.used+i) && ((castate=ca_state(*(info->pchid+i))) == cs_conn)) { + /* chan specific: omit channels with S-flag e.g.: returning strings + * and :COM:2 and :CSV:2 channels */ + if ((strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2") == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2") == NULL)){ + + status = ca_get(DBR_FLOAT, info->pchid[i],info->array+i); + *(info->status+i) = status; + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR,"epics_ca_init","Channel %d(%s): ca_get() returned status " + "0x%X = \"%s\"",i,info->settings.channel_names+CHN_NAME_LENGTH*i, + status, ca_message(status)); + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d(%s): CA get (%s,%p,float)", i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + ca_name(info->pchid[i]), *(info->pchid+i)); +#endif + *(info->get+i) = TRUE; + + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_init", "ca_flush_io() returned %d",status); + + /* flush send buffer and block for outstanding queries */ + iotimeout = 0.9; +#ifdef MIDEBUG + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for ca_get(%s) " + "before timeout",iotimeout, + info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) { + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() waiting %.1f seconds for " + "ca_get(%s) returned ECA_TIMEOUT",iotimeout, + info->settings.channel_names+CHN_NAME_LENGTH*i); + } else if (status == ECA_NORMAL) { + epics_ca_process(info); +#ifdef GFA_SPECIFIC + // show initial gfa specific status + show_gfa_changes(info,i,TRUE,-2.f,*(info->array+i),"INI"); +#endif /* #ifdef GFA_SPECIFIC */ + } + } + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S')) + cm_msg(MLOG,"","Channel %d(%s): S flag: NO CA get", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#ifdef MIDEBUG + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2")) + cm_msg(MLOG,"","Channel %d(%s): COM Channel : NO CA get",i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2")) + cm_msg(MLOG,"","Channel %d(%s): CSV Channel : NO CA get", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->get+i) = TRUE; /* mark as handled */ + } + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = FALSE; /* channel is currently up */ + } else { + if (!*(info->down+i)) { + if (ca_state(*(info->pchid+i)) != cs_conn) + cm_msg(MLOG,"","Channel %d(%s,%s) ID (%p) is DOWN",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(*(info->pchid+i)), + *(info->pchid+i)); + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = TRUE; /* channel is currently down */ + } + /* channel already marked for first read after DOWN */ + } + cm_msg_flush_buffer(); + } /* for */ + +#ifdef GFA_SPECIFIC + // show initial gfa specific demand value + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":SOL:") != NULL) { + if (*(info->status+i) == ECA_NORMAL) + cm_msg(MLOG,"","INI: Channel %d(%s): Current Demand is %f",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,*(info->array+i)); + } + cm_msg_flush_buffer(); + } /* for */ + + // show initial gfa specific scale factor + for (i = 0; i < channels; i++) { + epics_ca_process(info); + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":SCALE:2:factor")!=NULL){ + if (*(info->status+i) == ECA_NORMAL) + cm_msg(MLOG,"","INI: Channel %d(%s): Current scale factor is %f",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,*(info->array+i)); + } + cm_msg_flush_buffer(); + } /* for */ +#endif /* #ifdef GFA_SPECIFIC */ + +#ifdef OBSOLETE + /* flush send buffer and block for outstanding queries */ +#ifdef MIDEBUGCLA + printf("+++ CA client status before ca_pend_io(%f) after ca_get(,NOCB) ******\n", + iotimeout); + ca_client_status(CLLEVEL); + printf("--- CA client status before ca_pend_io(%f) after ca_get(,NOCB) ******\n", + iotimeout); +#endif + iotimeout = channels*0.4 + 1.0; + cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for DBR_FLOAT get call " + "of channels before timeout",iotimeout); + status = ca_pend_io(iotimeout); + if (status == ECA_TIMEOUT) + cm_msg(MERROR, "epics_ca_init", "ca_pend_io() returned ECA_TIMEOUT"); +#ifdef MIDEBUGCLA + printf("+++ CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_get(,NOCB) ******\n", iotimeout, ca_message(status)); + ca_client_status(CLLEVEL); + printf("--- CA client status after ca_pend_io(%f)=\"%s\" after " + "ca_get(,NOCB) ******\n", iotimeout, ca_message(status)); +#endif +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(%f) status 0X%x = \"%s\"", iotimeout, status, + ca_message(status)); +#endif + cm_msg_flush_buffer(); +#endif /* OBSOLETE */ + +#endif /* DO_GET */ + + /* initialize demand of channel (and array_m) from readout array + * if iscmd flag is set and not down + * else init mirror array_m from readout array + */ + for (i=0; i < info->num_channels; i++) { + epics_ca_process(info); + if (*(info->iscmd+i) && !*(info->down+i)) + epics_ca_get_demand(info, i, info->demand+i); + else + *(info->array_m+i) = *(info->array+i); + } + +#ifdef MIDEBUGCLA + printf("+++ CA client status before epics_ca_process() after init of demand channel " + "******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status before epics_ca_process() after init of demand channel " + "******\n"); +#endif + epics_ca_process(info); +#ifdef MIDEBUGCLA + printf("+++ CA client status after epics_ca_process() after init of demand channel " + "******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status after epics_ca_process() after init of demand channel " + "******\n"); +#endif + + /* got here without fatal error -> enable all commands */ + info->cmd_disabled = FALSE; + + do_callback = TRUE; /* ca_get_callback() may now be posted in epics_ca_get() */ +#ifdef DO_GET_CALLBACK + cm_msg(MLOG,"","DO_GET_CALLBACK periodically reading channels"); + cm_msg_flush_buffer(); +#endif + +#ifdef MIDEBUG1 + cm_msg(MLOG,"","--epics_ca_init()"); +#endif +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : --epics_ca_init()\n", ss_time()); +#endif + +#ifdef FORTIFY + Fortify_CheckAllMemory(); + Fortify_OutputStatistics(); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ + +INT epics_ca_exit(CA_INFO *info) { + int i, status; + +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : ++epics_ca_exit()\n", ss_time()); +#endif +#ifdef MIDEBUG1 + cm_msg(MLOG,"","++epics_ca_exit()"); +#endif + +#ifdef MIDEBUGCL + printf("+++ CA client status at begin of epics_ca_exit() ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status at begin of epics_ca_exit() ******\n"); +#endif + process_connection = FALSE; + process_event = FALSE; + do_callback = FALSE; + + status = ca_pend_io(0.1); + epics_ca_process(info); + + if (info) { + /* NOTE: not necessary to call ca_clear_subscription() */ + + /* release channels and event subscription */ + for (i = 0; i < info->num_channels; i++) { + if (*(info->pchid+i)) { /*&& (ca_state(*(info->pchid+i))==cs_conn)) { */ + status = ca_clear_channel(*(info->pchid+i)); + if (!(status & ECA_NORMAL)) + cm_msg(MERROR, "epics_ca_exit", "Channel %d(%s): ca_clear_channel() returned " + "status 0X%x = \"%s\"",i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + status, ca_message(status)); + ca_flush_io(); + status = ca_pend_io(0.01); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(0.01) status 0X%x = \"%s\"",status,ca_message(status)); +#endif + status = ca_poll(); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_poll()"); +#endif + } + } + + status = ca_pend_io(1.0); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(1.0) status 0X%x = \"%s\"", status, ca_message(status)); +#endif + if (info->num_channels > 50) ca_pend_event(1.0); + if (info->num_channels > 100) ca_pend_event(1.0); + if (info->num_channels > 150) ca_pend_event(1.0); + } + + ca_pend_event(1.0); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_event(1.0)"); +#endif + + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_exit", "ca_flush_io() returned status 0X%x = \"%s\")", + status, ca_message(status)); + + epics_ca_process(info); + process_exception = FALSE; + + status = ca_flush_io(); + +#ifdef PREEMPTIVE + /* join other thread */ + // maybe wait for thread to stop +#ifdef OS_UNIX + pollthreadkeeprunning = 0; + sched_yield(); + if (pollthreadid != 0) { + // NIY Maybe turn off Midas watchdog + pthread_join(pollthreadid,NULL); + pollthreadid = 0; + } + pollthreadkeeprunning = -1; + + sched_yield(); +#else + NIY for OS +#endif +#endif /* PREEMPTIVE */ + +#ifdef MIDEBUGCL + printf("+++ CA client status at end of epics_ca_exit() before calling " + "ca_context_destroy() ******\n"); + ca_client_status(CLLEVEL); + printf("--- CA client status at end of epics_ca_exit() before calling " + "ca_context_destroy() ******\n"); +#endif + + /* close channel access */ + ca_context_destroy(); +#ifdef MIDEBUG + cm_msg(MLOG,"","EPICS ca_context_destroy() closed CA access"); +#endif + + /* release mutex */ +#ifdef PREEMPTIVE + epicsMutexDestroy(process_mutex); + epicsMutexDestroy(exception_mutex); + epicsMutexDestroy(connection_mutex); + epicsMutexDestroy(event_mutex); +#endif + + /* --------------------------- End of epics CA exit --------------------- */ + + /* free arrays */ + if (info) { + if (info->settings.channel_names) { free(info->settings.channel_names); + info->settings.channel_names = NULL; } +#ifdef GFA_SPECIFIC + if (info->settings.dtype) { free(info->settings.dtype); + info->settings.dtype = NULL; } + if (info->settings.dsec) { free(info->settings.dsec); + info->settings.dsec = NULL; } +#endif + if (info->settings.chnflags) { free(info->settings.chnflags); + info->settings.chnflags = NULL; } + if (info->settings.minval) { free(info->settings.minval); + info->settings.minval = NULL; } + if (info->settings.maxval) { free(info->settings.maxval); + info->settings.maxval = NULL; } + if (info->settings.tolerance) { free(info->settings.tolerance); + info->settings.tolerance = NULL; } + if (info->settings.used) { free(info->settings.used); + info->settings.used = NULL; } + if (info->settings.readonly) { free(info->settings.readonly); + info->settings.readonly = NULL; } + + if (info->pchid) { free(info->pchid); info->pchid = NULL; } + if (info->array_new) { free(info->array_new); info->array_new = NULL; } + if (info->array_new_m) { free(info->array_new_m); info->array_new_m = NULL; } + if (info->array) { free(info->array); info->array = NULL; } + if (info->array_m) { free(info->array_m); info->array_m = NULL; } + if (info->demand) { free(info->demand); info->demand = NULL; } + if (info->lastset) { free(info->lastset); info->lastset = NULL; } + if (info->lastget) { free(info->lastget); info->lastget = NULL; } + if (info->get) { free(info->get); info->get = NULL; } + if (info->getcalled) { free(info->getcalled); info->getcalled = NULL; } + if (info->status_new) { free(info->status_new); info->status_new = NULL; } + if (info->status_new_m) { free(info->status_new_m); info->status_new_m = NULL; } + if (info->status) { free(info->status); info->status = NULL; } + if (info->found_new) { free(info->found_new); info->found_new = NULL; } + if (info->found_new_m) { free(info->found_new_m); info->found_new_m = NULL; } + if (info->found) { free(info->found); info->found = NULL; } + if (info->iscmd) { free(info->iscmd); info->iscmd = NULL; } + if (info->subscribed) { free(info->subscribed); info->subscribed = NULL; } + if (info->down) { free(info->down); info->down = NULL; } + if (info->first) { free(info->first); info->first = NULL; } + if (info->firstread) { free(info->firstread); info->firstread = NULL; } + if (info->nerr) { free(info->nerr); info->nerr = NULL; } + if (info->nmsg) { free(info->nmsg); info->nmsg = NULL; } +#ifdef GFA_SPECIFIC + if (info->tolchan) { free(info->tolchan); info->tolchan = NULL; } + if (info->gchan) { free(info->gchan); info->gchan = NULL; } + if (info->intol) { free(info->intol); info->intol = NULL; } +#endif + if (info->threshold) { free(info->threshold); info->threshold = NULL; } + + if (ehandler_nerr) { free(ehandler_nerr); ehandler_nerr = NULL; } + if (ehandler_lasterr) { free(ehandler_lasterr); ehandler_lasterr = NULL; } + + free(info); + } + +#ifdef MIDEBUG1 + cm_msg(MLOG,"","--epics_ca_exit()"); +#endif + +#ifdef MIDEBUGF + if (fp) { + fprintf(fp,"%u : --epics_ca_exit()\n",ss_time()); + fclose(fp); + fp = NULL; + } +#endif +#ifdef MIDEBUGFX + if (fpx) { + fclose(fpx); + fpx = NULL; + } +#endif +#ifdef MIDEBUGFC + if (fpc) { + fclose(fpc); + fpc = NULL; + } +#endif +#ifdef MIDEBUGFE + if (fpe) { + fclose(fpe); + fpe = NULL; + } +#endif + +#ifdef FORTIFY + Fortify_CheckAllMemory(); + Fortify_OutputStatistics(); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +/* + * epics_ca_set(channel,value,flush,forced_update) + * + * flush TRUE = flush the send buffer flushing all posted ca_put()calls + * forced_update: TRUE = post demand value to epics channel even if the demand value did + * not change + * + * GFA specific: for :SOL:2 EPICS channels + * and device type BEND, QUAD, SEPT, STER, PS, SOLE, TS or KICKER + * and P, Z or N flag set send Power ON or OFF command according to + * flag specific rule and demand value to :COM:2 + * GFA specific: for :SOL:2 EPICS channels or :SOL EPICS channels + * and device type FENTE + * and V flag set send MOVE_TO_SOL command to :COM:2 + */ + +INT epics_ca_set(CA_INFO *info, INT channel, float value, BOOL flush,BOOL forced_update){ +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : ++epics_ca_set(channel=%d(%s), value=%f, flush=%d, force_upd" + "=%d)\n", ss_time(), channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, flush, forced_update); +#endif +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_set(channel=%d, value=%f, flush=%d,force_upd=%d)", + channel,value,flush,forced_update); + cm_msg_flush_buffer(); +#endif + epics_ca_process(info); + if (info) { + if (channel < info->num_channels) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'U') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'S') == NULL) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'M') != NULL) { + if ((*(info->settings.minval+channel) != 0.f) || + (*(info->settings.maxval+channel) != 0.f)) { + if (*(info->found+channel) && *(info->settings.used+channel) && + !*(info->settings.readonly+channel) && *(info->iscmd+channel)) { + float scalefactor; + + scalefactor = 1.0f; +#ifdef GFA_SPECIFIC + /* :SOL:2 may have scale factor -> check min/max considering scalef */ + /* channel name contains :SOL:2 and is not FENTE */ + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ":SOL:2") && + (strncmp(info->settings.dtype+DTYPE_LENGTH*channel,"FENTE",5)!=0)) { + char chname[CHN_NAME_LENGTH]; + char *tpos; + + strcpy(chname,info->settings.channel_names+CHN_NAME_LENGTH*channel); + + if ((tpos = strstr(chname,":SOL:2")) != NULL) { + int i; + + strcpy(tpos,":SCALE:2:factor"); +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Looking for channel %s",chname); +#endif + /* try to find channel number of :SCALE:2:factor */ + for (i = 0; i < info->num_channels; i++) { + if (i==channel) continue; + if (strcmp(info->settings.channel_names+CHN_NAME_LENGTH*i,chname) + == 0) { + float tval; + + if (!*(info->settings.used+i)) break; /* NOT Used */ + + /* F flag currently set demand value else readout */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'F')) + scalefactor = *(info->demand+i); + else + scalefactor = *(info->array_m+i); + if (scalefactor != 1.f) + cm_msg(MLOG,"","Channel %d (%s): Scale factor is %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + scalefactor); + break; /* channel found */ + } + } + } + } +#endif + +#ifdef RESET_DEMAND_VALUE_TO_RANGE + if (value < *(info->settings.minval+channel)*scalefactor) { + if (scalefactor == 1.f) { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) lower than " + "min value (%f)! - Setting min value", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.minval+channel)*scalefactor); + } else { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) lower than " + "min value (%f) * scale factor (%f)! - Setting min " + "value (%f) * scale factor", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.minval+channel),scalefactor, + *(info->settings.minval+channel)*scalefactor); + } + value = *(info->settings.minval+channel)*scalefactor; + /* NOTE: demand in ODB is not (re)set! */ + } else if (value > *(info->settings.maxval+channel)*scalefactor) { + if (scalefactor == 1.f) { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) larger than " + "max value (%f)! - Setting max value", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.maxval+channel)*scalefactor); + } else { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) larger than " + "max value (%f) * scale factor (%f)! - Setting max " + "value (%f) * scale factor", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.maxval+channel),scalefactor, + *(info->settings.maxval+channel)*scalefactor); + } + value = *(info->settings.maxval+channel)*scalefactor; + /* NOTE: demand in ODB is not (re)set! */ + } +#else + if (value < *(info->settings.minval+channel)*scalefactor) { + if (scalefactor == 1.f) { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) lower than " + "min value (%f)! - NOT Setting min value", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.minval+channel)*scalefactor); + } else { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) lower than " + "min value (%f) * scale factor (%f)! - NOT Setting " + "min value (%f) * scale factor", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.minval+channel),scalefactor, + *(info->settings.minval+channel)*scalefactor); + } + if (forced_update) forced_update = FALSE; + value = *(info->demand+channel); + } else if (value > *(info->settings.maxval+channel)*scalefactor) { + if (scalefactor == 1.f) { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) larger than " + "max value (%f)! - NOT Setting max value", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.maxval+channel)*scalefactor); + } else { + cm_msg(MLOG,"","Channel %d (%s): New demand value (%f) larger than " + "max value (%f) * scale factor (%f)! - NOT Setting " + "max value (%f) * scale factor", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value, *(info->settings.maxval+channel),scalefactor, + *(info->settings.maxval+channel)*scalefactor); + } + if (forced_update) forced_update = FALSE; + value = *(info->demand+channel); + } +#endif /* #ifdef RESET_DEMAND_VALUE_TO_RANGE */ + } + } + } + } + } + + if ((value != *(info->demand+channel))||forced_update) { + if (*(info->found+channel) && *(info->settings.used+channel) && + !*(info->settings.readonly+channel) && *(info->iscmd+channel)) { + enum channel_state castate; + if ((castate=ca_state(*(info->pchid+channel)))== cs_conn) { + if (ca_write_access(*(info->pchid+channel))) { + int status; + +#ifdef GFA_SPECIFIC + { + char *tpos; + + /* channel has : */ + if ((tpos=strchr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ':')) != NULL) { + int j; + + for (j = 0; j < N_GFA_TYPES; j++) { + /* find type */ + if (strcmp(gfatype[j].type,info->settings.dtype+DTYPE_LENGTH*channel) + ==0){ + GFA_channel *tc; + int k; +#ifdef MIDEBUGE + cm_msg(MLOG,"","Found type %s", gfatype[j].type); +#endif + tc = gfatype[j].tc; + for (k=0; k < gfatype[j].nc; k++) { /* channels of this type */ + + if (!(tc+k)) continue; + + if (strcmp(tpos,(tc+k)->spec ) == 0) { /* matching : */ + int l; + GFA_mask *cm; +#ifdef MIDEBUGE + cm_msg(MLOG,"","Found spec %s NS=%d",(tc+k)->spec,(tc+k)->ns); +#endif + cm = (tc+k)->cm; + for (l = 0; l < (tc+k)->ns; l++) { /* for all masks */ + int m; + int ttval, tdem; + GFA_msg *sm; + + if (!(cm+l)) continue; +#ifdef MIDEBUGE + cm_msg(MLOG,"","Checking Mask 0x%4.4X",(cm+l)->mask); +#endif + ttval = (int)value & (cm+l)->mask; + tdem = (int) *(info->demand+channel) & (cm+l)->mask; + if (*(info->first+channel)) tdem = 0; /* hide setup */ + if (( ttval != tdem)||*(info->first+channel)) { + char *tpost,*tposa; + tpost = tposa = NULL; + + sm = (cm+l)->sm; + for (m = 0; m < (cm+l)->nm; m++) { + if (!(sm+m)) continue; + /* cm_msg(MLOG,"","check Value 0x%4.4X : \"%s\"", + * (sm+m)->value, (sm+m)->msg); */ + if ((sm+m)->value == ttval) { + if (tpost) cm_msg(MLOG,"","New value: %s overwritten" + " by %s", tpost, (sm+m)->msg); + tpost = (sm+m)->msg; + } + if ((sm+m)->value == tdem) { + if (tposa) cm_msg(MLOG,"","Prev value: %s overwritten" + " by %s", tposa, (sm+m)->msg); + tposa = (sm+m)->msg; + } + } /* for all values */ + + if (tpost) { + if (*(info->first+channel)) tposa = NULL;/*hide */ + if (tposa) { +#ifdef OMIT_ODBUPDATE + if (!strstr(info->settings.channel_names+ + CHN_NAME_LENGTH*channel,":COM:")) +#endif + cm_msg(MLOG,"","%s: \"%s\" changed to \"%s\"", + info->settings.channel_names+CHN_NAME_LENGTH*channel, + tposa, tpost); + } else { + if (strstr(info->settings.channel_names+ + CHN_NAME_LENGTH*channel,":COM:")) { +#ifndef OMIT_ODBUPDATE + cm_msg(MLOG,"","%s: Command is \"%s\"", + info->settings.channel_names+CHN_NAME_LENGTH*channel, + tpost); +#endif + } else { + cm_msg(MLOG,"","%s: \"%s\" is currently set", + info->settings.channel_names+CHN_NAME_LENGTH*channel, + tpost); + } + } + /* nothing assigned to mask */ + } else { + if (*(info->demand+channel) != -2.f) { +#ifndef OMIT_ODBUPDATE + if (!strstr(info->settings.channel_names + +CHN_NAME_LENGTH*channel, ":SOL:2")) +#endif + cm_msg(MLOG,"","Channel %d(%s) %f set to %f", + channel,info->settings.channel_names + +CHN_NAME_LENGTH*channel, + *(info->demand+channel),value); + } else + cm_msg(MLOG,"","Channel %d(%s) set to %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value); + } + } + } /* for all masks */ + if ((tc+k)->ns <= 0) { + if (*(info->demand+channel) != -2.f) + cm_msg(MLOG,"","Channel %d(%s) %f set to %f",channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + *(info->demand+channel),value); + else + cm_msg(MLOG,"","Channel %d(%s) set to %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value); + } + break; + } + } /* for all channels of this type */ + if (gfatype[j].nc <= 0) { + if (*(info->demand+channel) != -2.f) + cm_msg(MLOG,"","Channel %d(%s) %f set to %f",channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + *(info->demand+channel),value); + else + cm_msg(MLOG,"","Channel %d(%s) set to %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value); + } + break; + } + } /* for all GFA types */ + } + } +#else + if (*(info->demand+channel) != -2.f) + cm_msg(MLOG,"","Channel %d(%s) %f set to %f",channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + *(info->demand+channel),value); + else + cm_msg(MLOG,"","Channel %d(%s) set to %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + value); +#endif /* #ifdef GFA_SPECIFIC */ + + /* :COM:2 or :CSV:2 no readout therefore simulate readout of channel */ + if ((strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":COM:2")) + ||(strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":CSV:2"))) + *(info->array_m+channel) = value; + + // Do not send negative values for :COM:2 to the slit system FENTE, as neg. + // values are only used to make sure the command value to be set + // is not yet set in the ODB. + if ((strcmp(info->settings.dtype+DTYPE_LENGTH*channel,"FENTE")==0) && + strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":COM:2") && + (value < 0.f)) { + + } else { + /* NIY TODO VALVE send command AUF/ZU instead of 0/1 to :CSV:2 ? */ + ca_put(DBR_FLOAT, info->pchid[channel], &value); + } + +#ifdef GFA_SPECIFIC + /* GFA specific handling : maybe post pending command */ + if (!info->handling_disabled) { + char *ds; + + /* disable GFA specific handling */ + info->handling_disabled = TRUE; + + /* type and channel specific changes of other channel(s) + * when specific flags of this channel set and apply rule to new value + * e.g. Z flag : Turn power off when new value is zero */ + ds = info->settings.dtype+DTYPE_LENGTH*channel; +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Looking for device specific handling of Device type %s",ds); +#endif + if ((strcmp(ds,"BEND")==0) || (strcmp(ds,"QUAD")==0) || + (strcmp(ds,"SEPT")==0) || (strcmp(ds,"STER")==0) || + (strcmp(ds,"PS" )==0) || (strcmp(ds,"SOLE")==0) || + (strcmp(ds,"TS" )==0) || (strcmp(ds,"KICKER")==0)) { + char chname[CHN_NAME_LENGTH]; + char *tpos; + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Device type %s found",ds); +#endif + strcpy(chname,info->settings.channel_names+CHN_NAME_LENGTH*channel); + + if ((tpos = strstr(chname,":SOL:2")) != NULL) { + int i; + + strcpy(tpos,":COM:2"); +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Looking for channel %s",chname); +#endif + for (i = 0; i < info->num_channels; i++) { + if (i==channel) continue; + if (strcmp(info->settings.channel_names+CHN_NAME_LENGTH*i,chname) + == 0) { + float tval; + char *chflgs,flag; + + tval = *(info->demand+i); /* current value of :COM:2 channel */ + flag = '?'; + /* evaluate channel flags of channel */ + chflgs = info->settings.chnflags+CHNFLAGS_LENGTH*channel; + if (strchr(chflgs,'P')) { + tval = 0.f; /* ON */ + flag = 'P'; + } + if (strchr(chflgs,'Z')) { + if (value == 0.f) + tval = 1.f; /* OFF */ + else + tval = 0.f; /* ON */ + flag = 'Z'; + } + if (strchr(chflgs,'N')) { + if (value > 0.f) + tval = 0.f; /* ON */ + else + tval = 1.f; /* OFF */ + flag = 'N'; + } + if (tval != *(info->demand+i)) { + if (*(info->found+i) && *(info->settings.used+i) && + !*(info->settings.readonly+i)) { + HNDLE hDB; + + if (tval == 0.f) + cm_msg(MLOG,"","%c flag: Sending \"Power ON\" to channel " + "%d (%s)", flag, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + else + cm_msg(MLOG,"","%c flag: Sending \"Power OFF\" to channel" + " %d (%s)", flag, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + + epics_ca_set(info, i, tval, FALSE, forced_update); +#ifndef OMIT_ODBUPDATE + /* update ODB */ + cm_get_experiment_database(&hDB, NULL); + if (info->hkeyDemand != 0) { + INT mstatus; + mstatus = db_set_data_index(hDB, info->hkeyDemand, &tval, + sizeof(float), i, TID_FLOAT); + if (mstatus != DB_SUCCESS) ; /* NIY message */ + + } +#endif + } + } + break; /* channel found */ + } + } + } + + } else if (strcmp(ds,"FENTE")==0) { + char chname[CHN_NAME_LENGTH]; + char *tpos; + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Device type %s found",ds); +#endif + strcpy(chname,info->settings.channel_names+CHN_NAME_LENGTH*channel); + + if (((tpos = strstr(chname,":SOL:2")) != NULL) || + ((tpos = strstr(chname,":SOL")) != NULL)) { + int i,issol2; + + if (strstr(chname,":SOL:2") != NULL) // old slit system DAC + issol2 = TRUE; // :SOL:2 + else + issol2 = FALSE; // new slit system [mm] without STA:2 handling + // :SOL + strcpy(tpos,":COM:2"); +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Looking for channel %s",chname); +#endif + for (i = 0; i < info->num_channels; i++) { + if (i==channel) continue; + if (strcmp(info->settings.channel_names+CHN_NAME_LENGTH*i,chname) + == 0) { + float tval; + char *chflgs,flag; + + tval = *(info->demand+i); /* current value of :COM:2 channel */ + flag = '?'; + /* evaluate channel flags of channel */ + chflgs = info->settings.chnflags+CHNFLAGS_LENGTH*channel; + if (strchr(chflgs,'V')) { + if (issol2) + tval = 3.f; /* 3 is MOVE_TO_SOL for :SOL:2*/ + else + tval = 4.f; /* 4 is MOVE_TO_SOL for :SOL */ + flag = 'V'; + } + if ((tval != *(info->demand+i)) || !issol2) { + if (*(info->found+i) && *(info->settings.used+i) && + !*(info->settings.readonly+i)) { + HNDLE hDB; + + if (issol2) { // old slit system + if (tval == 3.f) + cm_msg(MLOG,"","%c flag: Sending 3=\"MOVE_TO_SOL\" to " + "channel %d (%s)", flag, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + else + cm_msg(MLOG,"","%c flag: Sending %.1f to channel %d (%s)", + flag, tval, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + } else { + if (tval == 4.f) { + float tval1; + + // flush all pending commands and do forced update of :COM:2 + // ca_pend_io(0.01) which flushes commands is a delay of 100ms + // to debug MOVE_TO_SOL command will be immediately flushed + + // Testing what may deblock the slits control + // the new slit control sometimes does not move + + status = ca_pend_io(0.01); // TEST flush previous commands + ss_sleep(500); // TEST wait 0.5 sec + +// Martin Gasche suggested STOP may be helpful when doing it manually? +// however in this case it seems to be even worse and should not be done here! +//#ifdef _USE_STOP_ +// /* TEST sending STOP */ +// cm_msg(MLOG,"","%c flag: Sending 3=\"STOP\" to " +// "channel %d (%s)", flag, i, +// info->settings.channel_names+CHN_NAME_LENGTH*i); +// tval1 = 3.f; +// epics_ca_set(info, i, tval1, TRUE, TRUE); // set and flush +// ss_sleep(500); // TEST wait 0.5 sec +//#endif + /* send MOVE_TO_SOL */ + cm_msg(MLOG,"","%c flag: Sending 4=\"MOVE_TO_SOL\" to " + "channel %d (%s)", flag, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + epics_ca_set(info, i, tval, TRUE, TRUE); // set and flush + ss_sleep(500); // TEST wait 0.5 sec + + // RA36 28-APR-2023 commented second execution of command + // do it a second time + //epics_ca_set(info, i, tval, TRUE, TRUE); // set and flush + //ss_sleep(500); // TEST wait 0.5 sec + } else { + cm_msg(MLOG,"","%c flag: Sending %.1f to channel %d (%s)", + flag, tval, i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + } + } + epics_ca_set(info, i, tval, FALSE, forced_update); // set COM:2 + // if called due to ODB change of SOL or SOL:2 flush=TRUE +#ifndef OMIT_ODBUPDATE + /* update ODB */ + cm_get_experiment_database(&hDB, NULL); + if (info->hkeyDemand != 0) { + INT mstatus; + mstatus = db_set_data_index(hDB, info->hkeyDemand, &tval, + sizeof(float), i, TID_FLOAT); + if (mstatus != DB_SUCCESS) ; /* NIY message */ + } +#endif + } + } + break; + } + } + } + } else if (strcmp(ds,"BX")==0) { + /* DO NOTHING */ + } else if (strcmp(ds,"VALVE")==0) { + /* DO NOTHING */ + } else if (strcmp(ds,"HVPS")==0) { + /* DO NOTHING (not enough information to be handled correctly) */ + } else { + cm_msg(MLOG,"","epics_ca_set: GFA type %s is currently not handled", + ds); + } + /* enable GFA specific handling */ + info->handling_disabled = FALSE; + } +#endif /* #ifdef GFA_SPECIFIC */ + + /* flush the send buffer sending all posted ca_put() calls */ + if (flush) { + status = ca_pend_io(0.01); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(0.01) status 0X%x = \"%s\"", status, + ca_message(status)); +#endif + } + epics_ca_process(info); + } else { + cm_msg(MLOG,"","SET: channel %d(%s) NO WRITE ACCESS!", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel); + } + } else { + cm_msg(MLOG,"","SET: channel %d(%s) is (currently?) not connected! " + "Channel state is %d = \"%s\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, castate, + castatestr(castate)); + } + } else if (!*(info->found+channel)){ + cm_msg(MLOG,"","SET: channel %d(%s) is (currently?) not enabled!", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel); + } else if (!*(info->settings.used+channel)) { + cm_msg(MLOG,"","SET: channel %d(%s) is not used!", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel); + } else if (*(info->settings.readonly+channel)) { + if (value != *(info->demand+channel)&& !info->silentreadonly) + cm_msg(MLOG,"","SET: channel %d(%s) %f <> %f is set read ONLY!", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel, + *(info->demand+channel), value); + } else if (!*(info->iscmd+channel)){ + cm_msg(MLOG,"","SET: channel %d(%s) is not a channel to be set!", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel); + } + } else { +#ifdef MIDEBUG + cm_msg(MLOG,"","Demand and readout (%f) did not change -> not updating channel" + " %d(%s)", value, channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); +#endif + } + if (info->lastset) *(info->lastset+channel) = ss_time(); + if (info->demand) *(info->demand+channel) = value; + } + } +#ifdef MIDEBUG1 + cm_msg(MLOG,""," --epics_ca_set()"); +#endif +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : --epics_ca_set(channel=%d, value=%f)\n", ss_time(), + channel,value); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ + +INT epics_ca_set_all(CA_INFO * info, INT channels, float value) { + INT status,i; + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_set_all(%d channels)",channels); +#endif + for (i = 0; i < MIN(info->num_channels, channels); i++) + epics_ca_set(info, i, value, FALSE, FALSE); + + status = ca_pend_io(0.01); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(0.01) status 0X%x = \"%s\"", status, ca_message(status)); +#endif + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," --epics_ca_set_all()"); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ + +INT epics_ca_set_label(CA_INFO * info, INT channel, char *label) { + int status; + char str[256]; + chid chan_id; + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_set_label(channel %d, label %s)", channel, label); +#endif + sprintf(str, "%s.DESC", info->settings.channel_names+CHN_NAME_LENGTH*channel); + status = ca_create_channel(str, NULL, NULL, 0, &chan_id); + if (status == ECA_NORMAL) { + status = ca_pend_io(2.0); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(2.0) status 0X%x = \"%s\"", status, ca_message(status)); +#endif + if (status == ECA_NORMAL) { + if (ca_state(chan_id) == cs_conn) { + status = ca_put(ca_field_type(chan_id), chan_id, label); + status = ca_pend_io(0.01); +#ifdef MIDEBUGP + status = cm_msg(MLOG,"", "ca_pend_io(0.01) status 0X%x = \"%s\"", status, + ca_message(status)); +#endif + } else { + cm_msg(MLOG,"","epics_ca_set_label: Channel %s is not connected", str); + } + } else { + cm_msg(MERROR,"epics_ca_set_label","%s not found", str); + } + ca_clear_channel(chan_id); + } else { + cm_msg(MERROR,"epics_ca_set_label","ca_create_channel(%s) Returned status " + "0X%x = \"%s\"",str,status,ca_message(status)); + } +#ifdef MIDEBUG1 + cm_msg(MLOG,""," --epics_ca_set_label()"); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +/* epics_ca_get + * get readout of channel calling epics_ca_get_demand() + * + * update demand value of channel if value changed + * check if all 'C' flagged channels are connected + * GFA specific: check if all 'T' flagged channels are in tolerance range + */ +INT epics_ca_get(CA_INFO * info, INT channel, float *pvalue) { + int status; + int i; + enum channel_state castate; + double iotimeout; + +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : ++epics_ca_get(channel=%d(%s))\n", ss_time(),channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); +#endif +#ifdef MIDEBUG2 + cm_msg(MLOG,""," ++epics_ca_get(channel %d)",channel); +#endif + + if (info) { + + /* NOTE: pending events will be handled in event handler callback */ + epics_ca_process(info); + + if (channel == 0) { + if (info->num_channels > 50) { +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_event(0.01)"); +#endif + ca_pend_event(0.01); + } else { +#ifdef MIDEBUG2 + /*cm_msg(MLOG,"","ss_sleep(50)"); */ +#endif + /*ss_sleep(50); */ +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_event(0.1)"); +#endif + ca_pend_event(0.1); + } + status = ca_pend_io(0.001); +#ifdef MIDEBUGP + cm_msg(MLOG,"","ca_pend_io(0.001) status 0X%x =\"%s\"",status,ca_message(status)); +#endif + } else { + ca_pend_event(0.001); + } + +#ifdef DO_GET_CALLBACK + if (do_callback ){ /*&& (channel == 0)) { */ + /* buffer add change notifications */ + for (i = 0; i < info->num_channels; i++) { + epics_ca_process(info); + /* only for connected channels */ + if (*(info->settings.used+i) && ((castate=ca_state(info->pchid[i])) == cs_conn)) { + /* chan specific: omit channels with S-flag e.g.: returning strings + * and :COM:2 and :CSV:2 channels */ + if ((strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S') == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2") == NULL) && + (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2") == NULL)){ + if (*(info->lastget+i) > ss_time()) *(info->lastget+i) = 0; + if ((ss_time() - *(info->lastget+i)) > 1) { + *(info->lastget+i) = ss_time(); + /* ca get callback for this channel converting value to float */ + status = ca_get_callback(DBR_FLOAT, *(info->pchid+i), + epics_ca_event_handler, info); + if (!(status & ECA_NORMAL)) { + cm_msg(MERROR,"epics_ca_get","Channel %d(%s): ca_get_callback() retur" + "ned status 0x%X = \"%s\"",i,info->settings.channel_names+CHN_NAME_LENGTH*i, + status, ca_message(status)); + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d(%s): CA get callback (float) is called", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->subscribed+i) = TRUE; + *(info->getcalled+i) = TRUE; + /* flush send buffer and block for outstanding queries */ + /* iotimeout = 0.1; + * cm_msg(MLOG,"","ca_pend_io() waiting %.1f seconds for ca_create_subscr " + * "before timeout",iotimeout); + * status = ca_pend_io(iotimeout); + * if (status == ECA_TIMEOUT) + * cm_msg(MERROR, "epics_ca_get", "ca_pend_io() returned ECA_TIMEOUT"); + */ + status = ca_flush_io(); + if (status != ECA_NORMAL) + cm_msg(MERROR, "epics_ca_get", "ca_flush_io() returned %d",status); + + } + } + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'S')) + cm_msg(MLOG,"","Channel %d(%s): S flag: CA get callback is NOT " + "called", i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#ifdef MIDEBUG + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":COM:2")) + cm_msg(MLOG,"","Channel %d(%s): COM Channel : CA get callback is NOT " + "called",i,info->settings.channel_names+CHN_NAME_LENGTH*i); + else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,":CSV:2")) + cm_msg(MLOG,"","Channel %d(%s): CSV Channel : CA get callback is NOT " + "called",i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->getcalled+i) = TRUE; /* mark as handled */ + } + /* NOTE: RECONNECT will be handled in epics_ca_process () */ + } else { + if (!*(info->down+i)) { + if (ca_state(*(info->pchid+i)) != cs_conn) + cm_msg(MLOG,"","Channel %d(%s,%s) ID (%p) is DOWN",i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ca_name(*(info->pchid+i)), + *(info->pchid+i)); + /*if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C') != NULL) */ + *(info->down+i) = TRUE; /* channel is currently down */ + } + *(info->firstread+i) = TRUE; /* mark channel for first read after DOWN */ + } + } + } +#endif /* DO_GET_CALLBACK */ + + /* return last value */ + if (*(info->found+channel)) { + if (ca_state(*(info->pchid+channel)) == cs_conn) { + if (ca_read_access(*(info->pchid+channel))) { + float newdemand,diff; + + newdemand = *(info->demand+channel); + /* process and then get demand value and also update array_m of dependent chans */ + epics_ca_get_demand(info, channel, &newdemand); + + /* returned readout */ + if (pvalue) *pvalue = *(info->array_m+channel); + + /* demand value changed */ + if (newdemand < *(info->demand+channel)) + diff = *(info->demand+channel) - newdemand; + else + diff = newdemand - *(info->demand+channel); + + if (diff > 0.0001f) { + /* difference of DWORD not negative due to change of system time? */ + if (ss_time() > *(info->lastset+channel)) { + /* if ((ss_time() - *(info->lastset+channel)) > 60) { */ + if ((ss_time() - *(info->lastset+channel)) > 2) { /* set to 2 secs */ + /* to speed up update */ + if (*(info->iscmd+channel)) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'F')==NULL){ + if (*(info->demand+channel) != -2.f) { +#ifdef OMIT_ODBUPDATE + if (newdemand != -2.f) +#else + if (!strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ":COM:2") && + !strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ":CSV:2")) +#endif + cm_msg(MLOG,"","Channel %d(%s): demand (%f) changed to %f", + channel,info->settings.channel_names+CHN_NAME_LENGTH*channel, + *(info->demand+channel),newdemand); + } else { + cm_msg(MLOG,"","Channel %d(%s): current demand is %f", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, + newdemand); + } + } + } + +#ifdef GFA_SPECIFIC + if ((*(info->iscmd+channel)) && ((*(info->gchan+channel) > 0) || + (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'F')!=NULL))){ +#else + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'F')!=NULL){ +#endif + int size; + HNDLE hDB; +#ifdef GFA_SPECIFIC + BOOL oldflag; +#endif + +#ifdef GFA_SPECIFIC + oldflag = info->handling_disabled; + /* maybe disable handling of GFA specific */ + /* info->handling_disabled = TRUE; */ + /* F flag: set new demand value (limits may be checked and set) */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'F')!=NULL){ +#endif + DWORD lastset; + /* NOTE: ...ca_set() sets *(info->demand+channel) to newdemand + * and puts new value to "command" channel */ +#ifndef OMIT_ODBUPDATE + info->silentreadonly = TRUE; +#endif + lastset = *(info->lastset+channel); /* keep lastset time */ + epics_ca_set(info,channel,newdemand,TRUE,FALSE); + *(info->lastset+channel) = lastset; /* restore lastset */ + info->silentreadonly = FALSE; +#ifdef GFA_SPECIFIC + } else { + /* G flag: "status" feedback therefore not necessary to put newdemand + * to "command" channel again + * or not defined GFA_SPECIFIC */ + *(info->demand+channel) = newdemand; + } + info->handling_disabled = oldflag; +#endif + +#ifndef OMIT_ODBUPDATE + /* update ODB Demand */ + size = sizeof(float); + cm_get_experiment_database(&hDB, NULL); + if (info->hkeyDemand != 0) { + INT mstatus; + mstatus = db_set_data_index(hDB,info->hkeyDemand, &newdemand, + size, channel, TID_FLOAT); + if (mstatus != DB_SUCCESS) ; /* NIY message */ + } +#endif + } else + *(info->demand+channel) = newdemand; + *(info->lastset+channel) = ss_time(); + } + } else { + *(info->lastset+channel) = ss_time(); + } + } + *(info->nmsg+channel) = 0; + } else { + if (*(info->nmsg+channel) == 0) { + cm_msg(MLOG, "", "Channel %d(%s): NO read access!", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->nmsg+channel) += 1; + } + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'D') && pvalue) + *pvalue = -1.f; + } + } else { + if (*(info->nmsg+channel) == 0) { + cm_msg(MLOG, "", "Channel %d(%s): NOT connected!", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->nmsg+channel) += 1; + } + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'D') && pvalue) + *pvalue = -1.f; + } + } else { + if (*(info->nmsg+channel) == 0) { + if (*(info->settings.used+channel)) + cm_msg(MLOG, "", "Channel %d(%s): NOT found!", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->nmsg+channel) += 1; + } + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'D') && pvalue) + *pvalue = -1.f; + } + + /* do checks after all channels were processed once again */ + if (channel == info->num_channels-1) { + /* check every 10 seconds */ + if ((ss_time() > info->lastSNC) && ((ss_time() - info->lastSNC) > 10)) { + int i,tSNC; +#ifdef MIDEBUG1 + cm_msg(MLOG,"","C-flag: Checking if channels with C-flag are connected"); +#endif + for (i=0,tSNC=0; i < info->num_channels; i++) { + if (*(info->settings.used+i) && + (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'C')) && + (ca_state(*(info->pchid+i)) != cs_conn)) { +#ifdef MIDEBUG + cm_msg(MLOG,"","C-flag: Channel %d (%s) is currently not connected",i, + info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + tSNC++; + } + } + + if (tSNC != info->settings.Sum_NOT_Connected) { + cm_msg(MLOG,"","Number of Channels (with C-flag) NOT connected changed from " + "%d to %d", info->settings.Sum_NOT_Connected, tSNC); + info->settings.Sum_NOT_Connected = tSNC; + /* update ODB when different */ + if (info->hkeySNC) { + HNDLE hDB; + cm_get_experiment_database(&hDB, NULL); + db_set_data(hDB, info->hkeySNC, &info->settings.Sum_NOT_Connected, + sizeof(int), 1, TID_INT); + } + /* reset alarm when sum is 0 */ + if ((tSNC==0) && (strncmp(info->settings.AlarmWhenNOTConnected,"NONE",4) != 0)) + al_reset_alarm(info->settings.AlarmWhenNOTConnected); + } + + info->lastSNC = ss_time(); + + } else if (ss_time() < info->lastSNC) { + cm_msg(MLOG,"","Sum of channels NOT connected: System time was reset"); + info->lastSNC = ss_time(); + } + +#ifdef GFA_SPECIFIC + /* check every 10 seconds */ + if ((ss_time() > info->lastSNT) && ((ss_time() - info->lastSNT) > 10)) { + int i,tSNT; + BOOL *handled; +#ifdef MIDEBUG + cm_msg(MLOG,"","T-flag: Checking if channels with T-flag are in tolerance"); +#endif + handled = (BOOL*)calloc(info->num_channels,sizeof(BOOL)); + for (i=0,tSNT=0; i < info->num_channels; i++) { + /* T flag set, not handled yet and tolerance > 0 */ + /* if (*(info->found+i)&&*(info->settings.used+i)&&!*(info->settings.readonly+i)&& */ + if ( *(info->settings.used+i) && + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'T') && + (!handled || !*(handled+i)) && + ((*(info->settings.tolerance+i)>0.f)|| + strstr(info->settings.dtype+DTYPE_LENGTH*i,"VALVE")|| + strstr(info->settings.dtype+DTYPE_LENGTH*i,"BX"))){ + if (*(info->tolchan+i) == -1.f) { + char tename[CHN_NAME_LENGTH]; + int j; + BOOL findrefchan; + char *ds; + char *tpos; + + findrefchan = TRUE; + strcpy(tename,info->settings.channel_names+CHN_NAME_LENGTH*i); + + ds = info->settings.dtype+DTYPE_LENGTH*i; +#ifdef MIDEBUG2 + cm_msg(MLOG,"","Looking for device specific handling of Device type %s",ds); +#endif + if ((strcmp(ds,"BEND")==0) || (strcmp(ds,"QUAD")==0) || + (strcmp(ds,"SEPT")==0) || (strcmp(ds,"STER")==0) || + (strcmp(ds,"PS" )==0) || (strcmp(ds,"SOLE")==0) || + (strcmp(ds,"TS" )==0) || (strcmp(ds,"KICKER")==0)) { + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","T-flag: Device type %s found",ds); +#endif + /* assign corresponding channel */ + if ((tpos = strstr(tename,":SOL:2")) != NULL) { + strcpy(tpos,":IST:2"); + } else if ((tpos = strstr(tename,":IST:2")) != NULL) { + strcpy(tpos,":SOL:2"); + } else { + cm_msg(MLOG,"","T-flag: Channel %d (%s) not handled for type %s", i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ds); + findrefchan = FALSE; + } + } else if (strcmp(ds,"FENTE")==0) { + /* assign corresponding channel steps */ + if ((tpos = strstr(tename,":SOL:2")) != NULL) { // NIY Ambiguity + strcpy(tpos,":IST1:2"); // with POSA:2 + } else if ((tpos = strstr(tename,":IST1:2")) != NULL) { + strcpy(tpos,":SOL:2"); + } else if ((tpos = strstr(tename,":POSA:2")) != NULL) { + strcpy(tpos,":SOL:2"); + /* assign corresponding channel mm */ + } else if ((tpos = strstr(tename,":POSA")) != NULL) { + strcpy(tpos,":SOL"); + } else if ((tpos = strstr(tename,":SOL")) != NULL) { + strcpy(tpos,":POSA"); + } else { + cm_msg(MLOG,"","T-flag: Channel %d (%s) not handled for type %s", i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ds); + findrefchan = FALSE; + } + } else if (strcmp(ds,"BX")==0) { + /* special case handle :STA:1 only */ + /* (use out of tolerance when beam blocker not open) */ + if (strstr(tename,":STA:1") != NULL) { + int ivali; + + ivali = (int) *(info->array+i); + /* not open -> closed, moving or error */ + if ((ivali & 0x0001) != 0x0001) { + tSNT++; + if (*(info->intol+i) ) { + cm_msg(MLOG,"","Channel %d (%s): Beam blocker is NOT OPEN", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + *(info->intol+i) = FALSE; + } + } else { + if (*(info->intol+i)!= TRUE) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d (%s): Beam blocker is OPEN", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->intol+i) = TRUE; + } + } + } + if (handled) *(handled+i) = TRUE; + findrefchan = FALSE; + } else if (strcmp(ds,"VALVE")==0) { + /* special case handle :STA:1 only */ + /* (use out of tolerance when valve not open) */ + if (strstr(tename,":STA:1") != NULL) { + DWORD dwvali; + + dwvali = (DWORD) *(info->array+i); + /* not open -> armed or closed */ + if ((dwvali & 0x00000010) != 0x00000010) { + tSNT++; + if (*(info->intol+i) ) { + cm_msg(MLOG,"","Channel %d (%s): Valve is NOT OPEN", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); + *(info->intol+i) = FALSE; + } + } else { + if (*(info->intol+i)!= TRUE) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d (%s): Valve is OPEN", + i,info->settings.channel_names+CHN_NAME_LENGTH*i); +#endif + *(info->intol+i) = TRUE; + } + } + } + if (handled) *(handled+i) = TRUE; + findrefchan = FALSE; + } else if (strcmp(ds,"HVPS")==0) { + /* assign corresponding channel NOTE: I(max) has no tolerance! */ + if ((tpos = strstr(tename,":SOLV:2")) != NULL) { + strcpy(tpos,":ISTV:2"); + } else if ((tpos = strstr(tename,":ISTV:2")) != NULL) { + strcpy(tpos,":SOLV:2"); + } else { + cm_msg(MLOG,"","T-flag: Channel %d (%s) not handled for type %s", i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ds); + findrefchan = FALSE; + } + } else { + cm_msg(MLOG,"","T-flag: Channel %d (%s) type %s is not handled", i, + info->settings.channel_names+CHN_NAME_LENGTH*i,ds); + findrefchan = FALSE; + } + + if (findrefchan) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","T-flag: Searching for channel %s",tename); +#endif + for (j=0; j < info->num_channels; j++) { + if (i != j) { + if (strcmp(tename,info->settings.channel_names+CHN_NAME_LENGTH*j)==0){ + *(info->tolchan+i) = j; + /* NIY if (*(info->tolchan+j) > 0) warning channel was already + * assigned */ + *(info->tolchan+j) = i; + break; + } + } + } /* for all (other) channels */ + } else { + if (((strcmp(ds,"VALVE")==0)||(strcmp(ds,"BX")==0)) && + (strstr(tename,":STA:1") != NULL)) + *(info->tolchan+i) = -1.f; /* mark as NOT processed to redo */ + else + *(info->tolchan+i) = -2.f; /* mark as already processed */ + } + } /* search for corresponding channels */ + + if (*(info->tolchan+i) > 0) { /* tolerance needs to be checked */ + float tvali,tvalr,diff; + int j; + BOOL omit; + + omit = FALSE; + j = *(info->tolchan+i); + if (*(info->iscmd+i)) { + tvali = *(info->demand+i); + tvalr = *(info->array+j); + /* exceptions? + * N flag: Power Off when Negative -> omit */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'N')&&(tvali < 0.f)) + omit = TRUE; + /* OBSOLETE QUICK FIX WEDL neg. Amp readout must be 0.0 */ + /* OBSOLETE if ((strstr(info->settings.channel_names+CHN_NAME_LENGTH*j,"WEDL:IST:2")) + * &&(tvalr < 0.f)) tvalr = 0.f; */ + } else { + tvali = *(info->array+i); + tvalr = *(info->demand+j); + /* exceptions? + * N flag: Power Off when Negative -> omit */ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*j,'N')&&(tvalr < 0.f)) + omit = TRUE; + /* OBSOLETE QUICK FIX WEDL neg. Amp readout must be 0.0 */ + /* OBSOLETE if ((strstr(info->settings.channel_names+CHN_NAME_LENGTH*i,"WEDL:IST:2")) + * &&(tvali < 0.f)) tvali = 0.f; */ + } + + diff = tvali - tvalr; + if (diff < 0.f) diff = -diff; + if (diff > *(info->settings.tolerance+i)) { + if (!omit) { + if (*(info->intol+i) || *(info->intol+j)) { + cm_msg(MLOG,"","Channel %d (%s)=%f and Channel %d (%s)=%f : " + "difference %f is out of tolerance %f", + i,info->settings.channel_names+CHN_NAME_LENGTH*i,tvali, + j,info->settings.channel_names+CHN_NAME_LENGTH*j,tvalr, + diff, *(info->settings.tolerance+i)); + *(info->intol+i) = FALSE; + *(info->intol+j) = FALSE; + } + tSNT++; + } + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","Channel %d (%s)=%f and Channel %d (%s)=%f : " + "difference %f is in tolerance %f", + i,info->settings.channel_names+CHN_NAME_LENGTH*i,tvali, + j,info->settings.channel_names+CHN_NAME_LENGTH*j,tvalr, + diff, *(info->settings.tolerance+i)); +#endif + *(info->intol+i) = TRUE; + *(info->intol+j) = TRUE; + } + + if (handled) { + *(handled+i) = TRUE; + *(handled+j) = TRUE; + } + + } + } else { +#ifdef MIDEBUG + if (!*(info->settings.used+i)) + cm_msg(MLOG,"","Channel %d (%s): not used",i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*i,'T') == NULL) + cm_msg(MLOG,"","Channel %d (%s): T flag not set",i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + if (handled && *(handled+i)) + cm_msg(MLOG,"","Channel %d (%s): already handled",i, + info->settings.channel_names+CHN_NAME_LENGTH*i); + if (*(info->settings.tolerance+i)<=0.f) + cm_msg(MLOG,"","Channel %d (%s): invalid tolerance %f", i, + info->settings.channel_names+CHN_NAME_LENGTH*i, + *(info->settings.tolerance+i)); +#endif + } /* channel used? */ + } /* for all channels */ + + if (info->pending > 0) { + tSNT += info->pending; + } + + if (info->settings.Sum_NOT_in_Tolerance != tSNT) { + /* pending operations, tolerance check is not reliable */ + if ((info->pending > 0) && (info->pending == tSNT)) { + if (info->pending == 1) + cm_msg(MLOG,"","Pending operation -> In Range checking blocked"); + else + cm_msg(MLOG,"","%d Pending operations -> In Range checking blocked", + info->pending); + + } else { + if (tSNT != 0) + cm_msg(MLOG,"","Number of Channels (T) NOT in Tolerance Range changed " + "from %d to %d", info->settings.Sum_NOT_in_Tolerance, tSNT); + else + cm_msg(MLOG,"","All Channels (T) in Tolerance Range"); + } + info->settings.Sum_NOT_in_Tolerance = tSNT; + /* update ODB when different */ + if (info->hkeySNT) { + HNDLE hDB; + cm_get_experiment_database(&hDB, NULL); + db_set_data(hDB,info->hkeySNT,&info->settings.Sum_NOT_in_Tolerance, + sizeof(int),1,TID_INT); + } + /* reset alarm when sum is 0 */ + if ((tSNT==0)&&(strncmp(info->settings.AlarmWhenNOTinTolerance,"NONE",4) != 0)) + al_reset_alarm(info->settings.AlarmWhenNOTinTolerance); + } + + info->lastSNT = ss_time(); + if (handled) { free(handled); handled = NULL;} + + } else if (ss_time() < info->lastSNT) { + cm_msg(MLOG,"","Sum of channels NOT in Tolerance: System time was reset"); + info->lastSNT = ss_time(); + } +#endif /* #ifdef GFA_SPECIFIC */ + } + + if ((!*(info->settings.used+channel)) && pvalue) *pvalue = -3.f; + +#ifdef MIDEBUG2 + if (pvalue) + cm_msg(MLOG,""," --epics_ca_get(value = %f)",*pvalue); + else + cm_msg(MLOG,""," --epics_ca_get(NULL pointer to return data)"); +#endif +#ifdef MIDEBUGF + if (fp) { + if (pvalue) + fprintf(fp,"%u : --epics_ca_get(channel=%d(%s), value=%f)\n", ss_time(), channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel, *pvalue); + else + fprintf(fp,"%u : --epics_ca_get(channel=%d, NULL pointer to return data)\n", + ss_time(), channel); + } +#endif + + } else { + if (pvalue) *pvalue = -4.f; + } + + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +/* + * return demand value of (mirrored) demand channel + * as a side-effect the mirrored channel readout will be updated + * with the newest readout received by the event handler callback + * GFA specific: handle command or demand channel by interpreting associated G-flag + * channel readout comparing previous (mirrored) channel readout with the + * newest readout + */ +INT epics_ca_get_demand(CA_INFO * info, INT channel, float *pvalue){ +#ifdef MIDEBUGF + if (fp) fprintf(fp,"%u : ++epics_ca_get_demand(channel=%d(%s))\n",ss_time(),channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); +#endif +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_get_demand(channel %d)",channel); +#endif + + if (info) { + if (channel < info->num_channels) { +#ifdef GFA_SPECIFIC + /* channel number of dependent channel is not initialized yet + * search for channel */ + if (*(info->gchan+channel) == -1.f) { + char tename[CHN_NAME_LENGTH]; + int j; + BOOL findrefchan; + char *ds; + char *tpos; + + findrefchan = TRUE; + strcpy(tename,info->settings.channel_names+CHN_NAME_LENGTH*channel); + + ds = info->settings.dtype+DTYPE_LENGTH*channel; +#ifdef MIDEBUG2 + cm_msg(MLOG,"","G-flag: search Looking for device specific handling of Device " + "type %s", ds); +#endif + if ((strcmp(ds,"BEND")==0) || (strcmp(ds,"QUAD")==0) || + (strcmp(ds,"SEPT")==0) || (strcmp(ds,"STER")==0) || + (strcmp(ds,"PS" )==0) || (strcmp(ds,"SOLE")==0) || + (strcmp(ds,"TS" )==0) || (strcmp(ds,"KICKER")==0)) { + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","G-flag: Device type %s found",ds); +#endif + /* assign corresponding channel */ + if ((tpos = strstr(tename,":COM:2")) != NULL) { + strcpy(tpos,":STA:1"); + } else if ((tpos = strstr(tename,":STA:1")) != NULL) { + strcpy(tpos,":COM:2"); + } else if ((tpos = strstr(tename,":SOL:2")) != NULL) { + findrefchan = FALSE; + } else if ((tpos = strstr(tename,":IST:2")) != NULL) { + findrefchan = FALSE; + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL) + cm_msg(MLOG,"","G-flag: search Channel %d (%s) not handled for type %s", + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + findrefchan = FALSE; + } + + } else if (strcmp(ds,"FENTE")==0) { + /* assign corresponding channel */ + if ((tpos = strstr(tename,":COM:2")) != NULL) { // ambiguity + // solve ambiguity :STA:1 or :STA:2 + BOOL present; + + present = FALSE; + strcpy(tpos,":STA:1"); + // search tename and if not found set :STA:2 + for (j=0; j < info->num_channels; j++) { + if (channel != j) { + if (strcmp(tename,info->settings.channel_names+CHN_NAME_LENGTH*j)==0){ + present = TRUE; + break; + } + } + } + if (!present) strcpy(tpos,":STA:2"); + } else if ((tpos = strstr(tename,":STA:1")) != NULL) { // steps + strcpy(tpos,":COM:2"); + } else if ((tpos = strstr(tename,":STA:2")) != NULL) { // mm + strcpy(tpos,":COM:2"); + } else if ((tpos = strstr(tename,":SOL:2")) != NULL) { // ambiguity + // solve ambiguity :IST1:2 or :POSA:2 + BOOL present; + + present = FALSE; + strcpy(tpos,":IST1:2"); + // search tename and if not found set :POSA:2 + for (j=0; j < info->num_channels; j++) { + if (channel != j) { + if (strcmp(tename,info->settings.channel_names+CHN_NAME_LENGTH*j)==0){ + present = TRUE; + break; + } + } + } + // FIXED RA36 17-JAN-2022 was ":POSA2:2" + if (!present) strcpy(tpos,":POSA:2"); + } else if ((tpos = strstr(tename,":SOL")) != NULL) { // mm + strcpy(tpos,":POSA"); + } else if ((tpos = strstr(tename,":IST1:2")) != NULL) { // steps + strcpy(tpos,":SOL:2"); + } else if ((tpos = strstr(tename,":POSA:2")) != NULL) { // steps + strcpy(tpos,":SOL:2"); + } else if ((tpos = strstr(tename,":POSA")) != NULL) { // mm + strcpy(tpos,":SOL"); + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL) + cm_msg(MLOG,"","G-flag: search Channel %d (%s) not handled for type %s", + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + findrefchan = FALSE; + } + + } else if (strcmp(ds,"BX")==0) { + if ((tpos = strstr(tename,":COM:2")) != NULL) { + strcpy(tpos,":STA:1"); + } else if ((tpos = strstr(tename,":STA:1")) != NULL) { + strcpy(tpos,":COM:2"); + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL) + cm_msg(MLOG,"","G-flag: search Channel %d (%s) not handled for type %s", + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + findrefchan = FALSE; + } + + } else if (strcmp(ds,"VALVE")==0) { + if ((tpos = strstr(tename,":CSV:2")) != NULL) { + strcpy(tpos,":STA:1"); + } else if ((tpos = strstr(tename,":STA:1")) != NULL) { + strcpy(tpos,":CSV:2"); + } else { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL) + cm_msg(MLOG,"","G-flag: search Channel %d (%s) not handled for type %s", + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + findrefchan = FALSE; + } + + } else if (strcmp(ds,"HVPS")==0) { + /* DO NOTHING (no G-flags possible) */ + findrefchan = FALSE; + + } else { + if ((strncmp(ds,"**",2) != 0) && + (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL)) + cm_msg(MLOG,"","G-flag: search Channel %d (%s) type %s is not handled", + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + findrefchan = FALSE; + } + + if (findrefchan) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","G-flag: search Searching for channel %s",tename); +#endif + for (j=0; j < info->num_channels; j++) { + if (channel != j) { + if (strcmp(tename,info->settings.channel_names+CHN_NAME_LENGTH*j)==0){ + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') || + strchr(info->settings.chnflags+CHNFLAGS_LENGTH*j,'G')) { + *(info->gchan+channel) = j; + /* NIY if (*(info->gchan+j) > 0) warning channel was already + * assigned to channel ... */ + *(info->gchan+j) = channel; + } else { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","G-flag: found channel %s but both have no G-flag", + tename); +#endif + *(info->gchan+channel) = -2.f; /* mark as already processed */ + findrefchan = FALSE; + } + break; + } + } + } /* for all (other) channels */ + + if (findrefchan && (*(info->gchan+channel) < 0)) { + if (strchr(info->settings.chnflags+CHNFLAGS_LENGTH*channel,'G') != NULL) + cm_msg(MLOG,"","G-flag: search Channel corresponding to %d (%s) NOT found" + "!", channel, info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->gchan+channel) = -2.f; /* mark as already processed */ + } + } else { + *(info->gchan+channel) = -2.f; /* mark as already processed */ + } + } /* search for corresponding channels */ + + /* command/setpoint channel? */ + if (*(info->iscmd+channel)) { + if (*(info->gchan+channel) > 0) { + char *ds; + char *tpos; + int gchan; + + gchan = *(info->gchan+channel); + ds = info->settings.dtype+DTYPE_LENGTH*channel; +#ifdef MIDEBUG2 + cm_msg(MLOG,"","G-flag: command/setpoint channel: Looking for device " + "specific handling of Device type %s",ds); +#endif + if ((strcmp(ds,"BEND")==0) || (strcmp(ds,"QUAD")==0) || + (strcmp(ds,"SEPT")==0) || (strcmp(ds,"STER")==0) || + (strcmp(ds,"PS" )==0) || (strcmp(ds,"SOLE")==0) || + (strcmp(ds,"TS" )==0) || (strcmp(ds,"KICKER")==0)) { + + /* update COM:2 reading STA:1 for KombiPS1 or MultiIO */ + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":COM:2")) { + INT tval; + tval = (int) *(info->array+gchan); + /* Status is ON : 0x0005 ("Soll ON" : 0x0001 and (|) "Ist ON" : 0x0004) */ + if ((tval & 0x0005) == 0x0005) { + if (*(info->array_m+channel) != 0.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"ON\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"ON\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 0.f; /* command ON */ + } + } else { /* everything NOT ON is OFF */ + if (*(info->array_m+channel) != 1.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"OFF\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"OFF\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 1.f; /* command OFF */ + } + } + *(info->array_m+gchan) = *(info->array+gchan); /* update :STA:1 */ + } + + } else if (strcmp(ds,"FENTE")==0) { + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":COM:2")) { + int cur, prev; + cur = (int) *(info->array +gchan); + prev = (int) *(info->array_m+gchan); + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*gchan,":STA:1")) { + /* when status changed maybe update COM:2 with info from STA:1 */ + if ((cur != prev)||(*(info->array_m+channel) == -2.f)) { + /* not moving anymore */ + if (((cur & 0x0100) != 0x0100) && ((cur & 0x0200) != 0x0200)) { + /* stopped and NOT error */ + if ((cur & 0x0004) && ((cur & 0x0003) != 0x0003)) { + /* not command STOP and not MOVE_TO_SOL? */ + if ((*(info->array_m+channel) != (float) 0x0002) && + (*(info->array_m+channel) != (float) 0x0003)) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"STOP\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"STOP\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = (float) 0x0002; /* command STOP */ + } + } + } + } + } else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*gchan,":STA:2")) { + // NIY hier TODO + } + *(info->array_m+gchan) = *(info->array+gchan); /* update :STA:x */ + } else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ":SOL:2")) { + /* NIY G-flag described in epics_ca_private.h should be handled here + * + * NIY SOL:2 and IST1 or POLA and :STA:1 - > maybe set SOL:2 */ + *(info->array_m+channel) = *(info->array+channel); /* update :SOL:2 */ + *(info->array_m+gchan) = *(info->array+gchan); /* update IST1 or POLA */ + } else if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel, + ":SOL")) { + /* NIY G-flag described in epics_ca_private.h should be handled here + * + * NIY SOL and POLA and :STA:2 - > maybe set SOL */ + *(info->array_m+channel) = *(info->array+channel); /* update :SOL */ + *(info->array_m+gchan) = *(info->array+gchan); /* update :POLA */ + } + + } else if (strcmp(ds,"BX") == 0) { + /* update COM:2 reading STA:1 for BX */ + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":COM:2")) { + INT tval; + tval = (int) *(info->array+gchan); + /* Status is Open : 0x0001 or Closed : 0x0002 */ + if ((tval & 0x0001) == 0x0001) { + if (*(info->array_m+channel) != 0.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"OPEN\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"OPEN\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 0.f; /* command OPEN */ + } + } else { /* NOT OPEN -> CLOSE */ + if (*(info->array_m+channel) != 1.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"CLOSE\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"CLOSE\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 1.f; /* command CLOSE */ + } + } + *(info->array_m+gchan) = *(info->array+gchan); /* update :STA:1 */ + } + + } else if (strcmp(ds,"VALVE") == 0) { + /* update CSV:2 reading STA:1 for VALVE */ + if (strstr(info->settings.channel_names+CHN_NAME_LENGTH*channel,":CSV:2")) { + DWORD tval; + tval = (DWORD) *(info->array+gchan); + /* Status is Open : 0x00000010, Armed: 0x00000004 or Closed: 0x00000008 */ + if (((tval & 0x00000010) == 0x00000010) || + ((tval & 0x00000004) == 0x00000004)) { + if (*(info->array_m+channel) != 0.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"OPEN\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"OPEN\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 0.f; /* command OPEN */ + } + } else { /* NOT OPEN -> CLOSE */ + if (*(info->array_m+channel) != 1.f) { + if (*(info->array_m+channel) != -2.f) + cm_msg(MLOG,"","%d(%s): Command changed to \"CLOSE\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + else + cm_msg(MLOG,"","%d(%s): Current Command is \"CLOSE\"", channel, + info->settings.channel_names+CHN_NAME_LENGTH*channel); + *(info->array_m+channel) = 1.f; /* command CLOSE */ + } + } + *(info->array_m+gchan) = *(info->array+gchan); /* update :STA:1 */ + } + } else { + cm_msg(MLOG,"","G-flag: command/setpoint Channel %d (%s) type %s is not han" + "dled", channel, info->settings.channel_names+CHN_NAME_LENGTH*channel,ds); + } + } else { + /* no corresponding "status" channel -> mirror demand readout of channel */ + *(info->array_m+channel) = *(info->array+channel); + } + + if (pvalue) *pvalue = *(info->array_m+channel); + + /* not a command channel */ + } else { + /* and no corresponding command channel -> mirror readout of channel */ + if (*(info->gchan+channel) < -1.f) + *(info->array_m+channel) = *(info->array+channel); + if (pvalue) *pvalue = 0.f; /* NO demand value to return */ + } +#else + *(info->array_m+channel) = *(info->array+channel); + if (pvalue) *pvalue = *(info->array_m+channel); +#endif + } + } +#ifdef MIDEBUG1 + if (pvalue) + cm_msg(MLOG,""," --epics_ca_get_demand(value = %f)",*pvalue); + else + cm_msg(MLOG,""," --epics_ca_get_demand(NULL pointer to return)"); +#endif +#ifdef MIDEBUGF + if (fp) { + if (pvalue) + fprintf(fp,"%u : --epics_ca_get_demand(channel=%d(%s), value=%f)\n", ss_time(), + channel, info->settings.channel_names+CHN_NAME_LENGTH*channel, *pvalue); + else + fprintf(fp,"%u : --epics_ca_get_demand(channel=%d,NULL pointer to return " + "demand)\n" ,ss_time(), channel); + } +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +INT epics_ca_get_all(CA_INFO * info, INT channels, float *pvalue) { + int i; + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_get_all(%d channels)", channels); +#endif + for (i = 0; i < MIN(info->num_channels, channels); i++) + epics_ca_get(info, i, pvalue + i); + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," --epics_ca_get_all()"); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +INT epics_ca_get_default_name(CA_INFO *info, INT channel, char *name) { +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_get_default_name(channel %d, name %s)", channel, name); +#endif + if ((name != NULL) && (channel >= 0) && (channel< info->num_channels)) { + strcpy(name, info->settings.channel_names + CHN_NAME_LENGTH * channel); + } +#ifdef MIDEBUG1 + cm_msg(MLOG,""," --epics_ca_get_default_name(channel %d, name %s)", channel, name); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ + +INT epics_ca_get_default_threshold(CA_INFO *info, INT channel, float *pvalue) { + +#ifdef MIDEBUG1 + cm_msg(MLOG,""," ++epics_ca_get_default_threshold(channel %d)", channel); +#endif + if (pvalue != NULL) { + *pvalue = 0.1f; + if (info->threshold && (channel >= 0) && (channel < info->num_channels)) + *pvalue = *(info->threshold+channel); + } +#ifdef MIDEBUG1 + if (pvalue) + cm_msg(MLOG,""," --epics_ca_get_default_threshold(channel %d, threshold %f)", + channel, *pvalue); +#endif + return FE_SUCCESS; +} + +/*---- device driver entry point -----------------------------------*/ + +INT epics_ca(INT cmd, ...) { + va_list argptr; + HNDLE hKey; + INT channel, status; + DWORD flags; + float value, *pvalue; + CA_INFO *info; + char *label; + CA_INFO **pinfo; + + va_start(argptr, cmd); + status = FE_SUCCESS; + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","++epics_ca(CMD=%d)",cmd); +#endif + if (cmd == CMD_INIT) { + + hKey = va_arg(argptr, HNDLE); + pinfo = va_arg(argptr, CA_INFO **); + channel = va_arg(argptr, INT); + flags = va_arg(argptr, DWORD); + status = epics_ca_init(hKey, pinfo, channel); + if (pinfo) { + info = *(CA_INFO **) pinfo; + if (info) info->flags = flags; + } + } else { + info = (CA_INFO *)va_arg(argptr, void *); + + /* only execute command if enabled */ + if (!info || info->cmd_disabled) { + ss_sleep(100); +#ifdef MIDEBUG2 + cm_msg(MLOG,"","ss_sleep(100)"); +#endif + status = FE_ERR_DISABLED; + } else { + switch (cmd) { + case CMD_INIT: + break; + + case CMD_EXIT: + status = epics_ca_exit(info); + break; + + case CMD_SET: + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = epics_ca_set(info, channel, value, TRUE, FALSE); + break; + +#ifdef OBSOLETE_2_1 + case CMD_SET_ALL: + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = epics_ca_set_all(info, channel, value); + break; +#endif + + case CMD_SET_LABEL: + channel = va_arg(argptr, INT); + label = va_arg(argptr, char *); + /* NOTE: may not be operable at PSI */ + /*status = epics_ca_set_label(info, channel, label); */ + status = FE_SUCCESS; + break; + + case CMD_GET: + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + status = epics_ca_get(info, channel, pvalue); + break; + + case CMD_GET_DEMAND: + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + status = epics_ca_get_demand(info, channel, pvalue); + break; + +#ifdef OBSOLETE_2_1 + case CMD_GET_ALL: + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + status = epics_ca_get_all(info, channel, pvalue); + break; +#endif + + case CMD_GET_LABEL: + channel = va_arg(argptr, INT); + label = va_arg(argptr, char *); + status = epics_ca_get_default_name(info, channel, label); + break; + + case CMD_GET_THRESHOLD: + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + status = epics_ca_get_default_threshold(info, channel, pvalue); + break; + + default: +#ifdef MIDEBUG + cm_msg(MLOG,"","**epics_ca(CMD=%d) is not handled",cmd); +#endif + break; + } + } + } + + va_end(argptr); + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","--epics_ca()"); +#endif + return status; +} + +/*--- EOF epics_ca.c -------------------------------------------------*/ diff --git a/device/epics_ca.h b/device/epics_ca.h new file mode 100644 index 0000000..bf1685d --- /dev/null +++ b/device/epics_ca.h @@ -0,0 +1,12 @@ +/********************************************************************\ + + Name: epics_ca.h + Created by: Stefan Ritt + + Contents: Channel Access device driver function declarations + + $Id: epics_ca.h,v 1.1.1.1 2019/02/21 10:07:20 raselli Exp $ + +\********************************************************************/ + +INT epics_ca(INT cmd, ...); diff --git a/device/epics_ca_private.h b/device/epics_ca_private.h new file mode 100644 index 0000000..73049ad --- /dev/null +++ b/device/epics_ca_private.h @@ -0,0 +1,458 @@ +/********************************************************************\ + + Name: epics_ca_private.h + Created by: RA36 (modified part of epics_ca.c) + + Contents: Channel Access device driver private function declarations + epics_ca_...() functions may also be used as sub device driver + + $Id: epics_ca_private.h,v 1.5 2024/06/30 10:09:16 raselli Exp $ + +\********************************************************************/ + +#include +#include +#include +#include "cadef.h" +#include "epicsEvent.h" + +#ifdef _MSC_VER /* collision between varargs and stdarg */ +#undef va_arg +#undef va_start +#undef va_end +#endif + +#include "midas.h" + +#ifndef _EPICS_CA_PRIVATE_H_ +#define _EPICS_CA_PRIVATE_H_ + +/*----------------------------------------------------------------------------*/ + +#define CHN_NAME_LENGTH 32 /* length of channel names */ +#define CHNFLAGS_LENGTH 16 /* chan flags string size */ + +/*----------------------------------------------------------------------------*/ + +#ifdef GFA_SPECIFIC + +#define DTYPE_LENGTH 40 /* epics DBR_STRING size - fixed */ +#define DSEC_LENGTH 40 /* epics DBR_STRING size - fixed */ + +#endif + /* DD settings + * DD/Loadfile (size is NAME_LENGTH-1 = 31) + * loadfile location is usually /usr/local/midas for Midas 1.9.5 or + * /usr/local/midas2_32 (i686) or /usr/local/midas2_64 (x86_64) for Midas 2.1 + * /usr/local/midas2n_32 (i686) or /usr/local/midas2n_64 (x86_64) for Midas 2.1N + * will be read and interpreted and DD entries for + * EPICS Channel name, Used, Readonly, Chn_flags, Minval, Maxval, Threshold + * and Tolerance will be created, if it they did not previously exist. + * + * DD/AlarmWhenNOTConnected + * if not NONE, Alarm will initially be reset in epics_ca_init() and + * in epics_ca_get() when Sum_NOT_Connected (of all C flagged channels) + * changes to 0. + * e.g.: Alarm condition for Alarm "BL connected" for the PiM3 area is + * /Equipment/eblpim3/Settings/Devices/EPICSGFA/DD/Sum_NOT_Connected > 0 + * + * DD/Sum_NOT_Connected + * > 0 may be used as Alarm level + * + * DD/AlarmWhenNOTinTolerance + * if not NONE, Alarm will initially be reset in epics_ca_init() and + * when Sum_NOT_in_Tolerance changes (of all T flagged channels) changes to 0. + * e.g.: Alarm condition for Alarm "BL tolerance" for the PiM3 area is + * /Equipment/eblpim3/Settings/Devices/EPICSGFA/DD/Sum_NOT_in_Tolerance > 0 + * + * DD/Sum_NOT_in_Tolerance + * > 0 may be used as Alarm level + * NOTE: Pending operations set Sum_NOT_in_tolerance > 0 until reliable + * checks are possible. To make sure WAIT INRANGE in autorun + * waits until all operations are completed. + * + * DD/EPICS Channel name [] max. 32 characters + * + * DD/Used [] y/n + * + * DD/Readonly [] y/n + * + * DD/Chn_Flags [] + * + * Chn_flags are evaluated when (re)connecting, reading or setting the + * respective channel + * NOTE: Only channel flag I should be used when epics_ca device driver + * routines are used as sub device driver for another device driver. + * + * - Initial values set when DD is created are "US". + * These must be changed when doing the initial setup + * U = NOT USED (Channel will not be handled) + * + * - Initialized + * I = Channel may be used and has no special flags + * + * - channel subscription may be disabled + * S = NO subscription of channel changes + * e.g. for strings + * + * - update demand value of channel when readout of demand value changes + * F = Feedback of demand channel readout to (Midas) demand value of + * channel when (Midas) demand value was not updated by epics_ca_set() + * in the last 2 seconds + * NOTE: + * when the demand value is out of range + * if #define RESET_DEMAND_VALUE_TO_RANGE is defined + * then + * *** the demand value will be reset + * to the corresponding range limit *** in epics_ca_set(), + * only, when the parameter forced_update is TRUE + * else + * ODB will be updated only and the demand value will not + * be sent to the EPICS channel by ca_put() + * else + * if the demand value readout is out of range a message will + * be logged but the demand value will not be updated. + * + * See GFA specific flag G for update of Command or Setpoint channel demand + * by the interpreted readout of a measured or status channel. + * + * - Min/Max Info for GUI or Min/Max value when sending new demand value + * to EPICS channel + * M = Write Min & Max in DD during first init of DD/MinVal and DD/MaxVal + * Change new demand value to Min or Max value when out of range + * + * NOTE: Maybe modify Min/Max of channels manually when channel is not + * available during init or Min or Max value should be different + * NOTE: any value may be set if MinVal and MaxVal are both equal to 0 + * NOTE: any value may be set if M is not specified in DD/Chn_Flags + * + * Delete DD/MinVal DD/MaxVal after the first setup to regenerate + * Min/Max from LOW/HIGH when channels are connected. + * + * NOTE: For GFA device type FENTE (=SLIT) LOW/HIGH is not set. Always + * set MinVal and MaxVal to 0 as Min/Max may not be determined + * reliably by moving into either limit switch. There is also a race + * condition when the slit position is set to an out of range + * position by another application. + * + * NOTE: S and U overrule M + * + * - information when channel is not connected .... + * D = Set measured value to -1 to flag channel not found, disconnected + * or no read access + * + * - information if channel is not connected + * C = check if channel is not connected. + * NOTE: When NOT connected DD/Sum_NOT_connected is incremented. + * + * - reset demand value + * R = (Re)set Demand value on EPICS channel when channel is reconnected + * and readout of demand value and demand value differ. + * NOTE: Setpoint must always be valid. + * e.g. GFA device type FENTE must therefore always be set to + * setpoint and command MOVE_TO_SOL must be commanded (G-flag). + * + * *** GFA Specific *** + * + * . GFA specific handling of channels when compilation flag GFA_SPECIFIC + * is defined. + * . The device type of an EPICS channel is determined by reading the + * device type channel :DESC:TYP of . + * exceptions that do not have a :DESC:TYP channel are + * KS -> BX + * SEPHVN:, SEPHVP: and SEPHV: -> HVPS + * ROTHVN:, ROTHVP: and ROTHV: -> HVPS + * CNT:INP e.g. ZMUE1CNT:INP3 -> DIAG + * The following GFA specific device types exist and are handled: + * BEND,QUAD,SEPT,STER,PS,TS,KICKER,BX,VALVE,FENTE,SOLE,DIAG and HVPS. + * . The following GFA specific device types will be treated as the basic + * device types: + * DEGR -> FENTE + * CS_DEGR -> FENTE + * CS_FENT -> FENTE + * CS_BX -> BX + * CS_PS -> PS + * MA_BEND -> BEND + * MA_QUAD -> QUAD + * MA_STER -> STER + * MA_SEXT -> SEXT + * MA_SOLE -> SOLE + * MA_SPIN -> PS + * MA_KICK -> KICKER + * MA_TR -> TS + * TR -> TS + * VA_VALVE -> VALVE + * VA_PUMP -> PUMP + * VA_GAUGE -> GAUGE + * DI_DIAG -> DIAG + * DI_MESTRA -> DIAG + * . BEND,QUAD,SEPT,STER,PS,SOLE and TS are power supplies for magnetic + * components of the beamlines. + * KICKER is a high voltage power supply using the MultIO interface. + * Digital power supplies are of type KombiPS1 and for analog power supplies + * the MultiIO interface is used. :COM:2 commands and :STA:2 status for + * both type of power supplies are identical. :SOL:2 and :IST:2 units are + * either A or kV. + * Some analog power supplies have a switch to set either 20% or 100% of + * the full range. An additional variable :SCALE:2:factor may be set + * for this device. + * . BX are beam blocker channels (KV = Kanalverschluss). + * . VALVE are valve channels of the vacuum system. + * . FENTE are slit channels + * . HVPS are channels of the separator/spinrotator high voltage system + * including vacuum pressure and xray readout. + * Until now there is no feedback of the one or two :STA:2 channels to the + * (single) :COM:2 implemented. + * Setting the F flag will reset :COM:2 to -2 in the ODB. ON (0), OFF (1) or + * RESET (2) may therefore be set irrespective of the previously set value. + * . DIAG are EPICS single scalers of the beamline for Optima. + * . readout of an EPICS command channel is not reliable. After sending a + * command to a :COM:2 channel the readout may not be used to update + * the demand value in the Midas ODB. If the device type of an EPICS channel + * is known the status channel :STA:1 information may be used to + * update the demand value instead. (see G-flag description) + * . readout of an EPICS channel representing a measured quantity and status + * channel information may be used to set the demand value in the ODB + * . :COM:2 may be set automatically when setting the demand value of + * e.g. to turn power off or on. + * . The location of a device may be determined by reading the + * :DESC:SEC EPICS channel of . + * Exceptions are KS<>,SEP<>,ROT<>, <>CNT:INP<>, some power supplies and + * some slits. These are hardcoded. + * + * - update demand value of channel corresponding to measured or status chan + * G = Feedback from "Measured" or "Status" channel readout to corresponding + * demand channel and demand value was not updated by epics_ca_set() + * in the last 2 seconds. + * + * NOTE: ODB update only - demand value will not be put to EPICS channel + * + * NOTE: Type FENTE :SOL:2 will only be updated with IST1:2 or POSA:2 + * when not moving anymore (NOT 0x0100 and NOT 0x0200) and in :STA:1 + * status "stopped" (0x0004) and previous cmd was SOL=POSA(0x0004). + * Updating :SOL:2 when cmd was MOVE_TO_CW/CCW (0x0000/0x0001) and + * status is "in CW/CCW limit switch" (0x0001/0x0002) was not + * implemented as :SOL:2 may not reliably be positioned to readout + * position of IST1:2 or POSA:2. + * NOTE: POSA and SOL are mm equivalent to POSA:2 and SOL:2 in Steps. + * :COM:2 has less commands. :STA:2 is different than :STA:1 + * + * See non GFA specific flag F for update of demand by demand readout + * + * - additional EPICS channel e.g. for Power ON/OFF may be set + * according to GFA device type + * + * P = Turn power ON (other channel e.g. :COM:2) when setting this channel + * e.g. :SOL:2 + * N = Turn power ON (other channel e.g. :COM:2) when setting > 0 + * Turn power OFF (other channel e.g. :COM:2) when setting <= 0 + * Z = Turn power OFF (other channel e.g. :COM:2) when setting to zero + * Turn power ON (other channel e.g. :COM:2) when setting <> zero + * NOTE: N overrules Z and P. Z overrules P. + * NOTE: No ramping to zero - command power off will be sent to :COM:2 + * channel immediately. If ramping to zero is not implemented in + * the EPICS IOC the GFA device has to be set close to zero before + * sending power off. + * + * - set command set_to_demand on other channel + * V = Set value for "Move to setpoint" MOVE_TO_SOL=0x0003 + * (in other channel e.g. :COM:2 for GFA of type FENTE) + * when setting this channel e.g. :SOL:2 + * + * - check tolerance of this channel + * T = check if (this) demand channel e.g. :SOL:2 and corresponding + * measured channel e.g.: :IST:2 are in tolerance + * when of demand is > 0. + * Does not check tolerance when demand value is negative and N flag is + * set. + * NOTE: When NOT in tolerance DD/Sum_NOT_in_Tolerance is incremented. + * NOTE: For BX and VALVE out of tolerance condition is met when NOT open. + * :COM:2 or :CSV:2 is derived from :STA:1 and therefore + * not evaluated. + * + * Examples of channel flags + * + * EPICS Channel GFA_type Channel flags Iscmd + * ------------- ---------- ------------- ------ + * in Steps old slit control + * FS11-L:COM:2 FENTE I y <-| <-| + * FS11-L:STA:1 FENTE IG n G | + * FS11-L:SOL:2 FENTE IVMCF y <-F <-| V + * FS11-L:IST1:2 FENTE IG n G* + * or in steps new slit control + * FS71-L:COM:2 FENTE IF y <-F <-| + * FS71-L:STA:2 FENTE | + * FS71-L:SOL:2 FENTE IVCF y <-F V + * FS71-L:POSA:2 FENTE I n + * NOTE: Use IVCF for :SOL:2 and IF for :COM:2 and I for :STA:2 and :POSA:2 + * or in mm new slit control + * FS11-L:COM:2 FENTE IF y <-| + * FS11-L:STA:2 FENTE | + * FS11-L:SOL FENTE IVMCF y <-F V + * FS11-L:POSA FENTE I n + * NOTE: Use IVCF for :SOL and IF for :COM:2, and I for :STA:2 and :POSA + * + * ASM11:COM:2 BEND I y <-| <-| + * ASM11:STA:1 BEND IG n G | + * ASM11:SOL:2 BEND IFZMCTR y <-F Z,N or P T R-> + * ASM11:IST:2 BEND I n T + * + * QSK81:COM:2 QUAD I y \ + * QSK81:STA:1 QUAD IG n > see ASM11 + * QSK81:SOL:2 QUAD IFZMCTR y / + * QSK81:SCALE:2:factor QUAD IF y F: demand of factor + * QSK81:SCALE:2:factor QUAD I y NOT F: readout of factor + * QSK81:IST:2 QUAD I n + * + * KSD11:COM:2 BX I y <-| + * KSD11:STA:1 BX IGCT n G T + * + * VSD11:CSV:2 VALVE I y <-| + * VSD11:STA:1 VALVE IGCT n G T + * + * SEP31HVN:STA:2 HVPS I n + * SEP31HVN:ISTI:2 HVPS I n + * SEP31HVN:ISTV:2 HVPS I n T + * SEP31HVN:SOLI:2 HVPS IF y <-F | + * SEP31HVN:SOLV:2 HVPS IFCT y <-F T + * SEP31HVP:STA:2 HVPS I n + * : + * SEP31HV:COM:2 HVPS IF y <-F dummy flag! + * NOTE: NO HV:STA:2! Demand HV:COM:2 will be reset to -2 + * SEP31HV:XRAY:2 HVPS I n + * SEP31HV:VAC:2 HVPS I n + * SEP31HV:TIME:2 HVPS I n + * + * ZMUE1CNT:INP3 DIAG I n + * MHC4:IST:2 DIAG I n + * + * DD/DESC:TYP[] GFA specific type used to handle channel according to type + * BEND, QUAD, SEPT, STER, PS, TS, KICKER, BX, VALVE, FENTE, SOLE, DIAG, + * HVPS, ... + * **CHAN_NOT_USED** DD/Used[] is N + * **TYPE_NOT_INIT** + * **TYPE_NOT_RETURNED** :DESC:TYP information was not returned + * **TYPE_NOT_FOUND** EPICS channel :DESC:TYP was not found + * and not assigned using + * + * DD/DESC:SEC[] GFA specific section informational only + * PIM1, PIM31, PIM32, PIM33, PIE1, PIE3, MUE1, ... + * **CHAN_NOT_USED** DD/Used[] of channel is N + * **SEC_NOT_INIT** + * **SEC_NOT_RETURNED** :DESC:SEC information was not returned + * **SEC_NOT_FOUND** EPICS channel :DESC:SEC was not found + * and not assigned using + * + * *** END GFA Specific *** + * + * DD/Minval [] [0.0] + * Minimum value from Loadfile or updated from LOW attribute of Channel + * when M flag is set and DD/Minval does not exist + * + * DD/Maxval [] [0.0] + * Maximum value from Loadfile or updated from HIGH attribute of Channel + * when M flag is set and DD/Maxval does not exist + * NOTE: if Minval[i] and Maxval[i] of EPICS channel [i] is 0.0 there will be + * no range checking when a demand value is set on channel [i] + * + * DD/Tolerance [] [0.0] + * Tolerance from Loadfile + * NOTE: Tolerance will not be checked when T flag of the channel is not set + * NOTE: Tolerance will not be checked when 0.0 is set as tolerance + * (except device type BX and VALVE where status OPEN is interpreted as + * in tolerance range and status CLOSED as out of tolerance range) + */ + +/*----------------------------------------------------------------------------*/ + +typedef struct { + char loadfile[NAME_LENGTH]; /* file name of load file */ + char AlarmWhenNOTConnected[NAME_LENGTH]; + int Sum_NOT_Connected; /* sum of DOWN channels with C flag set */ + char AlarmWhenNOTinTolerance[NAME_LENGTH]; + int Sum_NOT_in_Tolerance; /* out of tolerance channels with T flag & tol > 0.0 */ + char *channel_names; /* channel_names */ +#ifdef GFA_SPECIFIC + char *dtype; /* description type (also for interpretation by GUI) */ + char *dsec; /* description section (also for interpretation by GUI) */ +#endif + char *chnflags; /* special flags of channel */ + float *minval; /* channel minval */ + float *maxval; /* channel maxval */ + float *tolerance; /* channel tolerance */ + BOOL *used; /* used flag */ + BOOL *readonly; /* readonly flag */ +} CA_SETTINGS; + +typedef struct { + CA_SETTINGS settings; /* DD specific settings in ODB */ + chid *pchid; /* channel ID of channels */ + float *array_new; /* new channel readout */ + float *array_new_m; /* new channel readout mirror */ + float *array; /* channel readout */ + float *array_m; /* channel readout mirror */ + float *demand; /* channel demand */ + float *threshold; /* channel threshold (only used when reading loadfile) */ + DWORD *lastset; /* channel timestamp when last set */ + DWORD *lastget; /* channel timestamp when last get */ + BOOL *get; /* get flag */ + BOOL *getcalled; /* get flag */ + int *status_new; /* new status returned in event handler */ + int *status_new_m; /* new status returned in event handler mirror */ + int *status; /* status returned in event handler */ + BOOL *found_new; /* channel event found flag */ + BOOL *found_new_m; /* channel event found flag mirror */ + BOOL *found; /* channel found flag */ + BOOL *iscmd; /* iscmd flag */ + BOOL *subscribed; /* subscribed flag */ + BOOL *down; /* down flag */ + BOOL *first; /* first flag */ + BOOL *firstread; /* flag expecting first reading when UP again */ + INT *nerr; /* number of errors */ + INT *nmsg; /* number of messages */ +#ifdef GFA_SPECIFIC + INT *tolchan; /* index of corresponding tolerance channel */ + INT *gchan; /* index of corresponding channel with G-flag */ + BOOL *intol; /* channel is/is not in tolerance range */ +#endif + INT num_channels; /* number of channels */ + DWORD flags; /* flags supplied when epics_ca(CMD_INIT, ...) is called*/ + INT cmd_disabled; /* command execution in epics_ca() is initially disabled*/ + INT subscribe_disabled;/* subscribe (in connection handler) is initially disabled*/ +#ifdef GFA_SPECIFIC + BOOL handling_disabled; /* GFA specific handling is (currently) disabled */ +#endif /* to avoid infinit loop of handling dependencies... */ + char name[NAME_LENGTH]; /* equipment */ + HNDLE hkeyDemand; /* "/Equipment//Variables/Demand" key handle */ + HNDLE hkeyDD; /* "DD" key handle */ + HNDLE hkeySNC; /* "DD/Sum_NOT_Connected" key handle */ + HNDLE hkeySNT; /* "DD/Sum_NOT_in_Tolerance" key handle */ + DWORD lastSNC; /* last time Sum_NOT_Connected of C flag channels */ + DWORD lastSNT; /* last time Sum_NOT_in_Tolerance of T flag channels */ + INT pending; /* pending operation -> increment Sum_NOT_in_Tolerance */ + BOOL silentreadonly; /* do not show a readonly message when demand and */ + /* newdemand differ but ODB will be updated */ + +} CA_INFO; + +/*---- functions ---------------------------------------------------*/ + +INT epics_ca (INT cmd, ...); + +INT epics_ca_init (HNDLE , CA_INFO **, INT); +INT epics_ca_exit (CA_INFO *); +INT epics_ca_set (CA_INFO *, INT, float, BOOL, BOOL); +INT epics_ca_set_all (CA_INFO *, INT, float); +INT epics_ca_set_label (CA_INFO *, INT, char *); +INT epics_ca_get (CA_INFO *, INT, float *); +INT epics_ca_get_demand(CA_INFO *, INT, float *); +INT epics_ca_get_all (CA_INFO *, INT, float *); + +INT epics_ca_set_pending (CA_INFO *, BOOL); + +INT epics_ca_get_default_name (CA_INFO *, INT, char *); +INT epics_ca_get_default_threshold(CA_INFO *, INT, float *); + +#endif /* _EPICS_CA_PRIVATE_H_ */ + +/*--- EOF epics_ca_private.h -------------------------------------------------*/ diff --git a/device/expmag.cxx b/device/expmag.cxx index babb684..84d5b14 100644 --- a/device/expmag.cxx +++ b/device/expmag.cxx @@ -27,8 +27,8 @@ #include "midas.h" #include "msystem.h" #include "device/spline.h" -#include "midas_lan_gpib.h" #include "bus/tcpip.h" +#include "midas_lan_gpib.h" #ifdef EPICSGFA #define GFA_SPECIFIC /* GFA specific handling of EPICS channels :COM: :SOL: */ @@ -1624,7 +1624,7 @@ INT epics_wait(EXPMAG_INFO *info, int ntimes, INT interval) { /* the init function creates a ODB record which contains the settings and initialized it variables as well as the bus driver */ -INT expmag_init(HNDLE hkey, void **pinfo, INT channels, INT(*bd) (INT cmd, ...)) +INT expmag_init(HNDLE hkey, EXPMAG_INFO **pinfo, INT channels, INT(*bd) (INT cmd, ...)) { int status, size; HNDLE hDB, hkeydd; @@ -1753,7 +1753,7 @@ INT expmag_init(HNDLE hkey, void **pinfo, INT channels, INT(*bd) (INT cmd, ...)) if (db_find_key(hDB, info->hkey, "EPICS", &tkey) == DB_SUCCESS) { // init EPICS channel access - status = epics_ca_init( tkey, (void **)&info->cainfo, + status = epics_ca_init( tkey, (CA_INFO **)&info->cainfo, info->expmag_settings.epics_channels); if (status != SUCCESS) { cm_msg(MERROR,"expmag_init","ERROR returned from epics_ca_init() - EXIT"); @@ -3967,7 +3967,7 @@ mode_change: timeo = ss_time()+60; do { #ifdef EPICSGFA - epics_wait(info,3, 333); + epics_wait((EXPMAG_INFO*)info,3, 333); #else ss_sleep(1000); #endif @@ -6965,6 +6965,7 @@ INT expmag(INT cmd, ...) float value, *pvalue; void *info, *bd; char *name; + EXPMAG_INFO **pinfo; va_start(argptr, cmd); status = FE_SUCCESS; @@ -6976,11 +6977,11 @@ INT expmag(INT cmd, ...) switch (cmd) { case CMD_INIT: hKey = va_arg(argptr, HNDLE); - info = va_arg(argptr, void *); + pinfo = va_arg(argptr, EXPMAG_INFO **); channel = va_arg(argptr, INT); flags = va_arg(argptr, DWORD); bd = va_arg(argptr, void *); - status = expmag_init(hKey, &info, channel, (INT (*)(INT, ...))bd); + status = expmag_init(hKey, pinfo, channel, (INT (*)(INT, ...))bd); break; case CMD_EXIT: @@ -7012,14 +7013,14 @@ INT expmag(INT cmd, ...) gngets++; if (gngets > 100) { #ifdef EPICSGFA - epics_wait(info, 3, 333); + epics_wait((EXPMAG_INFO*)info, 3, 333); #else ss_sleep(1000); #endif } } else { #ifdef EPICSGFA - epics_wait(info, 6, 500); + epics_wait((EXPMAG_INFO*)info, 6, 500); #else ss_sleep(3000); #endif