diff --git a/config/Makefile.linux b/config/Makefile.linux index 54d2b373e99dac3e18065a5f581e3a2895f6dd51..9da057bf9104c7efef7c9c54c57131d84178d1c2 100644 --- a/config/Makefile.linux +++ b/config/Makefile.linux @@ -70,8 +70,9 @@ LIBFLAGS += -lpthread -lreadline -lcurses -lrt # User Makefiles's $(SRC) variable is split here in sequencer source # and all the rest, which should be C files SEQ_SRCS = $(filter %.st, $(SRC)) -CSRCS = $(filter-out %.st, $(SRC)) +CSRCS = $(filter-out %.cc, $(filter-out %.st, $(SRC))) # Required Epics files +CCSRCS += $(filter %.cc, $(SRC)) CCSRCS += build/$(TARGET)/registerRecordDeviceDriver.cc # Each .st file gets preprocessed twice to make one C and one C++ diff --git a/src/drv/fmReadCoeff.c b/src/drv/fmReadCoeff.c index 90d2410b4f75e6d80f7dc80265131018ef211377..597780b7ec3f55189eb1b30450a943b951f9afd2 100644 --- a/src/drv/fmReadCoeff.c +++ b/src/drv/fmReadCoeff.c @@ -30,9 +30,9 @@ #include "fm10Gen.h" #include "fmReadCoeff.h" #include "crc.h" +#include "util/user/check_file_crc.h" int getwords( char*, char*[], int ); -extern int checkFileCrc( char* ); /* Cat string and make upper case */ static char* diff --git a/src/epics/CMakeLists.txt b/src/epics/CMakeLists.txt index 807298ee007e48af880e82bb27b64ad38ffc04db..ac390263c324215f18de77852cd54ec1f25befaf 100644 --- a/src/epics/CMakeLists.txt +++ b/src/epics/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(seq) \ No newline at end of file +add_subdirectory(seq) +add_subdirectory(edcu) diff --git a/src/epics/edcu/CMakeLists.txt b/src/epics/edcu/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3863c819c1a3ff2a4b02ca46de1e712e230f3885 --- /dev/null +++ b/src/epics/edcu/CMakeLists.txt @@ -0,0 +1,36 @@ +if (Boost_FOUND) + + add_executable(standalone_edc + standalone_edcu.cc + ${CMAKE_CURRENT_SOURCE_DIR}/../../drv/rfm.c) + #target_compile_options(standalone_edc PRIVATE -fsanitize=address) + #target_link_libraries(standalone_edc PRIVATE asan) + #target_compile_options(standalone_edc PRIVATE + # -fstack-protector -fstack-protector-strong) + target_include_directories(standalone_edc PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/../../include" + "${CMAKE_CURRENT_SOURCE_DIR}/../../include/drv" + ${Boost_INCLUDE_DIRS}) + target_link_libraries(standalone_edc PUBLIC + args + epics::ca + driver::ini_parsing + pv::simple_pv + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + driver::gpsclock) + target_requires_cpp11(standalone_edc PUBLIC) + + configure_file(test/epics_test.py ${CMAKE_CURRENT_BINARY_DIR}/epics_test.py COPYONLY) + configure_file(test/daqdrc_standalone_edc_live_test ${CMAKE_CURRENT_BINARY_DIR}/daqdrc_standalone_edc_live_test COPYONLY) + configure_file(test/test_standalone_edc_live_nds.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test_standalone_edc_live_nds.sh @ONLY) + + add_test(NAME test_standalone_edc_live_nds + COMMAND /bin/bash ./test_standalone_edc_live_nds.sh + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + + install(TARGETS standalone_edc DESTINATION bin) + +else(Boost_FOUND) + message(WARNING "The standalone_edc will not be build as boost was not found") +endif (Boost_FOUND) \ No newline at end of file diff --git a/src/epics/seq/gps.hh b/src/epics/edcu/gps.hh similarity index 100% rename from src/epics/seq/gps.hh rename to src/epics/edcu/gps.hh diff --git a/src/epics/seq/standalone_edcu.cc b/src/epics/edcu/standalone_edcu.cc similarity index 98% rename from src/epics/seq/standalone_edcu.cc rename to src/epics/edcu/standalone_edcu.cc index e4c277b5323cdf2bdcaac8be2a12539be090ac78..f453d9cba9644ca458672d399a613d6672aa99f3 100644 --- a/src/epics/seq/standalone_edcu.cc +++ b/src/epics/edcu/standalone_edcu.cc @@ -58,6 +58,7 @@ extern "C" { #include "gps.hh" #include "args.h" #include "simple_pv.h" +#include "util/user/check_file_crc.h" #include <iostream> @@ -248,8 +249,6 @@ private: // Function prototypes // **************************************************************************************** -int checkFileCrc( const char* ); - typedef union edc_data_t { int16_t data_int16; @@ -1170,31 +1169,6 @@ edcuInitialize( const std::string& mbuf_name, return EdcuClock( sync_source, delay_ms ); } -/// Common routine to check file CRC. -/// @param[in] *fName Name of file to check. -/// @return File CRC or -1 if file not found. -int -checkFileCrc( const char* fName ) -{ - char buffer[ 256 ]; - FILE* pipePtr; - struct stat statBuf; - long chkSum = -99999; - strcpy( buffer, "cksum " ); - strcat( buffer, fName ); - if ( !stat( fName, &statBuf ) ) - { - if ( ( pipePtr = popen( buffer, "r" ) ) != NULL ) - { - fgets( buffer, 256, pipePtr ); - pclose( pipePtr ); - sscanf( buffer, "%ld", &chkSum ); - } - return ( chkSum ); - } - return ( -1 ); -} - std::pair< std::string, int > parse_address( const std::string& str ) { diff --git a/src/epics/seq/test/daqdrc_standalone_edc_live_test b/src/epics/edcu/test/daqdrc_standalone_edc_live_test similarity index 100% rename from src/epics/seq/test/daqdrc_standalone_edc_live_test rename to src/epics/edcu/test/daqdrc_standalone_edc_live_test diff --git a/src/epics/seq/test/epics_test.py b/src/epics/edcu/test/epics_test.py similarity index 100% rename from src/epics/seq/test/epics_test.py rename to src/epics/edcu/test/epics_test.py diff --git a/src/epics/seq/test/test_standalone_edc_live_nds.sh.in b/src/epics/edcu/test/test_standalone_edc_live_nds.sh.in similarity index 100% rename from src/epics/seq/test/test_standalone_edc_live_nds.sh.in rename to src/epics/edcu/test/test_standalone_edc_live_nds.sh.in diff --git a/src/epics/seq/CMakeLists.txt b/src/epics/seq/CMakeLists.txt index 243116de634291c6d8233b49f7dd8f616181ef75..3c9e1226b484e065e7b4226242dfadb56953c4b9 100644 --- a/src/epics/seq/CMakeLists.txt +++ b/src/epics/seq/CMakeLists.txt @@ -1,47 +1,21 @@ -if (Boost_FOUND) +add_executable(test_sequencer_unit_tests + test/test_main.cc + test/test_simple_range.cc + test/test_fixed_size_string.cc + test/test_fixed_size_vector.cc + ) +target_include_directories(test_sequencer_unit_tests PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(test_sequencer_unit_tests PUBLIC + catch2) -add_executable(standalone_edc - standalone_edcu.cc - ${CMAKE_CURRENT_SOURCE_DIR}/../../drv/rfm.c) -#target_compile_options(standalone_edc PRIVATE -fsanitize=address) -#target_link_libraries(standalone_edc PRIVATE asan) -#target_compile_options(standalone_edc PRIVATE -# -fstack-protector -fstack-protector-strong) -target_include_directories(standalone_edc PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/../../include" - "${CMAKE_CURRENT_SOURCE_DIR}/../../include/drv" - ${Boost_INCLUDE_DIRS}) -target_link_libraries(standalone_edc PUBLIC - args - epics::ca - driver::ini_parsing - pv::simple_pv - ${Boost_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - driver::gpsclock) -target_requires_cpp11(standalone_edc PUBLIC) - -configure_file(test/epics_test.py ${CMAKE_CURRENT_BINARY_DIR}/epics_test.py COPYONLY) -configure_file(test/daqdrc_standalone_edc_live_test ${CMAKE_CURRENT_BINARY_DIR}/daqdrc_standalone_edc_live_test COPYONLY) -configure_file(test/test_standalone_edc_live_nds.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test_standalone_edc_live_nds.sh @ONLY) - -add_test(NAME test_standalone_edc_live_nds - COMMAND /bin/bash ./test_standalone_edc_live_nds.sh - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -install(TARGETS standalone_edc DESTINATION bin) - -else(Boost_FOUND) - message(WARNING "The standalone_edc will not be build as boost was not found") -endif (Boost_FOUND) +add_executable(casdf_seq + main.cc + sdf_file_loaded.c) +target_link_libraries(casdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca) +target_compile_definitions(casdf_seq PUBLIC "-DCA_SDF=1") add_executable(modelsdf_seq - main.c + main.cc sdf_file_loaded.c) target_link_libraries(modelsdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca) - -add_executable(casdf_seq - main.c - sdf_file_loaded.c) -target_link_libraries(casdf_seq PUBLIC ini_parsing shmem epics::seq epics::ca) -target_compile_definitions(casdf_seq PUBLIC "-DCA_SDF=1") \ No newline at end of file diff --git a/src/epics/seq/Makefile b/src/epics/seq/Makefile deleted file mode 100644 index e167994a60f861b583b2c579b7d0383aa61758d2..0000000000000000000000000000000000000000 --- a/src/epics/seq/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -LIBFLAGS += -lezca - -edcu.o edcu.c diff --git a/src/epics/seq/edcu.c b/src/epics/seq/edcu.c deleted file mode 100644 index 49c1598f5dcc26baa47d91c53a12c76c39eb04cc..0000000000000000000000000000000000000000 --- a/src/epics/seq/edcu.c +++ /dev/null @@ -1,951 +0,0 @@ -/// @file /src/epics/seq/edcu.c -/// @brief Contains required 'main' function to startup EPICS sequencers, along with supporting routines. -///< This code is taken from EPICS example included in the EPICS distribution and modified for LIGO use. - -/********************COPYRIGHT NOTIFICATION********************************** -This software was developed under a United States Government license -described on the COPYRIGHT_UniversityOfChicago file included as part -of this distribution. -****************************************************************************/ - -// TODO: -// - Make appropriate log file entries -// - Get rid of need to build skeleton.st - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stdarg.h> -#include <unistd.h> -#include <time.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> - -#include "iocsh.h" -#include "dbStaticLib.h" -#include "crc.h" -#include "dbCommon.h" -#include "recSup.h" -#include "dbDefs.h" -#include "dbFldTypes.h" -#include "dbAccess.h" - -#define epicsExportSharedSymbols -#include "asLib.h" -#include "asCa.h" -#include "asDbLib.h" - -#include <daqmap.h> -#include <param.h> -// #include "fm10Gen.h" -#include "findSharedMemory.h" -#include "cadef.h" -#include "fb.h" -#include "../../drv/gpstime/gpstime.h" - -#include <pthread.h> - -#define EDCU_MAX_CHANS 50000 -// Gloabl variables **************************************************************************************** -char timechannel[256]; ///< Name of the GPS time channel for timestamping. -char reloadtimechannel[256]; ///< Name of EPICS channel which contains the BURT reload requests. -struct timespec t; -char logfilename[128]; -char edculogfilename[128]; -unsigned char naughtyList[EDCU_MAX_CHANS][64]; - -// Function prototypes **************************************************************************************** -int checkFileCrc(char *); -void getSdfTime(char *); -void logFileEntry(char *); -void* check_crc(void* arg); - -unsigned long daqFileCrc; -typedef struct daqd_c { - int num_chans; - int con_chans; - int val_events; - int con_events; - float channel_value[EDCU_MAX_CHANS]; - char channel_name[EDCU_MAX_CHANS][64]; - int channel_status[EDCU_MAX_CHANS]; - long gpsTime; - long epicsSync; -} daqd_c; - -typedef struct check_crc_params { - char *ini_filename; - int orig_crc; - dbAddr message_dest; -} check_crc_params; - -int num_chans_index = -1; -int con_chans_index = -1; -int nocon_chans_index = -1; -int internal_channel_count = 0; -pthread_t check_crc_thread; - -daqd_c daqd_edcu1; -static struct rmIpcStr *dipc; -static struct rmIpcStr *sipc; -static char *shmDataPtr; -static struct cdsDaqNetGdsTpNum *shmTpTable; -static const int buf_size = DAQ_DCU_BLOCK_SIZE; -static const int header_size = sizeof(struct rmIpcStr) + sizeof(struct cdsDaqNetGdsTpNum); -static DAQ_XFER_INFO xferInfo; -static float dataBuffer[2][EDCU_MAX_CHANS]; -static int timeIndex; -static int cycleIndex; -static int symmetricom_fd = -1; -int timemarks[16] = {1000,63500,126000,188500,251000,313500,376000,438500,501000,563500,626000,688500,751000,813500,876000,938500}; -int nextTrig = 0; - - -// End Header ************************************************************ -// - -// ************************************************************************** -/// Get current GPS time from the symmetricom IRIG-B card -unsigned long symm_gps_time(unsigned long *frac, int *stt) { -// ************************************************************************** - unsigned long t[3]; - ioctl (symmetricom_fd, IOCTL_SYMMETRICOM_TIME, &t); - t[1] *= 1000; - t[1] += t[2]; - if (frac) *frac = t[1]; - if (stt) *stt = 0; - // return t[0] + daqd.symm_gps_offset; - return t[0]; -} -// ************************************************************************** -void waitGpsTrigger(unsigned long gpssec, int cycle) -// No longer used in favor of sync to IOP -// ************************************************************************** -{ -unsigned long gpsSec, gpsuSec; -int gpsx; - do{ - usleep(1000); - gpsSec = symm_gps_time(&gpsuSec, &gpsx); - gpsuSec /= 1000; - }while(gpsSec < gpssec || gpsuSec < timemarks[cycle]); -} - -// ************************************************************************** -long waitNewCycle(long *gps_sec) -// ************************************************************************** -{ - static long newCycle = 0; - static int lastCycle = 0; - static int sync21pps = 1; - unsigned long lastSec; - - if(sync21pps) { - for (;sipc->cycle;) usleep(1000); - printf("Found Sync at %ld %ld\n",sipc->bp[lastCycle].timeSec, sipc->bp[lastCycle].timeNSec); - sync21pps = 0; - } - do { - usleep(1000); - newCycle = sipc->cycle; - }while (newCycle == lastCycle); - *gps_sec = sipc->bp[newCycle].timeSec; - // printf("new cycle %d %ld\n",newCycle,*gps_sec); - lastCycle = newCycle; - return newCycle; -} - -// ************************************************************************** -/// See if the GPS card is locked. -int symm_gps_ok() { -// ************************************************************************** - unsigned long req = 0; - ioctl (symmetricom_fd, IOCTL_SYMMETRICOM_STATUS, &req); - printf("Symmetricom status: %s\n", req? "LOCKED": "UNCLOCKED"); - return req; -} - -// ************************************************************************** -unsigned long symm_initialize() -// ************************************************************************** -{ - symmetricom_fd = open ("/dev/gpstime", O_RDWR | O_SYNC); - if (symmetricom_fd < 0) { - perror("/dev/gpstime"); - exit(1); - } - unsigned long gpsSec, gpsuSec; - int gpsx; - int gpssync; - gpssync = symm_gps_ok(); - gpsSec = symm_gps_time(&gpsuSec, &gpsx); - printf("GPS SYNC = %d %d\n",gpssync,gpsx); - printf("GPS SEC = %ld USEC = %ld OTHER = %d\n",gpsSec,gpsuSec,gpsx); - // Set system to start 2 sec from now. - gpsSec += 2; - return(gpsSec); -} - -// ************************************************************************** -void connectCallback(struct connection_handler_args args) { -// ************************************************************************** - unsigned long chnum = (unsigned long)ca_puser(args.chid); - daqd_edcu1.channel_status[chnum] = args.op == CA_OP_CONN_UP? 0: 0xbad; - if (args.op == CA_OP_CONN_UP) daqd_edcu1.con_chans++; else daqd_edcu1.con_chans--; - daqd_edcu1.con_events++; -} - - -// ************************************************************************** -void subscriptionHandler(struct event_handler_args args) { -// ************************************************************************** - daqd_edcu1.val_events++; - if (args.status != ECA_NORMAL) { - return; - } - if (args.type == DBR_FLOAT) { - float val = *((float *)args.dbr); - daqd_edcu1.channel_value[(unsigned long)args.usr] = val; - }else{ - printf("Arg type unknown\n"); - } -} - -// ************************************************************************** -int edcuClearSdf(char *pref) -// ************************************************************************** -{ -unsigned char clearString[64] = " "; -int flength = 62; -long status; -dbAddr saddr; -int ii; -char s[64]; -char s1[64]; -char s2[64]; -char s3[64]; -char s4[64]; -dbAddr baddr; -dbAddr maddr; -dbAddr taddr; -dbAddr daddr; - - - - for(ii=0;ii<40;ii++) - { - sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii); - status = dbNameToAddr(s,&saddr); - status = dbPutField(&saddr,DBR_UCHAR,clearString,16); - - sprintf(s1, "%s_%s_STAT%d_BURT", pref,"SDF_SP", ii); - status = dbNameToAddr(s1,&baddr); - status = dbPutField(&baddr,DBR_UCHAR,clearString,flength); - - sprintf(s2, "%s_%s_STAT%d_LIVE", pref,"SDF_SP", ii); - status = dbNameToAddr(s2,&maddr); - status = dbPutField(&maddr,DBR_UCHAR,clearString,flength); - - sprintf(s3, "%s_%s_STAT%d_TIME", pref,"SDF_SP", ii); - status = dbNameToAddr(s3,&taddr); - status = dbPutField(&taddr,DBR_UCHAR,clearString,flength); - - sprintf(s4, "%s_%s_STAT%d_DIFF", pref,"SDF_SP", ii); - status = dbNameToAddr(s4,&daddr); - status = dbPutField(&daddr,DBR_UCHAR,clearString,flength); - - } - - -} - -// ************************************************************************** -int edcuFindUnconnChannels() -// ************************************************************************** -{ -int ii; -int dcc = 0; - - for (ii=0;ii<daqd_edcu1.num_chans;ii++) - { - if(daqd_edcu1.channel_status[ii] != 0) - { - sprintf(naughtyList[dcc],"%s",daqd_edcu1.channel_name[ii]); - dcc ++; - } - } - return(dcc); -} -// ************************************************************************** -int edcuReportUnconnChannels(char *pref, int dc, int offset) -// ************************************************************************** -{ -int ii; -dbAddr saddr; -dbAddr laddr; -dbAddr sperroraddr; -char s[64]; -char sl[64]; -long status; -int flength = 62; -unsigned char tmpstr[64]; -int rc = 0; -int myindex = 0; -int numDisp = 0; -int lineNum = 0; -int erucError = 0; - - - myindex = offset * 40; - if(myindex > dc) { - myindex = 0; - erucError = -1; - } - rc = myindex + 40; - if(rc > dc) rc = dc; - // printf("Naught = %d to %d\n",myindex,rc); - // printf("In error listing \n"); - for (ii=myindex;ii<rc;ii++) - { - sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", (ii - myindex)); - status = dbNameToAddr(s,&saddr); - if(status) - { - printf("Can't connect to %s\n",s); - } else { - sprintf(tmpstr,"%s",naughtyList[ii]); - status = dbPutField(&saddr,DBR_UCHAR,tmpstr,flength); - numDisp ++; - } - sprintf(sl, "%s_SDF_LINE_%d", pref, (ii - myindex)); - status = dbNameToAddr(sl,&laddr); - if(status) - { - printf("Can't connect to %s\n",s); - } else { - lineNum = ii + 1; - status = dbPutField(&laddr,DBR_LONG,&lineNum,1); - } - } - // Clear out remaining reporting channels. - sprintf(tmpstr,"%s"," "); - for (ii=numDisp;ii<40;ii++) { - sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii); - status = dbNameToAddr(s,&saddr); - status = dbPutField(&saddr,DBR_UCHAR,tmpstr,flength); - // sprintf(sl, "%s_SDF_LINE_%d", pref, (ii - myindex)); - sprintf(sl, "%s_SDF_LINE_%d", pref, ii); - status = dbNameToAddr(sl,&laddr); - lineNum += 1; - status = dbPutField(&laddr,DBR_LONG,&lineNum,1); - } - char speStat[256]; sprintf(speStat, "%s_%s", pref, "SDF_TABLE_ENTRIES"); // Setpoint diff counter - status = dbNameToAddr(speStat,&sperroraddr); // Get Address - status = dbPutField(&sperroraddr,DBR_LONG,&dc,1); // Init to zero. - - return(erucError); -} - -/** - * Scan the input text for the first non-whitespace character and return a pointer to that location. - * @param line NULL terminated string to check. - * @return Pointer to the the first non whitespace (space, tab, nl, cr) character. Returns NULL iff - * line is NULL. - */ -const char* skip_whitespace(const char* line) -{ - const char* cur = line; - char ch = 0; - if (!line) - { - return NULL; - } - ch = *cur; - while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') - { - ++cur; - ch = *cur; - } - return cur; -} - -/** - * Given a line with an comment denoted by '#' terminate - * the line at the start of the comment. - * @param line The line to modify, a NULL terminated string - * @note This may modify the string pointed to by line. - * This is safe to call with a NULL pointer. - */ -void remove_line_comments(char *line) -{ - char ch = 0; - - if (!line) - { - return; - } - while ((ch = *line)) - { - if (ch == '#') - { - *line = '\0'; - return; - } - ++line; - } -} - -/** - * Given a NULL terminated string remove any trailing whitespace - * @param line The line to modify, a NULL terminated string - * @note This may modify the string pointed to by line. - * This is safe to call with a NULL pointer. - */ -void remove_trailing_whitespace(char* newname) -{ - char* cur = newname; - char* last_non_ws = NULL; - char ch = 0; - - if (!newname) - { - return; - } - ch = *cur; - while (ch) - { - if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') - { - last_non_ws = cur; - } - ++cur; - ch = *cur; - } - if (!last_non_ws) - { - *newname = '\0'; - } - else - { - last_non_ws++; - *last_non_ws = '\0'; - } -} - -// ************************************************************************** -void edcuCreateChanFile(char *fdir, char *edcuinifilename, char *fecid) { -// ************************************************************************** - int ok = 0; - int i = 0; - int status = 0; - char errMsg[64] = ""; - FILE *daqfileptr = NULL; - FILE *edcuini = NULL; - FILE *edcumaster = NULL; - char masterfile[64] = ""; - char edcuheaderfilename[64] = ""; - char line[128] = ""; - char *newname = 0; - char edcufilename[64] = ""; - char *dcuid = 0; - - - sprintf(errMsg,"%s",fecid); - dcuid = strtok(errMsg,"-"); - dcuid = strtok(NULL,"-"); - sprintf(masterfile, "%s%s", fdir, "edcumaster.txt"); - sprintf(edcuheaderfilename, "%s%s", fdir, "edcuheader.txt"); - - // Open the master file which contains list of EDCU files to read channels from. - edcumaster = fopen(masterfile,"r"); - if(edcumaster == NULL) { - sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",masterfile); - logFileEntry(errMsg); - goto done; - } - // Open the file to write the composite channel list. - edcuini = fopen(edcuinifilename,"w"); - if(edcuini == NULL) { - sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",edcuinifilename); - logFileEntry(errMsg); - goto done; - } - - // Write standard header into .ini file - fprintf(edcuini,"%s","[default] \n"); - fprintf(edcuini,"%s","gain=1.00 \n"); - fprintf(edcuini,"%s","datatype=4 \n"); - fprintf(edcuini,"%s","ifoid=0 \n"); - fprintf(edcuini,"%s","slope=1 \n"); - fprintf(edcuini,"%s","acquire=3 \n"); - fprintf(edcuini,"%s","offset=0 \n"); - fprintf(edcuini,"%s","units=undef \n"); - fprintf(edcuini,"%s%s%s","dcuid=",dcuid," \n"); - fprintf(edcuini,"%s","datarate=16 \n\n"); - - // Read the master file entries. - while(fgets(line,sizeof line,edcumaster) != NULL) { - newname = strtok(line,"\n"); - if (!newname) - { - continue; - } - newname = (char*)skip_whitespace(newname); - remove_line_comments(newname); - remove_trailing_whitespace(newname); - - if (*newname == '\0') - { - continue; - } - strcpy(edcufilename,fdir); - strcat(edcufilename,newname); - printf("File in master = %s\n",edcufilename); - daqfileptr = fopen(edcufilename,"r"); - if(daqfileptr == NULL) { - sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST OR CANNOT BE READ!\n",edcufilename); - logFileEntry(errMsg); - goto done; - } - while(fgets(line,sizeof line,daqfileptr) != NULL) { - fprintf(edcuini,"%s",line); - } - fclose(daqfileptr); - daqfileptr = NULL; - } - ok = 1; -done: - if (daqfileptr) fclose(daqfileptr); - if (edcuini) fclose(edcuini); - if (edcumaster) fclose(edcumaster); - if (!ok) - { - exit(1); - } -} - -// ************************************************************************** -void edcuCreateChanList(char *daqfilename) { -// ************************************************************************** -int i; -int status; -FILE *daqfileptr; -FILE *edculog; -char errMsg[64]; -// char daqfile[64]; -char line[128]; -char *newname; - - -char *pref = getenv("PREFIX"); -char eccname[256]; sprintf(eccname, "%s_%s", pref, "EDCU_CHAN_CONN"); -char chcntname[256]; sprintf(chcntname, "%s_%s", pref, "EDCU_CHAN_CNT"); -char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "EDCU_CHAN_NOCON"); - - - // sprintf(daqfile, "%s%s", fdir, "EDCU.ini"); - daqd_edcu1.num_chans = 0; - daqfileptr = fopen(daqfilename,"r"); - if(daqfileptr == NULL) { - sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",daqfilename); - logFileEntry(errMsg); - } - edculog = fopen(edculogfilename,"w"); - if(daqfileptr == NULL) { - sprintf(errMsg,"DAQ FILE ERROR: FILE %s DOES NOT EXIST\n",edculogfilename); - logFileEntry(errMsg); - } - while(fgets(line,sizeof line,daqfileptr) != NULL) { - fprintf(edculog,"%s",line); - status = strlen(line); - if(strncmp(line,"[",1) == 0 && status > 0) { - newname = strtok(line,"]"); - // printf("status = %d New name = %s and %s\n",status,line,newname); - newname = strtok(line,"["); - // printf("status = %d New name = %s and %s\n",status,line,newname); - if(strcmp(newname,"default") == 0) { - printf("DEFAULT channel = %s\n", newname); - } else { - // printf("NEW channel = %s\n", newname); - sprintf(daqd_edcu1.channel_name[daqd_edcu1.num_chans],"%s",newname); - daqd_edcu1.num_chans ++; - } - } - } - fclose(daqfileptr); - fclose(edculog); - - xferInfo.crcLength = 4 * daqd_edcu1.num_chans; - printf("CRC data length = %d\n",xferInfo.crcLength); - - chid chid1; - ca_context_create(ca_enable_preemptive_callback); - - - for (i = 0; i < daqd_edcu1.num_chans; i++) { - if (strcmp(daqd_edcu1.channel_name[i], chcntname) == 0) { - num_chans_index = i; - internal_channel_count = internal_channel_count + 1; - daqd_edcu1.channel_status[i] = 0; - } - else if (strcmp(daqd_edcu1.channel_name[i], eccname) == 0) { - con_chans_index = i; - internal_channel_count = internal_channel_count + 1; - daqd_edcu1.channel_status[i] = 0; - } - else if (strcmp(daqd_edcu1.channel_name[i], cnfname) == 0) { - nocon_chans_index = i; - internal_channel_count = internal_channel_count + 1; - daqd_edcu1.channel_status[i] = 0; - } - else { - status = ca_create_channel(daqd_edcu1.channel_name[i], connectCallback, (void *)i, 0, &chid1); - status = ca_create_subscription(DBR_FLOAT, 0, chid1, DBE_VALUE, - subscriptionHandler, (void *)i, 0); - } - } - - daqd_edcu1.con_chans = daqd_edcu1.con_chans + internal_channel_count; - - timeIndex = 0; -} - -// ************************************************************************** -void edcuWriteData(int daqBlockNum, unsigned long cycle_gps_time, int dcuId, int daqreset) -// ************************************************************************** -{ -float *daqData; -int buf_size; -int ii; - - - - if (num_chans_index != -1) { - daqd_edcu1.channel_value[num_chans_index] = daqd_edcu1.num_chans; - } - - if (con_chans_index != -1) { - daqd_edcu1.channel_value[con_chans_index] = daqd_edcu1.con_chans; - } - - if (nocon_chans_index != -1) { - daqd_edcu1.channel_value[nocon_chans_index] = daqd_edcu1.num_chans - daqd_edcu1.con_chans; - } - - - - - buf_size = DAQ_DCU_BLOCK_SIZE*DAQ_NUM_SWING_BUFFERS; - daqData = (float *)(shmDataPtr + (buf_size * daqBlockNum)); - memcpy(daqData, daqd_edcu1.channel_value, daqd_edcu1.num_chans * sizeof(float)); - dipc->dcuId = dcuId; - dipc->crc = daqFileCrc; - dipc->dataBlockSize = xferInfo.crcLength; - dipc->bp[daqBlockNum].cycle = daqBlockNum; - dipc->bp[daqBlockNum].crc = xferInfo.crcLength; - dipc->bp[daqBlockNum].timeSec = (unsigned int) cycle_gps_time; - dipc->bp[daqBlockNum].timeNSec = (unsigned int)daqBlockNum; - if(daqreset) { - shmTpTable->count = 1; - shmTpTable->tpNum[0] = daqreset; - } else { - shmTpTable->count = 0; - shmTpTable->tpNum[0] = 0; - } - dipc->cycle = daqBlockNum; // Triggers sending of data by mx_stream. - -} - -// ************************************************************************** -void edcuInitialize(char *shmem_fname, char *sync_source) -// ************************************************************************** -{ - - // Find start of DAQ shared memory - void *dcu_addr = findSharedMemory(shmem_fname); - // Find the IPC area to communicate with mxstream - dipc = (struct rmIpcStr *)((char *)dcu_addr + CDS_DAQ_NET_IPC_OFFSET); - // Find the DAQ data area. - shmDataPtr = (char *)((char *)dcu_addr + CDS_DAQ_NET_DATA_OFFSET); - shmTpTable = (struct cdsDaqNetGdsTpNum *)((char *)dcu_addr + CDS_DAQ_NET_GDS_TP_TABLE_OFFSET); - // Find Sync source - void *sync_addr = findSharedMemory(sync_source); - sipc = (struct rmIpcStr *)((char *)sync_addr + CDS_DAQ_NET_IPC_OFFSET); -} - - - -/// Common routine to check file CRC. -/// @param[in] *fName Name of file to check. -/// @return File CRC or -1 if file not found. -int checkFileCrc(char *fName) -{ -char buffer[256]; -FILE *pipePtr; -struct stat statBuf; -long chkSum = -99999; - strcpy(buffer, "cksum "); - strcat(buffer, fName); - if (!stat(fName, &statBuf) ) { - if ((pipePtr = popen(buffer, "r")) != NULL) { - fgets(buffer, 256, pipePtr); - pclose(pipePtr); - sscanf(buffer, "%ld", &chkSum); - } - return(chkSum); - } - return(-1); -} - -/// Routine for reading GPS time from model EPICS record. -/// @param[out] timestring Pointer to char string in which GPS time is to be written. -void getSdfTime(char *timestring) -{ - - dbAddr paddr; - long ropts = 0; - long nvals = 1; - long status; - - status = dbNameToAddr(timechannel,&paddr); - status = dbGetField(&paddr,DBR_STRING,timestring,&ropts,&nvals,NULL); -} - -/// Routine for logging messages to ioc.log file. -/// @param[in] message Ptr to string containing message to be logged. -void logFileEntry(char *message) -{ - FILE *log; - char timestring[256]; - long status; - dbAddr paddr; - - getSdfTime(timestring); - log = fopen(logfilename,"a"); - if(log == NULL) { - status = dbNameToAddr(reloadtimechannel,&paddr); - status = dbPutField(&paddr,DBR_STRING,"ERR - NO LOG FILE FOUND",1); - } else { - fprintf(log,"%s\n%s\n",timestring,message); - fprintf(log,"***************************************************\n"); - fclose(log); - } - -} - -/// Called on EPICS startup; This is generic EPICS provided function, modified for LIGO use. -int main(int argc,char *argv[]) -{ - // Addresses for SDF EPICS records. - // Initialize request for file load on startup. - long status; - int request; - int daqTrigger; - long ropts = 0; - long nvals = 1; - int rdstatus = 0; - char timestring[128]; - int ii; - long coeffFileCrc; - char modfilemsg[] = "Modified File Detected "; - struct stat st = {0}; - char filemsg[128]; - char logmsg[256]; - int pageNum = 0; - int pageNumDisp = 0; - int daqreset = 0; - char errMsg[64]; - char *dcuid; - int send_daq_reset = 0; - - check_crc_params crc_params; - - if(argc>=2) { - iocsh(argv[1]); - - memset(&crc_params, 0, sizeof(crc_params)); - - // printf("Executing post script commands\n"); - // Get environment variables from startup command to formulate EPICS record names. - char *pref = getenv("PREFIX"); - char *modelname = getenv("SDF_MODEL"); - char daqsharedmemname[64]; - sprintf(daqsharedmemname, "%s%s", modelname, "_daq"); - char *sync = getenv("SYNC_SRC"); - char syncsharedmemname[64]; - sprintf(syncsharedmemname, "%s%s", sync, "_daq"); - char *targetdir = getenv("TARGET_DIR"); - char *daqFile = getenv("DAQ_FILE"); - char *daqDir = getenv("DAQ_DIR"); - char *coeffFile = getenv("COEFF_FILE"); - char *logdir = getenv("LOG_DIR"); - if(stat(logdir, &st) == -1) mkdir(logdir,0777); - sprintf(errMsg,"%s",pref); - dcuid = strtok(errMsg,"-"); - dcuid = strtok(NULL,"-"); - int mydcuid = atoi(dcuid); - // strcat(sdf,"_safe"); - printf("My prefix is %s\n",pref); - printf("My dcuid is %d\n",mydcuid); - sprintf(logfilename, "%s%s", logdir, "/ioc.log"); - printf("LOG FILE = %s\n",logfilename); -sleep(2); - // ********************************************** - // - dbAddr eccaddr; - char eccname[256]; sprintf(eccname, "%s_%s", pref, "EDCU_CHAN_CONN"); // Number of setting channels in EPICS db - status = dbNameToAddr(eccname,&eccaddr); - - dbAddr chcntaddr; - // char chcntname[256]; sprintf(chcntname, "%s", "X1:DAQ-FEC_54_EPICS_CHAN_CNT"); // Request to monitor all channels. - char chcntname[256]; sprintf(chcntname, "%s_%s", pref, "EDCU_CHAN_CNT"); // Request to monitor all channels. - status = dbNameToAddr(chcntname,&chcntaddr); // Get Address. - - dbAddr chnotfoundaddr; - char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "EDCU_CHAN_NOCON"); // Number of channels not found. - status = dbNameToAddr(cnfname,&chnotfoundaddr); - - dbAddr daqbyteaddr; - char daqbytename[256]; sprintf(daqbytename, "%s_%s", pref, "DAQ_BYTE_COUNT"); // Request to monitor all channels. - status = dbNameToAddr(daqbytename,&daqbyteaddr); // Get Address. - - char moddaqfilemsg[256]; sprintf(moddaqfilemsg, "%s_%s", pref, "MSGDAQ"); // Record to write if DAQ file changed. - status = dbNameToAddr(moddaqfilemsg, &(crc_params.message_dest)); - - sprintf(timechannel,"%s_%s", pref, "TIME_STRING"); - // printf("timechannel = %s\n",timechannel); - - dbAddr reloadtimeaddr; - sprintf(reloadtimechannel,"%s_%s", pref, "MSGDAQ"); // Time of last BURT reload - status = dbNameToAddr(reloadtimechannel,&reloadtimeaddr); - - getSdfTime(timestring); - status = dbPutField(&reloadtimeaddr,DBR_STRING,timestring,1); - - dbAddr gpstimedisplayaddr; - char gpstimedisplayname[256]; sprintf(gpstimedisplayname, "%s_%s", pref, "TIME_DIAG"); // SDF Save command. - status = dbNameToAddr(gpstimedisplayname,&gpstimedisplayaddr); // Get Address. - - dbAddr pagereqaddr; - char pagereqname[256]; sprintf(pagereqname, "%s_%s", pref, "SDF_PAGE"); // SDF Page request. - status = dbNameToAddr(pagereqname,&pagereqaddr); // Get Address. - - dbAddr daqresetaddr; - char daqresetname[256]; sprintf(daqresetname, "%s_%s", pref, "EDCU_DAQ_RESET"); // SDF Page request. - status = dbNameToAddr(daqresetname,&daqresetaddr); // Get Address. - -// EDCU STUFF ******************************************************************************************************** - - sprintf(edculogfilename, "%s%s", logdir, "/edcu.log"); - for (ii=0;ii<EDCU_MAX_CHANS;ii++) daqd_edcu1.channel_status[ii] = 0xbad; - edcuInitialize(daqsharedmemname,syncsharedmemname); - edcuCreateChanFile(daqDir,daqFile,pref); - edcuCreateChanList(daqFile); - status = dbPutField(&chcntaddr,DBR_LONG,&daqd_edcu1.num_chans,1); - int datarate = daqd_edcu1.num_chans * 64 / 1000; - status = dbPutField(&daqbyteaddr,DBR_LONG,&datarate,1); - -// Start SPECT - daqd_edcu1.gpsTime = symm_initialize(); - daqd_edcu1.epicsSync = 0; - -// End SPECT - - int dropout = 0; - int numDC = 0; - int cycle = 0; - int numReport = 0; - - // Initialize DAQ and COEFF file CRC checksums for later compares. - daqFileCrc = checkFileCrc(daqFile); - printf("DAQ file CRC = %u \n",daqFileCrc); - coeffFileCrc = checkFileCrc(coeffFile); - sprintf(logmsg,"%s\n%s = %u\n%s = %d","EDCU code restart","File CRC",daqFileCrc,"Chan Cnt",daqd_edcu1.num_chans); - logFileEntry(logmsg); - edcuClearSdf(pref); - - crc_params.ini_filename = daqFile; - crc_params.orig_crc = daqFileCrc; - - pthread_create(&check_crc_thread, NULL, check_crc, &crc_params); - // Start Infinite Loop ******************************************************************************* - for(;;) { - dropout = 0; - daqd_edcu1.epicsSync = waitNewCycle(&daqd_edcu1.gpsTime); - edcuWriteData(daqd_edcu1.epicsSync, daqd_edcu1.gpsTime,mydcuid,send_daq_reset); - send_daq_reset = 0; - status = dbPutField(&gpstimedisplayaddr,DBR_LONG,&daqd_edcu1.gpsTime,1); // Init to zero. - status = dbPutField(&daqbyteaddr,DBR_LONG,&datarate,1); - int conChans = daqd_edcu1.con_chans; - status = dbPutField(&eccaddr,DBR_LONG,&conChans,1); - // Check unconnected channels once per second - if (daqd_edcu1.epicsSync == 0) { - status = dbGetField(&daqresetaddr,DBR_LONG,&daqreset,&ropts,&nvals,NULL); - if(daqreset) { - status = dbPutField(&daqresetaddr,DBR_LONG,&ropts,1); // Init to zero. - send_daq_reset = daqreset; - } - status = dbGetField(&pagereqaddr,DBR_LONG,&pageNum,&ropts,&nvals,NULL); - if((int)pageNum != 0) { - pageNumDisp += pageNum; - if(pageNumDisp < 0) pageNumDisp = 0; - status = dbPutField(&pagereqaddr,DBR_LONG,&ropts,1); // Init to zero. - } - numDC = edcuFindUnconnChannels(); - if(numDC < (pageNumDisp * 40)) pageNumDisp --; - numReport = edcuReportUnconnChannels(pref,numDC,pageNumDisp); - } - status = dbPutField(&chnotfoundaddr,DBR_LONG,&numDC,1); - } - sleep(0xfffffff); - } else - iocsh(NULL); - return(0); -} - - -/** - * @brief main loop for the crc checking thread. - * @param arg Input structure defining the epics variables to set. - * @return null - * @note this is outside of the main thread so that it does not stall - * the filling of the mbuf while waiting for filesystem I/O (ie crc check - * of the file). - */ -void* check_crc(void* arg) { - check_crc_params* crc_params = (check_crc_params*)arg; - char okfilemsg[] = ""; - char modfilemsg[] = "Modified File Detected "; - int cur_crc = 0; - int was_ok = 1; - - - if (!crc_params) - { - logFileEntry("Unable to check the ini file"); - return 0; - } - - while (1) { - sleep(5); - cur_crc = checkFileCrc(crc_params->ini_filename); - - if (cur_crc != crc_params->orig_crc) { - dbPutField(&(crc_params->message_dest),DBR_STRING,modfilemsg,1); - if (was_ok) { - logFileEntry("Detected Change to DAQ Config file."); - } - was_ok = 0; - } else { - dbPutField(&(crc_params->message_dest), DBR_STRING, okfilemsg, 1); - if (!was_ok) { - logFileEntry("DAQ config file restored to its expected state."); - } - was_ok = 1; - } - } - return 0; -} - diff --git a/src/epics/seq/epics_channel.hh b/src/epics/seq/epics_channel.hh new file mode 100644 index 0000000000000000000000000000000000000000..8074e0d378d116bbcf8ec95fb7c289111d8784c0 --- /dev/null +++ b/src/epics/seq/epics_channel.hh @@ -0,0 +1,125 @@ +// +// Created by jonathan.hanks on 9/7/21. +// + +#ifndef DAQD_TRUNK_EPICS_CHANNEL_HH +#define DAQD_TRUNK_EPICS_CHANNEL_HH + +#include <cstdint> +#include <type_traits> +#include <stdexcept> + +#include "dbAccess.h" + +#include "epics_channel_common.hh" + +namespace epics +{ + namespace detail + { + template < PVType T > + struct epics_lookup + { + }; + + template <> + struct epics_lookup<PVType::UInt16> + { + static const auto value = DBF_USHORT; + }; + + template <> + struct epics_lookup<PVType::Int32> + { + static const auto value = DBF_LONG; + }; + + template <> + struct epics_lookup<PVType::UInt32> + { + static const auto value = DBF_ULONG; + }; + + template <> + struct epics_lookup<PVType::Float64> + { + static const auto value = DBF_DOUBLE; + }; + + template <> + struct epics_lookup<PVType::String> + { + static const auto value = DBF_STRING; + }; + } + + template<PVType DataType> + class DBEntry { + static constexpr const dbfType epicsType = detail::epics_lookup<DataType>::value; + public: + struct ValidateAddress {}; + + using value_type = typename type_lookup<DataType>::type; + + explicit DBEntry(channel_name name) + { + dbNameToAddr(name.c_str(), &addr_); + } + + DBEntry(channel_name name, ValidateAddress tag) + { + auto status = dbNameToAddr(name.c_str(), &addr_); + if ( status ) + { + throw std::runtime_error("Failure with PV lookup in the EPICS " + "database. If this is the first PV " + "this usually signifies an error " + "with the .cmd passed to iocsh(), " + "or a misconfigured EPICS " + "environment"); + } + } + + DBEntry(channel_name name, const char* val) + { + static_assert(epicsType == DBF_STRING, "Initialization by const char* must be on a string type"); + dbNameToAddr(name.c_str(), &addr_); + dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(val), 1); + } + + + DBEntry(channel_name name, const value_type& val) + { + dbNameToAddr(name.c_str(), &addr_); + dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(&val), 1); + } + + void + set(const value_type& val) + { + dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(&val), 1); + } + + void + set(const char* val) + { + static_assert(epicsType == DBF_STRING, "Setting a const char* must be done on a string type"); + dbPutField(&addr_, epicsType, reinterpret_cast<const void*>(val), 1); + } + + void + get(value_type& val) + { + long ropts{0}; + long nvals{1}; + dbGetField(&addr_, epicsType, &val, &ropts, &nvals, nullptr); + } + private: + + + dbAddr addr_{}; + }; + +} + +#endif //DAQD_TRUNK_EPICS_CHANNEL_HH diff --git a/src/epics/seq/epics_channel_common.hh b/src/epics/seq/epics_channel_common.hh new file mode 100644 index 0000000000000000000000000000000000000000..2f3d3af8d04bdd2f6d571b9d6fe837070d1ebd96 --- /dev/null +++ b/src/epics/seq/epics_channel_common.hh @@ -0,0 +1,66 @@ +// +// Created by jonathan.hanks on 3/21/22. +// + +#ifndef DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH +#define DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH + +#include "fixed_size_string.hh" + +namespace epics +{ + using channel_name = embedded::fixed_string<256>; + + inline channel_name + make_name(const char* prefix, const char* name) + { + channel_name output{}; + output.printf("%s_%s", prefix, name); + return output; + } + + enum class PVType { + UInt16, + Int32, + UInt32, + Float64, + String, + }; + + template < PVType T > + struct type_lookup + { + }; + + template <> + struct type_lookup<PVType::UInt16> + { + using type=std::uint16_t; + }; + + template <> + struct type_lookup<PVType::Int32> + { + using type=std::int32_t; + }; + + template <> + struct type_lookup<PVType::UInt32> + { + using type=std::uint32_t; + }; + + template <> + struct type_lookup<PVType::Float64> + { + using type=double; + }; + + template <> + struct type_lookup<PVType::String> + { + using type=embedded::fixed_string<256>; + }; +} + +#endif // DAQD_TRUNK_EPICS_CHANNEL_TYPES_HH diff --git a/src/epics/seq/fixed_size_string.hh b/src/epics/seq/fixed_size_string.hh new file mode 100644 index 0000000000000000000000000000000000000000..822db31163758b66b3499761419a7942c76e3dde --- /dev/null +++ b/src/epics/seq/fixed_size_string.hh @@ -0,0 +1,272 @@ +// +// Created by jonathan.hanks on 8/16/18. +// + +#ifndef FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH +#define FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH + +#include <algorithm> +#include <cstdarg> +#include <cstddef> +#include <cstdio> +#include <cstring> + +namespace embedded +{ + + /*! + * @brief A fixed size string abstraction that knows its bounds and does not + * overflow. + * @tparam max_size + * @note this class does not allocation and should not throw an exception + * ever. + */ + template < std::size_t max_size > + class fixed_string + { + static_assert( max_size >= 1, "Fixed string capacity must be >= 1" ); + + public: + typedef std::size_t size_type; + + /** + * @brief the buffer object is a way to get raw access to manipulate the + * fixed_string. It looses most protections of the fixed_string class, but + * ensures that the buffer is null terminated when the buffer is destroyed. + */ + class buffer + { + friend class fixed_string; + explicit buffer( char* buf ) : buf_{ buf } + { + } + + public: + buffer( buffer&& other ) noexcept = default; + ~buffer( ) + { + if ( buf_ ) + { + buf_[ fixed_string< max_size >::last_index( ) ] = '\0'; + } + } + buffer( const buffer& other ) = delete; + buffer& operator=( const buffer& other ) = delete; + buffer& operator=( buffer&& other ) noexcept = default; + + char* + data( ) noexcept + { + return buf_; + } + constexpr size_type + capacity( ) const noexcept + { + return max_size; + } + + private: + char* buf_; + }; + + fixed_string( ) noexcept : data_( ) + { + data_[ 0 ] = '\0'; + } + fixed_string( const fixed_string& ) noexcept = default; + + template < std::size_t other_max_size > + explicit fixed_string( + const fixed_string< other_max_size >& other ) noexcept : data_( ) + { + copy_in_data( other.c_str( ) ); + } + explicit fixed_string( const char* in ) noexcept : data_( ) + { + copy_in_data( in ); + } + + fixed_string& operator=( const fixed_string& other ) noexcept = default; + + template < std::size_t other_max_size > + fixed_string& + operator=( const fixed_string< other_max_size >& other ) noexcept + { + operator=( other.c_str( ) ); + return *this; + } + + fixed_string& + operator=( const char* in ) noexcept + { + copy_in_data( in ); + return *this; + } + + fixed_string& + operator+=( const char* in ) noexcept + { + if ( in ) + { + size_type cur_size = size( ); + size_type in_size = std::min( std::strlen( in ), remaining( ) ); + std::memcpy( data_ + cur_size, in, in_size ); + data_[ cur_size + in_size ] = 0; + } + return *this; + } + + template < std::size_t in_max > + fixed_string& + operator+=( const fixed_string< in_max >& in ) noexcept + { + size_type in_size = std::min( in.size( ), remaining( ) ); + if ( in_size > 0 ) + { + size_type cur_size = size( ); + std::memcpy( data_ + cur_size, in.c_str( ), in_size ); + data_[ cur_size + in_size ] = 0; + } + return *this; + } + + explicit operator const char*( ) const noexcept + { + return c_str( ); + } + + void + clear( ) noexcept + { + data_[ 0 ] = '\0'; + } + + size_type + printf( const char* fmt, ... ) noexcept + { + if ( !fmt ) + { + clear( ); + return 0; + } + va_list args; + va_start( args, fmt ); + size_type results = std::vsnprintf( data_, capacity( ), fmt, args ); + va_end( args ); + return results; + } + + bool + operator!=( const fixed_string& other ) const noexcept + { + return !operator==( other ); + } + + bool + operator!=( const char* str ) const noexcept + { + return !operator==( str ); + } + + template < std::size_t other_max_size > + bool + operator!=( const fixed_string< other_max_size >& other ) const noexcept + { + return !operator==( other ); + } + + bool + operator==( const fixed_string& other ) const noexcept + { + return std::strcmp( c_str( ), other.c_str( ) ) == 0; + } + + bool + operator==( const char* str ) const noexcept + { + static const char* dummy = ""; + return std::strcmp( c_str( ), ( str ? str : dummy ) ) == 0; + } + + template < std::size_t other_max_size > + bool + operator==( const fixed_string< other_max_size >& other ) const noexcept + { + return std::strcmp( c_str( ), other.c_str( ) ) == 0; + } + + size_type + remaining( ) const noexcept + { + return last_index( ) - size( ); + } + + constexpr size_type + capacity( ) const noexcept + { + return max_size; + } + + size_type + size( ) const noexcept + { + return std::strlen( data_ ); + } + + const char* + c_str( ) const noexcept + { + return data_; + } + + const char* + data( ) const noexcept + { + return data_; + } + + void + pop_back_n( size_type n ) noexcept + { + if ( n > 0 ) + { + auto length = size( ); + auto remove = std::min( length, n ); + data_[ length - remove ] = '\0'; + } + } + + buffer + get_buffer( ) noexcept + { + return buffer( data_ ); + } + + private: + void + copy_in_data( const char* in ) noexcept + { + if ( in ) + { + size_type in_size = + std::min( std::strlen( in ), last_index( ) ); + std::memcpy( data_, in, in_size ); + data_[ in_size ] = 0; + } + else + { + data_[ 0 ] = 0; + } + } + + static constexpr size_type + last_index( ) noexcept + { + return max_size - 1; + } + char data_[ max_size ]; + }; + +} // namespace embedded + +#endif // FIXED_SIZE_STRING_FIXED_SIZE_STRING_HH diff --git a/src/epics/seq/fixed_size_vector.hh b/src/epics/seq/fixed_size_vector.hh new file mode 100644 index 0000000000000000000000000000000000000000..6c060a5eff4ee7e80b63ccb7b81ba71cafab4fda --- /dev/null +++ b/src/epics/seq/fixed_size_vector.hh @@ -0,0 +1,240 @@ +// +// Created by jonathan.hanks on 11/13/20. +// + +#ifndef DAQD_TRUNK_FIXED_SIZE_VECTOR_HH +#define DAQD_TRUNK_FIXED_SIZE_VECTOR_HH + +#include <cstddef> +#include <type_traits> +#include <utility> + +namespace embedded +{ + + /*! + * @brief a simple vector with a fixed backing store. + * @details + * @tparam T the type being stored, some constraints are place on T to keep + * the vector noexcept + * @tparam max_size + */ + template < typename T, std::size_t max_size > + class fixed_size_vector + { + static_assert( std::is_nothrow_copy_constructible< T >::value, + "T must be no throw copy constructable" ); + static_assert( std::is_nothrow_copy_assignable< T >::value, + "T must be no throw copy assignable" ); + static_assert( std::is_nothrow_move_constructible< T >::value, + "T must be no throw move constructable" ); + static_assert( std::is_nothrow_move_assignable< T >::value, + "T must be no throw move assignable" ); + static_assert( std::is_nothrow_destructible< T >::value, + "T must be no throw destructable" ); + + using storage_type = + typename std::aligned_storage< sizeof( T ), alignof( T ) >::type; + + public: + using value_type = T; + using size_type = std::size_t; + using iterator = typename std::add_pointer< T >::type; + using const_iterator = typename std::add_pointer< + typename std::add_const< T >::type >::type; + + ~fixed_size_vector( ) + { + clear( ); + } + + size_type + size( ) const noexcept + { + return entries_; + } + + bool + empty( ) const noexcept + { + return size( ) == 0; + } + + constexpr size_type + capacity( ) const noexcept + { + return max_size; + } + + iterator + begin( ) noexcept + { + return reinterpret_cast< T* >( &storage_[ 0 ] ); + } + + iterator + end( ) noexcept + { + return begin( ) + entries_; + } + + const_iterator + begin( ) const noexcept + { + return reinterpret_cast< const_iterator >( &storage_[ 0 ] ); + } + + const_iterator + end( ) const noexcept + { + return begin( ) + entries_; + } + + const_iterator + cbegin( ) const noexcept + { + return begin( ); + } + + const_iterator + cend( ) const noexcept + { + return end( ); + } + + /*! + * @brief push an entry onto the back of the vector if space is + * available. + * @param val + * @return true on success, else false (no change made to the vector) + */ + bool + push_back( const T& val ) noexcept + { + if ( size( ) < capacity( ) ) + { + T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] ); + new ( dest ) T( std::move( val ) ); + ++entries_; + return true; + } + return false; + } + + /*! + * @brief push an entry onto the back of the vector if space is + * available. + * @param val + * @return true on success, else false (no change made to the vector) + */ + bool + push_back( T&& val ) noexcept + { + if ( size( ) < capacity( ) ) + { + T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] ); + new ( dest ) T( std::move( val ) ); + ++entries_; + return true; + } + return false; + } + + /*! + * @brief emplace an entry onto the back of the vector if space is + * available. + * @tparam Args The argument types to pass to T's constructor + * @param args The argument to pass to T's constructor + * @return true on success, else false (no change made to the vector) + */ + template < typename... Args > + bool + emplace_back( Args... args ) noexcept + { + if ( size( ) < capacity( ) ) + { + T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] ); + new ( dest ) T( std::forward< Args >( args )... ); + ++entries_; + return true; + } + return false; + } + + T& operator[]( size_type index ) noexcept + { + return *reinterpret_cast< T* >( &storage_[ index ] ); + } + + const T& operator[]( size_type index ) const noexcept + { + return *reinterpret_cast< const T* >( &storage_[ index ] ); + } + + void + pop_back( ) noexcept + { + if ( !empty( ) ) + { + size_type index = entries_ - 1; + T* cur = reinterpret_cast< T* >( &storage_[ index ] ); + cur->~T( ); + entries_--; + } + } + + template < typename T_ = T, + typename std::enable_if< + std::is_trivially_destructible< T_ >::value, + int >::type = 0 > + void + clear( ) + { + entries_ = 0; + } + + template < typename T_ = T, + typename std::enable_if< + !std::is_trivially_destructible< T_ >::value, + int >::type = 0 > + void + clear( ) + { + while ( !empty( ) ) + { + pop_back( ); + } + } + + T& + front() noexcept + { + return operator[](0); + } + + T& + back() noexcept + { + return operator[](entries_-1); + } + + const T& + front() const noexcept + { + return operator[](0); + } + + const T& + back() const noexcept + { + return operator[](entries_-1); + } + + private: + storage_type storage_[ max_size ]{}; + size_type entries_{ 0 }; + }; + +} // namespace embedded + +#endif // DAQD_TRUNK_FIXED_SIZE_VECTOR_HH diff --git a/src/epics/seq/main.c b/src/epics/seq/main.cc similarity index 59% rename from src/epics/seq/main.c rename to src/epics/seq/main.cc index 5631df33620b45eef0a9feab7ec742ae5be4f7cd..88cd9227a1c5cdd5fb0b4090684ef4179c771346 100644 --- a/src/epics/seq/main.c +++ b/src/epics/seq/main.cc @@ -19,6 +19,7 @@ of this distribution. /* * Main program for demo sequencer */ +#include "epics_channel.hh" #include <stddef.h> #include <stdlib.h> #include <string.h> @@ -28,6 +29,10 @@ of this distribution. #include <sys/stat.h> #include <errno.h> #include <ctype.h> +#include <algorithm> +#include <array> +#include <iterator> +#include <memory> #ifdef USE_SYSTEM_TIME #include <time.h> @@ -35,7 +40,9 @@ of this distribution. #include "iocsh.h" #include "dbStaticLib.h" -#include "crc.h" +extern "C" { + #include "crc.h" +} #include "dbCommon.h" #include "recSup.h" #include "dbDefs.h" @@ -45,28 +52,35 @@ of this distribution. #include "math.h" #include "rcgversion.h" #include "sdf_file_loaded.h" +#include "util/user/check_file_crc.h" #define epicsExportSharedSymbols #include "asLib.h" #include "asCa.h" #include "asDbLib.h" +#include "fixed_size_string.hh" +#include "fixed_size_vector.hh" +#include "simple_range.hh" + +using embedded::fixed_string; +using embedded::fixed_size_vector; + #ifdef CA_SDF +#include <mutex> -// The identifiers below were defined in the EPICS local db headers but -// also are defined in cadef.h. -// They are undefined here to avoid compiler warnings. +// Including the DB/IOC based code + CA based client code gives warnings on redefinitions +// so undef these before including the CA code. #undef DBR_SHORT #undef DBR_GR_LONG #undef DBR_GR_DOUBLE -#undef DBR_CTRL_LONG +#undef DBR_GR_LONG #undef DBR_CTRL_DOUBLE +#undef DBR_CTRL_LONG #undef DBR_PUT_ACKT #undef DBR_PUT_ACKS #undef VALID_DB_REQ #undef INVALID_DB_REQ - -#include <pthread.h> #include "cadef.h" #endif @@ -101,6 +115,14 @@ of this distribution. #define CA_STATE_RUNNING 2 #define CA_STATE_EXIT 3 +enum SDF_TYPE { + SDF_NUM=0, + SDF_STR=1, + SDF_UNKNOWN=-1, +}; + +using PV_INT_TYPE = unsigned int; + #if 0 /// Pointers to status record DBENTRY *pdbentry_status[2][10]; @@ -132,7 +154,7 @@ union Data { /// Structure for holding BURT settings in local memory. typedef struct CDS_CD_TABLE { - char chname[128]; + fixed_string<128> chname; int datatype; union Data data; int mask; @@ -140,7 +162,7 @@ typedef struct CDS_CD_TABLE { int filterswitch; int filterNum; int error; - char errMsg[64]; + fixed_string<64> errMsg; int chFlag; #ifdef CA_SDF int connected; @@ -149,7 +171,7 @@ typedef struct CDS_CD_TABLE { /// Structure for creating/holding filter module switch settings. typedef struct FILTER_TABLE { - char fname[64]; + fixed_string<64> fname; int swreq; int swmask; int sw[2]; @@ -160,15 +182,15 @@ typedef struct FILTER_TABLE { /// Structure for table data to be presented to operators. typedef struct SET_ERR_TABLE { - char chname[64]; - char burtset[64]; - char liveset[64]; - char timeset[64]; - char diff[64]; + fixed_string<64> chname; + fixed_string<64> burtset; + fixed_string<64> liveset; + fixed_string<64> timeset; + fixed_string<64> diff; double liveval; int sigNum; int chFlag; - int filtNum; + int filtNum{-1}; unsigned int sw[2]; } SET_ERR_TABLE; @@ -191,6 +213,15 @@ typedef struct CA_STARTUP_INFO { #endif +using table_range = embedded::range<SET_ERR_TABLE*>; + +template <std::size_t entry_count> +table_range +make_range(fixed_size_vector<SET_ERR_TABLE, entry_count>& cont) +{ + return embedded::make_range(&cont[0], &cont[0] + cont.size()); +} + // Gloabl variables **************************************************************************************** int chNum = 0; ///< Total number of channels held in the local lookup table. int chNotMon = 0; ///< Total number of channels not being monitored. @@ -202,30 +233,30 @@ int chNotFound = 0; ///< Total number of channels read from BURT file which did int chNotInit = 0; ///< Total number of channels not initialized by the safe.snap BURT file. int rderror = 0; #ifndef USE_SYSTEM_TIME -char timechannel[256]; ///< Name of the GPS time channel for timestamping. +std::unique_ptr< epics::DBEntry< epics::PVType::String > > timechannel{nullptr}; ///the GPS time channel for timestamping. #endif char reloadtimechannel[256]; ///< Name of EPICS channel which contains the BURT reload requests. struct timespec t; char logfilename[128]; -CDS_CD_TABLE cdTable[SDF_MAX_TSIZE]; ///< Table used to hold EPICS database info for monitoring settings. -CDS_CD_TABLE cdTableP[SDF_MAX_CHANS]; ///< Temp table filled on BURT read and set to EPICS channels. -FILTER_TABLE filterTable[SDF_MAX_FMSIZE]; ///< Table for holding filter module switch settings for comparisons. -SET_ERR_TABLE setErrTable[SDF_ERR_TSIZE]; ///< Table used to report settings diffs. -SET_ERR_TABLE unknownChans[SDF_ERR_TSIZE]; ///< Table used to report channels not found in local database. -SET_ERR_TABLE uninitChans[SDF_ERR_TSIZE]; ///< Table used to report channels not initialized by BURT safe.snap. -SET_ERR_TABLE unMonChans[SDF_MAX_TSIZE]; ///< Table used to report channels not being monitored. -SET_ERR_TABLE readErrTable[SDF_ERR_TSIZE]; ///< Table used to report file read errors.. -SET_ERR_TABLE cdTableList[SDF_MAX_TSIZE]; ///< Table used to report file read errors.. -time_t timeTable[SDF_MAX_CHANS]; ///< Holds timestamps for cdTableP +fixed_size_vector<CDS_CD_TABLE, SDF_MAX_TSIZE> cdTable; ///< Table used to hold EPICS database info for monitoring settings. +fixed_size_vector<CDS_CD_TABLE, SDF_MAX_CHANS> cdTableP; ///< Temp table filled on BURT read and set to EPICS channels. +fixed_size_vector<FILTER_TABLE, SDF_MAX_FMSIZE> filterTable; ///< Table for holding filter module switch settings for comparisons. +fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> setErrTable; ///< Table used to report settings diffs. +fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> unknownChans; ///< Table used to report channels not found in local database. +fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> uninitChans; ///< Table used to report channels not initialized by BURT safe.snap. +fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> unMonChans; ///< Table used to report channels not being monitored. +fixed_size_vector<SET_ERR_TABLE, SDF_ERR_TSIZE> readErrTable; ///< Table used to report file read errors.. +fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> cdTableList; ///< Table used to report file read errors.. +fixed_size_vector<time_t, SDF_MAX_CHANS> timeTable; ///< Holds timestamps for cdTableP -int filterMasks[SDF_MAX_FMSIZE]; ///< Filter monitor masks, as manipulated by the user +fixed_size_vector<int, SDF_MAX_FMSIZE> filterMasks; ///< Filter monitor masks, as manipulated by the user -dbAddr fmMaskChan[SDF_MAX_FMSIZE]; ///< We reflect the mask value into these channels -dbAddr fmMaskChanCtrl[SDF_MAX_FMSIZE]; ///< We signal bit changes to the masks on these channels +fixed_size_vector<dbAddr, SDF_MAX_FMSIZE> fmMaskChan; ///< We reflect the mask value into these channels +fixed_size_vector<dbAddr, SDF_MAX_FMSIZE> fmMaskChanCtrl; ///< We signal bit changes to the masks on these channels #ifdef CA_SDF -SET_ERR_TABLE disconnectChans[SDF_MAX_TSIZE]; +fixed_size_vector<SET_ERR_TABLE, SDF_MAX_TSIZE> disconnectChans; // caTable, caConnTAble, chConnNum, chEnumDetermined are protected by the caTableMutex EPICS_CA_TABLE caTable[SDF_MAX_TSIZE]; ///< Table used to hold the data returned by channel access @@ -245,68 +276,65 @@ int chEnumDetermined = 0; ///< Total number of enums whos types have been dete long chDisconnected = 0; ///< Total number of channels that are disconnected. This is used as the max index for disconnectedChans long chDisconnectedCount = 0; ///< The count of the disconnected channels. Seperate from chDisconnected so that it can be updated even when the table is not updated. -pthread_t caThread; -pthread_mutex_t caStateMutex; -pthread_mutex_t caTableMutex; -pthread_mutex_t caEvidMutex; + +std::mutex caStateMutex; +std::mutex caTableMutex; +std::mutex caEvidMutex; + +using lock_guard = std::lock_guard<std::mutex>; int caThreadState; long droppedPVCount; -#define ADDRESS int -#define SETUP setupCASDF(); -#define CLEANUP cleanupCASDF(); -#define GET_ADDRESS(NAME,ADDRP) getCAIndex((NAME),(ADDRP)) -#define PUT_VALUE(ADDR,TYPE,PVAL) setCAValue((ADDR),(TYPE),(PVAL)) -#define PUT_VALUE_INT(ADDR,PVAL) setCAValueEPICSLong((ADDR),(PVAL)) -#define GET_VALUE_NUM(ADDR,DESTP,TIMEP,CONNP) syncEpicsDoubleValue((ADDR),(DESTP),(TIMEP),(CONNP)) -#define GET_VALUE_INT(ADDR,DESTP,TIMEP,CONNP) syncEpicsIntValue((ADDR),(DESTP),(TIMEP),(CONNP)) -#define GET_VALUE_STR(ADDR,DESTP,LEN,TIMEP,CONNP) syncEpicsStrValue((ADDR),(DESTP),(LEN),(TIMEP),(CONNP)) +using ADDRESS = int; + + #else -#define ADDRESS dbAddr -#define SETUP -#define CLEANUP -#define GET_ADDRESS(NAME,ADDRP) dbNameToAddr((NAME),(ADDRP)) -#define PUT_VALUE(ADDR,TYPE,PVAL) dbPutField(&(ADDR),((TYPE)==SDF_NUM ? DBR_DOUBLE : DBR_STRING),(PVAL),1) -#define PUT_VALUE_INT(ADDR,PVAL) dbPutField(&(ADDR),DBR_LONG,(int *)(PVAL),1); -#define GET_VALUE_NUM(ADDR,DESTP,TIMEP,CONNP) getDbValueDouble(&(ADDR),(double*)(DESTP),(TIMEP)) -#define GET_VALUE_INT(ADDR,DESTP,TIMEP,CONNP) getDbValueLong(&(ADDR),(unsigned int*)(DESTP),(TIMEP)) -#define GET_VALUE_STR(ADDR,DESTP,LEN,TIMEP,CONNP) getDbValueString(&(ADDR),(char*)(DESTP),(LEN),(TIMEP)) +using ADDRESS = dbAddr; #endif -#define SDF_NUM 0 -#define SDF_STR 1 -#define SDF_UNKNOWN -1 - // Function prototypes **************************************************************************************** -int isAlarmChannel(char *); -int checkFileCrc(char *); -int checkFileMod( char* , time_t* , int ); +bool isAlarmChannelRaw(const char *); +int checkFileMod( const char* , time_t* , int ); unsigned int filtCtrlBitConvert(unsigned int); -void getSdfTime(char *, int size); -void logFileEntry(char *); -void getEpicsSettings(int, time_t *); -int writeTable2File(char *,char *,int,CDS_CD_TABLE *); -int savesdffile(int,int,char *,char *,char *,char *,char *,dbAddr,dbAddr,dbAddr); -int createSortTableEntries(int,int,char *,int *,time_t*); -int reportSetErrors(char *,int,SET_ERR_TABLE *,int,int); -int spChecker(int,SET_ERR_TABLE *,int,char *,int,int *); -void newfilterstats(int); -int writeEpicsDb(int,CDS_CD_TABLE *,int); -int readConfig( char *,char *,int,char *); +void getSdfTime(embedded::fixed_string<256>&); +void logFileEntry(const char *); +void getEpicsSettings(); + +template <std::size_t entry_count> +bool writeTable2File(const char *burtdir, const char *filename, int ftype, fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable); + +int savesdffile(int,int,const char *,const char *,const char *,const char *,const char *,epics::DBEntry<epics::PVType::String>&,epics::DBEntry<epics::PVType::String>&,epics::DBEntry<epics::PVType::String>&); +int createSortTableEntries(int,int,const char *,int *,time_t*); +template <std::size_t entry_count> +int reportSetErrors(char *,fixed_size_vector<SET_ERR_TABLE, entry_count>&,int,int); +template <std::size_t entry_count> +int spChecker(int,fixed_size_vector<SET_ERR_TABLE, entry_count>&,int,const char *,int,int *); +void newfilterstats(); + +template <std::size_t entry_count> +int writeEpicsDb(fixed_size_vector<CDS_CD_TABLE, entry_count>&,int); +int readConfig( const char *, const char *,int, const char *); int parseLine(char *, int,char *,char *,char *,char *,char *,char *); -int modifyTable(int,SET_ERR_TABLE *); -int resetSelectedValues(int,SET_ERR_TABLE *); -void clearTableSelections(int,SET_ERR_TABLE *, int *); -void setAllTableSelections(int,SET_ERR_TABLE *, int *,int); -void changeSelectCB_uninit(int, SET_ERR_TABLE *, int *); -void decodeChangeSelect(int, int, int, SET_ERR_TABLE *, int *, void (*)(int, SET_ERR_TABLE *, int *)); -int appendAlarms2File(char *,char *,char *); +int modifyTable(table_range); +template <std::size_t entry_count> +int resetSelectedValues(fixed_size_vector<SET_ERR_TABLE, entry_count>&); +template <std::size_t entry_count> +void clearTableSelections(fixed_size_vector<SET_ERR_TABLE, entry_count>& dcsErrTable, int sc[]); +void setAllTableSelections(table_range, int *,int); +void changeSelectCB_uninit(SET_ERR_TABLE&, int *); +void decodeChangeSelect(int, int, table_range, int *, void (*)(SET_ERR_TABLE&, int *)); +int appendAlarms2File(const char *,const char *,const char *); void registerFilters(); -void setupFMArrays(char *,int*,dbAddr*,dbAddr*); -void resyncFMArrays(int *,dbAddr*); -void processFMChanCommands(int*,dbAddr*,dbAddr*,int*,int,SET_ERR_TABLE*); +void setupFMArrays(char *,fixed_size_vector<int, SDF_MAX_FMSIZE>&, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&); +void resyncFMArrays(fixed_size_vector<int, SDF_MAX_FMSIZE>&,fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&); +void processFMChanCommands(fixed_size_vector<int, SDF_MAX_FMSIZE>&, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>&, + int*,table_range); void countDisconnectedChannels(int); void saveStrSetPoint(int, const char*); void updateStrVarSetPoint(int); @@ -315,15 +343,15 @@ long countDisconnectedChans(); void nullCACallback(struct event_handler_args); -int getCAIndex(char *, ADDRESS *); +bool getCAIndex(const char *, ADDRESS *); int canFindCAChannel(char *entry); -int setCAValue(ADDRESS, int, void *); -int setCAValueEPICSLong(ADDRESS, unsigned int *); +bool setCAValue(ADDRESS, SDF_TYPE, const void *); +bool setCAValueLong(ADDRESS, const PV_INT_TYPE *); -int syncEpicsDoubleValue(ADDRESS, double *, time_t *, int *); -int syncEpicsIntValue(ADDRESS, unsigned int *, time_t *, int *); -int syncEpicsStrValue(ADDRESS, char *, int, time_t *, int *); +bool syncEpicsDoubleValue(ADDRESS, double *, time_t *, int *); +bool syncEpicsIntValue(ADDRESS, PV_INT_TYPE *, time_t *, int *); +bool syncEpicsStrValue(ADDRESS, char *, int, time_t *, int *); void subscriptionHandler(struct event_handler_args); void connectCallback(struct connection_handler_args); @@ -343,13 +371,100 @@ void *caMainLoop(void *); void initCAConnections(char *); void setupCASDF(); void cleanupCASDF(); + +void SETUP() +{ + setupCASDF(); +} +void CLEANUP() +{ + cleanupCASDF(); +} + +bool GET_ADDRESS(const char* name, ADDRESS* addr) +{ + return getCAIndex(name, addr); +} +bool PUT_VALUE(ADDRESS addr, SDF_TYPE type, const void* pval) +{ + return setCAValue(addr, type, pval); +} +bool PUT_VALUE_INT(ADDRESS addr, const PV_INT_TYPE* val) +{ + return setCAValueLong(addr, val); +} +bool GET_VALUE_NUM(ADDRESS addr, double* dest, time_t* tp, int* connp) +{ + return syncEpicsDoubleValue(addr, dest, tp, connp); +} +bool GET_VALUE_INT(ADDRESS addr, PV_INT_TYPE* dest, time_t* tp, int* connp) +{ + return syncEpicsIntValue(addr, dest, tp, connp); +} +bool GET_VALUE_STR(ADDRESS addr, char* dest, int dest_size, time_t* tp, int *connp) { + return syncEpicsStrValue(addr, dest, dest_size, tp, connp); +} #else -int getDbValueDouble(ADDRESS*,double *,time_t *); -int getDbValueString(ADDRESS*,char *, int, time_t *); -int getDbValueLong(ADDRESS *,unsigned int * ,time_t *); +bool getDbValueDouble(ADDRESS*,double *,time_t *); +bool getDbValueString(ADDRESS*,char *, int, time_t *); +bool getDbValueLong(ADDRESS *,PV_INT_TYPE * ,time_t *); void dbDumpRecords(DBBASE *,const char *); + +void SETUP() +{} + +void CLEANUP() +{} + +bool GET_ADDRESS(const char* name, ADDRESS* addr) +{ + return dbNameToAddr(name, addr) == 0; +} + +bool PUT_VALUE(ADDRESS addr, SDF_TYPE type, const void* pval) +{ + return dbPutField(&addr,(type==SDF_NUM ? DBR_DOUBLE : DBR_STRING),pval,1) == 0; +} +bool PUT_VALUE_INT(ADDRESS addr, const PV_INT_TYPE *val) +{ + return dbPutField(&addr, DBR_LONG, val, 1) == 0; +} +bool GET_VALUE_NUM(ADDRESS addr, double* dest, time_t* tp, int* connp) +{ + return getDbValueDouble(&addr, dest, tp); +} +bool GET_VALUE_INT(ADDRESS addr, PV_INT_TYPE* dest, time_t* tp, int* connp) +{ + return getDbValueLong(&addr, dest, tp); +} +bool GET_VALUE_STR(ADDRESS addr, char* dest, int max_len, time_t *tp, int* connp) +{ + return getDbValueString(&addr, dest, max_len, tp); +} #endif +template <typename StringLike> +bool +PUT_VALUE_STR(ADDRESS addr, const StringLike& value) +{ + return PUT_VALUE(addr, SDF_STR, reinterpret_cast<void*>(const_cast<char*>(value.c_str()))); +} + +template <typename StringLike> +bool +GET_ADDRESS(const StringLike& name, ADDRESS* addr) +{ + return GET_ADDRESS(name.c_str(), addr); +} + +template <typename StringLike> +bool +GET_VALUE_STR(ADDRESS addr, StringLike& dest, time_t *tp, int* connp) +{ + auto buffer = dest.get_buffer(); + return GET_VALUE_STR(addr, buffer.data(), buffer.capacity(), tp, connp); +} + #ifdef VERBOSE_DEBUG #define D_NOW (__output_iter == 0) #define D(...) {if (__output_iter == 0) { fprintf(stderr, __VA_ARGS__); } } @@ -385,16 +500,32 @@ void iterate_output_counter() {} // /// Given a channel name return 1 if it is an alarm channel, else 1 -int isAlarmChannel(char *chname) { - if (!chname) return 0; - return ((strstr(chname,".HIGH") != NULL) || - (strstr(chname,".HIHI") != NULL) || - (strstr(chname,".LOW") != NULL) || - (strstr(chname,".LOLO") != NULL) || - (strstr(chname,".HSV") != NULL) || - (strstr(chname,".OSV") != NULL) || - (strstr(chname,".ZSV") != NULL) || - (strstr(chname,".LSV") != NULL) ); +template <typename StringLike> +bool isAlarmChannel(const StringLike& chname) { + return isAlarmChannelRaw(chname.c_str()); +// return ((strstr(chname.c_str(),".HIGH") != NULL) || +// (strstr(chname.c_str(),".HIHI") != NULL) || +// (strstr(chname.c_str(),".LOW") != NULL) || +// (strstr(chname.c_str(),".LOLO") != NULL) || +// (strstr(chname.c_str(),".HSV") != NULL) || +// (strstr(chname.c_str(),".OSV") != NULL) || +// (strstr(chname.c_str(),".ZSV") != NULL) || +// (strstr(chname.c_str(),".LSV") != NULL) ); +} + +bool isAlarmChannelRaw(const char* chname) { + if (chname == nullptr) + { + return false; + } + return ((strstr(chname,".HIGH") != NULL) || + (strstr(chname,".HIHI") != NULL) || + (strstr(chname,".LOW") != NULL) || + (strstr(chname,".LOLO") != NULL) || + (strstr(chname,".HSV") != NULL) || + (strstr(chname,".OSV") != NULL) || + (strstr(chname,".ZSV") != NULL) || + (strstr(chname,".LSV") != NULL) ); } /// Common routine to parse lines read from BURT/SDF files.. @@ -481,12 +612,16 @@ int parseLine(char *s, int bufSize, char *str1, char *str2, char *str3, char *st /// @param[in] numEntries Number of entries in the table. /// @param[in] dcsErrtable Pointer to table /// @param[out] sc Pointer to change counters. -void clearTableSelections(int numEntries,SET_ERR_TABLE *dcsErrTable, int sc[]) +template <std::size_t entry_count> +void clearTableSelections(fixed_size_vector<SET_ERR_TABLE, entry_count>& dcsErrTable, int sc[]) { -int ii; + int ii; for(ii=0;ii<3;ii++) sc[ii] = 0; - for(ii=0;ii<numEntries;ii++) dcsErrTable[ii].chFlag = 0; - for(ii=0;ii<numEntries;ii++) dcsErrTable[ii].filtNum = -1; + for (auto& entry:dcsErrTable) + { + entry.chFlag = 0; + entry.filtNum = -1; + } } @@ -495,40 +630,43 @@ int ii; /// @param[in] dcsErrtable Pointer to table /// @param[out] sc Pointer to change counters. /// @param[in] selectOp Present selection. -void setAllTableSelections(int numEntries,SET_ERR_TABLE *dcsErrTable, int sc[],int selectOpt) +void setAllTableSelections(table_range dcsErrTable, int sc[],int selectOpt) { -int ii; -int filtNum = -1; -long status = 0; + int filtNum = -1; + long status = 0; + switch(selectOpt) { case 1: sc[0] = 0; - for(ii=0;ii<numEntries;ii++) { - dcsErrTable[ii].chFlag |= 2; + for (auto& entry:dcsErrTable) + { + entry.chFlag |= 2; sc[0] ++; - if(dcsErrTable[ii].chFlag & 4) { - dcsErrTable[ii].chFlag &= ~(4); + if(entry.chFlag & 4) { + entry.chFlag &= ~(4); sc[1] --; } } break; case 2: sc[1] = 0; - for(ii=0;ii<numEntries;ii++) { - dcsErrTable[ii].chFlag |= 4; + for (auto& entry:dcsErrTable) + { + entry.chFlag |= 4; sc[1] ++; - if(dcsErrTable[ii].chFlag & 2) { - dcsErrTable[ii].chFlag &= ~(2); + if(entry.chFlag & 2) { + entry.chFlag &= ~(2); sc[0] --; } } break; case 3: - sc[2] = numEntries; - for(ii=0;ii<numEntries;ii++) { - dcsErrTable[ii].chFlag |= 8; + sc[2] = std::distance(dcsErrTable.begin(), dcsErrTable.end()); + for (auto& entry:dcsErrTable) + { + entry.chFlag |= 8; - filtNum = dcsErrTable[ii].filtNum; + filtNum = entry.filtNum; if (filtNum >= 0) { if (filterMasks[filtNum] == filterTable[filtNum].mask) { filterMasks[filtNum] = ((ALL_SWSTAT_BITS & filterMasks[filtNum]) > 0 ? 0 : ~0); @@ -546,12 +684,12 @@ long status = 0; /// @param[in] index Number providing the index into the dcsErrTable /// @param[in] dcsErrTable Pointer to table /// @param[out] selectCounter Pointer to change counters -void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCounter[]) +void changeSelectCB_uninit(SET_ERR_TABLE &errTableEntry, int selectCounter[]) { - int revertBit = dcsErrTable[index].chFlag & 2; - int acceptBit = dcsErrTable[index].chFlag & 4; - int monBit = dcsErrTable[index].chFlag & 8; - int otherBits = 0; //dcsErrTable[index].chFlag | ~(2 | 4 | 8); + int revertBit = errTableEntry.chFlag & 2; + int acceptBit = errTableEntry.chFlag & 4; + int monBit = errTableEntry.chFlag & 8; + int otherBits = 0; //errTableEntry.chFlag | ~(2 | 4 | 8); // revert is not defined on this table if (revertBit) { @@ -563,7 +701,7 @@ void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCoun acceptBit = 4; selectCounter[1] ++; } - dcsErrTable[index].chFlag = revertBit | acceptBit | monBit | otherBits; + errTableEntry.chFlag = revertBit | acceptBit | monBit | otherBits; } /// Common routine to set/unset individual entries for table changes.. @@ -572,40 +710,42 @@ void changeSelectCB_uninit(int index, SET_ERR_TABLE *dcsErrTable, int selectCoun /// @param[in] totalItems Number items selected for change. /// @param[in] dcsErrtable Pointer to table /// @param[out] selectCounter Pointer to change counters. -void decodeChangeSelect(int selNum, int page, int totalItems, SET_ERR_TABLE *dcsErrTable, int selectCounter[], void (*cb)(int, SET_ERR_TABLE*, int*)) +void decodeChangeSelect(int selNum, int page, table_range dcsErrTable, int selectCounter[], void (*cb)(SET_ERR_TABLE&, int*)) { -int selectBit; -int selectLine; + int selectBit; + int selectLine; selectBit = selNum / 100; selectLine = selNum % 100 - 1; selNum += page * SDF_ERR_DSIZE; selectLine += page * SDF_ERR_DSIZE; - if(selectLine < totalItems) { + + auto selection = dcsErrTable.begin() + selectLine; + if(selection < dcsErrTable.end()) { switch (selectBit) { case 0: - dcsErrTable[selectLine].chFlag ^= 2; - if(dcsErrTable[selectLine].chFlag & 2) selectCounter[0] ++; + selection->chFlag ^= 2; + if(selection->chFlag & 2) selectCounter[0] ++; else selectCounter[0] --; - if(dcsErrTable[selectLine].chFlag & 4) { - dcsErrTable[selectLine].chFlag &= ~(4); + if(selection->chFlag & 4) { + selection->chFlag &= ~(4); selectCounter[1] --; } break; case 1: - dcsErrTable[selectLine].chFlag ^= 4; - if(dcsErrTable[selectLine].chFlag & 4) selectCounter[1] ++; + selection->chFlag ^= 4; + if(selection->chFlag & 4) selectCounter[1] ++; else selectCounter[1] --; - if(dcsErrTable[selectLine].chFlag & 2) { - dcsErrTable[selectLine].chFlag &= ~(2); + if(selection->chFlag & 2) { + selection->chFlag &= ~(2); selectCounter[0] --; } break; case 2: /* we only handle non-filter bank entries here */ - if (dcsErrTable[selectLine].filtNum < 0) { - dcsErrTable[selectLine].chFlag ^= 8; - if(dcsErrTable[selectLine].chFlag & 8) selectCounter[2] ++; + if (selection->filtNum < 0) { + selection->chFlag ^= 8; + if(selection->chFlag & 8) selectCounter[2] ++; else selectCounter[2] --; } break; @@ -613,13 +753,13 @@ int selectLine; break; } if (cb) - cb(selectLine, dcsErrTable, selectCounter); + cb(*selection, selectCounter); } } // Check for file modification time changes int -checkFileMod( char* fName, time_t* last_mod_time, int gettime ) +checkFileMod( const char* fName, time_t* last_mod_time, int gettime ) { struct stat statBuf; if ( !stat( fName, &statBuf ) ) @@ -645,32 +785,7 @@ checkFileMod( char* fName, time_t* last_mod_time, int gettime ) } } -/// Common routine to check file CRC. -/// @param[in] *fName Name of file to check. -/// @return File CRC or -1 if file not found. -int checkFileCrc(char *fName) -{ - char cbuf[ 128 ]; - char* cp; - int flen = 0; - int clen = 0; - unsigned int crc = 0; - FILE* fp = fopen( fName, "r" ); - if ( fp == NULL ) - { - return 0; - } - while ( ( cp = fgets( cbuf, 128, fp ) ) != NULL ) - { - clen = strlen( cbuf ); - flen += clen; - crc = crc_ptr( cbuf, clen, crc ); - } - crc = crc_len( flen, crc ); - fclose( fp ); - return crc; -} /// Quick convert of filter switch settings to match SWMASK /// @param[in] v UINT32 representation of filter module switch setting. @@ -687,7 +802,8 @@ unsigned int filtCtrlBitConvert(unsigned int v) { /// @param[in] v SWSTAT representation of switch setting. /// @param[out] filterstring Returned string representation of switch setting. /// @return Always zero at this point. -int filtStrBitConvert(int stype, unsigned int v, char *filterstring) { +template <typename StringLike> +int filtStrBitConvert(int stype, unsigned int v, StringLike& filterstring) { unsigned int val[5],jj; unsigned int x; char bitDefShort[16][8] = {"IN,","OF,","1,","2,", @@ -705,26 +821,26 @@ char bitDefLong[16][8] = {"IN,","OF,","F1,","F2,", val[3] = (v & 0x1000) << 1; // Output val[4] = (v & 0x18000) >> 1; // Dec and Hold x = val[0] + val[1] + val[2] + val[3] + val[4]; - sprintf(filterstring,"%s",""); + filterstring.clear(); for(jj=0;jj<16;jj++) { - if(x & 1 && stype == 0) strcat(filterstring,bitDefShort[jj]); - if(x & 1 && stype == 1) strcat(filterstring,bitDefLong[jj]); + if(x & 1 && stype == 0) filterstring += bitDefShort[jj]; + if(x & 1 && stype == 1) filterstring += bitDefLong[jj]; x = x >> 1; } - filterstring[strlen(filterstring) - 1] = 0; // strcat(filterstring,"\0"); return(0); } /// Given the a filter table entry print out the bits set in the mask. /// @param[in] filter the filter table entry to extract the current value from -/// @param[out] dest buffer to put the results in. must be >= 64 bytes -void createSwitchText(FILTER_TABLE *filter, char *dest) { +/// @param[out] dest buffer to put the results in +template <typename StringLike> +void createSwitchText(FILTER_TABLE *filter, StringLike& dest) { #if 1 int sw1s=0; int sw2s=0; - dest[0] = 0; + dest.clear(); sw1s = (int)cdTableP[filter->sw[0]].data.chval; sw2s = (int)cdTableP[filter->sw[1]].data.chval; #else @@ -750,10 +866,10 @@ void createSwitchText(FILTER_TABLE *filter, char *dest) { } /// Routine to check filter switch settings and provide info on switches not set as requested. -/// @param[in] fcount Number of filters in the control model. /// @param[in] monitorAll Global monitoring flag. /// @return Number of errant switch settings found. -int checkFilterSwitches(int fcount, SET_ERR_TABLE setErrTable[], int monitorAll, int displayall, int wcflag, char *wcstr, int *diffcntr) +template <std::size_t entry_count> +int checkFilterSwitches(fixed_size_vector<SET_ERR_TABLE, entry_count>& setErrTable, int monitorAll, int displayall, int wcflag, const char *wcstr, int *diffcntr) { unsigned int refVal=0; unsigned int presentVal=0; @@ -761,15 +877,14 @@ unsigned int x=0,y=0; unsigned int mask = 0; unsigned int maskedRefVal = 0; unsigned int maskedSwReqVal = 0; -int ii=0,jj=0; +int jj=0; int errCnt = 0; -char swname[3][64]; -char tmpname[64]; +fixed_string<64>swname[3]; +fixed_string<64> tmpname; time_t mtime=0; -char localtimestring[256]; -char swstrE[64]; -char swstrB[64]; -char swstrD[64]; +fixed_string<64> swstrE; +fixed_string<64> swstrB; +fixed_string<64> swstrD; struct buffer { time_t t; unsigned int rval; @@ -779,38 +894,36 @@ ADDRESS paddr1; ADDRESS paddr2; long status=0; - swstrE[0]='\0'; - swstrB[0]='\0'; - swstrD[0]='\0'; - for(ii=0;ii<fcount;ii++) - { + auto filtNum = -1; + for (const auto& curFilt:filterTable) + { + ++filtNum; bzero(buffer, sizeof(struct buffer)*2); - bzero(swname[0],sizeof(swname[0])); - bzero(swname[1],sizeof(swname[1])); - strcpy(swname[0],filterTable[ii].fname); - strcat(swname[0],"SW1S"); - strcpy(swname[1],filterTable[ii].fname); - strcat(swname[1],"SW2S"); - sprintf(swname[2],"%s",filterTable[ii].fname); - strcat(swname[2],"SWSTR"); - status = GET_ADDRESS(swname[0],&paddr); - status = GET_VALUE_INT(paddr,&(buffer[0].rval),&(buffer[0].t), 0); - status = GET_ADDRESS(swname[1],&paddr1); - status = GET_VALUE_INT(paddr1,&(buffer[1].rval),&(buffer[1].t), 0); + + swname[0] = curFilt.fname; + swname[0] += "SW1S"; + swname[1] = curFilt.fname; + swname[1] += "SW2S"; + swname[2] = curFilt.fname; + swname[2] += "SWSTR"; + GET_ADDRESS(swname[0],&paddr); + GET_VALUE_INT(paddr,&(buffer[0].rval),&(buffer[0].t), 0); + GET_ADDRESS(swname[1],&paddr1); + GET_VALUE_INT(paddr1,&(buffer[1].rval),&(buffer[1].t), 0); for(jj=0;jj<2;jj++) { if(buffer[jj].rval > 0xffff || buffer[jj].rval < 0) // Switch setting overrange { - sprintf(setErrTable[errCnt].chname,"%s", swname[jj]); - sprintf(setErrTable[errCnt].burtset, "%s", " "); - sprintf(setErrTable[errCnt].liveset, "0x%x", buffer[jj].rval); - sprintf(setErrTable[errCnt].diff, "%s", "OVERRANGE"); - setErrTable[errCnt].liveval = 0.0; - setErrTable[errCnt].sigNum = filterTable[ii].sw[jj]; + setErrTable.emplace_back(); + auto& curErr = setErrTable.back(); + curErr.chname = swname[jj]; + curErr.burtset = " "; + curErr.liveset.printf("0x%x", buffer[jj].rval); + curErr.diff = "OVERRANGE"; + curErr.liveval = 0.0; + curErr.sigNum = curFilt.sw[jj]; // take the latest change time between the SW1S & SW2S channels mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t); - strcpy(localtimestring, ctime(&mtime)); - localtimestring[strlen(localtimestring) - 1] = 0; - sprintf(setErrTable[errCnt].timeset, "%s", localtimestring); + curErr.timeset = ctime(&mtime); buffer[jj].rval &= 0xffff; errCnt ++; } @@ -819,44 +932,43 @@ long status=0; refVal = filtCtrlBitConvert(presentVal); filtStrBitConvert(0,refVal,swstrE); x = refVal; - status = GET_ADDRESS(swname[2],&paddr2); - status = PUT_VALUE(paddr2,SDF_STR,swstrE); - setErrTable[errCnt].filtNum = -1; + GET_ADDRESS(swname[2],&paddr2); + PUT_VALUE_STR(paddr2,swstrE); + //setErrTable[errCnt].filtNum = -1; - mask = (unsigned int)filterTable[ii].mask; + mask = (unsigned int)curFilt.mask; maskedRefVal = refVal & mask; - maskedSwReqVal = filterTable[ii].swreq & mask; - if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && filterTable[ii].init) ) + maskedSwReqVal = curFilt.swreq & mask; + if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && curFilt.init) ) *diffcntr += 1; - if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && filterTable[ii].init) || displayall) + if(( maskedRefVal != maskedSwReqVal && errCnt < SDF_ERR_TSIZE && (mask || monitorAll) && curFilt.init) || displayall) { filtStrBitConvert(1,refVal,swstrE); - filtStrBitConvert(1,filterTable[ii].swreq,swstrB); + filtStrBitConvert(1,curFilt.swreq,swstrB); x = maskedRefVal ^ maskedSwReqVal; filtStrBitConvert(1,x,swstrD); - bzero(tmpname,sizeof(tmpname)); - strncpy(tmpname,filterTable[ii].fname,(strlen(filterTable[ii].fname)-1)); - - if(!wcflag || (wcflag && (strstr(tmpname,wcstr) != NULL))) { - sprintf(setErrTable[errCnt].chname,"%s", tmpname); - sprintf(setErrTable[errCnt].burtset, "%s", swstrB); - sprintf(setErrTable[errCnt].liveset, "%s", swstrE); - sprintf(setErrTable[errCnt].diff, "%s", swstrD); - - setErrTable[errCnt].liveval = 0.0; - setErrTable[errCnt].sigNum = filterTable[ii].sw[0] + (filterTable[ii].sw[1] * SDF_MAX_TSIZE); - setErrTable[errCnt].filtNum = ii; - setErrTable[errCnt].sw[0] = buffer[0].rval; - setErrTable[errCnt].sw[1] = buffer[1].rval; - if(filterTable[ii].mask & ALL_SWSTAT_BITS) setErrTable[errCnt].chFlag |= 0x1; - else setErrTable[errCnt].chFlag &= 0xe; - - // take the latest change time between the SW1S & SW2S channels - mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t); - strcpy(localtimestring, ctime(&mtime)); - localtimestring[strlen(localtimestring) - 1] = 0; - sprintf(setErrTable[errCnt].timeset, "%s", localtimestring); - errCnt ++; + tmpname = curFilt.fname; + + if(!wcflag || (wcflag && (strstr(tmpname.c_str(),wcstr) != NULL))) { + setErrTable.emplace_back(); + auto& curErr = setErrTable.back(); + curErr.chname = tmpname; + curErr.burtset = swstrB; + curErr.liveset = swstrE; + curErr.diff = swstrD; + + curErr.liveval = 0.0; + curErr.sigNum = curFilt.sw[0] + (curFilt.sw[1] * SDF_MAX_TSIZE); + curErr.filtNum = filtNum; + curErr.sw[0] = buffer[0].rval; + curErr.sw[1] = buffer[1].rval; + if(curFilt.mask & ALL_SWSTAT_BITS) curErr.chFlag |= 0x1; + else curErr.chFlag &= 0xe; + + // take the latest change time between the SW1S & SW2S channels + mtime = (buffer[0].t >= buffer[1].t ? buffer[0].t : buffer[1].t); + curErr.timeset = ctime(&mtime); + errCnt ++; } } } @@ -866,7 +978,7 @@ long status=0; /// Routine for reading and formatting the time as a string. /// @param[out] timestring Pointer to char string in which GPS time is to be written. /// @note This can use the GPS time from the model, or if configured with USE_SYSTEM_TIME the system time. -void getSdfTime(char *timestring, int size) +void getSdfTime(embedded::fixed_string<256>& timestring) { #ifdef USE_SYSTEM_TIME time_t t=0; @@ -874,32 +986,29 @@ void getSdfTime(char *timestring, int size) t = time(NULL); localtime_r(&t, &tdata); - if (timestring && size > 0) { - if (strftime(timestring, size, "%a %b %e %H:%M:%S %Y", &tdata) == 0) { - timestring[0] = '\0'; - } - } + + auto buf = timestring.get_buffer(); + strftime(buf.data(), buf.capacity(), "%a %b %e %H:%M:%S %Y", &tdata); #else dbAddr paddr; long ropts = 0; long nvals = 1; long status; - status = dbNameToAddr(timechannel,&paddr); - status = dbGetField(&paddr,DBR_STRING,timestring,&ropts,&nvals,NULL); + timechannel->get(timestring); #endif } /// Routine for logging messages to ioc.log file. /// @param[in] message Ptr to string containing message to be logged. -void logFileEntry(char *message) +void logFileEntry(const char *message) { FILE *log=0; - char timestring[256]; + embedded::fixed_string<256> timestring{}; long status=0; dbAddr paddr; - getSdfTime(timestring, 256); + getSdfTime(timestring); log = fopen(logfilename,"a"); if(log == NULL) { status = dbNameToAddr(reloadtimechannel,&paddr); @@ -916,43 +1025,36 @@ void logFileEntry(char *message) /// Function to read all settings, listed in main table, from the EPICS database. /// @param[in] numchans The number of channels listed in the main table. /// @param[out] times (Optional) An array of at least numchans time_t values to put change times in -void getEpicsSettings(int numchans, time_t *times) +void getEpicsSettings() { -int ii; -long status; -int chcount = 0; -double dval = 0; -ADDRESS geaddr; -long statusR = 0; -char sval[64]; -time_t *change_t = 0; - - for(ii=0;ii<numchans;ii++) - { - // Load main table settings into temp (cdTableP) - sprintf(cdTableP[ii].chname,"%s",cdTable[ii].chname); - cdTableP[ii].datatype = cdTable[ii].datatype; - cdTableP[ii].mask = cdTable[ii].mask; - cdTableP[ii].initialized = cdTable[ii].initialized; - // Find address of channel - status = GET_ADDRESS(cdTableP[ii].chname,&geaddr); - if(!status) { - change_t = ( times ? ×[ii] : 0 ); - if(cdTableP[ii].datatype == SDF_NUM) - { - statusR = GET_VALUE_NUM(geaddr,&dval,change_t, NULL); //&(cdTableP[ii].connected)); - if (!statusR) cdTableP[ii].data.chval = (cdTable[ii].filterswitch ? (int)dval & 0xffff : dval); - } else { - statusR = GET_VALUE_STR(geaddr,sval,sizeof(sval),change_t, NULL); //&(cdTableP[ii].connected)); - if(!statusR) sprintf(cdTableP[ii].data.strval,"%s",sval); - } - chcount ++; - } - } -} - -void encodeBURTString(char *src, char *dest, int dest_size) { - char *ch = 0; + cdTableP.clear(); + std::transform(cdTable.begin(), cdTable.end(), std::back_inserter( cdTableP), + [](const CDS_CD_TABLE& entry) -> CDS_CD_TABLE { + CDS_CD_TABLE result; + result.chname = entry.chname; + result.datatype = entry.datatype; + result.mask = entry.mask; + result.initialized = entry.initialized; + + ADDRESS geaddr; + if(GET_ADDRESS(result.chname,&geaddr)) { + time_t* change_t = nullptr; + if(result.datatype == SDF_NUM) + { + double dval; + if (GET_VALUE_NUM(geaddr,&dval,change_t, NULL)) { + result.data.chval = (entry.filterswitch ? (int) dval & 0xffff : dval); + } + } else { + GET_VALUE_STR(geaddr,result.data.strval, sizeof(result.data.strval),change_t, NULL); + } + } + return result; + }); +} + +void encodeBURTString(const char *src, char *dest, int dest_size) { + const char *ch = 0; int expand = 0; if (!src || !dest || dest_size < 1) return; @@ -982,15 +1084,16 @@ void encodeBURTString(char *src, char *dest, int dest_size) { #define SDF_FILE_PARAMS_ONLY 1 #define SDF_FILE_BURT_ONLY 2 /// Common routine for saving table data to BURT files. -int writeTable2File(char *burtdir, - char *filename, ///< Name of file to write +template <std::size_t entry_count> +bool writeTable2File(const char *burtdir, + const char *filename, ///< Name of file to write int ftype, ///< Type of file to write - CDS_CD_TABLE myTable[]) ///< Table to be written. + fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable) ///< Table to be written. { int ii; FILE *csFile=0; char filemsg[128]; - char timestring[128]; + embedded::fixed_string<256> timestring; char monitorstring[128]; char burtString[64+2]; #ifdef CA_SDF @@ -1006,12 +1109,12 @@ int writeTable2File(char *burtdir, { sprintf(filemsg,"ERROR Failed to open %s - %s",filename,strerror(errno)); logFileEntry(filemsg); - return(-1); + return(false); } // Write BURT header - getSdfTime(timestring, 128); + getSdfTime(timestring); fprintf(csFile,"%s\n","--- Start BURT header"); - fprintf(csFile,"%s%s\n","Time: ",timestring); + fprintf(csFile,"%s%s\n","Time: ",timestring.c_str()); fprintf(csFile,"%s\n","Login ID: controls ()"); fprintf(csFile,"%s\n","Eff UID: 1001 "); fprintf(csFile,"%s\n","Group ID: 1001 "); @@ -1022,18 +1125,18 @@ int writeTable2File(char *burtdir, fprintf(csFile,"%s\n","Req File: autoBurt.req "); fprintf(csFile,"%s\n","--- End BURT header"); - for(ii=0;ii<chNum;ii++) + for (const auto& entry:myTable) { - if (myTable[ii].filterswitch) { - if ((myTable[ii].mask & ALL_SWSTAT_BITS)== ~0) { + if (entry.filterswitch) { + if ((entry.mask & ALL_SWSTAT_BITS)== ~0) { strcpy(monitorstring, "1"); - } else if ((myTable[ii].mask & ALL_SWSTAT_BITS) == 0) { + } else if ((entry.mask & ALL_SWSTAT_BITS) == 0) { strcpy(monitorstring, "0"); } else { - snprintf(monitorstring, sizeof(monitorstring), "0x%x", myTable[ii].mask); + snprintf(monitorstring, sizeof(monitorstring), "0x%x", entry.mask); } } else { - if (myTable[ii].mask) + if (entry.mask) strcpy(monitorstring,"1"); else strcpy(monitorstring,"0"); @@ -1042,28 +1145,28 @@ int writeTable2File(char *burtdir, { case SDF_WITH_INIT_FLAG: if(myTable[ii].datatype == SDF_NUM) { - fprintf(csFile,"%s %d %.*e %s %d\n",myTable[ii].chname,1,precision,myTable[ii].data.chval,monitorstring,myTable[ii].initialized); + fprintf(csFile,"%s %d %.*e %s %d\n",entry.chname,1,precision,entry.data.chval,monitorstring,entry.initialized); } else { - encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString)); - fprintf(csFile,"%s %d %s %s %d\n",myTable[ii].chname,1,burtString,monitorstring,myTable[ii].initialized); + encodeBURTString(entry.data.strval, burtString, sizeof(burtString)); + fprintf(csFile,"%s %d %s %s %d\n",entry.chname,1,burtString,monitorstring,entry.initialized); } break; case SDF_FILE_PARAMS_ONLY: - if(myTable[ii].initialized) { - if(myTable[ii].datatype == SDF_NUM) { - fprintf(csFile,"%s %d %.*e %s\n",myTable[ii].chname,1,precision,myTable[ii].data.chval,monitorstring); + if(entry.initialized) { + if(entry.datatype == SDF_NUM) { + fprintf(csFile,"%s %d %.*e %s\n",entry.chname,1,precision,entry.data.chval,monitorstring); } else { - encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString)); - fprintf(csFile,"%s %d %s %s\n",myTable[ii].chname,1,burtString,monitorstring); + encodeBURTString(entry.data.strval, burtString, sizeof(burtString)); + fprintf(csFile,"%s %d %s %s\n",entry.chname,1,burtString,monitorstring); } } break; case SDF_FILE_BURT_ONLY: - if(myTable[ii].datatype == SDF_NUM) { - fprintf(csFile,"%s %d %.*e\n",myTable[ii].chname,1,precision,myTable[ii].data.chval); + if(entry.datatype == SDF_NUM) { + fprintf(csFile,"%s %d %.*e\n",entry.chname,1,precision,entry.data.chval); } else { - encodeBURTString(myTable[ii].data.strval, burtString, sizeof(burtString)); - fprintf(csFile,"%s %d %s \n",myTable[ii].chname,1,burtString); + encodeBURTString(entry.data.strval, burtString, sizeof(burtString)); + fprintf(csFile,"%s %d %s \n",entry.chname,1,burtString); } break; default: @@ -1071,6 +1174,7 @@ int writeTable2File(char *burtdir, } } fclose(csFile); + return true; } /// Function to append alarm settings to saved files.. @@ -1078,9 +1182,9 @@ int writeTable2File(char *burtdir, /// @param[in] sdffile Name of the file being saved. /// @param[in] currentload Base file name of the associated alarms.snap file.. int appendAlarms2File( - char *sdfdir, - char *sdffile, - char *currentload + const char *sdfdir, + const char *sdffile, + const char *currentload ) { char sdffilename[256]; @@ -1123,23 +1227,23 @@ int lderror = 0; /// Routine used to decode and handle BURT save requests. int savesdffile(int saveType, ///< Save file format definition. int saveOpts, ///< Save file options. - char *sdfdir, ///< Directory to save file in. - char *model, ///< Name of the model used to build file name. - char *currentfile, ///< Name of file last read (Used if option is to overwrite). - char *saveasfile, ///< Name of file to be saved. - char *currentload, ///< Name of file, less directory info. - dbAddr sfaddr, ///< Address of EPICS channel to write save file name. - dbAddr staddr, ///< Address of EPICS channel to write save file time. - dbAddr rladdr) + const char *sdfdir, ///< Directory to save file in. + const char *model, ///< Name of the model used to build file name. + const char *currentfile, ///< Name of file last read (Used if option is to overwrite). + const char *saveasfile, ///< Name of file to be saved. + const char *currentload, ///< Name of file, less directory info. + epics::DBEntry<epics::PVType::String>& save_file_entry, ///< Address of EPICS channel to write save file name. + epics::DBEntry<epics::PVType::String>& save_time_entry, ///< Address of EPICS channel to write save file time. + epics::DBEntry<epics::PVType::String>& reload_time_entry) { char filename[256]; char ftype[16]; int status; char filemsg[128]; -char timestring[64]; -char shortfilename[64]; +embedded::fixed_string<256> timestring{}; +embedded::fixed_string<256> shortfilename{}; - time_t now = time(NULL); + time_t now = time(nullptr); struct tm *mytime = localtime(&now); switch(saveOpts) @@ -1147,29 +1251,29 @@ char shortfilename[64]; case SAVE_TIME_NOW: sprintf(filename,"%s%s_%d%02d%02d_%02d%02d%02d.snap", sdfdir,currentload, (mytime->tm_year - 100), (mytime->tm_mon + 1), mytime->tm_mday, mytime->tm_hour, mytime->tm_min, mytime->tm_sec); - sprintf(shortfilename,"%s_%d%02d%02d_%02d%02d%02d", currentload, + shortfilename.printf("%s_%d%02d%02d_%02d%02d%02d", currentload, (mytime->tm_year - 100), (mytime->tm_mon + 1), mytime->tm_mday, mytime->tm_hour, mytime->tm_min, mytime->tm_sec); // printf("File to save is TIME NOW: %s\n",filename); break; case SAVE_OVERWRITE: sprintf(filename,"%s",currentfile); - sprintf(shortfilename,"%s",currentload); + shortfilename.printf("%s",currentload); // printf("File to save is OVERWRITE: %s\n",filename); break; case SAVE_BACKUP: sprintf(filename,"%s%s_%d%02d%02d_%02d%02d%02d.snap",sdfdir,currentload, (mytime->tm_year - 100), (mytime->tm_mon + 1), mytime->tm_mday, mytime->tm_hour, mytime->tm_min, mytime->tm_sec); - sprintf(shortfilename,"%s",currentload); + shortfilename.printf("%s",currentload); // printf("File to save is BACKUP: %s\n",filename); break; case SAVE_OVERWRITE_TABLE: sprintf(filename,"%s%s.snap",sdfdir,currentload); - sprintf(shortfilename,"%s",currentload); + shortfilename.printf("%s",currentload); // printf("File to save is BACKUP OVERWRITE: %s\n",filename); break; case SAVE_AS: sprintf(filename,"%s%s.snap",sdfdir,saveasfile); - sprintf(shortfilename,"%s",saveasfile); + shortfilename.printf("%s",saveasfile); // printf("File to save is SAVE_AS: %s\n",filename); break; @@ -1181,13 +1285,12 @@ char shortfilename[64]; { case SAVE_TABLE_AS_SDF: // printf("Save table as sdf\n"); - status = writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTable); - if(status != 0) { + if(!writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTable)) { sprintf(filemsg,"FAILED FILE SAVE %s",filename); logFileEntry(filemsg); return(-2); } - status = appendAlarms2File(sdfdir,shortfilename,currentload); + status = appendAlarms2File(sdfdir,shortfilename.c_str(),currentload); if(status != 0) { sprintf(filemsg,"FAILED To Append Alarms - %s",currentload); logFileEntry(filemsg); @@ -1197,9 +1300,8 @@ char shortfilename[64]; break; case SAVE_EPICS_AS_SDF: // printf("Save epics as sdf\n"); - getEpicsSettings(chNum, NULL); - status = writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTableP); - if(status != 0) { + getEpicsSettings(); + if(!writeTable2File(sdfdir,filename,SDF_FILE_PARAMS_ONLY,cdTableP)) { sprintf(filemsg,"FAILED EPICS SAVE %s",filename); logFileEntry(filemsg); return(-2); @@ -1212,12 +1314,14 @@ char shortfilename[64]; return(-1); } logFileEntry(filemsg); - getSdfTime(timestring, 128); + getSdfTime(timestring); // printf(" Time of save = %s\n",timestring); - status = dbPutField(&sfaddr,DBR_STRING,shortfilename,1); - status = dbPutField(&staddr,DBR_STRING,timestring,1); - status = dbPutField(&rladdr,DBR_STRING,timestring,1); + save_file_entry.set(shortfilename); + save_time_entry.set(timestring); + reload_time_entry.set(timestring); return(0); + + // I can type above your caret } @@ -1243,7 +1347,7 @@ void resetASG(char *name, int lock) { #endif /// Routine used to create local tables for reporting uninitialize and not monitored channels on request. -int createSortTableEntries(int numEntries,int wcval,char *wcstring,int *noInit,time_t *times) +int createSortTableEntries(int numEntries,int wcval,const char *wcstring,int *noInit,time_t *times) { int ii,jj; int notMon = 0; @@ -1253,10 +1357,10 @@ int lnb = 0; // line b - non mon chan list index #ifdef CA_SDF int lnc = 0; // line c - disconnected chan list index #endif -char tmpname[64]; +fixed_string<64> tmpname; time_t mtime=0; long nvals = 1; -char liveset[64]; +fixed_string<64> liveset; double liveval = 0.0; chNotInit = 0; @@ -1271,14 +1375,13 @@ double liveval = 0.0; for(ii=0;ii<fmNum;ii++) { if(!filterTable[ii].init) chNotInit += 1; if(filterTable[ii].init && !(filterTable[ii].mask & ALL_SWSTAT_BITS)) chNotMon += 1; - if(wcval && (ret = strstr(filterTable[ii].fname,wcstring) == NULL)) { + if(wcval && (ret = strstr(filterTable[ii].fname.c_str(),wcstring) == NULL)) { continue; } - bzero(tmpname,sizeof(tmpname)); - strncpy(tmpname,filterTable[ii].fname,(strlen(filterTable[ii].fname)-1)); + tmpname = filterTable[ii].fname; if(!filterTable[ii].init) { - sprintf(uninitChans[lna].chname,"%s",tmpname); - strcpy(uninitChans[lna].burtset,""); + uninitChans[lna].chname.printf("%s",tmpname); + uninitChans[lna].burtset.clear(); uninitChans[lna].liveval = 0.0; uninitChans[lna].sw[0] = (int)cdTableP[filterTable[ii].sw[0]].data.chval; uninitChans[lna].sw[1] = (int)cdTableP[filterTable[ii].sw[1]].data.chval; @@ -1288,7 +1391,8 @@ double liveval = 0.0; lna ++; } if(filterTable[ii].init && !(filterTable[ii].mask & ALL_SWSTAT_BITS)) { - sprintf(unMonChans[lnb].chname,"%s",tmpname); + + unMonChans[lnb].chname = tmpname; unMonChans[lnb].liveval = 0.0; unMonChans[lnb].sw[0] = (int)cdTableP[filterTable[ii].sw[0]].data.chval; unMonChans[lnb].sw[1] = (int)cdTableP[filterTable[ii].sw[1]].data.chval; @@ -1314,65 +1418,54 @@ double liveval = 0.0; #ifdef CA_SDF if(!cdTable[jj].connected) chDisconnected += 1; #endif - if(wcval && (ret = strstr(cdTable[jj].chname,wcstring) == NULL)) { + if(wcval && (ret = strstr(cdTable[jj].chname.c_str(),wcstring) == NULL)) { continue; } // Uninitialized channels if(!cdTable[jj].initialized && !cdTable[jj].filterswitch) { // printf("Chan %s not init %d %d %d\n",cdTable[jj].chname,cdTable[jj].initialized,jj,numEntries); if(lna < SDF_ERR_TSIZE) { - sprintf(uninitChans[lna].chname,"%s",cdTable[jj].chname); + uninitChans[lna].chname = cdTable[jj].chname; if(cdTable[jj].datatype == SDF_NUM) { liveval = cdTableP[jj].data.chval; - snprintf(liveset, sizeof(liveset),"%.10lf", liveval); + liveset.printf("%.10lf", liveval); } else { liveval = 0.0; - strncpy(liveset, cdTableP[jj].data.strval, sizeof(liveset)); - liveset[sizeof(liveset)-1] = '\0'; + liveset = cdTableP[jj].data.strval; } - strncpy(uninitChans[lna].liveset, liveset, sizeof(uninitChans[lna].liveset)); - uninitChans[lna].liveset[sizeof(uninitChans[lna].liveset)-1] = '\0'; + uninitChans[lna].liveset = liveset; uninitChans[lna].liveval = liveval; uninitChans[lna].sigNum = jj; uninitChans[lna].filtNum = -1; - if (times) { - snprintf(unMonChans[lna].timeset, sizeof(unMonChans[lna].timeset), "%s", (times ? ctime(×[jj]) : " ")); - } else { - sprintf(unMonChans[lna].timeset,"%s"," "); - } + unMonChans[lna].timeset = (times ? ctime(×[jj]) : " "); - sprintf(uninitChans[lna].timeset,"%s"," "); - sprintf(uninitChans[lna].diff,"%s"," "); - lna ++; + uninitChans[lna].timeset = " "; + uninitChans[lna].diff = " "; + lna++; } } // Unmonitored channels if(cdTable[jj].initialized && !cdTable[jj].mask && !cdTable[jj].filterswitch) { if(lnb < SDF_ERR_TSIZE) { - sprintf(unMonChans[lnb].chname,"%s",cdTable[jj].chname); + unMonChans[lnb].chname = cdTable[jj].chname; if(cdTable[jj].datatype == SDF_NUM) { - snprintf(unMonChans[lnb].burtset, sizeof(unMonChans[lnb].burtset),"%.10lf",cdTable[jj].data.chval); - snprintf(unMonChans[lnb].liveset, sizeof(unMonChans[lnb].liveset),"%.10lf",cdTableP[jj].data.chval); + unMonChans[lnb].burtset.printf("%.10lf",cdTable[jj].data.chval); + unMonChans[lnb].liveset.printf("%.10lf",cdTableP[jj].data.chval); unMonChans[lnb].liveval = cdTableP[jj].data.chval; } else { - sprintf(unMonChans[lnb].burtset,"%s",cdTable[jj].data.strval); - sprintf(unMonChans[lnb].liveset,"%s",cdTableP[jj].data.strval); + unMonChans[lnb].burtset = cdTable[jj].data.strval; + unMonChans[lnb].liveset = cdTableP[jj].data.strval; unMonChans[lnb].liveval = 0.0; } unMonChans[lnb].sigNum = jj; unMonChans[lnb].filtNum = -1; - if (times) { - snprintf(unMonChans[lnb].timeset, sizeof(unMonChans[lnb].timeset), "%s", (times ? ctime(×[jj]) : " ")); - sprintf(unMonChans[lnb].diff,"%s"," "); - } else { - sprintf(unMonChans[lnb].timeset,"%s"," "); - sprintf(unMonChans[lnb].diff,"%s"," "); - } + unMonChans[lnb].timeset = (times ? ctime(×[jj]) : " "); + unMonChans[lnb].diff = " "; lnb ++; } @@ -1380,25 +1473,22 @@ double liveval = 0.0; #ifdef CA_SDF if(!cdTable[jj].connected && !cdTable[jj].filterswitch) { if (lnc < SDF_ERR_TSIZE) { - sprintf(disconnectChans[lnc].chname, "%s", cdTable[jj].chname); + disconnectChans[lnc].chname = cdTable[jj].chname; if(cdTable[jj].datatype == SDF_NUM) { liveval = cdTableP[jj].data.chval; - snprintf(liveset, sizeof(liveset),"%.10lf", liveval); + liveset.printf("%.10lf", liveval); } else { liveval = 0.0; - strncpy(liveset, cdTableP[jj].data.strval, sizeof(liveset)); - liveset[sizeof(liveset)-1] = '\0'; + liveset = cdTableP[jj].data.strval; } - strncpy(disconnectChans[lnc].liveset, liveset, sizeof(disconnectChans[lnc].liveset)); - disconnectChans[lnc].liveset[sizeof(disconnectChans[lnc].liveset)-1] = '\0'; + disconnectChans[lnc].liveset = liveset; disconnectChans[lnc].liveval = liveval; disconnectChans[lnc].sigNum = jj; disconnectChans[lnc].filtNum = -1; - sprintf(disconnectChans[lnc].timeset,"%s"," "); - sprintf(disconnectChans[lnc].diff,"%s"," "); - lnc ++; - + disconnectChans[lnc].timeset = " "; + disconnectChans[lnc].diff = " "; + lnc++; } } #endif @@ -1406,24 +1496,24 @@ double liveval = 0.0; // Clear out the uninit tables. for(jj=lna;jj<(lna + 50);jj++) { - sprintf(uninitChans[jj].chname,"%s"," "); - sprintf(uninitChans[jj].burtset,"%s"," "); - sprintf(uninitChans[jj].liveset,"%s"," "); + uninitChans[jj].chname = " "; + uninitChans[jj].burtset = " "; + uninitChans[jj].liveset = " "; uninitChans[jj].liveval = 0.0; - sprintf(uninitChans[jj].timeset,"%s"," "); - sprintf(uninitChans[jj].diff,"%s"," "); + uninitChans[jj].timeset = " "; + uninitChans[jj].diff = " "; uninitChans[jj].sigNum = 0; // is this the right value, should it be -1? uninitChans[jj].filtNum = -1; } // Clear out the unmon tables. for(jj=lnb;jj<(lnb + 50);jj++) { - sprintf(unMonChans[jj].chname,"%s"," "); - sprintf(unMonChans[jj].burtset,"%s"," "); - sprintf(unMonChans[jj].liveset,"%s"," "); + unMonChans[jj].chname = " "; + unMonChans[jj].burtset = " "; + unMonChans[jj].liveset = " "; unMonChans[jj].liveval = 0.0; - sprintf(unMonChans[jj].timeset,"%s"," "); - sprintf(unMonChans[jj].diff,"%s"," "); + unMonChans[jj].timeset = " "; + unMonChans[jj].diff = " "; unMonChans[jj].sigNum = 0; unMonChans[jj].filtNum = -1; } @@ -1431,12 +1521,12 @@ double liveval = 0.0; // Clear out the disconnected tables. for(jj=lnc;jj<(lnc + 50);jj++) { - sprintf(disconnectChans[jj].chname,"%s"," "); - sprintf(disconnectChans[jj].burtset,"%s"," "); - sprintf(disconnectChans[jj].liveset,"%s"," "); + disconnectChans[jj].chname = " "; + disconnectChans[jj].burtset = " "; + disconnectChans[jj].liveset = " "; disconnectChans[jj].liveval = 0.0; - sprintf(disconnectChans[jj].timeset,"%s"," "); - sprintf(disconnectChans[jj].diff,"%s"," "); + disconnectChans[jj].timeset = " "; + disconnectChans[jj].diff = " "; disconnectChans[jj].sigNum = 0; disconnectChans[jj].filtNum = -1; } @@ -1446,9 +1536,9 @@ double liveval = 0.0; } /// Common routine to load monitoring tables into EPICS channels for MEDM screen. +template <std::size_t entry_count> int reportSetErrors(char *pref, ///< Channel name prefix from EPICS environment. - int numEntries, ///< Number of entries in table to be reported. - SET_ERR_TABLE setErrTable[], ///< Which table to report to EPICS channels. + fixed_size_vector<SET_ERR_TABLE , entry_count>& setErrTable, ///< Which table to report to EPICS channels. int page, ///< Which page of 40 to display. int linkInFilters) ///< Should the SDF_FM_LINE values be set. { @@ -1484,21 +1574,22 @@ int minusOne = -1; // Get the page number to display mypage = page; - // Calculat start index to the diff table. + // Calculate start index to the diff table. myindex = page * SDF_ERR_DSIZE; + int numEntries = setErrTable.size(); if(myindex == numEntries && numEntries > 0) { mypage --; myindex = mypage * SDF_ERR_DSIZE; } // If index is > number of entries in the table, then page back. - if(myindex > numEntries) { + if(myindex > numEntries) { mypage = numEntries / SDF_ERR_DSIZE; myindex = mypage * SDF_ERR_DSIZE; - } + } // Set the stop index to the diff table. rc = myindex + SDF_ERR_DSIZE; // If stop index beyond last diff table entry, set it to last entry. - if(rc > numEntries) rc = numEntries; + if(rc > numEntries) rc = numEntries; numDisp = rc - myindex; // Fill in table entries. @@ -1507,7 +1598,7 @@ int minusOne = -1; sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", lineNum); status = dbNameToAddr(s,&saddr); //sprintf(stmp, "%s%s", (setErrTable[ii].filtNum >= 0 ? "* " : ""), setErrTable[ii].chname); - status = dbPutField(&saddr,DBR_UCHAR,&setErrTable[ii].chname,flength); + status = dbPutField(&saddr,DBR_UCHAR,setErrTable[ii].chname.c_str(),flength); sprintf(s1, "%s_%s_STAT%d_BURT", pref,"SDF_SP", lineNum); status = dbNameToAddr(s1,&baddr); @@ -1542,7 +1633,7 @@ int minusOne = -1; } // Clear out empty table entries. - for(ii=numDisp;ii<40;ii++) + for(ii=numDisp;ii<SDF_ERR_DSIZE;ii++) { sprintf(s, "%s_%s_STAT%d", pref,"SDF_SP", ii); status = dbNameToAddr(s,&saddr); @@ -1591,21 +1682,22 @@ int minusOne = -1; // - Present Value // - Time the present setting was applied. /// Setpoint monitoring routine. -int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstring, int listAll, int *totalDiffs) +template <std::size_t entry_count> +int spChecker(int monitorAll, fixed_size_vector<SET_ERR_TABLE, entry_count>& setErrTable,int wcVal, const char *wcstring, int listAll, int *totalDiffs) { int errCntr = 0; ADDRESS paddr; long status=0; - int ii; + //int ii; double rval; - char sval[128]; + fixed_string<128> sval; time_t mtime; - char localtimestring[256]; + fixed_string<256> localtimestring; int localErr = 0; - char liveset[64]; - char burtset[64]; - char diffB2L[64]; - char swName[64]; + fixed_string<64> liveset; + fixed_string<64> burtset; + fixed_string<64> diffB2L; + fixed_string<64> swName; double sdfdiff = 0.0; double liveval = 0.0; int filtDiffs=0; @@ -1622,72 +1714,76 @@ int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstr */ #endif // Check filter switch settings first - errCntr = checkFilterSwitches(fmNum,setErrTable,monitorAll,listAll,wcVal,wcstring,&filtDiffs); - *totalDiffs = filtDiffs; - if(chNum) { - for(ii=0;ii<chNum;ii++) { + setErrTable.clear(); + errCntr = checkFilterSwitches(setErrTable,monitorAll,listAll,wcVal,wcstring,&filtDiffs); + *totalDiffs = filtDiffs; + + int index = -1; + for (auto& curSP: cdTable) + { + ++index; if((errCntr < SDF_ERR_TSIZE) && // Within table max size - ((cdTable[ii].mask != 0) || (monitorAll)) && // Channel is to be monitored - cdTable[ii].initialized && // Channel was set in BURT - cdTable[ii].filterswitch == 0 || // Not a filter switch channel - (listAll && cdTable[ii].filterswitch == 0)) + ((curSP.mask != 0) || (monitorAll)) && // Channel is to be monitored + curSP.initialized && // Channel was set in BURT + curSP.filterswitch == 0 || // Not a filter switch channel + (listAll && curSP.filterswitch == 0)) { localErr = 0; mtime = 0; // Find address of channel - status = GET_ADDRESS(cdTable[ii].chname,&paddr); + GET_ADDRESS(curSP.chname,&paddr); // If this is a digital data type, then get as double. - if(cdTable[ii].datatype == SDF_NUM) + if(curSP.datatype == SDF_NUM) { - status = GET_VALUE_NUM(paddr,&rval,&mtime, NULL); //&(cdTable[ii].connected)); - if(cdTable[ii].data.chval != rval || listAll) + GET_VALUE_NUM(paddr,&rval,&mtime, NULL); //&(curSP.connected)); + if(curSP.data.chval != rval || listAll) { - sdfdiff = fabs(cdTable[ii].data.chval - rval); - snprintf(burtset,sizeof(burtset),"%.10lf",cdTable[ii].data.chval); - snprintf(liveset,sizeof(liveset),"%.10lf",rval); + sdfdiff = fabs(curSP.data.chval - rval); + burtset.printf("%.10lf",curSP.data.chval); + liveset.printf("%.10lf",rval); liveval = rval; - snprintf(diffB2L,sizeof(diffB2L),"%.8le",sdfdiff); + diffB2L.printf("%.8le",sdfdiff); localErr = 1; } // If this is a string type, then get as string. } else { - updateStrVarSetPoint(ii); - status = GET_VALUE_STR(paddr,sval,sizeof(sval),&mtime, NULL); //&(cdTable[ii].connected)); - if(strcmp(cdTable[ii].data.strval,sval) != 0 || listAll) + updateStrVarSetPoint(index); + GET_VALUE_STR(paddr,sval,&mtime, NULL); //&(curSP.connected)); + if(sval != curSP.data.strval || listAll) { - sprintf(burtset,"%s",cdTable[ii].data.strval); - sprintf(liveset,"%s",sval); + burtset = curSP.data.strval; + liveset = sval; liveval = 0.0; - sprintf(diffB2L,"%s"," "); + diffB2L.printf("%s"," "); localErr = 1; } } if(localErr) *totalDiffs += 1; - if(localErr && wcVal && (strstr(cdTable[ii].chname,wcstring) == NULL)) + if(localErr && wcVal && (strstr(curSP.chname.c_str(),wcstring) == NULL)) localErr = 0; // If a diff was found, then write info the EPICS setpoint diff table. if(localErr) { - sprintf(setErrTable[errCntr].chname,"%s", cdTable[ii].chname); - - sprintf(setErrTable[errCntr].burtset, "%s", burtset); - - sprintf(setErrTable[errCntr].liveset, "%s", liveset); - setErrTable[errCntr].liveval = liveval; - sprintf(setErrTable[errCntr].diff, "%s", diffB2L); - setErrTable[errCntr].sigNum = ii; - setErrTable[errCntr].filtNum = -1; - - strcpy(localtimestring, ctime(&mtime)); - localtimestring[strlen(localtimestring) - 1] = 0; - sprintf(setErrTable[errCntr].timeset, "%s", localtimestring); - if(cdTable[ii].mask) setErrTable[errCntr].chFlag |= 1; - else setErrTable[errCntr].chFlag &= ~(1); + setErrTable.emplace_back(); + auto& curErr = setErrTable.back(); + curErr.chname = curSP.chname; + + curErr.burtset = burtset; + + curErr.liveset = liveset; + curErr.liveval = liveval; + curErr.diff = diffB2L; + curErr.sigNum = index; + curErr.filtNum = -1; + + localtimestring = ctime(&mtime); + curErr.timeset = localtimestring; + if(curSP.mask) curErr.chFlag |= 1; + else curErr.chFlag &= ~(1); errCntr ++; } } } - } return(errCntr); } @@ -1697,29 +1793,30 @@ int spChecker(int monitorAll, SET_ERR_TABLE setErrTable[],int wcVal, char *wcstr /// @param modTable[in] An array of SET_ERR_TABLE entries that hold the current views state to be merged with cdTable /// /// @remark modifies cdTable -int modifyTable(int numEntries,SET_ERR_TABLE modTable[]) +int modifyTable(table_range modTable) { -int ii,jj; -int fmIndex = -1; -unsigned int sn,sn1; -int found = 0; - for(ii=0;ii<numEntries;ii++) + int jj; + int fmIndex = -1; + unsigned int sn,sn1; + int found = 0; + + for (const auto& entry:modTable) { // if accept or monitor is set - if(modTable[ii].chFlag > 3) + if(entry.chFlag > 3) { found = 0; for(jj=0;jj<chNum;jj++) { - if (strcmp(cdTable[jj].chname,modTable[ii].chname) == 0) { - if ( CHFLAG_ACCEPT_BIT(modTable[ii].chFlag) ) { - if(cdTable[jj].datatype == SDF_NUM) cdTable[jj].data.chval = modTable[ii].liveval;/* atof(modTable[ii].liveset);*/ - else sprintf(cdTable[jj].data.strval,"%s",modTable[ii].liveset); + if (cdTable[jj].chname == entry.chname) { + if ( CHFLAG_ACCEPT_BIT(entry.chFlag) ) { + if(cdTable[jj].datatype == SDF_NUM) cdTable[jj].data.chval = entry.liveval;/* atof(entry.liveset);*/ + else sprintf(cdTable[jj].data.strval,"%s",entry.liveset); cdTable[jj].initialized = 1; found = 1; fmIndex = cdTable[jj].filterNum; } - if(CHFLAG_MONITOR_BIT(modTable[ii].chFlag)) { + if(CHFLAG_MONITOR_BIT(entry.chFlag)) { fmIndex = cdTable[jj].filterNum; if (fmIndex >= 0 && fmIndex < SDF_MAX_FMSIZE) { // filter module use the manualy modified state, cannot just toggle @@ -1732,21 +1829,21 @@ int found = 0; } } } - if(modTable[ii].filtNum >= 0 && !found) { - fmIndex = modTable[ii].filtNum; + if(entry.filtNum >= 0 && !found) { + fmIndex = entry.filtNum; // printf("This is a filter from diffs = %s\n",filterTable[fmIndex].fname); filterTable[fmIndex].newSet = 1; - sn = modTable[ii].sigNum; + sn = entry.sigNum; sn1 = sn / SDF_MAX_TSIZE; sn %= SDF_MAX_TSIZE; - if(CHFLAG_ACCEPT_BIT(modTable[ii].chFlag)) { - cdTable[sn].data.chval = modTable[ii].sw[0]; + if(CHFLAG_ACCEPT_BIT(entry.chFlag)) { + cdTable[sn].data.chval = entry.sw[0]; cdTable[sn].initialized = 1; - cdTable[sn1].data.chval = modTable[ii].sw[1]; + cdTable[sn1].data.chval = entry.sw[1]; cdTable[sn1].initialized = 1; filterTable[fmIndex].init = 1; } - if(CHFLAG_MONITOR_BIT(modTable[ii].chFlag)) { + if(CHFLAG_MONITOR_BIT(entry.chFlag)) { filterTable[fmIndex].mask = filterMasks[fmIndex]; cdTable[sn].mask = filterMasks[fmIndex]; cdTable[sn1].mask = filterMasks[fmIndex]; @@ -1754,33 +1851,33 @@ int found = 0; } } } - newfilterstats(fmNum); + newfilterstats(); return(0); } -int resetSelectedValues(int errNum, SET_ERR_TABLE modTable[]) +template <std::size_t entry_count> +int resetSelectedValues(fixed_size_vector<SET_ERR_TABLE, entry_count>& modTable) { long status; -int ii; int sn; int sn1 = 0; ADDRESS saddr; - for(ii=0;ii<errNum;ii++) + for(const auto& entry:modTable) { - if (modTable[ii].chFlag & 2) + if (entry.chFlag & 2) { - sn = modTable[ii].sigNum; + sn = entry.sigNum; if(sn > SDF_MAX_TSIZE) { sn1 = sn / SDF_MAX_TSIZE; sn %= SDF_MAX_TSIZE; } - status = GET_ADDRESS(cdTable[sn].chname,&saddr); - if (cdTable[sn].datatype == SDF_NUM) status = PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn].data.chval)); - else status = PUT_VALUE(saddr,SDF_STR,cdTable[sn].data.strval);; + GET_ADDRESS(cdTable[sn].chname,&saddr); + if (cdTable[sn].datatype == SDF_NUM) PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn].data.chval)); + else PUT_VALUE(saddr,SDF_STR,cdTable[sn].data.strval);; if(sn1) { - status = GET_ADDRESS(cdTable[sn1].chname,&saddr); - status = PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn1].data.chval)); + GET_ADDRESS(cdTable[sn1].chname,&saddr); + PUT_VALUE(saddr,SDF_NUM,&(cdTable[sn1].data.chval)); } } } @@ -1788,48 +1885,44 @@ ADDRESS saddr; } /// This function sets filter module request fields to aid in decoding errant filter module switch settings. -void newfilterstats(int numchans) { +void newfilterstats() { ADDRESS paddr; long status; - int ii; + FILE *log=0; - char chname[128]; - int mask = 0x1ffff; + fixed_string<128> chname; + unsigned int mask = 0x1ffff; int tmpreq; int counter = 0; int rsw1,rsw2; unsigned int tmpL = 0; printf("In newfilterstats\n"); - for(ii=0;ii<numchans;ii++) { - if(filterTable[ii].newSet) { + for(auto& filterEntry:filterTable) { + if(filterEntry.newSet) { counter ++; - filterTable[ii].newSet = 0; - filterTable[ii].init = 1; - rsw1 = filterTable[ii].sw[0]; - rsw2 = filterTable[ii].sw[1]; - filterTable[ii].mask = cdTable[rsw1].mask | cdTable[rsw2].mask;; - cdTable[rsw1].mask = filterTable[ii].mask; - cdTable[rsw2].mask = filterTable[ii].mask; + filterEntry.newSet = 0; + filterEntry.init = 1; + rsw1 = filterEntry.sw[0]; + rsw2 = filterEntry.sw[1]; + filterEntry.mask = cdTable[rsw1].mask | cdTable[rsw2].mask;; + cdTable[rsw1].mask = filterEntry.mask; + cdTable[rsw2].mask = filterEntry.mask; tmpreq = ((unsigned int)cdTable[rsw1].data.chval & 0xffff) + (((unsigned int)cdTable[rsw2].data.chval & 0xffff) << 16); - filterTable[ii].swreq = filtCtrlBitConvert(tmpreq); - bzero(chname,sizeof(chname)); + filterEntry.swreq = filtCtrlBitConvert(tmpreq); // Find address of channel - strcpy(chname,filterTable[ii].fname); - strcat(chname,"SWREQ"); - status = GET_ADDRESS(chname,&paddr); - if(!status) { - tmpL = (unsigned int)filterTable[ii].swreq; - status = PUT_VALUE_INT(paddr,&tmpL); + chname = filterEntry.fname; + chname += "SWREQ"; + if(GET_ADDRESS(chname,&paddr)) { + tmpL = (unsigned int)filterEntry.swreq; + PUT_VALUE_INT(paddr,&tmpL); } - bzero(chname,sizeof(chname)); // Find address of channel - strcpy(chname,filterTable[ii].fname); - strcat(chname,"SWMASK"); - status = GET_ADDRESS(chname,&paddr); - if(!status) { - status = PUT_VALUE_INT(paddr,(unsigned int*)&mask); + chname = filterEntry.fname; + chname += "SWMASK"; + if(GET_ADDRESS(chname,&paddr)) { + PUT_VALUE_INT(paddr,&mask); } // printf("New filter %d %s = 0x%x\t0x%x\t0x%x\n",ii,filterTable[ii].fname,filterTable[ii].swreq,filterTable[ii].sw[0],filterTable[ii].sw[1]); } @@ -1838,55 +1931,43 @@ void newfilterstats(int numchans) { } /// This function writes BURT settings to EPICS records. -int writeEpicsDb(int numchans, ///< Number of channels to write - CDS_CD_TABLE myTable[], ///< Table with data to be written. +template <std::size_t entry_count> +int writeEpicsDb(fixed_size_vector<CDS_CD_TABLE, entry_count>& myTable, ///< Table with data to be written. int command) ///< Write request. { ADDRESS paddr; long status; int ii; - // chNotFound = 0; switch (command) { case SDF_LOAD_DB_ONLY: case SDF_LOAD_PARTIAL: - for(ii=0;ii<numchans;ii++) { - // Find address of channel - status = GET_ADDRESS(myTable[ii].chname,&paddr); - if (!status) - { - if (myTable[ii].datatype == SDF_NUM) - { - status = PUT_VALUE(paddr,SDF_NUM,&(myTable[ii].data.chval)); - } else { - status = PUT_VALUE(paddr,SDF_STR,myTable[ii].data.strval); - } - } - else { // Write errors to chan not found table. - printf("CNF for %s = %d\n",myTable[ii].chname,status); - #if 0 - if(chNotFound < SDF_ERR_TSIZE) { - sprintf(unknownChans[chNotFound].chname,"%s",myTable[ii].chname); - sprintf(unknownChans[chNotFound].liveset,"%s"," "); - unknownChans[chNotFound].liveval = 0.0; - sprintf(unknownChans[chNotFound].timeset,"%s"," "); - sprintf(unknownChans[chNotFound].diff,"%s"," "); - unknownChans[chNotFound].chFlag = 0; - } - chNotFound ++; - #endif - } - } - - // Set the SDF_FILE_LOADED environment var to 1, - // signalling to the epipcs sequencer to - // trigger a BURT_RESTORE + for (const auto& entry:myTable) + { + // Find address of channel + if (GET_ADDRESS(entry.chname,&paddr)) { - set_sdf_file_loaded(1); + if (entry.datatype == SDF_NUM) + { + PUT_VALUE(paddr,SDF_NUM,&(entry.data.chval)); + } else { + PUT_VALUE(paddr,SDF_STR,entry.data.strval); + } } - break; + else { // Write errors to chan not found table. + printf("CNF for %s = %d\n",entry.chname,status); + } + } + // Set the SDF_FILE_LOADED environment var to 1, + // signalling to the epipcs sequencer to + // trigger a BURT_RESTORE + + { + set_sdf_file_loaded(1); + } + break; case SDF_READ_ONLY: // If request was only to re-read the BURT file, then don't want to apply new settings. // This is typically the case where only mask fields were changed in the BURT file to @@ -1895,24 +1976,24 @@ int writeEpicsDb(int numchans, ///< Number of channels to write break; case SDF_RESET: // Only want to set those channels marked by a mask back to their original BURT setting. - for(ii=0;ii<numchans;ii++) { - // FIXME: check does this make sense w/ a bitmask in mask? - // can filter modules get here? It seems that they will, so would we need - // to mask the value we set ? - if(myTable[ii].mask) { - //Find address of channel - status = GET_ADDRESS(myTable[ii].chname,&paddr); - if (!status) - { - if (myTable[ii].datatype == SDF_NUM) - { - status = PUT_VALUE(paddr,SDF_NUM,&(myTable[ii].data.chval)); - } else { - status = PUT_VALUE(paddr,SDF_STR,myTable[ii].data.strval); - } - } - } - } + for (const auto& entry: myTable) + { + // FIXME: check does this make sense w/ a bitmask in mask? + // can filter modules get here? It seems that they will, so would we need + // to mask the value we set ? + if(entry.mask) { + //Find address of channel + if (GET_ADDRESS(entry.chname,&paddr)) + { + if (entry.datatype == SDF_NUM) + { + PUT_VALUE(paddr,SDF_NUM,&(entry.data.chval)); + } else { + PUT_VALUE(paddr,SDF_STR,entry.data.strval); + } + } + } + } break; default: printf("writeEpicsDb setting routine got unknown request \n"); @@ -1925,10 +2006,10 @@ int writeEpicsDb(int numchans, ///< Number of channels to write /// Function to read BURT files and load data into local tables. -int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. - char *sdfile, ///< Name of the file to read. +int readConfig( const char *pref, ///< EPICS channel prefix from EPICS environment. + const char *sdfile, ///< Name of the file to read. int command, ///< Read file request type. - char *alarmfile) + const char *alarmfile) { FILE *cdf=0; FILE *adf=0; @@ -1937,13 +2018,14 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. int lock=0; char s1[128],s2[128],s3[128],s4[128],s5[128],s6[128],s7[128],s8[128]; char ls[6][64]; - dbAddr paddr; + ADDRESS paddr; + dbAddr reloadDbAddr; long status=0; int lderror = 0; int ropts = 0; int nvals = 1; int starttime=0,totaltime=0; - char timestring[256]; + embedded::fixed_string<256> timestring{}; char line[128]; char *fs=0; char ifo[4]; @@ -1964,7 +2046,6 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. s1[0]=s2[0]=s3[0]=s4[0]=s5[0]=s6[0]=s7[0]=s8[0]='\0'; - timestring[0]='\0'; line[0]='\0'; ifo[0]='\0'; fname[0]=errMsg[0]='\0'; @@ -1974,10 +2055,10 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. clock_gettime(CLOCK_REALTIME,&t); starttime = t.tv_nsec; - getSdfTime(timestring, 256); + getSdfTime(timestring); if(command == SDF_RESET) { - lderror = writeEpicsDb(chNum,cdTable,command); + lderror = writeEpicsDb(cdTable,command); } else { printf("PARTIAL %s\n",sdfile); cdf = fopen(sdfile,"r"); @@ -2030,12 +2111,12 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. argcount = parseLine(line,sizeof(s1),s1,s2,s3,s4,s5,s6); if (strcmp(s4, "") == 0) strcpy(s4, "0"); if(argcount == -1) { - sprintf(readErrTable[rderror].chname,"%s", s1); - sprintf(readErrTable[rderror].burtset, "%s", "Improper quotations "); - sprintf(readErrTable[rderror].liveset, "Line # %d", lineCnt); + readErrTable[rderror].chname = s1; + readErrTable[rderror].burtset = "Improper quotations "; + readErrTable[rderror].liveset.printf("Line # %d", lineCnt); readErrTable[rderror].liveval = 0.0; - sprintf(readErrTable[rderror].diff, "%s", sdfile); - sprintf(readErrTable[rderror].timeset, "%s", timestring); + readErrTable[rderror].diff = sdfile; + readErrTable[rderror].timeset = timestring; rderror ++; printf("Read error --- %s\n",s1); continue; @@ -2050,10 +2131,8 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. strstr(s1,"_SWREQ") == NULL && argcount > 2) { - // Clear out the local tabel channel name string. - bzero(cdTableP[chNumP].chname,strlen(cdTableP[chNumP].chname)); // Load channel name into local table. - strcpy(cdTableP[chNumP].chname,s1); + cdTableP[chNumP].chname = s1; // Check if s4 (monitor or not) is set (0/1). If doesn/'t exist in file, set to zero in local table. if(argcount > 3 && isdigit(s4[0])) { // printf("%s %s %s %s\n",s1,s2,s3,s4); @@ -2095,7 +2174,7 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. // Add settings to local table. for(ii=0;ii<chNum;ii++) { - if(strcmp(cdTable[ii].chname,cdTableP[chNumP].chname) == 0) + if(cdTable[ii].chname == cdTableP[chNumP].chname) { saveStrSetPoint(ii, s3); fmatch = 1; @@ -2113,12 +2192,12 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. cdTableP[chNumP].data.chval = atof(s3); if(cdTable[ii].filterswitch) { if(cdTableP[chNumP].data.chval > 0xffff) { - sprintf(readErrTable[rderror].chname,"%s", cdTable[ii].chname); - sprintf(readErrTable[rderror].burtset, "0x%x", (int)cdTableP[chNumP].data.chval); - sprintf(readErrTable[rderror].liveset, "%s", "OVERRANGE"); + readErrTable[rderror].chname = cdTable[ii].chname; + readErrTable[rderror].burtset.printf("0x%x", (int)cdTableP[chNumP].data.chval); + readErrTable[rderror].liveset = "OVERRANGE"; readErrTable[rderror].liveval = 0.0; - sprintf(readErrTable[rderror].diff, "%s", "MAX VAL = 0xffff"); - sprintf(readErrTable[rderror].timeset, "%s", timestring); + readErrTable[rderror].diff = "MAX VAL = 0xffff"; + readErrTable[rderror].timeset = timestring; printf("Read error --- %s\n", cdTable[ii].chname); rderror ++; } @@ -2145,7 +2224,7 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. strncpy(fname,s1,(strlen(s1)-4)); for(ii=0;ii<fmNum;ii++) { - if(strcmp(filterTable[ii].fname,fname) == 0) + if(filterTable[ii].fname == fname) { fmIndex = ii; filterTable[fmIndex].newSet = 1; @@ -2158,17 +2237,15 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. } else { // printf("CNF for %s \n",cdTableP[chNumP].chname); if(chNotFound < SDF_ERR_TSIZE) { - sprintf(unknownChans[chNotFound].chname,"%s",cdTableP[chNumP].chname); - ADDRESS tmpaddr; - status = GET_ADDRESS(cdTableP[chNumP].chname, &tmpaddr); - if(!status) { - sprintf(unknownChans[chNotFound].liveset,"%s","RO Channel "); + unknownChans[chNotFound].chname = cdTableP[chNumP].chname; + if(GET_ADDRESS(cdTableP[chNumP].chname,&paddr)) { + unknownChans[chNotFound].liveset = "RO Channel "; } else { - sprintf(unknownChans[chNotFound].liveset,"%s"," "); + unknownChans[chNotFound].liveset = " "; } unknownChans[chNotFound].liveval = 0.0; - sprintf(unknownChans[chNotFound].timeset,"%s"," "); - sprintf(unknownChans[chNotFound].diff,"%s"," "); + unknownChans[chNotFound].timeset = " "; + unknownChans[chNotFound].diff = " "; unknownChans[chNotFound].chFlag = 0; } chNotFound ++; @@ -2187,9 +2264,9 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. fclose(cdf); fclose(adf); printf("Loading epics %d\n",chNumP); - lderror = writeEpicsDb(chNumP,cdTableP,command); + lderror = writeEpicsDb(cdTableP,command); sleep(2); - newfilterstats(fmNum); + newfilterstats(); fmtInit = 1; } @@ -2203,8 +2280,8 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. sprintf(errMsg,"New SDF request (No table update): %s\nFile = %s\nTotal Chans = %d with load time = %d usec\n",timestring,sdfile,chNumP,(totaltime/1000)); } logFileEntry(errMsg); - status = dbNameToAddr(reloadtimechannel,&paddr); - status = dbPutField(&paddr,DBR_STRING,timestring,1); + status = dbNameToAddr(reloadtimechannel,&reloadDbAddr); + status = dbPutField(&reloadDbAddr,DBR_STRING,timestring.c_str(),1); printf("Number of read errors = %d\n",rderror); if(rderror) lderror = 2; return(lderror); @@ -2212,25 +2289,27 @@ int readConfig( char *pref, ///< EPICS channel prefix from EPICS environment. void registerFilters() { int ii = 0; - char tmpstr[64]; + fixed_string<64> tmpstr; int amatch = 0, jj = 0; fmNum = 0; for(ii=0;ii<chNum;ii++) { if(cdTable[ii].filterswitch == 1) { - strncpy(filterTable[fmNum].fname,cdTable[ii].chname,(strlen(cdTable[ii].chname)-4)); - snprintf(tmpstr, sizeof(tmpstr),"%s%s",filterTable[fmNum].fname,"SW2S"); + filterTable[fmNum].fname = cdTable[ii].chname; + filterTable[fmNum].fname.pop_back_n(4); + tmpstr = filterTable[fmNum].fname; + tmpstr += "SW2S"; filterTable[fmNum].sw[0] = ii; cdTable[ii].filterNum = fmNum; amatch = 0; for(jj=0;jj<chNum;jj++) { - if(strcmp(tmpstr,cdTable[jj].chname) == 0) + if(tmpstr == cdTable[jj].chname) { filterTable[fmNum].sw[1] = jj; amatch = 1; } } - if(!amatch) printf("No match for %s\n",tmpstr); + if(!amatch) printf("No match for %s\n",tmpstr.c_str()); fmNum ++; } } @@ -2239,40 +2318,50 @@ void registerFilters() { /// Provide initial values to the filterMasks arrray and the SDF_FM_MASK and SDF_FM_MASK_CTRL variables /// -void setupFMArrays(char *pref, int *fmMasks, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr) { +void setupFMArrays(char *pref,fixed_size_vector<int, SDF_MAX_FMSIZE>& fmMasks, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmCtrlAddr) { int ii = 0; int sw1 = 0; int sw2 = 0; - long status = 0; - char name[256]; - char ctrl[256]; + fixed_string<256> name; + fixed_string<256> ctrl; int zero = 0; - for (ii = 0; ii < SDF_MAX_FMSIZE; ++ii) { - sw1 = filterTable[ii].sw[0]; - sw2 = filterTable[ii].sw[1]; - fmMasks[ii] = filterTable[ii].mask = cdTable[sw1].mask | cdTable[sw2].mask; + fmMasks.clear(); + fmMaskAddr.clear(); + fmCtrlAddr.clear(); + for (ii = 0; ii < filterTable.size(); ++ii) + { + sw1 = filterTable[ii].sw[0]; + sw2 = filterTable[ii].sw[1]; + fmMasks[ii] = filterTable[ii].mask = cdTable[sw1].mask | cdTable[sw2].mask; - sprintf(name, "%s_SDF_FM_MASK_%d", pref, ii); - status = dbNameToAddr(name, &fmMaskAddr[ii]); - status = dbPutField(&fmMaskAddr[ii],DBR_LONG,&(fmMasks[ii]),1); + name.printf("%s_SDF_FM_MASK_%d", pref, ii); + fmMaskAddr.emplace_back(); + dbNameToAddr(name.c_str(), &fmMaskAddr[ii]); + dbPutField(&fmMaskAddr[ii],DBR_LONG,&(fmMasks[ii]),1); - sprintf(ctrl, "%s_SDF_FM_MASK_CTRL_%d", pref, ii); - status = dbNameToAddr(ctrl, &fmCtrlAddr[ii]); - status = dbPutField(&fmCtrlAddr[ii],DBR_LONG,&zero,1); + ctrl.printf("%s_SDF_FM_MASK_CTRL_%d", pref, ii); + fmCtrlAddr.emplace_back(); + dbNameToAddr(ctrl.c_str(), &fmCtrlAddr[ii]); + dbPutField(&fmCtrlAddr[ii],DBR_LONG,&zero,1); - } + } } /// Copy the filter mask information from cdTable into fmMasks /// -void resyncFMArrays(int *fmMasks, dbAddr *fmMaskAddr) { +void resyncFMArrays(fixed_size_vector<int, SDF_MAX_FMSIZE>&fmMasks, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr) +{ int ii = 0; int sw1 = 0; int sw2 = 0; long status = 0; - for (ii = 0; ii < SDF_MAX_FMSIZE; ++ii) { + + for (ii = 0; ii < filterTable.size(); ++ii) { sw1 = filterTable[ii].sw[0]; sw2 = filterTable[ii].sw[1]; fmMasks[ii] = filterTable[ii].mask = (cdTable[sw1].mask | cdTable[sw2].mask) & ALL_SWSTAT_BITS; @@ -2296,7 +2385,11 @@ void resyncFMArrays(int *fmMasks, dbAddr *fmMaskAddr) { /// /// @remark Each of the input arrays is expected to have a length of SDF_MAX_FMSIZE /// with fmNum in actual use. -void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, int *selectCounter, int errCnt, SET_ERR_TABLE *setErrTable) { +void processFMChanCommands( + fixed_size_vector<int, SDF_MAX_FMSIZE>& fMask, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmMaskAddr, + fixed_size_vector<dbAddr, SDF_MAX_FMSIZE>& fmCtrlAddr, + int *selectCounter, table_range setErrTable) { int ii = 0; int jj = 0; int refMask = 0; @@ -2308,7 +2401,8 @@ void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, i long nvals = 1; int foundCh = 0; - for (ii = 0; ii < SDF_MAX_FMSIZE && ii < fmNum; ++ii) { + //std::distance(setErrTable.begin(), setErrTable.end()); + for (ii = 0; ii < filterTable.size(); ++ii) { status = dbGetField(&fmCtrlAddr[ii], DBR_LONG, &ctrl, &ropts, &nvals, NULL); // only do work if there is a change @@ -2325,10 +2419,17 @@ void processFMChanCommands(int *fMask, dbAddr *fmMaskAddr, dbAddr *fmCtrlAddr, i foundCh = 0; /* if there is a change, update4 the selection count) */ if (differsPre != differsPost) { - for (jj = 0; jj < errCnt && setErrTable[jj].filtNum >= 0 && setErrTable[jj].filtNum != ii; ++jj) {} - if (jj < errCnt && setErrTable[jj].filtNum == ii) { - setErrTable[jj].chFlag ^= CHFLAG_MONITOR_BIT_VAL; - foundCh = setErrTable[jj].chFlag; + // search through setErrTable + //for (jj = 0; jj < errCnt && setErrTable[jj].filtNum >= 0 && setErrTable[jj].filtNum != ii; ++jj) {} + auto match = std::find_if(setErrTable.begin(), setErrTable.end(), [ii](const SET_ERR_TABLE& cur) -> bool { + return cur.filtNum == ii; + }); + // act on search + //if (jj < errCnt && setErrTable[jj].filtNum == ii) + if ( match != setErrTable.end()) + { + match->chFlag ^= CHFLAG_MONITOR_BIT_VAL; + foundCh = match->chFlag; selectCounter[2] += ( differsPre ? -1 : 1 ); } } @@ -2394,53 +2495,53 @@ long countDisconnectedChans() void nullCACallback(struct event_handler_args args) {} -int getCAIndex(char *entry, ADDRESS *addr) { +bool getCAIndex(const char *entry, ADDRESS *addr) { int ii = 0; - if (!entry || !addr) return 1; + if (!entry || !addr) return false; for (ii = 0; ii < chNum; ++ii) { - if (strcmp(cdTable[ii].chname, entry) == 0) { + if (cdTable[ii].chname == entry) { *addr = ii; - return 0; + return true; } } *addr = -1; - return 1; + return false; } -int setCAValue(ADDRESS ii, int type, void *data) +bool setCAValue(ADDRESS ii, SDF_TYPE type, const void *data) { + bool result = false; int status = ECA_NORMAL; - int result = 1; - + if (ii >= 0 && ii < chNum) { if (type == SDF_NUM) { status = ca_put_callback(DBR_DOUBLE, caTable[ii].chanid, (double *)data, nullCACallback, NULL); } else { status = ca_put_callback(DBR_STRING, caTable[ii].chanid, (char *)data, nullCACallback, NULL); } - result = (status == ECA_NORMAL ? 0 : 1); + result = (status == ECA_NORMAL); } return result; } -int setCAValueEPICSLong(ADDRESS ii, unsigned int *data) { +bool setCAValueLong(ADDRESS ii, const PV_INT_TYPE *data) { double tmp = 0.0; - if (!data) return 1; + if (!data) return false; tmp = (double)*data; - return setCAValue(ii,SDF_NUM,(void*)&tmp); + return setCAValue(ii,SDF_NUM,(const void*)&tmp); } -int syncEpicsDoubleValue(int index, double *dest, time_t *tp, int *connp) { +bool syncEpicsDoubleValue(ADDRESS index, double *dest, time_t *tp, int *connp) { int debug = 0; - if (!dest || index < 0 || index >= chNum) return 1; + if (!dest || index < 0 || index >= chNum) return false; #if VERBOSE_DEBUG if (strcmp(cdTable[caTable[index].chanIndex].chname, TEST_CHAN) == 0) { debug=1; } #endif - pthread_mutex_lock(&caTableMutex); + lock_guard l_(caTableMutex); if (caTable[index].datatype == SDF_NUM) { *dest = caTable[index].data.chval; if (tp) { @@ -2450,24 +2551,23 @@ int syncEpicsDoubleValue(int index, double *dest, time_t *tp, int *connp) { *connp = caTable[index].connected; } } - pthread_mutex_unlock(&caTableMutex); - return 0; + return true; } -int syncEpicsIntValue(ADDRESS index, unsigned int *dest, time_t *tp, int *connp) { +bool syncEpicsIntValue(ADDRESS index, PV_INT_TYPE *dest, time_t *tp, int *connp) { + bool result = true; double tmp = 0.0; - int result = 0; - if (!dest) return 1; + if (!dest) return false; result = syncEpicsDoubleValue(index, &tmp, tp, connp); - *dest = (unsigned int)(tmp); + *dest = (PV_INT_TYPE)(tmp); return result; } -int syncEpicsStrValue(int index, char *dest, int dest_size, time_t *tp, int *connp) { - if (!dest || index < 0 || index >= chNum || dest_size < 1) return 1; +bool syncEpicsStrValue(ADDRESS index, char *dest, int dest_size, time_t *tp, int *connp) { + if (!dest || index < 0 || index >= chNum || dest_size < 1) return false; dest[0] = '\0'; - pthread_mutex_lock(&caTableMutex); + lock_guard l_(caTableMutex); if (caTable[index].datatype == SDF_STR) { const int MAX_STR_LEN = sizeof(caTable[index].data.strval); strncpy(dest, caTable[index].data.strval, (dest_size < MAX_STR_LEN ? dest_size : MAX_STR_LEN)); @@ -2479,14 +2579,13 @@ int syncEpicsStrValue(int index, char *dest, int dest_size, time_t *tp, int *con *connp = caTable[index].connected; } } - pthread_mutex_unlock(&caTableMutex); - return 0; + return true; } /// Routine to handle subscription callbacks void subscriptionHandler(struct event_handler_args args) { float val = 0.0; - EPICS_CA_TABLE *entry = args.usr; + EPICS_CA_TABLE *entry = reinterpret_cast<EPICS_CA_TABLE*>(args.usr); EPICS_CA_TABLE *origEntry = entry; int initialRedirIndex = 0; @@ -2494,7 +2593,7 @@ void subscriptionHandler(struct event_handler_args args) { return; } - pthread_mutex_lock(&caTableMutex); + lock_guard l_(caTableMutex); // If this entry has just reconnected, then do not write the old copy, write to the temporary/redir dest initialRedirIndex = entry->redirIndex; @@ -2549,7 +2648,6 @@ void subscriptionHandler(struct event_handler_args args) { // The dbr_gr_enum type does not have time information, so we use current time entry->mod_time = time(NULL); } - pthread_mutex_unlock(&caTableMutex); } void connectCallback(struct connection_handler_args args) { @@ -2576,65 +2674,68 @@ void connectCallback(struct connection_handler_args args) { } } - pthread_mutex_lock(&caTableMutex); - if (args.op == CA_OP_CONN_UP) { - // connection - if (is_enum) { - entry->redirIndex = entry->chanIndex + SDF_MAX_TSIZE; - connEntry = &(caConnEnumTable[entry->chanIndex]); - } else { - // reserve a new conn table entry if one is not already reserved - if (entry->redirIndex < 0) { - entry->redirIndex = chConnNum; - ++chConnNum; - } - connEntry = &(caConnTable[entry->redirIndex]); - } - entry->chanid = args.chid; + { + lock_guard l_(caTableMutex); + if (args.op == CA_OP_CONN_UP) { + // connection + if (is_enum) { + entry->redirIndex = entry->chanIndex + SDF_MAX_TSIZE; + connEntry = &(caConnEnumTable[entry->chanIndex]); + } else { + // reserve a new conn table entry if one is not already reserved + if (entry->redirIndex < 0) { + entry->redirIndex = chConnNum; + ++chConnNum; + } + connEntry = &(caConnTable[entry->redirIndex]); + } + entry->chanid = args.chid; - // now copy items over to the connection record - connEntry->redirIndex = -1; + // now copy items over to the connection record + connEntry->redirIndex = -1; - connEntry->datatype = sdf_type; - connEntry->data.chval = 0.0; - entry->connected = 1; - connEntry->connected = 1; - connEntry->mod_time = 0; - connEntry->chanid = args.chid; - connEntry->chanIndex = entry->chanIndex; - typeChange = (connEntry->datatype != entry->datatype); + connEntry->datatype = sdf_type; + connEntry->data.chval = 0.0; + entry->connected = 1; + connEntry->connected = 1; + connEntry->mod_time = 0; + connEntry->chanid = args.chid; + connEntry->chanIndex = entry->chanIndex; + typeChange = (connEntry->datatype != entry->datatype); - } else { - // disconnect - entry->connected = 0; - } - pthread_mutex_unlock(&caTableMutex); + } else { + // disconnect + entry->connected = 0; + } + } - // now register/clear the subscription callback - pthread_mutex_lock(&caEvidMutex); - if (args.op == CA_OP_CONN_UP) { - // connect - // if we are subscribed but the types are wrong, then unsubscribe - if (caEvid[chanIndex] && typeChange) { - ca_clear_subscription(caEvid[chanIndex]); - caEvid[chanIndex] = 0; - } - // if we are not subscribed become subscribed - if (!caEvid[chanIndex]) { - chtype subtype = (sdf_type == SDF_NUM ? DBR_TIME_DOUBLE : DBR_TIME_STRING); - if (is_enum) { - subtype = DBR_GR_ENUM; - } - ca_create_subscription(subtype, 0, args.chid, DBE_VALUE, subscriptionHandler, entry, &(caEvid[chanIndex])); - } - } else { - // disconnect - if (caEvid[chanIndex]) { - ca_clear_subscription(caEvid[chanIndex]); - caEvid[chanIndex] = 0; - } - } - pthread_mutex_unlock(&caEvidMutex); + { + // now register/clear the subscription callback + lock_guard l_(caEvidMutex); + if (args.op == CA_OP_CONN_UP) { + // connect + // if we are subscribed but the types are wrong, then unsubscribe + if (caEvid[chanIndex] && typeChange) { + ca_clear_subscription(caEvid[chanIndex]); + caEvid[chanIndex] = 0; + } + // if we are not subscribed become subscribed + if (!caEvid[chanIndex]) { + chtype subtype = (sdf_type == SDF_NUM ? DBR_TIME_DOUBLE : DBR_TIME_STRING); + if (is_enum) { + subtype = DBR_GR_ENUM; + } + ca_create_subscription(subtype, 0, args.chid, DBE_VALUE, subscriptionHandler, entry, + &(caEvid[chanIndex])); + } + } else { + // disconnect + if (caEvid[chanIndex]) { + ca_clear_subscription(caEvid[chanIndex]); + caEvid[chanIndex] = 0; + } + } + } } } @@ -2649,19 +2750,19 @@ void registerPV(char *PVname) return; } //printf("Registering %s\n", PVname); - pthread_mutex_lock(&caTableMutex); - caTable[chNum].datatype = SDF_NUM; - caTable[chNum].connected = 0; - strncpy(cdTable[chNum].chname, PVname, 128); - cdTable[chNum].chname[128-1] = '\0'; - cdTable[chNum].datatype = SDF_NUM; - cdTable[chNum].initialized = 0; - cdTable[chNum].filterswitch = 0; - cdTable[chNum].filterNum = -1; - cdTable[chNum].error = 0; - cdTable[chNum].initialized = 0; - cdTable[chNum].mask = 0; - pthread_mutex_unlock(&caTableMutex); + { + lock_guard l_(caTableMutex); + caTable[chNum].datatype = SDF_NUM; + caTable[chNum].connected = 0; + cdTable[chNum].chname = PVname; + cdTable[chNum].datatype = SDF_NUM; + cdTable[chNum].initialized = 0; + cdTable[chNum].filterswitch = 0; + cdTable[chNum].filterNum = -1; + cdTable[chNum].error = 0; + cdTable[chNum].initialized = 0; + cdTable[chNum].mask = 0; + } status = ca_create_channel(PVname, connectCallback, &(caTable[chNum]), 0, &chid1); @@ -2724,7 +2825,7 @@ void parseChannelListReq(char *fname) { if (strstr(s1,"_SWMASK") != NULL || strstr(s1,"_SDF_NAME") != NULL || strstr(s1,"_SWREQ") != NULL) continue; - if (isAlarmChannel(s1)) continue; + if (isAlarmChannelRaw(s1)) continue; registerPV(s1); } fclose(f); @@ -2732,18 +2833,14 @@ void parseChannelListReq(char *fname) { /// Routine to get the state of the CA Thread int getCAThreadState() { - int state = 0; - pthread_mutex_lock(&caStateMutex); - state = caThreadState; - pthread_mutex_unlock(&caStateMutex); - return state; + lock_guard l_(caStateMutex); + return caThreadState; } /// Routine to set the state of the CA Thread void setCAThreadState(int state) { - pthread_mutex_lock(&caStateMutex); + lock_guard l_(caStateMutex); caThreadState = state; - pthread_mutex_unlock(&caStateMutex); } // copy the given entry (which should be in caConnTable or caConnEnumTable) to caTable @@ -2779,7 +2876,7 @@ void syncCAConnections(long *disconnected_count) int chanIndex = SDF_MAX_TSIZE; chtype subtype = DBR_TIME_DOUBLE; - pthread_mutex_lock(&caTableMutex); + lock_guard l_(caTableMutex); // sync all non-enum channel connections that have taken place for (ii = 0; ii < chConnNum; ++ii) { copyConnectedCAEntry(&(caConnTable[ii])); @@ -2817,7 +2914,6 @@ void syncCAConnections(long *disconnected_count) // } // *disconnected_count = tmp; //} - pthread_mutex_unlock(&caTableMutex); } /// Main loop of the CA Thread @@ -2835,6 +2931,7 @@ void *caMainLoop(void *param) registerFilters(); printf("Done with parsing, CA thread continuing\n"); setCAThreadState(CA_STATE_RUNNING); + return nullptr; } /// Routine used to read in all channels to monitor from an INI file and create local connections @@ -2864,7 +2961,7 @@ void setupCASDF() caTable[ii].datatype = SDF_NUM; caTable[ii].data.chval = 0.0; caTable[ii].connected = 0; - caTable[ii].chanid = (void *)-1; + caTable[ii].chanid = nullptr; caTable[ii].mod_time = (time_t)0; caTable[ii].chanIndex = ii; @@ -2872,7 +2969,7 @@ void setupCASDF() caConnTable[ii].datatype = SDF_NUM; caConnTable[ii].data.chval = 0.0; caConnTable[ii].connected = 0; - caConnTable[ii].chanid = (void *)-1; + caConnTable[ii].chanid = nullptr; caConnTable[ii].mod_time = (time_t)0; caConnTable[ii].chanIndex = 0; @@ -2880,7 +2977,7 @@ void setupCASDF() caConnEnumTable[ii].datatype = SDF_UNKNOWN; caConnEnumTable[ii].data.chval = 0.0; caConnEnumTable[ii].connected = 0; - caConnEnumTable[ii].chanid = (void *)-1; + caConnEnumTable[ii].chanid = nullptr; caConnEnumTable[ii].mod_time = (time_t)0; caConnEnumTable[ii].chanIndex = ii; @@ -2906,20 +3003,13 @@ void setupCASDF() bzero((void *)&(readErrTable[ii]), sizeof(readErrTable[ii])); } droppedPVCount = 0; - pthread_mutex_init(&caStateMutex, NULL); // FIXME check for errors - pthread_mutex_init(&caTableMutex, NULL); - pthread_mutex_init(&caEvidMutex, NULL); } /// Routine to tear-down mutexes and other resources used by the CA SDF system void cleanupCASDF() { - pthread_mutex_destroy(&caEvidMutex); - pthread_mutex_destroy(&caTableMutex); - pthread_mutex_destroy(&caStateMutex); } #else - void countDisconnectedChannels(int curTable) { } @@ -2932,17 +3022,18 @@ void updateStrVarSetPoint(int index) { } -int getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) { +bool getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) { + struct buffer { DBRtime double dval; } buffer; long options = DBR_TIME; long nvals = 1; - int result = 1; + bool result = false; if (dest && paddr) { - result = dbGetField(paddr, DBR_DOUBLE, &buffer, &options, &nvals, NULL); + result = dbGetField(paddr, DBR_DOUBLE, &buffer, &options, &nvals, NULL) == 0; *dest = buffer.dval; if (tp) { *tp = buffer.time.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH; @@ -2950,17 +3041,17 @@ int getDbValueDouble(ADDRESS *paddr,double *dest,time_t *tp) { } return result; } -int getDbValueLong(ADDRESS *paddr,unsigned int *dest,time_t *tp) { +bool getDbValueLong(ADDRESS *paddr,PV_INT_TYPE *dest,time_t *tp) { struct buffer { DBRtime unsigned int dval; } buffer; long options = DBR_TIME; long nvals = 1; - int result = 1; + bool result = false; if (dest && paddr) { - result = dbGetField(paddr, DBR_LONG, &buffer, &options, &nvals, NULL); + result = dbGetField(paddr, DBR_LONG, &buffer, &options, &nvals, NULL) == 0; *dest = buffer.dval; if (tp) { *tp = buffer.time.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH; @@ -2968,16 +3059,16 @@ int getDbValueLong(ADDRESS *paddr,unsigned int *dest,time_t *tp) { } return result; } -int getDbValueString(ADDRESS *paddr,char *dest, int max_len, time_t *tp) { +bool getDbValueString(ADDRESS *paddr,char *dest, int max_len, time_t *tp) { struct buffer { DBRtime char sval[128]; } strbuffer; long options = DBR_TIME; long nvals = 1; - int result = 1; + bool result = false; if (dest && paddr && (max_len > 0)) { - result = dbGetField(paddr,DBR_STRING,&strbuffer,&options,&nvals,NULL); + result = dbGetField(paddr,DBR_STRING,&strbuffer,&options,&nvals,NULL) == 0; strncpy(dest, strbuffer.sval, max_len); dest[max_len-1]='\0'; if (tp) { @@ -3025,9 +3116,9 @@ void dbDumpRecords(DBBASE *pdbbase, const char *pref) printf("\n Alias:%s\n",dbGetRecordName(pdbentry)); } else { //fprintf(stderr, "processing %s\n", dbGetRecordName(pdbentry)); - sprintf(cdTable[chNum].chname,"%s",dbGetRecordName(pdbentry)); + cdTable[chNum].chname = dbGetRecordName(pdbentry); // do not monitor the the SDF mask channels, they are part of this IOC - if (strncmp(cdTable[chNum].chname, mask_pref, pref_len)==0) { + if ( cdTable[chNum].chname == mask_pref ) { --cnt; continue; } @@ -3035,12 +3126,12 @@ void dbDumpRecords(DBBASE *pdbbase, const char *pref) cdTable[chNum].filterNum = -1; // Check if this is a filter module // If so, initialize parameters - if((strstr(cdTable[chNum].chname,"_SW1S") != NULL) && (strstr(cdTable[chNum].chname,"_SW1S.") == NULL)) + if((strstr(cdTable[chNum].chname.c_str(),"_SW1S") != NULL) && (strstr(cdTable[chNum].chname.c_str(),"_SW1S.") == NULL)) { cdTable[chNum].filterswitch = 1; fmNum ++; } - if((strstr(cdTable[chNum].chname,"_SW2S") != NULL) && (strstr(cdTable[chNum].chname,"_SW2S.") == NULL)) + if((strstr(cdTable[chNum].chname.c_str(),"_SW2S") != NULL) && (strstr(cdTable[chNum].chname.c_str(),"_SW2S.") == NULL)) { cdTable[chNum].filterswitch = 2; } @@ -3094,42 +3185,6 @@ void listLocalRecords(DBBASE *pdbbase) { /// Called on EPICS startup; This is generic EPICS provided function, modified for LIGO use. int main(int argc,char *argv[]) { - // Addresses for SDF EPICS records. - dbAddr reload_addr; - dbAddr sdfname_addr; - dbAddr reloadstat_addr; - dbAddr loadedfile_addr; - dbAddr sperroraddr; - dbAddr alrmchcountaddr; -#ifdef CA_SDF - dbAddr disconnectcountaddr; - dbAddr droppedcountaddr; -#endif - dbAddr filesetcntaddr; - dbAddr fulldbcntaddr; - dbAddr monchancntaddr; - dbAddr tablesortreqaddr; - dbAddr wcreqaddr; - dbAddr chnotfoundaddr; - dbAddr chnotinitaddr; - dbAddr sorttableentriesaddr; - dbAddr monflagaddr; - dbAddr reloadtimeaddr; - dbAddr rcgversion_addr; - dbAddr msgstraddr; - dbAddr edbloadedaddr; - dbAddr savecmdaddr; - dbAddr saveasaddr; - dbAddr wcstringaddr; - dbAddr savetypeaddr; - dbAddr saveoptsaddr; - dbAddr savefileaddr; - dbAddr savetimeaddr; - dbAddr daqmsgaddr; - dbAddr coeffmsgaddr; - dbAddr resetoneaddr; - dbAddr selectaddr[4]; - dbAddr pagelockaddr[3]; // why is this an array of 3. It looks like we can make this a single value #ifdef CA_SDF // CA_SDF does not do a partial load on startup. int sdfReq = SDF_READ_ONLY; @@ -3143,8 +3198,8 @@ int main(int argc,char *argv[]) long nvals = 1; int rdstatus = 0; int burtstatus = 0; - char loadedSdf[256]; - char sdffileloaded[256]; + embedded::fixed_string<256> loadedSdf{}; + embedded::fixed_string<256> sdffileloaded{}; int sperror = 0; int noMon = 0; int noInit = 0; @@ -3157,9 +3212,9 @@ int main(int argc,char *argv[]) int monFlag = 0; int sdfSaveReq = 0; int saveType = 0; - char saveTypeString[64]; + embedded::fixed_string<256> saveTypeString{}; int saveOpts = 0; - char saveOptsString[64]; + embedded::fixed_string<256> saveOptsString{}; int fivesectimer = 0; long daqFileCrc = 0; long coeffFileCrc = 0; @@ -3183,18 +3238,13 @@ int main(int argc,char *argv[]) int selectAll = 0; int freezeTable = 0; int zero = 0; - char backupName[64]; + embedded::fixed_string<256> backupName{}; int lastTable = 0; int cdSort = 0; int diffCnt = 0; - char errMsg[128]; + char errMsg[128]; - loadedSdf[0] = '\0'; - sdffileloaded[0] = '\0'; tsrString[0] = '\0'; - saveTypeString[0] = '\0'; - saveOptsString[0] = '\0'; - backupName[0] = '\0'; errMsg[0] = '\0'; if(argc>=2) { @@ -3224,24 +3274,23 @@ int main(int argc,char *argv[]) char *fotonDiffFile = getenv("FOTON_DIFF_FILE"); char *logdir = getenv("LOG_DIR"); char myDiffCmd[256]; - SET_ERR_TABLE *currentTable = 0; - int currentTableCnt = 0; + table_range currentTable; + if(stat(logdir, &st) == -1) mkdir(logdir,0777); // strcat(sdf,"_safe"); - char sdf[256]; - char sdfile[256]; - char sdalarmfile[256]; - char bufile[256]; - char saveasfilename[128]; - char wcstring[64]; - - strncpy(sdf, sdfenv, sizeof(sdf)); - sdf[sizeof(sdf)-1] = '\0'; + embedded::fixed_string<256> sdf; + embedded::fixed_string<256> sdfile; + embedded::fixed_string<256> sdalarmfile; + embedded::fixed_string<256> bufile; + embedded::fixed_string<256> saveasfilename; + embedded::fixed_string<256> wcstring; + + sdf = sdfenv; printf("My prefix is %s\n",pref); - sprintf(sdfile, "%s%s%s", sdfDir, sdf,".snap"); // Initialize with BURT_safe.snap - sprintf(bufile, "%s%s", sdfDir, "fec.snap"); // Initialize table dump file + sdfile.printf("%s%s%s", sdfDir, sdf.c_str(), ".snap"); // Initialize with BURT_safe.snap + bufile.printf("%s%s", sdfDir, "fec.snap"); // Initialize table dump file sprintf(logfilename, "%s%s", logdir, "/ioc.log"); // Initialize table dump file printf("SDF FILE = %s\n",sdfile); printf("CURRENt FILE = %s\n",bufile); @@ -3253,136 +3302,76 @@ int main(int argc,char *argv[]) int myreleased = RCG_VERSION_REL; double myversion; - SETUP; - // listLocalRecords(*iocshPpdbbase); - myversion = majorversion + 0.1 * subversion1 + 0.01 * subversion2; - if(!myreleased) myversion *= -1.0; - char rcgversionname[256]; sprintf(rcgversionname, "%s_%s", pref, "RCG_VERSION"); // Set RCG Version EPICS - status = dbNameToAddr(rcgversionname,&rcgversion_addr); - if( status ) - { - fprintf(stderr, "*\nFATAL: The address for the %s variable could not be " - "located in the EPICS database. \n", rcgversionname); - fprintf(stderr, "Failure with first lookup in the EPICS database, " - "this usually signifies an error with the .cmd passed to iocsh(), " - "or a misconfigured EPCS environment.\n*\n"); - return -1; - } - status = dbPutField(&rcgversion_addr,DBR_DOUBLE,&myversion,1); + auto common_name = [pref](const char* name) -> epics::channel_name { + return epics::make_name(pref, name); + }; - // Create BURT/SDF EPICS channel names - char reloadChan[256]; sprintf(reloadChan, "%s_%s", pref, "SDF_RELOAD"); // Request to load new BURT - // Set request to load safe.snap on startup - status = dbNameToAddr(reloadChan,&reload_addr); - status = dbPutField(&reload_addr,DBR_LONG,&sdfReq,1); // Init request for startup. - char reloadStat[256]; sprintf(reloadStat, "%s_%s", pref, "SDF_RELOAD_STATUS"); // Status of last reload - status = dbNameToAddr(reloadStat,&reloadstat_addr); - status = dbPutField(&reloadstat_addr,DBR_LONG,&rdstatus,1); // Init to zero. + SETUP(); +#ifndef USE_SYSTEM_TIME + timechannel = std::make_unique< epics::DBEntry< epics::PVType::String > >( common_name("TIME_STRING") ); +#endif + // listLocalRecords(*iocshPpdbbase); + myversion = majorversion + 0.1 * subversion1 + 0.01 * subversion2; + if(!myreleased) + { + myversion *= -1.0; + } + epics::DBEntry< epics::PVType::Float64 > rcgversion_channel(common_name("RCG_VERSION"), myversion); + rcgversion_channel.set( myversion ); - char sdfFileName[256]; sprintf(sdfFileName, "%s_%s", pref, "SDF_NAME"); // Name of file to load next request - // Initialize BURT file to be loaded next request = safe.snap - status = dbNameToAddr(sdfFileName,&sdfname_addr); // Get Address - status = dbPutField(&sdfname_addr,DBR_STRING,sdf,1); // Init to safe.snap - char loadedFile[256]; sprintf(loadedFile, "%s_%s", pref, "SDF_LOADED"); // Name of file presently loaded - status = dbNameToAddr(loadedFile,&loadedfile_addr); //Get Address + // Create BURT/SDF EPICS channel names - char edbloadedFile[256]; sprintf(edbloadedFile, "%s_%s", pref, "SDF_LOADED_EDB"); // Name of file presently loaded - status = dbNameToAddr(edbloadedFile,&edbloadedaddr); // Get Address + epics::DBEntry< epics::PVType::Int32 > reload_channel(common_name("SDF_RELOAD"), sdfReq); // Init request for startup. + epics::DBEntry< epics::PVType::Int32 > reload_stat( common_name("SDF_RELOAD_STATUS"), rdstatus); // Init to zero - char speStat[256]; sprintf(speStat, "%s_%s", pref, "SDF_DIFF_CNT"); // Setpoint diff counter - status = dbNameToAddr(speStat,&sperroraddr); // Get Address - status = dbPutField(&sperroraddr,DBR_LONG,&sperror,1); // Init to zero. + // Initialize BURT file to be loaded next request = safe.snap + epics::DBEntry<epics::PVType::String> sdfname_channel(common_name("SDF_NAME"), sdf); - char spaStat[256]; sprintf(spaStat, "%s_%s", pref, "SDF_ALARM_CNT"); // Number of alarm settings in a BURT file. - status = dbNameToAddr(spaStat,&alrmchcountaddr); // Get Address - status = dbPutField(&alrmchcountaddr,DBR_LONG,&alarmCnt,1); // Init to zero. + epics::DBEntry<epics::PVType::String> loadedfile_channel(common_name("SDF_LOADED")); - char fcc[256]; sprintf(fcc, "%s_%s", pref, "SDF_FULL_CNT"); // Number of setting channels in EPICS db - status = dbNameToAddr(fcc,&fulldbcntaddr); + epics::DBEntry< epics::PVType::String > edbloaded_channel(common_name("SDF_LOADED_EDB")); // Name of file presently loaded - char fsc[256]; sprintf(fsc, "%s_%s", pref, "SDF_FILE_SET_CNT"); // Number of settings inBURT file - status = dbNameToAddr(fsc,&filesetcntaddr); + epics::DBEntry< epics::PVType::Int32 > sperror_channel(common_name("SDF_DIFF_CNT"), sperror); // Setpoint diff counter + epics::DBEntry< epics::PVType::Int32 > alrmchcount_channel(common_name("SDF_ALARM_CNT"), alarmCnt); // Number of alarm settings in a BURT file. + epics::DBEntry< epics::PVType::Int32 > fulldbcnt_channel(common_name("SDF_FULL_CNT"));// Number of setting channels in EPICS db - char mcc[256]; sprintf(mcc, "%s_%s", pref, "SDF_UNMON_CNT"); // Number of settings NOT being monitored. - status = dbNameToAddr(mcc,&monchancntaddr); + epics::DBEntry< epics::PVType::Int32 > filesetcnt_channel(common_name("SDF_FILE_SET_CNT")); // Number of settings inBURT file + epics::DBEntry< epics::PVType::Int32 > unmonchancnt_channel(common_name("SDF_UNMON_CNT")); // Number of settings NOT being monitored. #ifdef CA_SDF - char dsc[256]; sprintf(dsc, "%s_%s", pref, "SDF_DISCONNECTED_CNT"); - status = dbNameToAddr(dsc,&disconnectcountaddr); + epics::DBEntry< epics::PVType::Int32 > disconnectcount_channel(common_name("SDF_DISCONNECTED_CNT")); - char dpdc[256]; sprintf(dpdc, "%s_%s", pref, "SDF_DROPPED_CNT"); - status = dbNameToAddr(dpdc,&droppedcountaddr); + epics::DBEntry< epics::PVType::Int32 > droppedcount_channel(common_name("SDF_DROPPED_CNT")); #endif - char tsrname[256]; sprintf(tsrname, "%s_%s", pref, "SDF_SORT"); // SDF Table sorting request - status = dbNameToAddr(tsrname,&tablesortreqaddr); - - char wcname[256]; sprintf(wcname, "%s_%s", pref, "SDF_WILDCARD"); // SDF Table sorting request - status = dbNameToAddr(wcname,&wcreqaddr); - status = dbPutField(&wcreqaddr,DBR_LONG,&zero,1); // Init to zero. - - char cnfname[256]; sprintf(cnfname, "%s_%s", pref, "SDF_DROP_CNT"); // Number of channels not found. - status = dbNameToAddr(cnfname,&chnotfoundaddr); + epics::DBEntry< epics::PVType::UInt16 > tablesortreq_channel(common_name("SDF_SORT"));// SDF Table sorting request + epics::DBEntry< epics::PVType::Int32 > wcreq_channel(common_name("SDF_WILDCARD"), 0); // SDF Table sorting request + epics::DBEntry< epics::PVType::Int32 > chnotfound_channel(common_name("SDF_DROP_CNT")); // Number of channels not found. + epics::DBEntry< epics::PVType::Int32 > chnotinit_channel(common_name("SDF_UNINIT_CNT"));// Number of channels not initialized. + epics::DBEntry< epics::PVType::Int32 > sorttableentries_channel(common_name("SDF_TABLE_ENTRIES")); // Number of entries in an SDF reporting table. + epics::DBEntry< epics::PVType::Int32 > monflag_channel(common_name("SDF_MON_ALL"), rdstatus); // Request to monitor all channels. + epics::DBEntry< epics::PVType::Int32 > savecmd_channel(common_name("SDF_SAVE_CMD"), rdstatus); // SDF Save command. + epics::DBEntry< epics::PVType::Int32 > pagelock_channel(common_name("SDF_TABLE_LOCK"), freezeTable); // SDF Save command. - char cniname[256]; sprintf(cniname, "%s_%s", pref, "SDF_UNINIT_CNT"); // Number of channels not initialized. - status = dbNameToAddr(cniname,&chnotinitaddr); - - char stename[256]; sprintf(stename, "%s_%s", pref, "SDF_TABLE_ENTRIES"); // Number of entries in an SDF reporting table. - status = dbNameToAddr(stename,&sorttableentriesaddr); - - char monflagname[256]; sprintf(monflagname, "%s_%s", pref, "SDF_MON_ALL"); // Request to monitor all channels. - status = dbNameToAddr(monflagname,&monflagaddr); // Get Address. - status = dbPutField(&monflagaddr,DBR_LONG,&rdstatus,1); // Init to zero. - - char savecmdname[256]; sprintf(savecmdname, "%s_%s", pref, "SDF_SAVE_CMD"); // SDF Save command. - status = dbNameToAddr(savecmdname,&savecmdaddr); // Get Address. - status = dbPutField(&savecmdaddr,DBR_LONG,&rdstatus,1); // Init to zero. - - char pagelockname[128]; sprintf(pagelockname, "%s_%s", pref, "SDF_TABLE_LOCK"); // SDF Save command. - status = dbNameToAddr(pagelockname,&(pagelockaddr[0])); // Get Address. - status = dbPutField(&(pagelockaddr[0]),DBR_LONG,&freezeTable,1); // Init to zero. - - char saveasname[256]; sprintf(saveasname, "%s_%s", pref, "SDF_SAVE_AS_NAME"); // SDF Save as file name. // Clear out the save as file name request - status = dbNameToAddr(saveasname,&saveasaddr); // Get Address. - status = dbPutField(&saveasaddr,DBR_STRING,"default",1); // Set as dummy 'default' - - char wcstringname[256]; sprintf(wcstringname, "%s_%s", pref, "SDF_WC_STR"); // SDF Save as file name. - status = dbNameToAddr(wcstringname,&wcstringaddr); // Get Address. - status = dbPutField(&wcstringaddr,DBR_STRING,"",1); // Set as dummy 'default' - - char savetypename[256]; sprintf(savetypename, "%s_%s", pref, "SDF_SAVE_TYPE"); // SDF Save file type. - status = dbNameToAddr(savetypename,&savetypeaddr); - - char saveoptsname[256]; sprintf(saveoptsname, "%s_%s", pref, "SDF_SAVE_OPTS"); // SDF Save file options. - status = dbNameToAddr(saveoptsname,&saveoptsaddr); + epics::DBEntry< epics::PVType::String > saveas_channel(common_name("SDF_SAVE_AS_NAME"), epics::channel_name("default")); // Set as dummy 'default' - char savefilename[256]; sprintf(savefilename, "%s_%s", pref, "SDF_SAVE_FILE"); // SDF Name of last file saved. - status = dbNameToAddr(savefilename,&savefileaddr); - status = dbPutField(&savefileaddr,DBR_STRING,"",1); + epics::DBEntry< epics::PVType::String > wcstring_channel(common_name("SDF_WC_STR"), "");// SDF Save as file name. - char savetimename[256]; sprintf(savetimename, "%s_%s", pref, "SDF_SAVE_TIME"); // SDF Time of last file save. - status = dbNameToAddr(savetimename,&savetimeaddr); - status = dbPutField(&savetimeaddr,DBR_STRING,"",1); + epics::DBEntry< epics::PVType::String > savetype_channel( common_name("SDF_SAVE_TYPE"));// SDF Save file type. + epics::DBEntry< epics::PVType::String > saveopts_channel( common_name("SDF_SAVE_OPTS"));// SDF Save file options. - char moddaqfilemsg[256]; sprintf(moddaqfilemsg, "%s_%s", pref, "MSGDAQ"); // Record to write if DAQ file changed. - status = dbNameToAddr(moddaqfilemsg,&daqmsgaddr); - - char modcoefffilemsg[128]; sprintf(modcoefffilemsg, "%s_%s", pref, "MSG2"); // Record to write if Coeff file changed. - status = dbNameToAddr(modcoefffilemsg,&coeffmsgaddr); + epics::DBEntry<epics::PVType::String> savefile_channel(common_name("SDF_SAVE_FILE"), ""); // SDF Name of last file saved. + epics::DBEntry<epics::PVType::String> savetime_channel( common_name("SDF_SAVE_TIME"), ""); // SDF Time of last file save. + epics::DBEntry< epics::PVType::String > daqmsg_channel(common_name("MSGDAQ"));// Record to write if DAQ file changed. + epics::DBEntry< epics::PVType::String > coeffmsg_channel(common_name("MSG2")); // Record to write if Coeff file changed. + epics::DBEntry< epics::PVType::String > msgstr_channel(common_name("SDF_MSG_STR"), "");// SDF Time of last file save. char msgstrname[128]; sprintf(msgstrname, "%s_%s", pref, "SDF_MSG_STR"); // SDF Time of last file save. - status = dbNameToAddr(msgstrname,&msgstraddr); - status = dbPutField(&msgstraddr,DBR_STRING,"",1); -#ifndef USE_SYSTEM_TIME - sprintf(timechannel,"%s_%s", pref, "TIME_STRING"); - // printf("timechannel = %s\n",timechannel); -#endif - sprintf(reloadtimechannel,"%s_%s", pref, "SDF_RELOAD_TIME"); // Time of last BURT reload - status = dbNameToAddr(reloadtimechannel,&reloadtimeaddr); + epics::DBEntry<epics::PVType::String> reloadtime_channel(common_name("SDF_RELOAD_TIME"), ""); // Time of last BURT reload int pageNum = 0; int pageNumSet = 0; @@ -3391,23 +3380,14 @@ int main(int argc,char *argv[]) status = dbNameToAddr(pagereqname,&pagereqaddr); // Get Address. unsigned int resetNum = 0; - char resetOneName[256]; sprintf(resetOneName, "%s_%s", pref, "SDF_RESET_CHAN"); // SDF reset one value. - status = dbNameToAddr(resetOneName,&resetoneaddr); - status = dbPutField(&resetoneaddr,DBR_LONG,&resetNum,1); - - char selectName[256]; - sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM0"); // SDF reset one value. - status = dbNameToAddr(selectName,&selectaddr[0]); - status = dbPutField(&selectaddr[0],DBR_LONG,&resetNum,1); - sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM1"); // SDF reset one value. - status = dbNameToAddr(selectName,&selectaddr[1]); - status = dbPutField(&selectaddr[1],DBR_LONG,&resetNum,1); - sprintf(selectName, "%s_%s", pref, "SDF_SELECT_SUM2"); // SDF reset one value. - status = dbNameToAddr(selectName,&selectaddr[2]); - status = dbPutField(&selectaddr[2],DBR_LONG,&resetNum,1); - sprintf(selectName, "%s_%s", pref, "SDF_SELECT_ALL"); // SDF reset one value. - status = dbNameToAddr(selectName,&selectaddr[3]); - status = dbPutField(&selectaddr[3],DBR_LONG,&selectAll,1); + epics::DBEntry< epics::PVType::UInt32 > resetone_channel(common_name("SDF_RESET_CHAN"), resetNum); // SDF reset one value. + + std::array< epics::DBEntry< epics::PVType::Int32 >, 4> select_channel{ + epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM0"), 0), + epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM1"), 0), + epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SUM2"), 0), + epics::DBEntry< epics::PVType::Int32 >(common_name("SDF_SELECT_SELECT_ALL"), selectAll), + }; dbAddr confirmwordaddr; char confirmwordname[64]; @@ -3420,7 +3400,7 @@ int main(int argc,char *argv[]) char *buffer=0; char fname[]="/monitor.req"; int len = strlen(sdfDir)+strlen(fname)+1; - buffer = malloc(len); + buffer = (char*)malloc(len); if (!buffer) { fprintf(stderr, "Unable to allocate memory to hold the path to monitor.req, aborting!"); exit(1); @@ -3451,10 +3431,10 @@ int main(int argc,char *argv[]) status = checkFileMod( coeffFile, &coeffFileMt, 1 ); status = checkFileMod( fotonFile, &fotonFileMt, 1 ); - reportSetErrors(pref, 0,setErrTable,0,1); + reportSetErrors(pref,setErrTable,0,1); sleep(1); // Need to wait before first restore to allow sequencers time to do their initialization. - cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring,1,&status); + cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring.c_str(),1,&status); // Start Infinite Loop ******************************************************************************* for(;;) { @@ -3465,119 +3445,142 @@ int main(int argc,char *argv[]) // see bug 965 ca_poll(); syncCAConnections(NULL); - status = dbPutField(&disconnectcountaddr,DBR_LONG,&chDisconnectedCount,1); - status = dbPutField(&droppedcountaddr,DBR_LONG,&droppedPVCount,1); + disconnectcount_channel.set(chDisconnectedCount); + droppedcount_channel.set(droppedPVCount); } #endif fivesectimer = (fivesectimer + 1) % 50; // Increment 5 second timer for triggering CRC checks. // Check for reload request - status = dbGetField(&reload_addr,DBR_LONG,&request,&ropts,&nvals,NULL); + reload_channel.get(request); + // Get BURT Read File Name - status = dbNameToAddr(sdfFileName,&sdfname_addr); - status = dbGetField(&sdfname_addr,DBR_STRING,sdf,&ropts,&nvals,NULL); + sdfname_channel.get(sdf); + ////status = dbNameToAddr(sdfFileName,&sdfname_addr); + ////status = dbGetField(&sdfname_addr,DBR_STRING,sdf,&ropts,&nvals,NULL); // Create full filename including directory and extension. - sprintf(sdfile, "%s%s%s", sdfDir, sdf,".snap"); - sprintf(sdalarmfile, "%s%s%s", sdfDir, sdf,"_alarms.snap"); + sdfile.printf("%s%s%s", sdfDir, sdf.c_str(), ".snap"); + sdalarmfile.printf("%s%s%s", sdfDir, sdf.c_str(), "_alarms.snap"); + // Check if file name != to one presently loaded - if(strcmp(sdf,loadedSdf) != 0) burtstatus |= 1; - else burtstatus &= ~(1); - if(burtstatus == 0) - status = dbPutField(&msgstraddr,DBR_STRING," ",1); - if(burtstatus == 1) - status = dbPutField(&msgstraddr,DBR_STRING,"New SDF File Pending",1); - if(burtstatus & 2) - status = dbPutField(&msgstraddr,DBR_STRING,"Read Error: Errant line(s) in file",1); - if(burtstatus & 4) - status = dbPutField(&msgstraddr,DBR_STRING,"Read Error: File Not Found",1); + + if (sdf != loadedSdf) { + burtstatus |= 1; + } + else + { + burtstatus &= ~(1); + } + //if(strcmp(sdf,loadedSdf) != 0) burtstatus |= 1; + //else burtstatus &= ~(1); + + if(burtstatus == 0) { + msgstr_channel.set(" "); + } + if(burtstatus == 1) { + msgstr_channel.set("New SDF File Pending"); + } + if(burtstatus & 2) { + msgstr_channel.set("Read Error: Errant line(s) in file"); + } + if(burtstatus & 4) { + msgstr_channel.set("Read Error: File Not Found"); + } if(request != 0) { // If there is a read file request, then: - status = dbPutField(&reload_addr,DBR_LONG,&ropts,1); // Clear the read request. + reload_channel.set(ropts); reqValid = 1; if(reqValid) { - rdstatus = readConfig(pref,sdfile,request,sdalarmfile); + rdstatus = readConfig(pref,sdfile.c_str(),request,sdalarmfile.c_str()); resyncFMArrays(filterMasks,fmMaskChan); if (rdstatus) burtstatus |= rdstatus; else burtstatus &= ~(6); if(burtstatus < 4) { switch (request){ case SDF_LOAD_DB_ONLY: - strcpy(loadedSdf,sdf); - status = dbPutField(&edbloadedaddr,DBR_STRING,loadedSdf,1); + loadedSdf = sdf; + edbloaded_channel.set(loadedSdf); break; case SDF_RESET: break; case SDF_LOAD_PARTIAL: - strcpy(loadedSdf,sdf); - status = dbPutField(&loadedfile_addr,DBR_STRING,loadedSdf,1); - status = dbPutField(&edbloadedaddr,DBR_STRING,loadedSdf,1); + loadedSdf = sdf; + loadedfile_channel.set(loadedSdf); + edbloaded_channel.set(loadedSdf); break; case SDF_READ_ONLY: - strcpy(loadedSdf,sdf); - status = dbPutField(&loadedfile_addr,DBR_STRING,loadedSdf,1); + loadedSdf = sdf; + loadedfile_channel.set(loadedSdf); break; default: logFileEntry("Invalid READ Request"); reqValid = 0; break; } - status = dbPutField(&reloadstat_addr,DBR_LONG,&rdstatus,1); + reload_stat.set(rdstatus); // Get the file CRC for later checking if file changed. - sprintf(sdffileloaded, "%s%s%s", sdfDir, loadedSdf,".snap"); - sdfFileCrc = checkFileCrc(sdffileloaded); + sdffileloaded.printf("%s%s%s", sdfDir, loadedSdf.c_str(), ".snap"); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); // Get the file mod time - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); // Calculate and report the number of settings in the BURT file. setChans = chNumP - alarmCnt; - status = dbPutField(&filesetcntaddr,DBR_LONG,&setChans,1); + filesetcnt_channel.set(setChans); // Report number of settings in the main table. setChans = chNum - fmNum; - status = dbPutField(&fulldbcntaddr,DBR_LONG,&setChans,1); + fulldbcnt_channel.set(setChans); // Sort channels for data reporting via the MEDM table. - getEpicsSettings(chNum,NULL); + getEpicsSettings(); noMon = createSortTableEntries(chNum,0,"",&noInit,NULL); // Calculate and report number of channels NOT being monitored. - status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1); - status = dbPutField(&alrmchcountaddr,DBR_LONG,&alarmCnt,1); + unmonchancnt_channel.set(chNotMon); + alrmchcount_channel.set(alarmCnt); // Report number of channels in BURT file that are not in local database. - status = dbPutField(&chnotfoundaddr,DBR_LONG,&chNotFound,1); + chnotfound_channel.set(chNotFound); // Report number of channels that have not been initialized via a BURT read. - status = dbPutField(&chnotinitaddr,DBR_LONG,&chNotInit,1); + chnotinit_channel.set(chNotInit); // Write out local monitoring table as snap file. - status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable); + writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable); } } } - status = dbPutField(&reloadstat_addr,DBR_LONG,&burtstatus,1); + reload_stat.set(burtstatus); + // sleep(1); // Check for SAVE requests - status = dbGetField(&savecmdaddr,DBR_LONG,&sdfSaveReq,&ropts,&nvals,NULL); + savecmd_channel.get(sdfSaveReq); if(sdfSaveReq) // If there is a SAVE file request, then: { // Clear the save file request - status = dbPutField(&savecmdaddr,DBR_LONG,&ropts,1); + savecmd_channel.set(ropts); // Determine file type - status = dbGetField(&savetypeaddr,DBR_STRING,saveTypeString,&ropts,&nvals,NULL); + savetype_channel.get(saveTypeString); saveType = 0; - if(strcmp(saveTypeString,"TABLE TO FILE") == 0) saveType = SAVE_TABLE_AS_SDF; - if(strcmp(saveTypeString,"EPICS DB TO FILE") == 0) saveType = SAVE_EPICS_AS_SDF; + if(saveTypeString == "TABLE TO FILE") { + saveType = SAVE_TABLE_AS_SDF; + } + if(saveTypeString == "EPICS DB TO FILE") { + saveType = SAVE_EPICS_AS_SDF; + } // Determine file options saveOpts = 0; - status = dbGetField(&saveoptsaddr,DBR_STRING,saveOptsString,&ropts,&nvals,NULL); - if(strcmp(saveOptsString,"TIME NOW") == 0) saveOpts = SAVE_TIME_NOW; - if(strcmp(saveOptsString,"OVERWRITE") == 0) saveOpts = SAVE_OVERWRITE; - if(strcmp(saveOptsString,"SAVE AS") == 0) saveOpts = SAVE_AS; + saveopts_channel.get(saveOptsString); + if(saveOptsString == "TIME NOW") saveOpts = SAVE_TIME_NOW; + if(saveOptsString == "OVERWRITE") saveOpts = SAVE_OVERWRITE; + if(saveOptsString == "SAVE AS") saveOpts = SAVE_AS; // Determine if request is valid. if(saveType && saveOpts) { // Get saveas filename - status = dbGetField(&saveasaddr,DBR_STRING,saveasfilename,&ropts,&nvals,NULL); - if(saveOpts == SAVE_OVERWRITE) - savesdffile(saveType,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,loadedSdf,savefileaddr,savetimeaddr,reloadtimeaddr); + saveas_channel.get(saveasfilename); + if(saveOpts == SAVE_OVERWRITE) { + savesdffile(saveType, SAVE_TIME_NOW, sdfDir, modelname, sdfile.c_str(), saveasfilename.c_str(), loadedSdf.c_str(), + savefile_channel, savetime_channel, reloadtime_channel); + } // Save the file - savesdffile(saveType,saveOpts,sdfDir,modelname,sdfile,saveasfilename,loadedSdf,savefileaddr,savetimeaddr,reloadtimeaddr); + savesdffile(saveType,saveOpts,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),loadedSdf.c_str(),savefile_channel,savetime_channel,reloadtime_channel); if(saveOpts == SAVE_OVERWRITE) { - sdfFileCrc = checkFileCrc(sdffileloaded); - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); } } else { logFileEntry("Invalid SAVE File Request"); @@ -3585,20 +3588,20 @@ int main(int argc,char *argv[]) } // Check present settings vs BURT settings and report diffs. // Check if MON ALL CHANNELS is set - status = dbGetField(&monflagaddr,DBR_LONG,&monFlag,&ropts,&nvals,NULL); - status = dbGetField(&wcstringaddr,DBR_STRING,wcstring,&ropts,&nvals,NULL); - status = dbGetField(&wcstringaddr,DBR_STRING,saveasfilename,&ropts,&nvals,NULL); + monflag_channel.get(monFlag); + wcstring_channel.get(wcstring); + saveasfilename = wcstring; // Call the diff checking function. if(!freezeTable) - sperror = spChecker(monFlag,setErrTable,wcVal,wcstring,0,&diffCnt); + sperror = spChecker(monFlag,setErrTable,wcVal,wcstring.c_str(),0,&diffCnt); // Report number of diffs found. - status = dbPutField(&sperroraddr,DBR_LONG,&diffCnt,1); + sperror_channel.set(diffCnt); // Table sorting and presentation - status = dbGetField(&tablesortreqaddr,DBR_USHORT,&tsrVal,&ropts,&nvals,NULL); - status = dbGetField(&wcreqaddr,DBR_USHORT,&wcVal,&ropts,&nvals,NULL); + tablesortreq_channel.set(tsrVal); + wcreq_channel.set(wcVal); status = dbGetField(&pagereqaddr,DBR_LONG,&pageNumSet,&ropts,&nvals,NULL); status = dbGetField(&confirmwordaddr,DBR_LONG,&confirmVal,&ropts,&nvals,NULL); - status = dbGetField(&selectaddr[3],DBR_LONG,&selectAll,&ropts,&nvals,NULL); + select_channel[3].get(selectAll); if(pageNumSet != 0) { pageNum += pageNumSet; if(pageNum < 0) pageNum = 0; @@ -3609,237 +3612,232 @@ int main(int argc,char *argv[]) case SDF_TABLE_DIFFS: // Need to clear selections when moving between tables. if(lastTable != SDF_TABLE_DIFFS) { - clearTableSelections(sperror,setErrTable, selectCounter); + clearTableSelections(setErrTable, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; } - pageDisp = reportSetErrors(pref, sperror,setErrTable,pageNum,1); - currentTable = setErrTable; - currentTableCnt = sperror; - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&sperror,1); - status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL); + pageDisp = reportSetErrors(pref, setErrTable,pageNum,1); + currentTable = make_range(setErrTable); + + sorttableentries_channel.set(sperror); + resetone_channel.get(resetNum); if(selectAll) { - setAllTableSelections(sperror,setErrTable, selectCounter,selectAll); + setAllTableSelections(make_range(setErrTable), selectCounter,selectAll); } if(resetNum) { - decodeChangeSelect(resetNum, pageDisp, sperror, setErrTable,selectCounter, NULL); + decodeChangeSelect(resetNum, pageDisp, make_range(setErrTable),selectCounter, NULL); } if(confirmVal) { - if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(sperror, setErrTable); + if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(setErrTable); if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) { // Save present table as timenow. - status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL); + loadedfile_channel.get(backupName); // printf("BACKING UP: %s\n",backupName); - savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName, - savefileaddr,savetimeaddr,reloadtimeaddr); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); // Overwrite the table with new values - status = modifyTable(sperror,setErrTable); + status = modifyTable(make_range(setErrTable)); // Overwrite file - savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename, - backupName,savefileaddr,savetimeaddr,reloadtimeaddr); - sdfFileCrc = checkFileCrc(sdffileloaded); - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); - status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); + writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable); } - clearTableSelections(sperror,setErrTable, selectCounter); + clearTableSelections(setErrTable, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1); - noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL); - status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1); + noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL); + unmonchancnt_channel.set(chNotMon); } lastTable = SDF_TABLE_DIFFS; break; case SDF_TABLE_NOT_FOUND: // Need to clear selections when moving between tables. if(lastTable != SDF_TABLE_NOT_FOUND) { - clearTableSelections(sperror,setErrTable, selectCounter); + clearTableSelections(setErrTable, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; } - pageDisp = reportSetErrors(pref, chNotFound,unknownChans,pageNum,1); - currentTable = unknownChans; - currentTableCnt = chNotFound; - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&chNotFound,1); + pageDisp = reportSetErrors(pref, unknownChans,pageNum,1); + currentTable = make_range(unknownChans); + + sorttableentries_channel.set(chNotFound); /*if (resetNum > 200) { - decodeChangeSelect(resetNum, pageDisp, chNotFound, unknownChans,selectCounter, NULL); + decodeChangeSelect(resetNum, pageDisp, make_range(unknownChans),selectCounter, NULL); }*/ lastTable = SDF_TABLE_NOT_FOUND; break; case SDF_TABLE_NOT_INIT: if(lastTable != SDF_TABLE_NOT_INIT) { - clearTableSelections(sperror,setErrTable, selectCounter); + clearTableSelections(setErrTable, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; } if (!freezeTable) - getEpicsSettings(chNum,NULL); - noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL); - pageDisp = reportSetErrors(pref, noInit, uninitChans,pageNum,1); - currentTable = uninitChans; - currentTableCnt = noInit; - status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL); + getEpicsSettings(); + noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL); + pageDisp = reportSetErrors(pref, uninitChans,pageNum,1); + currentTable = make_range(uninitChans); + resetone_channel.get(resetNum); if(selectAll == 2 || selectAll == 3) { - setAllTableSelections(noInit,uninitChans,selectCounter,selectAll); + setAllTableSelections(make_range(uninitChans),selectCounter,selectAll); if (selectAll == 3) - setAllTableSelections(noInit,uninitChans,selectCounter,2); + setAllTableSelections(make_range(uninitChans),selectCounter,2); } if(resetNum > 100) { - decodeChangeSelect(resetNum, pageDisp, noInit, uninitChans,selectCounter, changeSelectCB_uninit); + decodeChangeSelect(resetNum, pageDisp, make_range(uninitChans),selectCounter, changeSelectCB_uninit); } if(confirmVal) { - if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(noInit, uninitChans); + if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(uninitChans); if(selectCounter[1] && (confirmVal & 2)) { // Save present table as timenow. - status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL); + loadedfile_channel.get(backupName); // printf("BACKING UP: %s\n",backupName); - savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName, - savefileaddr,savetimeaddr,reloadtimeaddr); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),backupName.c_str(), + savefile_channel,savetime_channel,reloadtime_channel); // Overwrite the table with new values - status = modifyTable(noInit,uninitChans); + status = modifyTable(make_range(uninitChans)); // Overwrite file - savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename, - backupName,savefileaddr,savetimeaddr,reloadtimeaddr); - sdfFileCrc = checkFileCrc(sdffileloaded); - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); - status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); + writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable); } - clearTableSelections(chNotInit,uninitChans, selectCounter); + clearTableSelections(uninitChans, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1); } - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&noInit,1); - status = dbPutField(&chnotinitaddr,DBR_LONG,&chNotInit,1); + sorttableentries_channel.set(noInit); + chnotinit_channel.set(chNotInit); lastTable = SDF_TABLE_NOT_INIT; break; case SDF_TABLE_NOT_MONITORED: D("In not mon\n"); if(lastTable != SDF_TABLE_NOT_MONITORED) { - clearTableSelections(noMon,unMonChans, selectCounter); + clearTableSelections(unMonChans, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1); } if (!freezeTable) - getEpicsSettings(chNum,NULL); //timeTable); - noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL);//timeTable); - status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1); - pageDisp = reportSetErrors(pref, noMon, unMonChans,pageNum,1); - currentTable = unMonChans; - currentTableCnt = noMon; - status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL); + getEpicsSettings(); //timeTable); + noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL);//timeTable); + unmonchancnt_channel.set(chNotMon); + pageDisp = reportSetErrors(pref, unMonChans,pageNum,1); + currentTable = make_range(unMonChans); + resetone_channel.get(resetNum); if(selectAll) { - setAllTableSelections(noMon,unMonChans,selectCounter,selectAll); + setAllTableSelections(make_range(unMonChans),selectCounter,selectAll); } if(resetNum) { - decodeChangeSelect(resetNum, pageDisp, noMon, unMonChans,selectCounter, NULL); + decodeChangeSelect(resetNum, pageDisp, make_range(unMonChans),selectCounter, NULL); } if(confirmVal) { - if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(noMon, unMonChans); + if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(unMonChans); if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) { // Save present table as timenow. - status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL); + loadedfile_channel.get(backupName); // printf("BACKING UP: %s\n",backupName); - savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename,backupName, - savefileaddr,savetimeaddr,reloadtimeaddr); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(),backupName.c_str(), + savefile_channel,savetime_channel,reloadtime_channel); // Overwrite the table with new values - status = modifyTable(noMon,unMonChans); + status = modifyTable(make_range(unMonChans)); // Overwrite file - savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename, - backupName,savefileaddr,savetimeaddr,reloadtimeaddr); - sdfFileCrc = checkFileCrc(sdffileloaded); - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); - status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); + writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable); } // noMon = createSortTableEntries(chNum,wcVal,wcstring); - clearTableSelections(noMon,unMonChans, selectCounter); + clearTableSelections(unMonChans, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1); // Calculate and report number of channels NOT being monitored. } - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&noMon,1); + sorttableentries_channel.set(noMon); lastTable = SDF_TABLE_NOT_MONITORED; break; case SDF_TABLE_FULL: if(lastTable != SDF_TABLE_FULL) { - clearTableSelections(cdSort,cdTableList, selectCounter); + clearTableSelections(cdTableList, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; } if (!freezeTable) - cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring,1,&status); - pageDisp = reportSetErrors(pref, cdSort, cdTableList,pageNum,1); - currentTable = cdTableList; - currentTableCnt = cdSort; - status = dbGetField(&resetoneaddr,DBR_LONG,&resetNum,&ropts,&nvals,NULL); + cdSort = spChecker(monFlag,cdTableList,wcVal,wcstring.c_str(),1,&status); + pageDisp = reportSetErrors(pref, cdTableList,pageNum,1); + currentTable = make_range(cdTableList); + resetone_channel.get(resetNum); if(selectAll == 3) { - setAllTableSelections(cdSort,cdTableList,selectCounter,selectAll); + setAllTableSelections(make_range(cdTableList),selectCounter,selectAll); } if(resetNum) { - decodeChangeSelect(resetNum, pageDisp, cdSort, cdTableList,selectCounter, NULL); + decodeChangeSelect(resetNum, pageDisp, make_range(cdTableList),selectCounter, NULL); } if(confirmVal) { - if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(cdSort, cdTableList); + if(selectCounter[0] && (confirmVal & 2)) status = resetSelectedValues(cdTableList); if((selectCounter[1] || selectCounter[2]) && (confirmVal & 2)) { // Save present table as timenow. - status = dbGetField(&loadedfile_addr,DBR_STRING,backupName,&ropts,&nvals,NULL); + loadedfile_channel.get(backupName); // printf("BACKING UP: %s\n",backupName); - savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile,saveasfilename, - backupName,savefileaddr,savetimeaddr,reloadtimeaddr); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_TIME_NOW,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); // Overwrite the table with new values - status = modifyTable(cdSort,cdTableList); + status = modifyTable(make_range(cdTableList)); // Overwrite file - savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile,saveasfilename, - backupName,savefileaddr,savetimeaddr,reloadtimeaddr); - sdfFileCrc = checkFileCrc(sdffileloaded); - status = checkFileMod( sdffileloaded, &sdfFileMt, 1 ); - status = writeTable2File(sdfDir,bufile,SDF_WITH_INIT_FLAG,cdTable); + savesdffile(SAVE_TABLE_AS_SDF,SAVE_OVERWRITE,sdfDir,modelname,sdfile.c_str(),saveasfilename.c_str(), + backupName.c_str(),savefile_channel,savetime_channel,reloadtime_channel); + sdfFileCrc = checkFileCrc(sdffileloaded.c_str()); + status = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 1 ); + writeTable2File(sdfDir,bufile.c_str(),SDF_WITH_INIT_FLAG,cdTable); } - clearTableSelections(cdSort,cdTableList, selectCounter); + clearTableSelections(cdTableList, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; status = dbPutField(&confirmwordaddr,DBR_LONG,&confirmVal,1); - noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL); + noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL); // Calculate and report number of channels NOT being monitored. - status = dbPutField(&monchancntaddr,DBR_LONG,&chNotMon,1); + unmonchancnt_channel.set(chNotMon); } - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&cdSort,1); + sorttableentries_channel.set(cdSort); lastTable = SDF_TABLE_FULL; break; #ifdef CA_SDF case SDF_TABLE_DISCONNECTED: if(lastTable != SDF_TABLE_DISCONNECTED) { - clearTableSelections(cdSort,cdTableList, selectCounter); + clearTableSelections(cdTableList, selectCounter); resyncFMArrays(filterMasks,fmMaskChan); confirmVal = 0; } - noMon = createSortTableEntries(chNum,wcVal,wcstring,&noInit,NULL); - pageDisp = reportSetErrors(pref, chDisconnected, disconnectChans, pageNum,0); + noMon = createSortTableEntries(chNum,wcVal,wcstring.c_str(),&noInit,NULL); + pageDisp = reportSetErrors(pref, disconnectChans, pageNum,0); chDisconnectedCount = chDisconnected; - currentTable = disconnectChans; - currentTableCnt = chDisconnected; - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&chDisconnected, 1); + currentTable = make_range(disconnectChans); + sorttableentries_channel.set(chDisconnected); if (resetNum > 200) { - decodeChangeSelect(resetNum, pageDisp, chDisconnected, disconnectChans, selectCounter, NULL); + decodeChangeSelect(resetNum, pageDisp, make_range(disconnectChans), selectCounter, NULL); } break; #endif default: - pageDisp = reportSetErrors(pref, sperror,setErrTable,pageNum,1); - status = dbPutField(&sorttableentriesaddr,DBR_LONG,&sperror,1); - currentTable = setErrTable; - currentTableCnt = sperror; + pageDisp = reportSetErrors(pref, setErrTable,pageNum,1); + sorttableentries_channel.set(sperror); + currentTable = make_range(setErrTable); break; } if (selectAll) { selectAll = 0; - status = dbPutField(&selectaddr[3],DBR_LONG,&selectAll,1); + select_channel[3].set(selectAll); } if (resetNum) { resetNum = 0; - status = dbPutField(&resetoneaddr,DBR_LONG,&resetNum,1); + resetone_channel.set(resetNum); } if(pageDisp != pageNum) { pageNum = pageDisp; @@ -3847,11 +3845,11 @@ int main(int argc,char *argv[]) freezeTable = 0; for(ii=0;ii<3;ii++) { freezeTable += selectCounter[ii]; - status = dbPutField(&selectaddr[ii],DBR_LONG,&selectCounter[ii],1); + select_channel[ii].set(selectCounter[ii]); } - status = dbPutField(&(pagelockaddr[0]),DBR_LONG,&freezeTable,1); + pagelock_channel.set(freezeTable); - processFMChanCommands(filterMasks, fmMaskChan,fmMaskChanCtrl,selectCounter, currentTableCnt, currentTable); + processFMChanCommands(filterMasks, fmMaskChan,fmMaskChanCtrl,selectCounter, currentTable); // Check file CRCs every 5 seconds. // DAQ and COEFF file checking was moved from skeleton.st to here RCG V2.9. @@ -3863,7 +3861,7 @@ int main(int argc,char *argv[]) status = checkFileCrc(daqFile); if(status != daqFileCrc) { daqFileCrc = status; - status = dbPutField(&daqmsgaddr,DBR_STRING,modfilemsg,1); + daqmsg_channel.set(modfilemsg); logFileEntry("Detected Change to DAQ Config file."); } } @@ -3875,9 +3873,9 @@ int main(int argc,char *argv[]) coeffFileCrc = checkFileCrc(coeffFile); fotonFileCrc = checkFileCrc(fotonFile); if(fotonFileCrc != coeffFileCrc) { - status = dbPutField(&coeffmsgaddr,DBR_STRING,modfilemsg,1); + coeffmsg_channel.set(modfilemsg); } else { - status = dbPutField(&coeffmsgaddr,DBR_STRING,"",1); + coeffmsg_channel.set(""); } if(fotonFileCrc != prevFotonFileCrc || prevCoeffFileCrc != coeffFileCrc) { sprintf(myDiffCmd,"%s %s %s %s %s","diff",fotonFile,coeffFile," > ",fotonDiffFile); @@ -3887,19 +3885,18 @@ int main(int argc,char *argv[]) } } // Check SDF file modified - fm_flag = checkFileMod( sdffileloaded, &sdfFileMt, 0 ); + fm_flag = checkFileMod( sdffileloaded.c_str(), &sdfFileMt, 0 ); if ( fm_flag ) { - status = checkFileCrc(sdffileloaded); + status = checkFileCrc(sdffileloaded.c_str()); if(status == -1) { sdfFileCrc = status; logFileEntry("SDF file not found."); - dbPutField(&reloadtimeaddr,DBR_STRING,"File Not Found",1); - } + } if(status != sdfFileCrc) { sdfFileCrc = status; logFileEntry("Detected Change to SDF file."); - dbPutField(&reloadtimeaddr,DBR_STRING,modfilemsg,1); + reloadtime_channel.set(modfilemsg); } } } @@ -3908,6 +3905,6 @@ int main(int argc,char *argv[]) sleep(0xfffffff); } else iocsh(NULL); - CLEANUP; + CLEANUP(); return(0); } diff --git a/src/epics/seq/sdf_monitor.c b/src/epics/seq/sdf_monitor.c index 13d3cfe38c2635b9a50ee99c6d883b86ea95749f..cab7c70b588da56043a098d4ebe373d13825ea39 100644 --- a/src/epics/seq/sdf_monitor.c +++ b/src/epics/seq/sdf_monitor.c @@ -44,6 +44,7 @@ of this distribution. #include "cadef.h" #include "fb.h" #include "../../drv/gpstime/gpstime.h" +#include "util/user/check_file_crc.h" #define GSDF_MAX_CHANS 30000 // Gloabl variables **************************************************************************************** @@ -57,7 +58,6 @@ char statelogfilename[128]; unsigned char naughtyList[GSDF_MAX_CHANS][64]; // Function prototypes **************************************************************************************** -int checkFileCrc(char *); void getSdfTime(char *); void logFileEntry(char *); @@ -444,29 +444,6 @@ void gsdfInitialize(char *shmem_fname) } - -/// Common routine to check file CRC. -/// @param[in] *fName Name of file to check. -/// @return File CRC or -1 if file not found. -int checkFileCrc(char *fName) -{ -char buffer[256]; -FILE *pipePtr; -struct stat statBuf; -long chkSum = -99999; - strcpy(buffer, "cksum "); - strcat(buffer, fName); - if (!stat(fName, &statBuf) ) { - if ((pipePtr = popen(buffer, "r")) != NULL) { - fgets(buffer, 256, pipePtr); - pclose(pipePtr); - sscanf(buffer, "%ld", &chkSum); - } - return(chkSum); - } - return(-1); -} - /// Routine for reading GPS time from model EPICS record. /// @param[out] timestring Pointer to char string in which GPS time is to be written. void getSdfTime(char *timestring) diff --git a/src/epics/seq/simple_range.hh b/src/epics/seq/simple_range.hh new file mode 100644 index 0000000000000000000000000000000000000000..36cbb0aefb8bc0e0af4e61b4d3db54a1d575fec6 --- /dev/null +++ b/src/epics/seq/simple_range.hh @@ -0,0 +1,78 @@ +// +// Created by jonathan.hanks on 11/17/20. +// + +#ifndef DAQD_TRUNK_SIMPLE_RANGE_HH +#define DAQD_TRUNK_SIMPLE_RANGE_HH + +#include <type_traits> +#include <utility> + +namespace embedded +{ + /*! + * @brief a very simple range class, just tracks two iterators in one unit, [start, end) + * @tparam It the iterator type, which must be noexcept on copy and move + */ + template <typename It> + class range + { + static_assert(std::is_nothrow_copy_constructible<It>::value, "It must be no throw copy constructable"); + static_assert(std::is_nothrow_copy_assignable<It>::value, "It must be no throw copyable"); + static_assert(std::is_nothrow_move_constructible<It>::value, "It must be no throw move constructable"); + static_assert(std::is_nothrow_move_assignable<It>::value, "It must be no throw movable"); + public: + using iterator = It; + + range() noexcept: start_{}, end_{} {} + range(const range&) noexcept = default; + range(range&&) noexcept = default; + range(It it1, It it2) noexcept: start_{std::move(it1)}, end_{std::move(it2)} {} + range& operator=(const range&) noexcept = default; + range& operator=(range&&) noexcept = default; + + iterator + begin() noexcept + { + return start_; + } + + iterator + end() noexcept + { + return end_; + } + + const iterator + begin() const noexcept + { + return start_; + } + + const iterator + end() const noexcept + { + return end_; + } + + private: + It start_; + It end_; + }; + + /*! + * @brief Given two iterators of type It construct a range + * @tparam It + * @param start + * @param end + * @return the resulting range + */ + template <typename It> + range<It> + make_range(It start, It end) + { + return range<It>(std::move(start), std::move(end)); + } +} + +#endif //DAQD_TRUNK_SIMPLE_RANGE_HH diff --git a/src/epics/seq/test/test_fixed_size_string.cc b/src/epics/seq/test/test_fixed_size_string.cc new file mode 100644 index 0000000000000000000000000000000000000000..4d808ef4ad1be346f712a248b72131433f0062e4 --- /dev/null +++ b/src/epics/seq/test/test_fixed_size_string.cc @@ -0,0 +1,378 @@ +// +// Created by jonathan.hanks on 8/16/18. +// +#include "catch.hpp" + +#include "fixed_size_string.hh" + +#include <cstring> +#include <type_traits> +#include <vector> + +static_assert( + std::is_trivially_copyable< embedded::fixed_string< 64 > >::value, + "Must be trivial" ); +static_assert( std::is_standard_layout< embedded::fixed_string< 64 > >::value, + "Must be standard layout" ); +static_assert( + std::is_nothrow_constructible< embedded::fixed_string< 64 > >::value, + "Must be nothrow constructable" ); +static_assert( + std::is_nothrow_copy_constructible< embedded::fixed_string< 64 > >::value, + "Must be nothrow copy constructable" ); +static_assert( + std::is_nothrow_copy_assignable< embedded::fixed_string< 64 > >::value, + "Must be nothrow copy assignable" ); +static_assert( std::is_nothrow_swappable< embedded::fixed_string< 64 > >::value, + "Must be nothrow swappable" ); + +TEST_CASE( "You can create a fixed_size_string" ) +{ + + SECTION( "Create an empty string" ) + { + embedded::fixed_string< 64 > s; + REQUIRE( s.capacity( ) == 64 ); + REQUIRE( s.remaining( ) == 63 ); + REQUIRE( sizeof( s ) >= 64 ); + REQUIRE( s.size( ) == 0 ); + REQUIRE( std::strcmp( s.c_str( ), "" ) == 0 ); + REQUIRE( s.c_str( ) == s.data( ) ); + } + SECTION( "Create a string from a literal" ) + { + std::vector< const char* > tests; + tests.push_back( "This is a test" ); + tests.push_back( (char*)0 ); + tests.push_back( "" ); + tests.push_back( " " ); + tests.push_back( + "123456789012345678901234567890123456789012345678901234567890123" ); + tests.push_back( "12345678901234567890123456789012345678901234567890123" + "45678901234" ); + tests.push_back( "12345678901234567890123456789012345678901234567890123" + "456789012345" ); + + for ( int i = 0; i < tests.size( ); ++i ) + { + embedded::fixed_string< 64 > s( tests[ i ] ); + REQUIRE( s.capacity( ) == 64 ); + REQUIRE( s.c_str( ) == s.data( ) ); + if ( tests[ i ] ) + { + if ( strlen( tests[ i ] ) >= s.capacity( ) ) + { + REQUIRE( strncmp( s.c_str( ), + tests[ i ], + s.capacity( ) - 1 ) == 0 ); + REQUIRE( strlen( s.c_str( ) ) == s.capacity( ) - 1 ); + REQUIRE( s.size( ) == s.capacity( ) - 1 ); + } + else + { + REQUIRE( strcmp( s.c_str( ), tests[ i ] ) == 0 ); + REQUIRE( strlen( tests[ i ] ) == s.size( ) ); + } + } + else + { + REQUIRE( strcmp( s.c_str( ), "" ) == 0 ); + REQUIRE( s.size( ) == 0 ); + } + } + } + SECTION( "Create via copy constructor" ) + { + embedded::fixed_string< 64 > s0( "Hello World!" ); + embedded::fixed_string< 64 > s1( s0 ); + REQUIRE( s0.size( ) == s1.size( ) ); + REQUIRE( s0.capacity( ) == s1.capacity( ) ); + REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 ); + REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 ); + } + SECTION( "Create via copy constructor from different size " + "embedded::fixed_string using copy constructor" ) + { + embedded::fixed_string< 32 > s0( "Hello World!" ); + embedded::fixed_string< 64 > s1( s0 ); + REQUIRE( s0.size( ) == s1.size( ) ); + REQUIRE( s0.capacity( ) == 32 ); + REQUIRE( s1.capacity( ) == 64 ); + REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 ); + REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 ); + } + SECTION( "Create via copy constructor to a smaller size " + "embedded::fixed_string using copy constructor" ) + { + embedded::fixed_string< 32 > s0( "Hello World!" ); + embedded::fixed_string< 6 > s1( s0 ); + REQUIRE( s0.size( ) == 12 ); + REQUIRE( s1.size( ) == 5 ); + REQUIRE( s0.capacity( ) == 32 ); + REQUIRE( s1.capacity( ) == 6 ); + REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) != 0 ); + REQUIRE( strcmp( s1.c_str( ), "Hello" ) == 0 ); + } + SECTION( "Create via copy constructor from different size " + "embedded::fixed_string using c_str()" ) + { + embedded::fixed_string< 32 > s0( "Hello World!" ); + embedded::fixed_string< 64 > s1( s0.c_str( ) ); + REQUIRE( s0.size( ) == s1.size( ) ); + REQUIRE( s0.capacity( ) == 32 ); + REQUIRE( s1.capacity( ) == 64 ); + REQUIRE( strcmp( s0.c_str( ), s1.c_str( ) ) == 0 ); + REQUIRE( strcmp( s0.c_str( ), "Hello World!" ) == 0 ); + } + SECTION( "Copy operators should work" ) + { + embedded::fixed_string< 64 > s; + REQUIRE( s.size( ) == 0 ); + embedded::fixed_string< 64 > other( "Hello World!" ); + s = other; + REQUIRE( s.size( ) == other.size( ) ); + REQUIRE( strcmp( s.c_str( ), other.c_str( ) ) == 0 ); + + const char* goodbye = "Goodbye World!"; + s = goodbye; + REQUIRE( s.size( ) == strlen( goodbye ) ); + REQUIRE( strcmp( s.c_str( ), goodbye ) == 0 ); + + std::vector< const char* > tests; + tests.push_back( "This is a test" ); + tests.push_back( (char*)0 ); + tests.push_back( "" ); + tests.push_back( " " ); + tests.push_back( + "123456789012345678901234567890123456789012345678901234567890123" ); + tests.push_back( "12345678901234567890123456789012345678901234567890123" + "45678901234" ); + tests.push_back( "12345678901234567890123456789012345678901234567890123" + "456789012345" ); + + for ( int i = 0; i < tests.size( ); ++i ) + { + s = tests[ i ]; + if ( tests[ i ] ) + { + if ( strlen( tests[ i ] ) >= s.capacity( ) ) + { + REQUIRE( strncmp( s.c_str( ), + tests[ i ], + s.capacity( ) - 1 ) == 0 ); + REQUIRE( strlen( s.c_str( ) ) == s.capacity( ) - 1 ); + REQUIRE( s.size( ) == s.capacity( ) - 1 ); + } + else + { + REQUIRE( strcmp( s.c_str( ), tests[ i ] ) == 0 ); + REQUIRE( strlen( tests[ i ] ) == s.size( ) ); + } + } + else + { + REQUIRE( strcmp( s.c_str( ), "" ) == 0 ); + REQUIRE( s.size( ) == 0 ); + } + } + } +} + +TEST_CASE( "Copy operators work even when the sizes are different, it just may " + "truncate" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + embedded::fixed_string< 32 > s1; + embedded::fixed_string< 6 > s2; + embedded::fixed_string< 64 > s3( "123456" ); + + s1 = s0; + REQUIRE( s1 == "123456" ); + s2 = s0; + REQUIRE( s2 == "12345" ); + s3 = s2; + REQUIRE( s3 == "12345" ); +} + +TEST_CASE( "You can append to a fixed size string" ) +{ + embedded::fixed_string< 64 > s; + embedded::fixed_string< 6 > single( "0" ); + embedded::fixed_string< 6 > five( "00000" ); + for ( int i = 1; i <= 100; ++i ) + { + s += "0"; + std::size_t expected_zeros = + ( i < s.capacity( ) ? i : s.capacity( ) - 1 ); + REQUIRE( s.size( ) == expected_zeros ); + std::size_t j = 0; + for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j ) + { + } + REQUIRE( j == expected_zeros ); + } + s = ""; + for ( int i = 1; i <= 100; ++i ) + { + s += "00000"; + std::size_t expected_zeros = + ( i * 5 < s.capacity( ) ? i * 5 : s.capacity( ) - 1 ); + REQUIRE( s.size( ) == expected_zeros ); + std::size_t j = 0; + for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j ) + { + } + REQUIRE( j == expected_zeros ); + } + s = ""; + for ( int i = 1; i <= 100; ++i ) + { + s += single; + std::size_t expected_zeros = + ( i < s.capacity( ) ? i : s.capacity( ) - 1 ); + REQUIRE( s.size( ) == expected_zeros ); + std::size_t j = 0; + for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j ) + { + } + REQUIRE( j == expected_zeros ); + } + s = ""; + for ( int i = 1; i <= 100; ++i ) + { + s += five; + std::size_t expected_zeros = + ( i * 5 < s.capacity( ) ? i * 5 : s.capacity( ) - 1 ); + REQUIRE( s.size( ) == expected_zeros ); + std::size_t j = 0; + for ( ; j < expected_zeros && s.c_str( )[ j ] == '0'; ++j ) + { + } + REQUIRE( j == expected_zeros ); + } + s = "abab"; + s += s; + REQUIRE( s.size( ) == 8 ); + REQUIRE( strcmp( s.c_str( ), "abababab" ) == 0 ); + REQUIRE( s.remaining( ) == 63 - 8 ); +} + +TEST_CASE( "You can do printf style formatted printing" ) +{ + embedded::fixed_string< 64 > s; + s.printf( "%d", 123456 ); + REQUIRE( strcmp( s.c_str( ), "123456" ) == 0 ); + s.printf( "%s", "abcdefg" ); + REQUIRE( strcmp( s.c_str( ), "abcdefg" ) == 0 ); + s.printf( + "%s", + "12345678901234567890123456789012345678901234567890123456789012345" ); + REQUIRE( s.size( ) == 63 ); + REQUIRE( strcmp( s.c_str( ), + "123456789012345678901234567890123456789012345678901234567" + "890123" ) == 0 ); + s.printf( + "%d %s", + -1, + "12345678901234567890123456789012345678901234567890123456789012345" ); + REQUIRE( s.size( ) == 63 ); + REQUIRE( + strcmp( + s.c_str( ), + "-1 " + "123456789012345678901234567890123456789012345678901234567890" ) == + 0 ); +} + +TEST_CASE( "You can assign and append via char arrays" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + char blob[ 1 ][ 8 ] = { "abc" }; + + s0 += blob[ 0 ]; + REQUIRE( s0 == "123456abc" ); +} + +TEST_CASE( "clear will reset the fixed_string to be empty" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + REQUIRE( s0.size( ) > 0 ); + s0.clear( ); + REQUIRE( s0.size( ) == 0 ); + REQUIRE( s0 == "" ); +} + +TEST_CASE( + "printf with a null format string is equal to a format string of \"\"" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + REQUIRE( s0.printf( nullptr ) == 0 ); + REQUIRE( s0 == "" ); +} + +TEST_CASE( + "You can compare with a fixed_string using operator== and operator!=" ) +{ + embedded::fixed_string< 64 > s0( "123456" ), s1( "123456789" ); + + REQUIRE( s0 == s0 ); + REQUIRE( s0 != s1 ); +} + +TEST_CASE( "You can compare with a fixed_string using operator== and " + "operator!= even when capacities are different" ) +{ + embedded::fixed_string< 64 > s0( "1234" ), s1( "123456789" ); + embedded::fixed_string< 6 > s2( "1234" ); + + REQUIRE( s0 == s2 ); + REQUIRE( s1 != s2 ); +} + +TEST_CASE( "You can compare with a char* using operator== and operator!=" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + + REQUIRE( s0 == "123456" ); + REQUIRE( s0 != "1234567890" ); +} + +TEST_CASE( "It is safe to compare with a null char*, it matches an empty " + "fixed_string" ) +{ + embedded::fixed_string< 64 > s0( "123456" ); + embedded::fixed_string< 64 > s1; + REQUIRE( s0.size( ) > 0 ); + REQUIRE( s1.size( ) == 0 ); + REQUIRE( s0 != nullptr ); + REQUIRE( s1 == "" ); + REQUIRE( s1 == nullptr ); +} + +TEST_CASE( "You can remove characters from the end" ) +{ + embedded::fixed_string< 64 > s0( "1234567890" ); + + REQUIRE( s0.size( ) == 10 ); + s0.pop_back_n( 4 ); + REQUIRE( s0.size( ) == 6 ); + REQUIRE( s0 == "123456" ); + s0.pop_back_n( 1 ); + REQUIRE( s0.size( ) == 5 ); + s0.pop_back_n( 10 ); + REQUIRE( s0.size( ) == 0 ); +} + +TEST_CASE( "For direct manipulation you can get a buffer which cleans up when " + "it is done" ) +{ + embedded::fixed_string< 10 > s0( "abc" ); + + { + auto buf = s0.get_buffer( ); + std::fill( buf.data( ), buf.data( ) + buf.capacity( ), 1 ); + REQUIRE( buf.data( )[ buf.capacity( ) - 1 ] == 1 ); + REQUIRE( s0.data( )[ s0.capacity( ) - 1 ] == 1 ); + } + REQUIRE( s0.data( )[ s0.capacity( ) - 1 ] == '\0' ); +} \ No newline at end of file diff --git a/src/epics/seq/test/test_fixed_size_vector.cc b/src/epics/seq/test/test_fixed_size_vector.cc new file mode 100644 index 0000000000000000000000000000000000000000..602dd286a92e73c2d5bf9caec2478218905d5df4 --- /dev/null +++ b/src/epics/seq/test/test_fixed_size_vector.cc @@ -0,0 +1,223 @@ +// +// Created by jonathan.hanks on 11/13/20. +// + +#include "catch.hpp" + +#include "fixed_size_vector.hh" + +#include <algorithm> + +TEST_CASE( "You can create a fixed size vector" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + + REQUIRE( v0.size( ) == 0 ); + REQUIRE( v0.empty( ) ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.begin( ) == v0.end( ) ); + + const embedded::fixed_size_vector< int, 20 > v1; + REQUIRE( v1.begin( ) == v1.end( ) ); + REQUIRE( v1.cbegin( ) == v1.cend( ) ); + REQUIRE( v1.begin( ) == v1.cbegin( ) ); + REQUIRE( v1.end( ) == v1.cend( ) ); + + static_assert( + std::is_same< + int*, + typename embedded::fixed_size_vector< int, 20 >::iterator >::value, + "The iterator is a pointer" ); + static_assert( + std::is_same< const int*, + typename embedded::fixed_size_vector< int, 20 >:: + const_iterator >::value, + "The iterator is pointer to const" ); +} + +TEST_CASE( "You can add elements to the back of a fixed size vector" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + REQUIRE( v0.push_back( 5 ) ); + REQUIRE( v0.size( ) == 1 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.push_back( 6 ) ); + REQUIRE( v0.size( ) == 2 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.push_back( 7 ) ); + REQUIRE( v0.size( ) == 3 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) ); + REQUIRE( *v0.begin( ) == 5 ); + REQUIRE( *( v0.begin( ) + 1 ) == 6 ); + REQUIRE( *( v0.begin( ) + 2 ) == 7 ); +} + +TEST_CASE( "You can add elements to the back of a fixed size vector with " + "emplace_back" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + REQUIRE( v0.emplace_back( 5 ) ); + REQUIRE( v0.size( ) == 1 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.emplace_back( 6 ) ); + REQUIRE( v0.size( ) == 2 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.emplace_back( 7 ) ); + REQUIRE( v0.size( ) == 3 ); + REQUIRE( v0.capacity( ) == 20 ); + REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) ); + REQUIRE( *v0.begin( ) == 5 ); + REQUIRE( *( v0.begin( ) + 1 ) == 6 ); + REQUIRE( *( v0.begin( ) + 2 ) == 7 ); +} + +TEST_CASE( "Attempting to add elements when full returns a false value" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + for ( auto i = 0; i < v0.capacity( ); ++i ) + { + REQUIRE( v0.push_back( i ) ); + } + REQUIRE( v0.size( ) == v0.capacity( ) ); + REQUIRE( v0.push_back( 42 ) == false ); + REQUIRE( v0.emplace_back( 43 ) == false ); + REQUIRE( v0.size( ) == v0.capacity( ) ); +} + +TEST_CASE( "You can access elements via []" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + for ( auto i = 0; i < v0.capacity( ); ++i ) + { + REQUIRE( v0.push_back( i ) ); + } + for ( auto i = 0; i < v0.capacity( ); ++i ) + { + REQUIRE( v0[ i ] == i ); + } +} + +TEST_CASE( "You can pop an entry off of the end of a vector" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + for ( auto i = 0; i < v0.capacity( ); ++i ) + { + REQUIRE( v0.push_back( i ) ); + } + REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) ); + REQUIRE( v0.size( ) == v0.capacity( ) ); + + v0.pop_back( ); + REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) - 1 ); + REQUIRE( v0.size( ) == v0.capacity( ) - 1 ); +} + +TEST_CASE( "You can clear a fixed sized vector to empty it" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + for ( auto i = 0; i < v0.capacity( ); ++i ) + { + REQUIRE( v0.push_back( i ) ); + } + REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) ); + REQUIRE( v0.size( ) == v0.capacity( ) ); + + v0.clear( ); + REQUIRE( v0.size( ) == 0 ); + REQUIRE( v0.begin( ) == v0.end( ) ); + REQUIRE( v0.capacity( ) == 20 ); +} + +class DestructionNotifier +{ +public: + explicit DestructionNotifier( int* counter_dest ) : counter_{ counter_dest } + { + } + DestructionNotifier( const DestructionNotifier& ) noexcept = default; + DestructionNotifier& + operator=( const DestructionNotifier& ) noexcept = default; + + ~DestructionNotifier( ) + { + ( *counter_ )++; + } + +private: + int* counter_; +}; + +TEST_CASE( "Destructors are called on items in the fixed size vector when it " + "is destroyed or elements are popped off" ) +{ + int counter = 0; + + { + embedded::fixed_size_vector< DestructionNotifier, 10 > v0; + + v0.emplace_back( &counter ); + REQUIRE( !v0.empty( ) ); + REQUIRE( counter == 0 ); + v0.pop_back( ); + REQUIRE( v0.empty( ) ); + REQUIRE( counter == 1 ); + + counter = 0; + while ( v0.emplace_back( &counter ) ) + { + } + REQUIRE( v0.size( ) == v0.capacity( ) ); + } + REQUIRE( counter == 10 ); +} + +TEST_CASE( "We have defined enough to make the this usable by std::algorithms" ) +{ + embedded::fixed_size_vector< int, 20 > v0; + v0.emplace_back( 5 ); + v0.emplace_back( 4 ); + v0.emplace_back( 3 ); + v0.emplace_back( 2 ); + v0.emplace_back( 1 ); + std::sort( v0.begin( ), v0.end( ) ); + REQUIRE( v0[ 0 ] == 1 ); + REQUIRE( v0[ 1 ] == 2 ); + REQUIRE( v0[ 2 ] == 3 ); + REQUIRE( v0[ 3 ] == 4 ); + REQUIRE( v0[ 4 ] == 5 ); +} + +TEST_CASE( "We can use front()/back() to reference the first/last elements when there are more than one.") +{ + embedded::fixed_size_vector<int, 20> v0; + v0.push_back(5); + REQUIRE( v0.front() == v0.back() ); + v0.front() = 42; + REQUIRE( v0[0] == 42 ); + v0.back() = 44; + REQUIRE( v0[0] == 44 ); + v0.push_back(10); + REQUIRE( v0.front() == 44 ); + REQUIRE( v0.back() == 10 ); + REQUIRE( v0[0] == v0.front() ); + REQUIRE( v0[1] == v0.back() ); +} + +TEST_CASE( "We can use front()/back() in a const setting as well") +{ + embedded::fixed_size_vector<int, 20> v0; + v0.push_back(5); + + const auto& vconst = v0; + REQUIRE( vconst.front() == vconst.back() ); +} + +// static_assert(std::is_trivially_destructible<embedded::fixed_size_vector<int, +// 20> >::value, "For trivial destructable types the fixed sized vector should +// be trivially destructable"); +static_assert( + !std::is_trivially_destructible< + embedded::fixed_size_vector< DestructionNotifier, 10 > >::value, + "for non-trivially destructable types the fixed size vector is not " + "trivially destructable" ); \ No newline at end of file diff --git a/src/epics/seq/test/test_main.cc b/src/epics/seq/test/test_main.cc new file mode 100644 index 0000000000000000000000000000000000000000..063e87874ea57a58d37496c85910b1f6cd2c6415 --- /dev/null +++ b/src/epics/seq/test/test_main.cc @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" \ No newline at end of file diff --git a/src/epics/seq/test/test_simple_range.cc b/src/epics/seq/test/test_simple_range.cc new file mode 100644 index 0000000000000000000000000000000000000000..efe9d32164013a1b4315294b7d56b8453dfff966 --- /dev/null +++ b/src/epics/seq/test/test_simple_range.cc @@ -0,0 +1,18 @@ +// +// Created by jonathan.hanks on 11/17/20. +// +#include "catch.hpp" + +#include "simple_range.hh" + +#include <vector> + +TEST_CASE("You can create a simple range from two iterators") +{ + using it_type = typename std::vector<int>::iterator; + std::vector<int> backing_store{1,2,3,4,5,6,7,8,9,0}; + + embedded::range<it_type> rng = embedded::make_range(backing_store.begin() + 3, backing_store.begin() + 3 + 3); + REQUIRE( *rng.begin() == 4 ); + REQUIRE( *rng.end() == 7 ); +} diff --git a/src/epics/util/lib/createEpicsMakefile.pm b/src/epics/util/lib/createEpicsMakefile.pm index f6106a4c67773c41c592df88e957c602f6dfe2ed..a5af6a63aad6e5b407ae4f48ca68209881c995f3 100644 --- a/src/epics/util/lib/createEpicsMakefile.pm +++ b/src/epics/util/lib/createEpicsMakefile.pm @@ -23,7 +23,7 @@ my ($fileName) = @_; }elsif ($globalsdf) { print OUTME "SRC += $::rcg_src_dir/src/epics/seq/sdf_monitor.c\n"; } else { - print OUTME "SRC += $::rcg_src_dir/src/epics/seq/main.c\n"; + print OUTME "SRC += $::rcg_src_dir/src/epics/seq/main.cc\n"; } print OUTME "SRC += $::rcg_src_dir/src/epics/seq/sdf_file_loaded.c\n"; print OUTME "SRC += $::rcg_src_dir/src/drv/rfm.c\n"; diff --git a/src/include/sdf_file_loaded.h b/src/include/sdf_file_loaded.h index 722e4d64ad28aa01dd001481d92fa8f88cd13f49..3e8cdf1ba6d82bacba52120b0537173342e87e9c 100644 --- a/src/include/sdf_file_loaded.h +++ b/src/include/sdf_file_loaded.h @@ -1,11 +1,19 @@ #ifndef DAQD_TRUNK_SDF_FILE_LOADED_H #define DAQD_TRUNK_SDF_FILE_LOADED_H +#ifdef __cplusplus +extern "C" { +#endif + // set or clear and sdf file loaded flag // this flag is used to trigger BURT_RESTORE // when the safe.snap or other sdf file is completely loaded -extern int get_sdf_file_loaded(); -extern void set_sdf_file_loaded(int); +extern int get_sdf_file_loaded( ); +extern void set_sdf_file_loaded( int ); + +#ifdef __cplusplus +} +#endif #endif // DAQD_TRUNK_SDF_FILE_LOADED_H diff --git a/src/include/util/user/check_file_crc.h b/src/include/util/user/check_file_crc.h new file mode 100644 index 0000000000000000000000000000000000000000..f756df0f4a579d03330fd6e59346585ca3cedec1 --- /dev/null +++ b/src/include/util/user/check_file_crc.h @@ -0,0 +1,56 @@ +// +// Created by jonathan.hanks on 3/30/22. +// + +#ifndef DAQD_TRUNK_CHECK_FILE_CRC_H +#define DAQD_TRUNK_CHECK_FILE_CRC_H + +#include <stdio.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "crc.h" + + +/// Common routine to check file CRC. +/// @param[in] *fName Name of file to check. +/// @return File CRC or -1 if file not found. +static inline int +checkFileCrc( const char* fName ) +{ + char cbuf[ 128 ]; + char* cp = 0; + int flen = 0; + int clen = 0; + unsigned int crc = 0; + FILE* fp = NULL; + + if ( !fName ) + { + return -1; + } + fp = fopen( fName, "r" ); + if ( fp == NULL ) + { + return -1; + } + + while ( ( cp = fgets( cbuf, 128, fp ) ) != NULL ) + { + clen = strlen( cbuf ); + flen += clen; + crc = crc_ptr( cbuf, clen, crc ); + } + crc = crc_len( flen, crc ); + fclose( fp ); + return crc; +} + +#ifdef __cplusplus +} +#endif + +#endif // DAQD_TRUNK_CHECK_FILE_CRC_H diff --git a/src/simple_pv/CMakeLists.txt b/src/simple_pv/CMakeLists.txt index 4a52fdc520d5df5f690f05e67a585550ee757137..130418a7c1176116cb0a67283e78f1b4a4c0e28c 100644 --- a/src/simple_pv/CMakeLists.txt +++ b/src/simple_pv/CMakeLists.txt @@ -1,7 +1,8 @@ add_library(simple_pv simple_pv.cc simple_epics.cc - simple_epics_internal.cc) + simple_pv_internal.cc) target_include_directories(simple_pv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(simple_pv PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private) target_link_libraries(simple_pv PUBLIC epics::cas epics::gdd /usr/lib/epics/lib/linux-x86_64/libCom.so) target_requires_cpp11(simple_pv PUBLIC) add_library(pv::simple_pv ALIAS simple_pv) @@ -9,3 +10,6 @@ add_library(pv::simple_pv ALIAS simple_pv) add_executable(test_simple_pv tests/test_simple_pv.cc) target_link_libraries(test_simple_pv PUBLIC simple_pv) target_include_directories(test_simple_pv PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(test_rw_ioc tests/test_rw_cas.cc) +target_link_libraries(test_rw_ioc PUBLIC simple_pv) \ No newline at end of file diff --git a/src/simple_pv/private/simple_pv_int.hh b/src/simple_pv/private/simple_pv_int.hh new file mode 100644 index 0000000000000000000000000000000000000000..ef7df71519162b4811b8fc4b051e307e3a74a85c --- /dev/null +++ b/src/simple_pv/private/simple_pv_int.hh @@ -0,0 +1,286 @@ +// +// Created by jonathan.hanks on 3/16/22. +// + +#ifndef DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH +#define DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH + +#include "simple_pv_types.hh" +#include "simple_pv_internal.hh" +#include <mutex> +#include <type_traits> + +#include <iostream> + +namespace simple_epics +{ + + namespace detail + { + + /*! + * @brief A representation of a R/O integer in a PV + */ + template < typename IntType > + class simpleBasicIntPV : public simplePVBase + { + static_assert( std::is_integral< IntType >::value, + "Must use an interger type" ); + + public: + simpleBasicIntPV( caServer& server, + pvBasicIntAttributes< IntType > attr ) + : simplePVBase( ), server_{ server }, attr_{ std::move( + attr ) }, + val_( ), monitored_{ false } + { + std::once_flag initted{ }; + std::call_once( + initted, []( ) { simpleBasicIntPV::setup_func_table( ); } ); + + val_ = new gddScalar( gddAppType_value, + ait_data_type< IntType >::value ); + val_->unreference( ); + set_value( *attr_.src( ) ); + } + ~simpleBasicIntPV( ) override = default; + + caStatus + read( const casCtx& ctx, gdd& prototype ) override + { + return get_func_table( ).read( *this, prototype ); + } + caStatus + write( const casCtx& ctx, const gdd& value ) override + { + if ( attr_.mode( ) != PVMode::ReadWrite ) + { + return S_casApp_noSupport; + } + aitType newValue; + value.get( &newValue, ait_data_type< IntType >::value ); + set_value( newValue ); + *const_cast< IntType* >( attr_.src( ) ) = newValue; + return S_casApp_success; + } + + void destroy( ) override{ }; + + aitEnum + bestExternalType( ) const override + { + return val_->primitiveType( ); + } + + const char* + getName( ) const override + { + return attr_.name( ).c_str( ); + } + + caStatus + interestRegister( ) override + { + monitored_ = true; + return S_casApp_success; + } + + void + interestDelete( ) override + { + monitored_ = false; + } + + void + update( ) override + { + set_value( *attr_.src( ) ); + } + + private: + using aitType = typename ait_data_type< IntType >::ait_type; + + void + set_value( aitType value ) + { + aitType current_value = 0; + + val_->getConvert( current_value ); + if ( current_value == value ) + { + return; + } + + val_->putConvert( value ); + aitTimeStamp ts = aitTimeStamp( epicsTime::getCurrent( ) ); + val_->setTimeStamp( &ts ); + + aitUint16 stat = epicsAlarmNone; + aitUint16 sevr = epicsSevNone; + if ( value >= attr_.alarm_high( ) ) + { + stat = epicsAlarmHiHi; + sevr = epicsSevMajor; + } + else if ( value <= attr_.alarm_low( ) ) + { + stat = epicsAlarmLoLo; + sevr = epicsSevMajor; + } + else if ( value >= attr_.warn_high( ) ) + { + stat = epicsAlarmHigh; + sevr = epicsSevMinor; + } + else if ( value <= attr_.warn_low( ) ) + { + stat = epicsAlarmLow; + sevr = epicsSevMinor; + } + val_->setSevr( sevr ); + val_->setStat( stat ); + + if ( monitored_ ) + { + casEventMask mask = + casEventMask( server_.valueEventMask( ) ); + bool alarm_changed = ( stat != val_->getStat( ) || + sevr != val_->getSevr( ) ); + if ( alarm_changed ) + { + mask |= server_.alarmEventMask( ); + } + postEvent( mask, *val_ ); + } + } + + static void + setup_func_table( ) + { + auto install = []( const char* name, + gddAppFuncTableStatus ( + simpleBasicIntPV::*handler )( gdd& ) ) { + gddAppFuncTableStatus status; + + // char error_string[100]; + + status = get_func_table( ).installReadFunc( name, handler ); + if ( status != S_gddAppFuncTable_Success ) + { + // errSymLookup(status, error_string, + // sizeof(error_string)); + // throw std::runtime_error(error_string); + throw std::runtime_error( + "Unable to initialize pv lookup table" ); + } + }; + + install( "units", &simpleBasicIntPV::read_attr_not_handled ); + install( "status", &simpleBasicIntPV::read_status ); + install( "severity", &simpleBasicIntPV::read_severity ); + install( "maxElements", + &simpleBasicIntPV::read_attr_not_handled ); + install( "precision", &simpleBasicIntPV::read_precision ); + install( "alarmHigh", &simpleBasicIntPV::read_alarm_high ); + install( "alarmLow", &simpleBasicIntPV::read_alarm_low ); + install( "alarmHighWarning", + &simpleBasicIntPV::read_warn_high ); + install( "alarmLowWarning", &simpleBasicIntPV::read_warn_low ); + install( "maxElements", + &simpleBasicIntPV::read_attr_not_handled ); + install( "graphicHigh", + &simpleBasicIntPV::read_attr_not_handled ); + install( "graphicLow", + &simpleBasicIntPV::read_attr_not_handled ); + install( "controlHigh", + &simpleBasicIntPV::read_attr_not_handled ); + install( "controlLow", + &simpleBasicIntPV::read_attr_not_handled ); + install( "enums", &simpleBasicIntPV::read_attr_not_handled ); + install( "menuitem", &simpleBasicIntPV::read_attr_not_handled ); + install( "timestamp", + &simpleBasicIntPV::read_attr_not_handled ); + install( "value", &simpleBasicIntPV::read_value ); + } + static gddAppFuncTable< simpleBasicIntPV >& + get_func_table( ) + { + static gddAppFuncTable< simpleBasicIntPV > func_table; + return func_table; + } + + gddAppFuncTableStatus + read_attr_not_handled( gdd& g ) + { + return S_casApp_success; + } + + gddAppFuncTableStatus + read_status( gdd& g ) + { + g.putConvert( val_->getStat( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_severity( gdd& g ) + { + g.putConvert( val_->getSevr( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_precision( gdd& g ) + { + g.putConvert( 0 ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_alarm_high( gdd& g ) + { + g.putConvert( attr_.alarm_high( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_alarm_low( gdd& g ) + { + g.putConvert( attr_.alarm_low( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_warn_high( gdd& g ) + { + g.putConvert( attr_.warn_high( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_warn_low( gdd& g ) + { + g.putConvert( attr_.warn_low( ) ); + return S_casApp_success; + } + + gddAppFuncTableStatus + read_value( gdd& g ) + { + auto status = gddApplicationTypeTable::app_table.smartCopy( + &g, val_.get( ) ); + return ( status ? S_cas_noConvert : S_casApp_success ); + } + + caServer& server_; + pvBasicIntAttributes< IntType > attr_; + smartGDDPointer val_; + bool monitored_; + }; + using simpleIntPV = simpleBasicIntPV< std::int32_t >; + using simpleUIntPV = simpleBasicIntPV< std::uint32_t >; + using simpleUShortPV = simpleBasicIntPV< std::uint16_t >; + } // namespace detail +} // namespace simple_epics + +#endif // DAQD_TRUNK_SIMPLE_EPICS_INTERNAL_INT_HH diff --git a/src/simple_pv/simple_epics_internal.hh b/src/simple_pv/private/simple_pv_internal.hh similarity index 69% rename from src/simple_pv/simple_epics_internal.hh rename to src/simple_pv/private/simple_pv_internal.hh index f3adeb4eedfa9b400918c8788f349c35e45a1e29..0ad59337988cd104096b721f69a9e70c4e3401ee 100644 --- a/src/simple_pv/simple_epics_internal.hh +++ b/src/simple_pv/private/simple_pv_internal.hh @@ -1,19 +1,20 @@ // -// Created by jonathan.hanks on 1/6/20. +// Created by jonathan.hanks on 3/16/22. // #ifndef DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH #define DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH -#include "simple_epics.hh" #include <memory> +#include "simple_epics.hh" +#include "simple_pv_int.hh" + namespace simple_epics { namespace detail { - // std::make_unique didn't make it into C++11, so // to allow this to work in a pre C++14 world, we // provide a simple replacement. @@ -36,80 +37,6 @@ namespace simple_epics new T( std::forward< Ts >( params )... ) ); } - class setup_int_pv_table; - - /*! - * @brief A representation of a R/O integer in a PV - */ - class simpleIntPV : public simplePVBase - { - friend class setup_int_pv_table; - - public: - simpleIntPV( caServer& server, pvIntAttributes attr ) - : simplePVBase( ), server_{ server }, attr_{ std::move( - attr ) }, - val_( ), monitored_{ false } - { - val_ = new gddScalar( gddAppType_value, aitEnumInt32 ); - val_->unreference( ); - set_value( *attr_.src( ) ); - } - ~simpleIntPV( ) override; - - caStatus read( const casCtx& ctx, gdd& prototype ) override; - caStatus write( const casCtx& ctx, const gdd& value ) override; - - void destroy( ) override{}; - - aitEnum bestExternalType( ) const override; - - const char* - getName( ) const override - { - return attr_.name( ).c_str( ); - } - - caStatus interestRegister( ) override; - - void interestDelete( ) override; - - void update( ) override; - - private: - void set_value( int value ); - - static void setup_func_table( ); - static gddAppFuncTable< simpleIntPV >& get_func_table( ); - - gddAppFuncTableStatus - read_attr_not_handled( gdd& g ) - { - return S_casApp_success; - } - - gddAppFuncTableStatus read_status( gdd& g ); - - gddAppFuncTableStatus read_severity( gdd& g ); - - gddAppFuncTableStatus read_precision( gdd& g ); - - gddAppFuncTableStatus read_alarm_high( gdd& g ); - - gddAppFuncTableStatus read_alarm_low( gdd& g ); - - gddAppFuncTableStatus read_warn_high( gdd& g ); - - gddAppFuncTableStatus read_warn_low( gdd& g ); - - gddAppFuncTableStatus read_value( gdd& g ); - - caServer& server_; - pvIntAttributes attr_; - smartGDDPointer val_; - bool monitored_; - }; - class setup_string_pv_table; /*! * @brief A representation of a R/O integer in a PV @@ -133,7 +60,7 @@ namespace simple_epics caStatus read( const casCtx& ctx, gdd& prototype ) override; caStatus write( const casCtx& ctx, const gdd& value ) override; - void destroy( ) override{}; + void destroy( ) override{ }; aitEnum bestExternalType( ) const override; @@ -199,7 +126,7 @@ namespace simple_epics caStatus read( const casCtx& ctx, gdd& prototype ) override; caStatus write( const casCtx& ctx, const gdd& value ) override; - void destroy( ) override{}; + void destroy( ) override{ }; aitEnum bestExternalType( ) const override; @@ -248,9 +175,7 @@ namespace simple_epics smartGDDPointer val_; bool monitored_; }; - } // namespace detail - } // namespace simple_epics #endif // DAQD_TRUNK_SIMPLE_PV_INTERNAL_HH diff --git a/src/simple_pv/private/simple_pv_types.hh b/src/simple_pv/private/simple_pv_types.hh new file mode 100644 index 0000000000000000000000000000000000000000..4e698ab7cfd01e3b9320c3a31746f1730e90adf3 --- /dev/null +++ b/src/simple_pv/private/simple_pv_types.hh @@ -0,0 +1,97 @@ +// +// Created by jonathan.hanks on 3/16/22. +// + +#ifndef DAQD_TRUNK_SIMPLE_PV_TYPES_HH +#define DAQD_TRUNK_SIMPLE_PV_TYPES_HH + +#include <cstdint> +#include <string> +#include <type_traits> +#include "aitTypes.h" + +namespace simple_epics +{ + namespace detail + { + /** + * @brief ait_data_type is used to map between C++ types and the + * CAS ait (architecture independent) data types. + * @tparam T The type to map + */ + template < typename T > + struct ait_data_type + { + static const aitEnum value = aitEnumInvalid; + using ait_type = void; + }; + + template <> + struct ait_data_type< std::int16_t > + { + static const aitEnum value = aitEnumInt16; + using ait_type = aitInt16; + static_assert( std::is_same< std::int16_t, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< std::uint16_t > + { + static const aitEnum value = aitEnumUint16; + using ait_type = aitUint16; + static_assert( std::is_same< std::uint16_t, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< std::int32_t > + { + static const aitEnum value = aitEnumInt32; + using ait_type = aitInt32; + static_assert( std::is_same< std::int32_t, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< std::uint32_t > + { + static const aitEnum value = aitEnumUint32; + using ait_type = aitUint32; + static_assert( std::is_same< std::uint32_t, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< float > + { + static const aitEnum value = aitEnumFloat32; + using ait_type = aitFloat32; + static_assert( std::is_same< float, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< double > + { + static const aitEnum value = aitEnumFloat64; + using ait_type = aitFloat64; + static_assert( std::is_same< double, ait_type >::value, + "aitType and C++ types must match" ); + }; + + template <> + struct ait_data_type< const char* > + { + static const aitEnum value = aitEnumString; + }; + + template <> + struct ait_data_type< std::string > + { + static const aitEnum value = aitEnumString; + }; + } // namespace detail +} // namespace simple_epics + +#endif // DAQD_TRUNK_SIMPLE_PV_TYPES_HH diff --git a/src/simple_pv/simple_epics.cc b/src/simple_pv/simple_epics.cc index dbce6e75f4b5656c6a30e09c8e66f607983f9ec0..3f6de43397f4144c42949deeb3248adf4f77d508 100644 --- a/src/simple_pv/simple_epics.cc +++ b/src/simple_pv/simple_epics.cc @@ -2,7 +2,7 @@ // Created by jonathan.hanks on 12/20/19. // #include "simple_epics.hh" -#include "simple_epics_internal.hh" +#include "simple_pv_internal.hh" #include <stdexcept> #include <algorithm> @@ -11,6 +11,24 @@ namespace simple_epics { Server::~Server( ) = default; + void + Server::addPV( pvUShortAttributes attr ) + { + std::lock_guard< std::mutex > l_( m_ ); + + auto it = pvs_.find( attr.name( ) ); + if ( it != pvs_.end( ) ) + { + throw std::runtime_error( + "Duplicate key insertion to the epics db" ); + } + std::string name{ attr.name( ) }; + pvs_.insert( + std::make_pair( std::move( name ), + detail::make_unique_ptr< detail::simpleUShortPV >( + *this, std::move( attr ) ) ) ); + } + void Server::addPV( pvIntAttributes attr ) { @@ -29,6 +47,24 @@ namespace simple_epics *this, std::move( attr ) ) ) ); } + void + Server::addPV( pvUIntAttributes attr ) + { + std::lock_guard< std::mutex > l_( m_ ); + + auto it = pvs_.find( attr.name( ) ); + if ( it != pvs_.end( ) ) + { + throw std::runtime_error( + "Duplicate key insertion to the epics db" ); + } + std::string name{ attr.name( ) }; + pvs_.insert( + std::make_pair( std::move( name ), + detail::make_unique_ptr< detail::simpleUIntPV >( + *this, std::move( attr ) ) ) ); + } + void Server::addPV( pvStringAttributes attr ) { diff --git a/src/simple_pv/simple_epics.hh b/src/simple_pv/simple_epics.hh index 2830457d85ade894cf65bac4b3b1f28325f528ac..c6a70ede3e8a3d824156a200a4636a33c91b1883 100644 --- a/src/simple_pv/simple_epics.hh +++ b/src/simple_pv/simple_epics.hh @@ -9,6 +9,7 @@ #include <memory> #include <mutex> #include <string> +#include <type_traits> #include <gddAppFuncTable.h> #include <epicsTimer.h> @@ -36,69 +37,94 @@ namespace simple_epics class Server; + enum class PVMode + { + ReadOnly = 0, + ReadWrite = 1, + }; + /*! * @brief A description of a PV, used to describe an int PV to the server. * @note this is given a pointer to the data. This value is only read * when a Server object is told to update its data. */ - class pvIntAttributes + template < typename IntType > + class pvBasicIntAttributes { + static_assert( std::is_integral< IntType >::value, + "integer type required" ); + public: - pvIntAttributes( std::string pv_name, - int* value, - std::pair< int, int > alarm_range, - std::pair< int, int > warn_range ) + using value_type = IntType; + + pvBasicIntAttributes( std::string pv_name, + IntType* value, + std::pair< IntType, IntType > alarm_range, + std::pair< IntType, IntType > warn_range, + PVMode mode = PVMode::ReadOnly ) : name_{ std::move( pv_name ) }, alarm_low_{ alarm_range.first }, alarm_high_{ alarm_range.second }, warn_low_{ warn_range.first }, - warn_high_{ warn_range.second }, src_{ value } + warn_high_{ warn_range.second }, mode_{ mode }, src_{ value } { } const std::string& - name( ) const + name( ) const noexcept { return name_; } - int - alarm_high( ) const + IntType + alarm_high( ) const noexcept { return alarm_high_; } - int - alarm_low( ) const + IntType + alarm_low( ) const noexcept { return alarm_low_; } - int - warn_high( ) const + IntType + warn_high( ) const noexcept { return warn_high_; } - int - warn_low( ) const + IntType + warn_low( ) const noexcept { return warn_low_; } - const int* - src( ) const + const IntType* + src( ) const noexcept { return src_; } + PVMode + mode( ) const noexcept + { + return mode_; + } + private: std::string name_; - int alarm_high_; - int alarm_low_; - int warn_high_; - int warn_low_; + IntType alarm_high_; + IntType alarm_low_; + IntType warn_high_; + IntType warn_low_; - int* src_; + PVMode mode_; + IntType* src_{ nullptr }; }; + using pvIntAttributes = pvBasicIntAttributes< std::int32_t >; + using pvUIntAttributes = pvBasicIntAttributes< std::uint32_t >; + using pvUShortAttributes = pvBasicIntAttributes< std::uint16_t >; + static_assert( sizeof( std::int32_t ) == sizeof( int ), + "int must be 32 bit" ); /*! * @brief A description of a PV, used to describe a string PV to the server. @@ -108,27 +134,50 @@ namespace simple_epics class pvStringAttributes { public: - pvStringAttributes( std::string pv_name, const char* value ) - : name_{ std::move( pv_name ) }, src_{ value } + pvStringAttributes( std::string pv_name, + const char* value, + PVMode mode = PVMode::ReadOnly, + std::size_t buffer_size = 0 ) + : name_{ std::move( pv_name ) }, mode_{ mode }, src_{ value }, + src_size_{ buffer_size } { } const std::string& - name( ) const + name( ) const noexcept { return name_; } const char* - src( ) const + src( ) const noexcept { return src_; } + char* + src( ) noexcept + { + return (char*)src_; + } + + std::size_t + src_size( ) const noexcept + { + return src_size_; + } + + PVMode + mode( ) const noexcept + { + return mode_; + } + private: std::string name_; - + PVMode mode_; const char* src_; + std::size_t src_size_; }; /*! @@ -142,48 +191,55 @@ namespace simple_epics pvDoubleAttributes( std::string pv_name, double* value, std::pair< double, double > alarm_range, - std::pair< double, double > warn_range ) + std::pair< double, double > warn_range, + PVMode mode = PVMode::ReadOnly ) : name_{ std::move( pv_name ) }, alarm_low_{ alarm_range.first }, alarm_high_{ alarm_range.second }, warn_low_{ warn_range.first }, - warn_high_{ warn_range.second }, src_{ value } + warn_high_{ warn_range.second }, mode_{ mode }, src_{ value } { } const std::string& - name( ) const + name( ) const noexcept { return name_; } double - alarm_high( ) const + alarm_high( ) const noexcept { return alarm_high_; } double - alarm_low( ) const + alarm_low( ) const noexcept { return alarm_low_; } double - warn_high( ) const + warn_high( ) const noexcept { return warn_high_; } double - warn_low( ) const + warn_low( ) const noexcept { return warn_low_; } const double* - src( ) const + src( ) const noexcept { return src_; } + PVMode + mode( ) const noexcept + { + return mode_; + } + private: std::string name_; @@ -192,7 +248,9 @@ namespace simple_epics double warn_high_; double warn_low_; - double* src_; + PVMode mode_; + + double* src_{ nullptr }; }; /*! @@ -201,7 +259,7 @@ namespace simple_epics class Server : public caServer { public: - Server( ) : caServer( ), pvs_{} + Server( ) : caServer( ), pvs_{ } { } ~Server( ) override; @@ -209,7 +267,9 @@ namespace simple_epics /*! * @brief Add a PV to the server. */ + void addPV( pvUShortAttributes attr ); void addPV( pvIntAttributes attr ); + void addPV( pvUIntAttributes attr ); void addPV( pvStringAttributes attr ); void addPV( pvDoubleAttributes attr ); diff --git a/src/simple_pv/simple_pv.cc b/src/simple_pv/simple_pv.cc index 7ab5c2b729a59dea61e94a3d77846938063c3d15..bffe8c2a2c90e37e4131b6d4d6fcdac3327221fa 100644 --- a/src/simple_pv/simple_pv.cc +++ b/src/simple_pv/simple_pv.cc @@ -3,7 +3,7 @@ // #include "simple_pv.h" #include "simple_epics.hh" -#include "simple_epics_internal.hh" +#include "simple_pv_internal.hh" #include <algorithm> #include <memory> @@ -29,15 +29,14 @@ simple_pv_server_create( const char* prefix, SimplePV* pvs, int pv_count ) std::for_each( pvs, pvs + pv_count, - [&prefix_, pv_server]( const SimplePV& pv ) -> void { + [ &prefix_, pv_server ]( const SimplePV& pv ) -> void { if ( !pv.name || !pv.data ) { return; } switch ( pv.pv_type ) { - case SIMPLE_PV_INT: - { + case SIMPLE_PV_INT: { pv_server->addPV( simple_epics::pvIntAttributes( prefix_ + pv.name, reinterpret_cast< int* >( pv.data ), @@ -45,14 +44,12 @@ simple_pv_server_create( const char* prefix, SimplePV* pvs, int pv_count ) std::make_pair< int, int >( pv.warn_low, pv.warn_high ) ) ); } break; - case SIMPLE_PV_STRING: - { + case SIMPLE_PV_STRING: { pv_server->addPV( simple_epics::pvStringAttributes( prefix_ + pv.name, reinterpret_cast< char* >( pv.data ) ) ); } break; - case SIMPLE_PV_DOUBLE: - { + case SIMPLE_PV_DOUBLE: { pv_server->addPV( simple_epics::pvDoubleAttributes( prefix_ + pv.name, reinterpret_cast< double* >( pv.data ), diff --git a/src/simple_pv/simple_epics_internal.cc b/src/simple_pv/simple_pv_internal.cc similarity index 64% rename from src/simple_pv/simple_epics_internal.cc rename to src/simple_pv/simple_pv_internal.cc index 2bf9c4a17636319d2df670ce52d9c6d346dbe474..adf5909f8e30c7e9ecc3a0336f765a7726378ddf 100644 --- a/src/simple_pv/simple_epics_internal.cc +++ b/src/simple_pv/simple_pv_internal.cc @@ -1,24 +1,13 @@ // // Created by jonathan.hanks on 1/6/20. // -#include "simple_epics_internal.hh" +#include "simple_pv_internal.hh" #include <cstring> namespace simple_epics { namespace detail { - class setup_int_pv_table - { - public: - setup_int_pv_table( ) - { - simpleIntPV::setup_func_table( ); - } - }; - - setup_int_pv_table pv_int_table_setup_; - class setup_string_pv_table { public: @@ -41,206 +30,6 @@ namespace simple_epics setup_double_pv_table pv_double_table_setup_; - /* - * Start of int PV - */ - - gddAppFuncTable< simpleIntPV >& - simpleIntPV::get_func_table( ) - { - static gddAppFuncTable< simpleIntPV > func_table; - return func_table; - } - - simpleIntPV::~simpleIntPV( ) = default; - - gddAppFuncTableStatus - simpleIntPV::read_status( gdd& g ) - { - g.putConvert( val_->getStat( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_severity( gdd& g ) - { - g.putConvert( val_->getSevr( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_precision( gdd& g ) - { - g.putConvert( 0 ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_alarm_high( gdd& g ) - { - g.putConvert( attr_.alarm_high( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_alarm_low( gdd& g ) - { - g.putConvert( attr_.alarm_low( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_warn_high( gdd& g ) - { - g.putConvert( attr_.warn_high( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_warn_low( gdd& g ) - { - g.putConvert( attr_.warn_low( ) ); - return S_casApp_success; - } - - gddAppFuncTableStatus - simpleIntPV::read_value( gdd& g ) - { - auto status = - gddApplicationTypeTable::app_table.smartCopy( &g, val_.get( ) ); - return ( status ? S_cas_noConvert : S_casApp_success ); - } - - void - simpleIntPV::setup_func_table( ) - { - auto install = - []( const char* name, - gddAppFuncTableStatus ( simpleIntPV::*handler )( gdd& ) ) { - gddAppFuncTableStatus status; - - // char error_string[100]; - - status = get_func_table( ).installReadFunc( name, handler ); - if ( status != S_gddAppFuncTable_Success ) - { - // errSymLookup(status, error_string, - // sizeof(error_string)); - // throw std::runtime_error(error_string); - throw std::runtime_error( - "Unable to initialize pv lookup table" ); - } - }; - - install( "units", &simpleIntPV::read_attr_not_handled ); - install( "status", &simpleIntPV::read_status ); - install( "severity", &simpleIntPV::read_severity ); - install( "maxElements", &simpleIntPV::read_attr_not_handled ); - install( "precision", &simpleIntPV::read_precision ); - install( "alarmHigh", &simpleIntPV::read_alarm_high ); - install( "alarmLow", &simpleIntPV::read_alarm_low ); - install( "alarmHighWarning", &simpleIntPV::read_warn_high ); - install( "alarmLowWarning", &simpleIntPV::read_warn_low ); - install( "maxElements", &simpleIntPV::read_attr_not_handled ); - install( "graphicHigh", &simpleIntPV::read_attr_not_handled ); - install( "graphicLow", &simpleIntPV::read_attr_not_handled ); - install( "controlHigh", &simpleIntPV::read_attr_not_handled ); - install( "controlLow", &simpleIntPV::read_attr_not_handled ); - install( "enums", &simpleIntPV::read_attr_not_handled ); - install( "menuitem", &simpleIntPV::read_attr_not_handled ); - install( "timestamp", &simpleIntPV::read_attr_not_handled ); - install( "value", &simpleIntPV::read_value ); - } - - caStatus - simpleIntPV::read( const casCtx& ctx, gdd& prototype ) - { - return get_func_table( ).read( *this, prototype ); - } - caStatus - simpleIntPV::write( const casCtx& ctx, const gdd& value ) - { - return S_casApp_noSupport; - } - - aitEnum - simpleIntPV::bestExternalType( ) const - { - return val_->primitiveType( ); - } - - caStatus - simpleIntPV::interestRegister( ) - { - monitored_ = true; - return S_casApp_success; - } - - void - simpleIntPV::interestDelete( ) - { - monitored_ = false; - } - - void - simpleIntPV::update( ) - { - set_value( *attr_.src( ) ); - } - - void - simpleIntPV::set_value( int value ) - { - int current_value = 0; - - val_->getConvert( current_value ); - if ( current_value == value ) - { - return; - } - - val_->putConvert( value ); - aitTimeStamp ts = aitTimeStamp( epicsTime::getCurrent( ) ); - val_->setTimeStamp( &ts ); - - aitUint16 stat = epicsAlarmNone; - aitUint16 sevr = epicsSevNone; - if ( value >= attr_.alarm_high( ) ) - { - stat = epicsAlarmHiHi; - sevr = epicsSevMajor; - } - else if ( value <= attr_.alarm_low( ) ) - { - stat = epicsAlarmLoLo; - sevr = epicsSevMajor; - } - else if ( value >= attr_.warn_high( ) ) - { - stat = epicsAlarmHigh; - sevr = epicsSevMinor; - } - else if ( value <= attr_.warn_low( ) ) - { - stat = epicsAlarmLow; - sevr = epicsSevMinor; - } - val_->setSevr( sevr ); - val_->setStat( stat ); - - if ( monitored_ ) - { - casEventMask mask = casEventMask( server_.valueEventMask( ) ); - bool alarm_changed = - ( stat != val_->getStat( ) || sevr != val_->getSevr( ) ); - if ( alarm_changed ) - { - mask |= server_.alarmEventMask( ); - } - postEvent( mask, *val_ ); - } - } - /* * Start of string PV */ @@ -334,7 +123,20 @@ namespace simple_epics caStatus simpleStringPV::write( const casCtx& ctx, const gdd& value ) { - return S_casApp_noSupport; + if ( attr_.mode( ) != PVMode::ReadWrite || attr_.src_size( ) < 1 ) + { + return S_casApp_noSupport; + } + aitString newValue; + value.get( &newValue, aitEnumString ); + if ( newValue.string( ) == nullptr ) + { + return S_casApp_noSupport; + } + set_value( newValue.string( ) ); + std::strncpy( attr_.src( ), newValue.string( ), attr_.src_size( ) ); + attr_.src( )[ attr_.src_size( ) - 1 ] == '\0'; + return S_casApp_success; } aitEnum @@ -505,7 +307,15 @@ namespace simple_epics caStatus simpleDoublePV::write( const casCtx& ctx, const gdd& value ) { - return S_casApp_noSupport; + if ( attr_.mode( ) != PVMode::ReadWrite ) + { + return S_casApp_noSupport; + } + aitFloat64 newValue; + value.get( &newValue, aitEnumFloat64 ); + set_value( newValue ); + *const_cast< double* >( attr_.src( ) ) = newValue; + return S_casApp_success; } aitEnum diff --git a/src/simple_pv/tests/test_rw_cas.cc b/src/simple_pv/tests/test_rw_cas.cc new file mode 100644 index 0000000000000000000000000000000000000000..260eb80bcbb04f278be7f89c46d48a8571278c1e --- /dev/null +++ b/src/simple_pv/tests/test_rw_cas.cc @@ -0,0 +1,52 @@ +// +// Created by jonathan.hanks on 3/14/22. +// +#include "simple_epics.hh" +#include <atomic> +#include <csignal> + +#include "fdManager.h" + +std::atomic< bool > done{ false }; + +void +signal_handler( int dummy ) +{ + done = true; +} + +int +main( int argc, char* argv[] ) +{ + using pvInt = simple_epics::pvIntAttributes; + simple_epics::Server server{ }; + + int int1val = 0; + pvInt int1{ "int1", + &int1val, + std::make_pair( -1, 100 ), + std::make_pair( -1, 100 ), + simple_epics::PVMode::ReadWrite }; + double float1val = 0.0; + simple_epics::pvDoubleAttributes float1{ "float1", + &float1val, + std::make_pair( -1, 100 ), + std::make_pair( -1, 100 ), + simple_epics::PVMode::ReadWrite }; + + server.addPV( int1 ); + server.addPV( float1 ); + + std::signal( SIGINT, signal_handler ); + server.update( ); + while ( !done ) + { + if ( int1val == 42 ) + { + done = true; + } + server.update( ); + fileDescriptorManager.process( 0. ); + } + return 0; +} \ No newline at end of file