From 09a9276c9421f3f79b9b04f3ebe6d13ef9b205ed Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Tue, 10 Mar 2026 15:02:12 +0000 Subject: [PATCH 1/5] Reconnect on error and save total energy histogram --- CAENMCAApp/Db/CAENMCADev.db | 9 ++ CAENMCAApp/src/CAENMCADriver.cpp | 143 +++++++++++++++++++------------ CAENMCAApp/src/CAENMCADriver.h | 4 + 3 files changed, 101 insertions(+), 55 deletions(-) diff --git a/CAENMCAApp/Db/CAENMCADev.db b/CAENMCAApp/Db/CAENMCADev.db index eba2632..b04eff5 100644 --- a/CAENMCAApp/Db/CAENMCADev.db +++ b/CAENMCAApp/Db/CAENMCADev.db @@ -22,6 +22,15 @@ record(waveform, "$(P)$(Q)DEVICE:ADDR") info(archive, "VAL") } +record(longin, "$(P)$(Q)NUMRECONNECT") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),0,0)NUMRECONNECT") + field(SCAN, "I/O Intr") + field(EGU, "") + info(archive, "VAL") +} + record(stringin, "$(P)$(Q)FILEDIRPREFIX") { field(DESC, "File Dir Prefix") diff --git a/CAENMCAApp/src/CAENMCADriver.cpp b/CAENMCAApp/src/CAENMCADriver.cpp index f885540..1cecf31 100644 --- a/CAENMCAApp/src/CAENMCADriver.cpp +++ b/CAENMCAApp/src/CAENMCADriver.cpp @@ -390,12 +390,14 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const 0, /* Default priority */ 0), /* Default stack size*/ m_famcode(CAEN_MCA_FAMILY_CODE_UNKNOWN),m_device_h(NULL),m_old_list_filename(2),m_file_fd(2, std::tuple{NULL,NULL}), - m_event_file_last_pos(2, 0),m_frame_time(2, 0),m_max_event_time(2, 0),m_pRaw(NULL), m_file_dir("ibex") + m_event_file_last_pos(2, 0),m_frame_time(2, 0),m_max_event_time(2, 0),m_pRaw(NULL), m_file_dir("ibex"), + m_device_addr(deviceAddr) { const char *functionName = "CAENMCADriver"; createParam(P_deviceNameString, asynParamOctet, &P_deviceName); createParam(P_deviceAddrString, asynParamOctet, &P_deviceAddr); + createParam(P_numReconnectString, asynParamInt32, &P_numReconnect); createParam(P_availableConfigurationsString, asynParamOctet, &P_availableConfigurations); createParam(P_configurationString, asynParamOctet, &P_configuration); createParam(P_numEnergySpecString, asynParamInt32, &P_numEnergySpec); @@ -565,13 +567,10 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const setStringParam(P_deviceName, deviceName); setStringParam(P_deviceAddr, deviceAddr); - m_device_h = CAENMCA::OpenDevice(deviceAddr, NULL); + setIntegerParam(P_numReconnect, 0); + connectDevice(); CAENMCA::GetData(m_device_h, CAEN_MCA_DATA_BOARD_INFO, DATAMASK_BRDINFO_FAMCODE, &m_famcode); getBoardInfo(); - - CAENMCA::getHandlesFromCollection(m_device_h, CAEN_MCA_HANDLE_CHANNEL, m_chan_h); - CAENMCA::getHandlesFromCollection(m_device_h, CAEN_MCA_HANDLE_HVCHANNEL, m_hv_chan_h); - getHVInfo(0); getHVInfo(1); @@ -604,6 +603,23 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const } } + +void CAENMCADriver::connectDevice() +{ + int nconnect = 0; + getIntegerParam(P_numReconnect, &nconnect); + setIntegerParam(P_numReconnect, ++nconnect); + if (m_device_h != NULL) { + CAENMCA::closeDevice(m_device_h); + m_device_h = NULL; + } + std::cerr << "Opening connection to " << m_device_addr << std::endl; + m_device_h = CAENMCA::OpenDevice(m_device_addr, NULL); + CAENMCA::getHandlesFromCollection(m_device_h, CAEN_MCA_HANDLE_CHANNEL, m_chan_h); + CAENMCA::getHandlesFromCollection(m_device_h, CAEN_MCA_HANDLE_HVCHANNEL, m_hv_chan_h); + std::cerr << "Successfully connected to " << m_device_addr << std::endl; +} + void CAENMCADriver::setRunNumberFromIRunNumber() { char runNumber[16]; @@ -934,7 +950,7 @@ std::string CAENMCADriver::createTemplateNexusFile(const std::string& filePrefix event2_energy_group.createDataSet("event_time_max", tmax); event2_energy_group.createDataSet("num_events", nevents); event2_energy_group.createDataSet("desc", desc); - + std::string event_energy2d_group_name = "detector_" + std::to_string(k) + "_energy2D"; hf::Group event_energy2d_group = createNeXusGroup(raw_data_1, event_energy2d_group_name, "NXdata"); int eventSpec_2d_nTBins = 0, eventSpec_2d_engBinGroup = 1; @@ -947,6 +963,21 @@ std::string CAENMCADriver::createTemplateNexusFile(const std::string& filePrefix if (driver->m_event_spec_2d[i].size() > 0) { counts2d.write_raw(driver->m_event_spec_2d[i].data()); } + + std::string energyHist_group_name = "detector_" + std::to_string(k) + "_energyHist"; + hf::Group energyHist_group = createNeXusGroup(raw_data_1, energyHist_group_name, "NXdata"); + hf::DataSet hist_counts = energyHist_group.createDataSet("counts", driver->m_energy_spec[i]); + hist_counts.createAttribute("signal", 1); + std::vector energyHist_x(driver->m_energy_spec[i].size()); + for(int j=0; jgetIntegerParam(i, driver->P_energySpecCounts, &nevents); + energyHist.createAttribute("scaleA", scaleA); + energyHist.createAttribute("scaleB", scaleB); + energyHist_group.createDataSet("num_events", nevents); + ++k; } } @@ -1636,68 +1667,70 @@ void CAENMCADriver::energySpectrumSetProperty(CAEN_MCA_HANDLE channel, int32_t s void CAENMCADriver::pollerTask() { - bool new_data; - epicsThreadSleep(0.2); // to allow class constructror to complete + bool new_data, reconnect = false; + epicsThreadSleep(0.2); // to allow class constructror to complete lock(); std::string deviceName; getStringParam(P_deviceName, deviceName); unlock(); - while(true) - { - lock(); + while(true) + { + lock(); try { - - //std::cerr << "hv0 on " << isHVOn(m_hv_chan_h[0]) << std::endl; - //std::cerr << "hv1 on " << isHVOn(m_hv_chan_h[1]) << std::endl; - - //std::cerr << isAcqRunning() << " " << isAcqRunning(m_chan_h[0]) << " " << isAcqRunning(m_chan_h[1]) << std::endl; - for(int i=0;i<2; ++i) - { - getEnergySpectrum(i, 0, m_energy_spec[i]); - doCallbacksInt32Array(m_energy_spec[i].data(), m_energy_spec[i].size(), P_energySpec, i); - getHVInfo(i); - getChannelInfo(i); - getLists(i); - if (!isAcqRunning(m_chan_h[i])) { - setDoubleParam(i, P_eventSpecRate, 0.0); - setDoubleParam(i, P_eventsSpecTriggerRate, 0.0); + if (reconnect) { + epicsThreadSleep(5.0); // sleep to avoid too many reconnections + connectDevice(); + setParamStatus(0, P_eventsSpecNTriggers, asynSuccess); // to clear an alarm in the DB + reconnect = false; } - new_data = processListFile(i); - setIntegerParam(i, P_loadDataStatus, 2); - callParamCallbacks(i); - updateAD(i, new_data); - doCallbacksFloat64Array(m_event_spec_x[i].data(), m_event_spec_x[i].size(), P_eventsSpecX, i); - doCallbacksFloat64Array(m_event_spec_y[i].data(), m_event_spec_y[i].size(), P_eventsSpecY, i); - doCallbacksInt32Array(m_energy_spec_event[i].data(), m_energy_spec_event[i].size(), P_energySpecEvent, i); - doCallbacksInt32Array(m_energy_spec2_event[i].data(), m_energy_spec2_event[i].size(), P_energySpec2Event, i); - setIntegerParam(i, P_loadDataStatus, 0); - callParamCallbacks(i); - } - bool acqRunning = isAcqRunning(); - setIntegerParam(P_acqRunning, (acqRunning ? 1 : 0)); - std::vector configs_v; - listConfigurations(configs_v); - std::string configs; - - for(int i=0; i configs_v; + listConfigurations(configs_v); + std::string configs; + + for(int i=0; i int computeArray(int addr, const std::vector& data, int maxSizeX, int maxSizeY); CAEN_MCA_HANDLE m_device_h; + std::string m_device_addr; epicsTime m_start_time[2]; epicsTime m_stop_time[2]; std::vector m_chan_h; @@ -123,6 +125,7 @@ class epicsShareClass CAENMCADriver : public ADDriver int P_deviceName; // string int P_deviceAddr; // string + int P_numReconnect; // int int P_availableConfigurations; // string int P_configuration; // string int P_numEnergySpec; // int @@ -257,6 +260,7 @@ class epicsShareClass CAENMCADriver : public ADDriver #define P_deviceNameString "DEVICENAME" #define P_deviceAddrString "DEVICEADDR" +#define P_numReconnectString "NUMRECONNECT" #define P_availableConfigurationsString "CONFIG_AVAIL" #define P_configurationString "CONFIG" #define P_numEnergySpecString "NUMENERGYSPEC" From 1457eb3eea1662a8df460e366c41d8148b3c74f7 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Fri, 27 Mar 2026 23:44:39 +0000 Subject: [PATCH 2/5] print hexagon name on timing reg error --- CAENMCAApp/Db/CAENMCADev.db | 1 + CAENMCAApp/src/CAENMCADriver.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CAENMCAApp/Db/CAENMCADev.db b/CAENMCAApp/Db/CAENMCADev.db index b04eff5..ac4ef4e 100644 --- a/CAENMCAApp/Db/CAENMCADev.db +++ b/CAENMCAApp/Db/CAENMCADev.db @@ -164,5 +164,6 @@ record(bi, "$(P)$(Q)TIMEREGS") field(INP, "@asyn($(PORT),0,0)TIMINGREGISTERS") field(ZNAM, "Error") field(ONAM, "OK") + field(ZSV, "MAJOR") field(SCAN, "I/O Intr") } diff --git a/CAENMCAApp/src/CAENMCADriver.cpp b/CAENMCAApp/src/CAENMCADriver.cpp index 1cecf31..3c69484 100644 --- a/CAENMCAApp/src/CAENMCADriver.cpp +++ b/CAENMCAApp/src/CAENMCADriver.cpp @@ -584,7 +584,7 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const // setTimingRegisters(); if (!checkTimingRegisters()) { - std::cerr << "WARNING: Timing registers not set" << std::endl; + std::cerr << "WARNING: Timing registers not set on " << deviceName << std::endl; } std::string ethPrefix = "eth://", deviceAddr_s(deviceAddr); @@ -1270,8 +1270,10 @@ void CAENMCADriver::stopAcquisition(int addr, int value) void CAENMCADriver::startAcquisition(int addr, int value) { // setTimingRegisters(); + std::string deviceName; + getStringParam(P_deviceName, deviceName); if (!checkTimingRegisters()) { - std::cerr << "WARNING: Timing registers not set" << std::endl; + std::cerr << "WARNING: Timing registers not set on " << deviceName << std::endl; } if (value < 2) // is it a bo record sending 0 or 1, if so single channel and use asyn addr for channel { From 892757c2a147812c5251a506bd4fd158d18fe3e1 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Sat, 28 Mar 2026 00:01:58 +0000 Subject: [PATCH 3/5] print hexagon name on timing reg error --- CAENMCAApp/src/CAENMCADriver.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CAENMCAApp/src/CAENMCADriver.cpp b/CAENMCAApp/src/CAENMCADriver.cpp index 3c69484..106bb11 100644 --- a/CAENMCAApp/src/CAENMCADriver.cpp +++ b/CAENMCAApp/src/CAENMCADriver.cpp @@ -1257,6 +1257,11 @@ void CAENMCADriver::getHVInfo(uint32_t hv_chan_id) void CAENMCADriver::stopAcquisition(int addr, int value) { + std::string deviceName; + getStringParam(P_deviceName, deviceName); + if (!checkTimingRegisters()) { + std::cerr << "WARNING: Timing registers not set on " << deviceName << std::endl; + } if (value < 2) // is it a bo record sending 0 or 1, if so single channel and use asyn addr for channel { controlAcquisition(1 << addr, false); From 4613f4fb842d8daa646da28658c808b4b5464911 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Thu, 2 Apr 2026 23:51:40 +0100 Subject: [PATCH 4/5] Check timing registers evry loop --- CAENMCAApp/src/CAENMCADriver.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CAENMCAApp/src/CAENMCADriver.cpp b/CAENMCAApp/src/CAENMCADriver.cpp index 106bb11..28f4429 100644 --- a/CAENMCAApp/src/CAENMCADriver.cpp +++ b/CAENMCAApp/src/CAENMCADriver.cpp @@ -1674,7 +1674,7 @@ void CAENMCADriver::energySpectrumSetProperty(CAEN_MCA_HANDLE channel, int32_t s void CAENMCADriver::pollerTask() { - bool new_data, reconnect = false; + bool new_data, reconnect = false, warn_timing_reg = true; epicsThreadSleep(0.2); // to allow class constructror to complete lock(); std::string deviceName; @@ -1690,6 +1690,7 @@ void CAENMCADriver::pollerTask() connectDevice(); setParamStatus(0, P_eventsSpecNTriggers, asynSuccess); // to clear an alarm in the DB reconnect = false; + warn_timing_reg = true; } for(int i=0;i<2; ++i) { @@ -1713,6 +1714,14 @@ void CAENMCADriver::pollerTask() setIntegerParam(i, P_loadDataStatus, 0); callParamCallbacks(i); } + if (!checkTimingRegisters()) { + if (warn_timing_reg) { + std::cerr << "WARNING: Timing registers not set on " << deviceName << std::endl; + warn_timing_reg = false; + } + } else { + warn_timing_reg = true; + } bool acqRunning = isAcqRunning(); setIntegerParam(P_acqRunning, (acqRunning ? 1 : 0)); std::vector configs_v; @@ -1727,7 +1736,7 @@ void CAENMCADriver::pollerTask() configs += ","; } } - setStringParam(P_availableConfigurations, configs.c_str()); + setStringParam(P_availableConfigurations, configs.c_str()); } catch(const std::exception& ex) { std::cerr << "exception in pollerTask: " << deviceName << ": " << ex.what() << std::endl; From 8f7430fa308b8f79b86e2d8ba8f46ea2eed9f306 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Thu, 30 Apr 2026 22:17:00 +0100 Subject: [PATCH 5/5] Update sim mode --- CAENMCAApp/src/CAENMCADriver.cpp | 228 +++++++++++++++++++++++++++---- 1 file changed, 203 insertions(+), 25 deletions(-) diff --git a/CAENMCAApp/src/CAENMCADriver.cpp b/CAENMCAApp/src/CAENMCADriver.cpp index 28f4429..d7f7451 100644 --- a/CAENMCAApp/src/CAENMCADriver.cpp +++ b/CAENMCAApp/src/CAENMCADriver.cpp @@ -94,16 +94,6 @@ static int spawnCommand(const std::string& program, const std::string& args) #endif -/// EPICS driver report function for iocsh dbior command -void CAENMCADriver::report(FILE* fp, int details) -{ - uint32_t val0 = 0, val1 = 0; - readRegister(0x10B8, val0); - readRegister(0x11B8, val1); - fprintf(fp, "0x10B8 and 0x11B8 registers for setting timing are: %u %u\n", val0, val1); - ADDriver::report(fp, details); -} - void CAENMCADriver::setADAcquire(int addr, int acquire) { int adstatus; @@ -235,6 +225,10 @@ class CAENMCAException : public std::runtime_error throw CAENMCAException(__func, __ret); \ } +static std::map sim_double_params; +static std::map sim_string_params; +static int sim_handle_counter = 1; + struct CAENMCA { static bool simulate; @@ -247,7 +241,8 @@ struct CAENMCA h = CAEN_MCA_OpenDevice(path.c_str(), &retcode, index); ERROR_CHECK("CAENMCA::OpenDevice()", retcode); } else { - std::cerr << "Opening simulated device for " << path << std::endl; + std::cerr << "Opening simulated device for " << path << " id " << sim_handle_counter << std::endl; + h = (CAEN_MCA_HANDLE)(sim_handle_counter++); } return h; } @@ -261,7 +256,103 @@ struct CAENMCA static void GetData(CAEN_MCA_HANDLE handle, CAEN_MCA_DataType_t dataType, uint64_t dataMask, ...) { - if (!simulate) { + if (simulate) { + va_list args; + va_start(args, dataMask); + if (dataType == CAEN_MCA_DATA_PARAMETER_VALUE) { + const char* param_name = (const char*)handle; + if (dataMask == DATAMASK_VALUE_NUMERIC) { + double* value = va_arg(args, double*); + *value = sim_double_params[param_name]; + } else if (dataMask == DATAMASK_VALUE_CODENAME) { + char* value = va_arg(args, char*); + strcpy(value, sim_string_params[param_name].c_str()); + } else { + std::cerr << "getdata: unknown datamask for CAEN_MCA_DATA_PARAMETER_VALUE " << dataMask << std::endl; + } + } else if (dataType == CAEN_MCA_DATA_LIST_MODE) { + if (dataMask & DATAMASK_LIST_ENABLE) { + dataMask &= ~DATAMASK_LIST_ENABLE; + *va_arg(args, uint32_t*) = sim_double_params["LIST_ENABLE" + std::to_string((unsigned long)handle)]; + } + if (dataMask & DATAMASK_LIST_SAVEMODE) { + dataMask &= ~DATAMASK_LIST_SAVEMODE; + *va_arg(args, CAEN_MCA_ListSaveMode_t*) = (CAEN_MCA_ListSaveMode_t)sim_double_params["LIST_SAVEMODE" + std::to_string((unsigned long)handle)]; + } + if (dataMask & DATAMASK_LIST_FILENAME) { + dataMask &= ~DATAMASK_LIST_FILENAME; + strcpy(va_arg(args, char*), sim_string_params["LIST_FILENAME" + std::to_string((unsigned long)handle)].c_str()); + } + if (dataMask & DATAMASK_LIST_FILE_DATAMASK) { + dataMask &= ~DATAMASK_LIST_FILE_DATAMASK; + *va_arg(args, uint32_t*) = sim_double_params["LIST_FILE_DATAMASK" + std::to_string((unsigned long)handle)]; + } + if (dataMask & DATAMASK_LIST_GETFAKEEVTS) { + dataMask &= ~DATAMASK_LIST_GETFAKEEVTS; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask & DATAMASK_LIST_MAXNEVTS) { + dataMask &= ~DATAMASK_LIST_MAXNEVTS; + *va_arg(args, uint32_t*) = 1024; + } + if (dataMask & DATAMASK_LIST_NEVTS) { + dataMask &= ~DATAMASK_LIST_NEVTS; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask != 0) { + std::cerr << "getdata: unknown datamask for CAEN_MCA_DATA_LIST_MODE " << dataMask << std::endl; + } + } else if (dataType == CAEN_MCA_DATA_ENERGYSPECTRUM) { + if (dataMask & DATAMASK_ENERGY_SPECTRUM_ARRAY) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_ARRAY; + memset(va_arg(args, uint32_t*), 0, ENERGYSPECTRUM_MAXLEN * sizeof(uint32_t)); + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_RTIME) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_RTIME; + *va_arg(args, uint64_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_LTIME) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_LTIME; + *va_arg(args, uint64_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_DTIME) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_DTIME; + *va_arg(args, uint64_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_OVERFLOW) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_OVERFLOW; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_UNDERFLOW) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_UNDERFLOW; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_NENTRIES) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_NENTRIES; + *va_arg(args, uint64_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_NROIS) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_NROIS; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_FILENAME) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_FILENAME; + strcpy(va_arg(args, char*), sim_string_params["ES_FILENAME" + std::to_string((unsigned long)handle)].c_str()); + } + if (dataMask & DATAMASK_ENERGY_SPECTRUM_AUTOSAVE_PERIOD) { + dataMask &= ~DATAMASK_ENERGY_SPECTRUM_AUTOSAVE_PERIOD; + *va_arg(args, uint32_t*) = 0; + } + if (dataMask != 0) { + std::cerr << "getdata: unknown datamask for CAEN_MCA_DATA_ENERGYSPECTRUM " << dataMask << std::endl; + } + } else if (dataType & (CAEN_MCA_DATA_HVCHANNEL_INFO|CAEN_MCA_DATA_HVRANGE_INFO|CAEN_MCA_DATA_CHANNEL_INFO)) { + ; + } else { + std::cerr << "getdata: unknown datatype " << dataType << std::endl; + } + va_end(args); + } else { va_list args; va_start(args, dataMask); int32_t retcode = CAEN_MCA_GetDataV(handle, dataType, dataMask, args); @@ -272,7 +363,52 @@ struct CAENMCA static void SetData(CAEN_MCA_HANDLE handle, CAEN_MCA_DataType_t dataType, uint64_t dataMask, ...) { - if (!simulate) { + if (simulate) { + va_list args; + va_start(args, dataMask); + if (dataType == CAEN_MCA_DATA_PARAMETER_VALUE) { + const char* param_name = (const char*)handle; + if (dataMask == DATAMASK_VALUE_NUMERIC) { + double value = va_arg(args, double); + sim_double_params[param_name] = value; + } else if (dataMask == DATAMASK_VALUE_CODENAME) { + const char* value = va_arg(args, char*); + sim_string_params[param_name] = value; + } else { + std::cerr << "setdata: unknown datamask for CAEN_MCA_DATA_PARAMETER_VALUE " << dataMask << std::endl; + } + } else if (dataType == CAEN_MCA_DATA_LIST_MODE) { + if (dataMask & DATAMASK_LIST_ENABLE) { + dataMask &= ~DATAMASK_LIST_ENABLE; + sim_double_params["LIST_ENABLE" + std::to_string((unsigned long)handle)] = va_arg(args, uint32_t); + } + if (dataMask & DATAMASK_LIST_SAVEMODE) { + dataMask &= ~DATAMASK_LIST_SAVEMODE; + sim_double_params["LIST_SAVEMODE" + std::to_string((unsigned long)handle)] = va_arg(args, uint32_t); + } + if (dataMask & DATAMASK_LIST_FILENAME) { + dataMask &= ~DATAMASK_LIST_FILENAME; + sim_string_params["LIST_FILENAME" + std::to_string((unsigned long)handle)] = va_arg(args, char*); + } + if (dataMask & DATAMASK_LIST_FILE_DATAMASK) { + dataMask &= ~DATAMASK_LIST_FILE_DATAMASK; + sim_double_params["LIST_FILE_DATAMASK" + std::to_string((unsigned long)handle)] = va_arg(args, uint32_t); + } + if (dataMask != 0) { + std::cerr << "setdata: unknown datamask for CAEN_MCA_DATA_LIST_MODE " << dataMask << std::endl; + } + } else if (dataType == CAEN_MCA_DATA_ENERGYSPECTRUM) { + if (dataMask == DATAMASK_ENERGY_SPECTRUM_FILENAME) { + const char* value = va_arg(args, char*); + sim_string_params["ES_FILENAME" + std::to_string((unsigned long)handle)] = value; + } else { + std::cerr << "setdata: unknown datamask CAEN_MCA_DATA_ENERGYSPECTRUM " << dataMask << std::endl; + } + } else { + std::cerr << "setdata: unknown datatype " << dataType << std::endl; + } + va_end(args); + } else { va_list args; va_start(args, dataMask); int32_t retcode = CAEN_MCA_SetDataV(handle, dataType, dataMask, args); @@ -283,7 +419,13 @@ struct CAENMCA static void SendCommand(CAEN_MCA_HANDLE handle, CAEN_MCA_CommandType_t cmdType, uint64_t cmdMaskIn, uint64_t cmdMaskOut, ...) { - if (!simulate) { + if (simulate) { + if (cmdType == CAEN_MCA_CMD_ACQ_START) { + sim_double_params["PARAM_ACQRUNNING"+ std::to_string((unsigned long)handle)] = 1.0; + } else if (cmdType == CAEN_MCA_CMD_ACQ_STOP) { + sim_double_params["PARAM_ACQRUNNING"+ std::to_string((unsigned long)handle)] = 0.0; + } + } else { va_list args; va_start(args, cmdMaskOut); int32_t retcode = CAEN_MCA_SendCommandV(handle, cmdType, cmdMaskIn, cmdMaskOut, args); @@ -301,6 +443,8 @@ struct CAENMCA { throw CAENMCAException("GetChildHandle(): failed"); } + } else { + h = (CAEN_MCA_HANDLE)(100 * (unsigned)handle + 10 * handleType + index + 1); } return h; } @@ -308,7 +452,13 @@ struct CAENMCA static CAEN_MCA_HANDLE GetChildHandleByName(CAEN_MCA_HANDLE handle, CAEN_MCA_HandleType_t handleType, const std::string& name) { CAEN_MCA_HANDLE h = NULL; - if (!simulate) { + if (simulate) { + if (handleType == CAEN_MCA_HANDLE_PARAMETER) { + return (CAEN_MCA_HANDLE)strdup((name + std::to_string((unsigned long)handle)).c_str()); + } else { + std::cerr << "GetChildHandleByName " << name << std::endl; + } + } else { h = CAEN_MCA_GetChildHandleByName(handle, handleType, name.c_str()); if (h == NULL) { @@ -322,8 +472,8 @@ struct CAENMCA { handles.resize(0); if (simulate) { - handles.push_back(NULL); - handles.push_back(NULL); + handles.push_back((CAEN_MCA_HANDLE)(10 * (unsigned)parent + 1)); + handles.push_back((CAEN_MCA_HANDLE)(10 * (unsigned)parent + 2)); return; } CAEN_MCA_HANDLE collection = CAENMCA::GetChildHandle(parent, CAEN_MCA_HANDLE_COLLECTION, handleType); @@ -373,7 +523,27 @@ struct CAENMCA bool CAENMCA::simulate = false; -/// Constructor for the webgetDriver class. +/// EPICS driver report function for iocsh dbior command +void CAENMCADriver::report(FILE* fp, int details) +{ + uint32_t val0 = 0, val1 = 0; + readRegister(0x10B8, val0); + readRegister(0x11B8, val1); + fprintf(fp, "0x10B8 and 0x11B8 registers for setting timing are: %u %u\n", val0, val1); + ADDriver::report(fp, details); + if (CAENMCA::simulate) { + fprintf(fp, "sim double\n"); + for(const auto& pair : sim_double_params) { + fprintf(fp, "%s = %f\n", pair.first.c_str(), pair.second); + } + fprintf(fp, "sim string\n"); + for(const auto& pair : sim_string_params) { + fprintf(fp, "%s = \"%s\"\n", pair.first.c_str(), pair.second.c_str()); + } + } +} + +/// Constructor /// Calls constructor for the asynPortDriver base class and sets up driver parameters. /// /// \param[in] portName @copydoc initArg0 @@ -555,7 +725,7 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const status |= setDoubleParam(i, P_eventSpecRate, 0.0); } - if (status) { + if (status) { printf("%s: unable to set CAENMCA parameters\n", functionName); return; } @@ -592,7 +762,10 @@ CAENMCADriver::CAENMCADriver(const char *portName, const char* deviceAddr, const if (!deviceAddr_s.compare(0, ethPrefix.size(), ethPrefix)) { m_share_path = std::string("\\\\") + deviceAddr_s.substr(ethPrefix.size()) + "\\storage"; } - + if (getenv("SHARE_PATH") != NULL) { + m_share_path = std::string(getenv("SHARE_PATH")) + "\\" + deviceName; + } + std::cerr << "Using share path " << m_share_path << std::endl; if (epicsThreadCreate("CAENMCADriverPoller", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), @@ -839,22 +1012,27 @@ void CAENMCADriver::copyData(const std::string& dataFile, const std::string& fil const char* runNumber, const std::string& copyDataArgs) { static const char* copycmd = getenv("HEXAGON_COPYCMD"); - if (copycmd == NULL) { - std::cerr << "No HEXAGON_COPYCMD defined" << std::endl; + static const char* convertcmd = getenv("HEXAGON_CONVERTCMD"); + if (copycmd == NULL || convertcmd == NULL) { + std::cerr << "No HEXAGON_COPYCMD/HEXAGON_CONVERTCMD defined" << std::endl; return; } std::string copycmd_s(copycmd); std::replace(copycmd_s.begin(), copycmd_s.end(), '/', '\\'); + std::string convertcmd_s(convertcmd); + std::replace(convertcmd_s.begin(), convertcmd_s.end(), '/', '\\'); std::ostringstream args; args << dataFile << " " << filePrefix << " " << runNumber << " " << copyDataArgs; - std::cerr << "Running \"" << copycmd_s << "\" " << args.str() << std::endl; #ifdef _WIN32 if (CAENMCA::simulate) { - std::cerr << "Not running copy command as simulation mode" << std::endl; + std::cerr << "Running converter but not copier command as simulation mode" << std::endl; + std::cerr << "Running \"" << convertcmd_s << "\" " << args.str() << std::endl; + spawnCommand(convertcmd_s, args.str()); } else { + std::cerr << "Running \"" << copycmd_s << "\" " << args.str() << std::endl; spawnCommand(copycmd_s, args.str()); - epicsThreadSleep(1.0); } + epicsThreadSleep(1.0); #endif /* _WIN32 */ }