From 1e853487aaccf7895d7091ed86d9b950a3f2c58c Mon Sep 17 00:00:00 2001 From: Edward Wall Date: Fri, 31 Oct 2025 13:23:55 +0100 Subject: [PATCH] adds a POC preset based count --- Makefile | 2 +- db/channels.db | 96 +++++++++++++- db/daq_common.db | 212 ++++++++++++++++++++++++++++++ scripts/st.cmd | 5 + src/asynStreamGeneratorDriver.cpp | 182 ++++++++++++++++--------- src/asynStreamGeneratorDriver.h | 25 ++++ 6 files changed, 460 insertions(+), 62 deletions(-) create mode 100644 db/daq_common.db diff --git a/Makefile b/Makefile index f17d207..5ae267d 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ REQUIRED+=asyn DBDS += src/asynStreamGeneratorDriver.dbd # DB files to include in the release -TEMPLATES += db/channels.db +TEMPLATES += db/channels.db db/daq_common.db # HEADERS += src/asynStreamGeneratorDriver.h diff --git a/db/channels.db b/db/channels.db index ef0175e..7073cef 100644 --- a/db/channels.db +++ b/db/channels.db @@ -3,9 +3,93 @@ # Macros # INSTR - Prefix # NAME - the device name, e.g. EL737 -# PORT - Stream Generator Port +# PORT - StreamGenerator Port # CHANNEL - the number associated with the measurment channel +################################################################################ +# Status Variables + +# # Trigger a change in status as clearing +# record(bo, "$(INSTR)$(NAME):T$(CHANNEL)") +# { +# field(DESC, "Trigger Clearing Status") +# field(VAL, 1) +# field(OUT, "$(INSTR)$(NAME):S$(CHANNEL) PP") +# } +# +# # Trigger a change in status as value returned to 0 +# record(seq, "$(INSTR)$(NAME):O$(CHANNEL)") +# { +# field(DESC, "Trigger Returned to 0 Status") +# field(LNK0, "$(INSTR)$(NAME):S$(CHANNEL) PP") +# field(DO0, 0) +# field(SELM, "Specified") +# field(SELL, "$(INSTR)$(NAME):M$(CHANNEL).VAL") +# } +# +# # Current Status of Channel, i.e. is it ready to count? +# record(bi, "$(INSTR)$(NAME):S$(CHANNEL)") +# { +# field(DESC, "Channel Status") +# field(VAL, 0) +# field(ZNAM, "OK") +# field(ONAM, "CLEARING") +# } + +################################################################################ +# Count Commands + +# # Unfortunately, clearing the channels is somewhat complicated as a result of +# # the addition of more channels over time and minimal changes to the underlying interface +# # +# # Urs Greuter provided the following explanation: +# # +# # bei den Befehlen CC r und HC r ist der Parameter r als bit-Maske zu verstehen: +# # +# # Bit0: Zähler Channel 1 +# # Bit2: Zähler Channel 2 +# # Bit3: Zähler Channel 3 +# # Bit4: Zähler Channel 4 +# # Bit5: Zähler Channel Timer +# # Bit6: Zähler Channel 5 +# # Bit7: Zähler Channel 6 +# # Bit8: Zähler Channel 7 +# # Bit9: Zähler Channel 8 +# # +# # Beispiele: +# # CC 1 setzt den Zähler des Channels 1 zurück +# # CC 4 setzt den Zähler des Channels 3 zurück +# # CC 5 setzt gleichzeitig die Zähler der Channels 1 und 3 zurück +# # CC 16 ist gleichbedeutend wie CT (Timer zurücksetzen) +# # CC 511 setzt gleichzeitig die Zähler aller Kanäle (auch des Timers) zurück. +# +# record(calc, "$(INSTR)$(NAME):BM$(CHANNEL)") +# { +# field(DESC, "Bit Mask for Channel") +# field(INPA, $(CHANNEL)) +# field(CALC, "A > 4 ? 2 ^ A : 2 ^ (A-1)") +# field(PINI, "YES") +# } +# +# record(longout, "$(INSTR)$(NAME):C$(CHANNEL)") +# { +# field(DESC, "Clear the current channel count") +# field(DTYP, "stream") +# field(OMSL, "closed_loop") +# field(DOL, "$(INSTR)$(NAME):BM$(CHANNEL) NPP") +# field(OUT, "@... clearChannel($(INSTR)$(NAME):) $(PORT)") +# field(FLNK, "$(INSTR)$(NAME):T$(CHANNEL)") +# } +# +# record(ao,"$(INSTR)$(NAME):THRESH$(CHANNEL)") +# { +# field(DESC, "Sets min rate for counting to proceed") +# field(OMSL, "supervisory") +# field(OROC, "0") +# field(OUT, "@... setMinRate($(INSTR)$(NAME):, $(CHANNEL)) $(PORT)") +# field(DTYP, "stream") +# } + ################################################################################ # Read all monitors values @@ -15,6 +99,16 @@ record(longin, "$(INSTR)$(NAME):M$(CHANNEL)") field(EGU, "cts") field(DTYP, "asynInt32") field(INP, "@asyn($(PORT),0,$(TIMEOUT=1)) COUNTS$(CHANNEL)") + # This is probably too fast. We could trigger things the same as sinqDAQ to ensure the db is update in the same order field(SCAN, "I/O Intr") field(PINI, "YES") } + +# record(ai, "$(INSTR)$(NAME):R$(CHANNEL)") +# { +# field(DESC, "Rate of DAQ CH$(CHANNEL)") +# field(INP, "@... readRate($(INSTR)$(NAME):, $(CHANNEL)) $(PORT)") +# field(DTYP, "stream") +# field(EGU, "cts/sec") +# field(SCAN, "1 second") +# } diff --git a/db/daq_common.db b/db/daq_common.db new file mode 100644 index 0000000..a5e5501 --- /dev/null +++ b/db/daq_common.db @@ -0,0 +1,212 @@ +# EPICS Database for streamdevice specific to measurement channels +# +# Macros +# INSTR - Prefix +# NAME - the device name, e.g. EL737 +# PORT - StreamGenerator Port + +record(longout, "$(INSTR)$(NAME):FULL-RESET") +{ + field(DESC, "Reset the DAQ") + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),0,$(TIMEOUT=1)) RESET") +} + +################################################################################ +# Status Variables + +# record(stringin, "$(INSTR)$(NAME):MsgTxt") +# { +# field(DESC, "Unexpected received response") +# field(DTYP, "devDAQStringError") +# field(FLNK, "$(INSTR)$(NAME):INVALID-CONFIG") +# } + +record(mbbi, "$(INSTR)$(NAME):STATUS") +{ + field(DESC, "DAQ Status") + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),0,$(TIMEOUT=1)) STATUS") + field(ZRVL, "0") + field(ZRST, "Idle") + field(ONVL, "1") + field(ONST, "Counting") + field(TWVL, "2") + field(TWST, "Low rate") + field(THVL, "3") + field(THST, "Paused") + # 4 should never happen, if it does it means the DAQ reports undocumented statusbits + field(FRVL, "4") + field(FRST, "INVALID") + # This is probably too fast. We could trigger things the same as sinqDAQ to ensure the db is update in the same order + field(SCAN, "I/O Intr") + field(PINI, "YES") +} + +record(longin, "$(INSTR)$(NAME):CHANNELS") +{ + field(DESC, "Total Supported Channels") + field(VAL, $(CHANNELS)) + field(DISP, 1) +} + +# # Trigger a change in status as clearing +# record(bo, "$(INSTR)$(NAME):ETT") +# { +# field(DESC, "Trigger Clearing Status") +# field(VAL, 1) +# field(OUT, "$(INSTR)$(NAME):ETS PP") +# } +# +# # Trigger a change in status as value returned to 0 +# record(seq, "$(INSTR)$(NAME):ETO") +# { +# field(DESC, "Trigger Returned to 0 Status") +# field(LNK0, "$(INSTR)$(NAME):ETS PP") +# field(DO0, 0) +# field(SELM, "Specified") +# field(SELL, "$(INSTR)$(NAME):ELAPSED-TIME.VAL") +# } +# +# # Current Status of Channel, i.e. is it ready to count? +# record(bi, "$(INSTR)$(NAME):ETS") +# { +# field(DESC, "Channel Status") +# field(VAL, 0) +# field(ZNAM, "OK") +# field(ONAM, "CLEARING") +# } + +################################################################################ +# Count Commands + +record(ao,"$(INSTR)$(NAME):PRESET-COUNT") +{ + field(DESC, "Count until preset reached") + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),0,$(TIMEOUT=1)) P_CNT") + field(VAL, 0) + field(PREC, 2) +} + +# record(ao,"$(INSTR)$(NAME):PRESET-TIME") +# { +# field(DESC, "Count for specified time") +# field(DTYP, "stream") +# field(OUT, "@... startWithTimePreset$(CHANNELS)($(INSTR)$(NAME):) $(PORT)") +# field(VAL, 0) +# field(PREC, 2) +# field(EGU, "seconds") +# field(FLNK, "$(INSTR)$(NAME):RAW-STATUS") +# } +# +# record(bo,"$(INSTR)$(NAME):PAUSE") +# { +# field(DESC, "Pause the current count") +# field(DTYP, "stream") +# field(OUT, "@... pauseCount($(INSTR)$(NAME):) $(PORT)") +# field(VAL, "0") +# field(FLNK, "$(INSTR)$(NAME):RAW-STATUS") +# } +# +# record(bo,"$(INSTR)$(NAME):CONTINUE") +# { +# field(DESC, "Continue with a count that was paused") +# field(DTYP, "stream") +# field(OUT, "@... continueCount($(INSTR)$(NAME):) $(PORT)") +# field(VAL, "0") +# field(FLNK, "$(INSTR)$(NAME):RAW-STATUS") +# } +# +# record(longout, "$(INSTR)$(NAME):STOP") +# { +# field(DESC, "Stop the current counting operation") +# field(DTYP, "stream") +# field(OUT, "@... stopCount($(INSTR)$(NAME):) $(PORT)") +# field(FLNK, "$(INSTR)$(NAME):RAW-STATUS") +# } + +record(longout, "$(INSTR)$(NAME):MONITOR-CHANNEL") +{ + field(DESC, "PRESET-COUNT Monitors this channel") + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),0,$(TIMEOUT=1)) MONITOR") + field(DRVL, "1") # Smallest Monitor Channel + field(DRVH, "$(CHANNELS)") # Largest Monitor Channel +} + +record(longin, "$(INSTR)$(NAME):MONITOR-CHANNEL_RBV") +{ + field(DESC, "PRESET-COUNT Monitors this channel") + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),0,$(TIMEOUT=1)) MONITOR") + field(SCAN, "I/O Intr") + field(PINI, "YES") +} + +# record(calc, "$(INSTR)$(NAME):RATE_MAP") +# { +# field(DESC, "Want a consistent lowrate pv") +# field(INPA, "$(INSTR)$(NAME):RAW-STATUS.B2 NPP") +# field(CALC, "(A=1)?1:0") +# } + +# +# record(ao,"$(INSTR)$(NAME):THRESHOLD") +# { +# field(DESC, "Minimum rate for counting to proceed") +# field(VAL, "1") # Default Rate +# # Could perhaps still be improved. +# # It seems to only accept whole counts? +# field(DRVL, "1") # Minimum Rate +# field(DRVH, "100000") # Maximum Rate +# field(OMSL, "supervisory") +# field(OROC, "0") +# field(OUT, "$(INSTR)$(NAME):THRESHOLD-F PP") +# } +# +# record(ai,"$(INSTR)$(NAME):THRESHOLD_RBV") +# { +# field(DESC, "Minimum rate for counting to proceed") +# field(INP, "@... readMinRate($(INSTR)$(NAME):) $(PORT)") +# field(DTYP, "stream") +# field(SCAN, "1 second") +# field(EGU, "cts/sec") +# } +# +# record(longout,"$(INSTR)$(NAME):THRESHOLD-MONITOR") +# { +# field(DESC, "Channel monitored for minimum rate") +# field(VAL, "1") # Monitor +# field(DRVL, "0") # Smallest Threshold Channel (0 is off) +# field(DRVH, "$(CHANNELS)") # Largest Threshold Channel +# field(OUT, "@... setRateMonitor($(INSTR)$(NAME):) $(PORT)") +# field(DTYP, "stream") +# } +# +# record(longin,"$(INSTR)$(NAME):THRESHOLD-MONITOR_RBV") +# { +# field(DESC, "Channel monitored for minimum rate") +# field(INP, "@... readRateMonitor($(INSTR)$(NAME):) $(PORT)") +# field(DTYP, "stream") +# field(SCAN, "1 second") +# field(EGU, "CH") +# } +# +# record(longout, "$(INSTR)$(NAME):CT") +# { +# field(DESC, "Clear the timer") +# field(DTYP, "stream") +# field(OUT, "@... clearTimer($(INSTR)$(NAME):) $(PORT)") +# field(FLNK, "$(INSTR)$(NAME):ETT") +# } + +################################################################################ +# Read all monitors values + +# record(ai,"$(INSTR)$(NAME):ELAPSED-TIME") +# { +# field(DESC, "DAQ Measured Time") +# field(EGU, "sec") +# field(FLNK, "$(INSTR)$(NAME):ETO") +# } diff --git a/scripts/st.cmd b/scripts/st.cmd index 5d8db12..d8fe540 100755 --- a/scripts/st.cmd +++ b/scripts/st.cmd @@ -11,7 +11,12 @@ epicsEnvSet("NAME", "SG") drvAsynIPPortConfigure("ASYN_IP_PORT", "127.0.0.1:9071:54321 UDP", 0, 0, 0) asynStreamGenerator("ASYN_SG", "ASYN_IP_PORT", 4) +dbLoadRecords("$(StreamGenerator_DB)daq_common.db", "INSTR=$(INSTR), NAME=$(NAME), PORT=ASYN_SG, CHANNELS=5") + +# Detector Count Channel dbLoadRecords("$(StreamGenerator_DB)channels.db", "INSTR=$(INSTR), NAME=$(NAME), PORT=ASYN_SG, CHANNEL=0") + +# Monitor Channels dbLoadRecords("$(StreamGenerator_DB)channels.db", "INSTR=$(INSTR), NAME=$(NAME), PORT=ASYN_SG, CHANNEL=1") dbLoadRecords("$(StreamGenerator_DB)channels.db", "INSTR=$(INSTR), NAME=$(NAME), PORT=ASYN_SG, CHANNEL=2") dbLoadRecords("$(StreamGenerator_DB)channels.db", "INSTR=$(INSTR), NAME=$(NAME), PORT=ASYN_SG, CHANNEL=3") diff --git a/src/asynStreamGeneratorDriver.cpp b/src/asynStreamGeneratorDriver.cpp index c933a75..96a52b1 100644 --- a/src/asynStreamGeneratorDriver.cpp +++ b/src/asynStreamGeneratorDriver.cpp @@ -90,16 +90,44 @@ asynStreamGeneratorDriver::asynStreamGeneratorDriver(const char *portName, char pv_name_buffer[100]; P_Counts = new int[this->num_channels]; - asynStatus status; + asynStatus status = asynSuccess; + + status = (asynStatus)(status | createParam(P_StatusString, asynParamInt32, + &P_Status)); + status = (asynStatus)(status | setIntegerParam(P_Status, STATUS_IDLE)); + + status = (asynStatus)(status | + createParam(P_ResetString, asynParamInt32, &P_Reset)); + status = (asynStatus)(status | setIntegerParam(P_Reset, 0)); + + status = (asynStatus)(status | createParam(P_CountPresetString, + asynParamInt32, &P_CountPreset)); + status = (asynStatus)(status | setIntegerParam(P_CountPreset, 0)); + + status = + (asynStatus)(status | createParam(P_MonitorChannelString, + asynParamInt32, &P_MonitorChannel)); + status = (asynStatus)(status | setIntegerParam(P_MonitorChannel, 0)); // Create PVs templated on Channel Number for (size_t i = 0; i < this->num_channels; ++i) { memset(pv_name_buffer, 0, 100); epicsSnprintf(pv_name_buffer, 100, P_CountsString, i); - status = createParam(pv_name_buffer, asynParamInt32, P_Counts + i); - setIntegerParam(P_Counts[i], 0); + status = + (asynStatus)(status | createParam(pv_name_buffer, asynParamInt32, + P_Counts + i)); + status = (asynStatus)(status | setIntegerParam(P_Counts[i], 0)); } + if (status) { + printf("%s:%s: failed to create or setup parameters, status=%d\n", + driverName, functionName, status); + exit(1); + } + + // Create Events + this->pausedEventId = epicsEventCreate(epicsEventEmpty); + this->monitorProducer = create_kafka_producer(); this->detectorProducer = create_kafka_producer(); @@ -160,6 +188,53 @@ asynStreamGeneratorDriver::~asynStreamGeneratorDriver() { // epicsStdoutPrintf("Kafka Queue Size %d\n", rd_kafka_outq_len(producer)); } +asynStatus asynStreamGeneratorDriver::writeInt32(asynUser *pasynUser, + epicsInt32 value) { + int function = pasynUser->reason; + asynStatus status = asynSuccess; + const char *paramName; + const char *functionName = "writeInt32"; + getParamName(function, ¶mName); + + // if (status) { + // epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + // "%s:%s: status=%d, function=%d, name=%s, value=%d", + // driverName, functionName, status, function, paramName, + // value); + // return status; + // } + + if (function == P_CountPreset) { + setIntegerParam(function, value); + setIntegerParam(P_Status, STATUS_COUNTING); + status = (asynStatus)callParamCallbacks(); + epicsEventSignal(this->pausedEventId); + } else if (function == P_Reset) { + // TODO should probably set back everything to defaults + setIntegerParam(P_Status, STATUS_IDLE); + status = (asynStatus)callParamCallbacks(); + } else if (function == P_MonitorChannel) { + epicsInt32 currentStatus; + getIntegerParam(this->P_Status, ¤tStatus); + if (!currentStatus) { + setIntegerParam(function, value); + status = (asynStatus)callParamCallbacks(); + } + } else { + setIntegerParam(function, value); + status = (asynStatus)callParamCallbacks(); + } + + if (status) + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%s:%s: status=%d, function=%d, name=%s, value=%d", + driverName, functionName, status, function, paramName, + value); + return status; +} + +// TODO probably I will have to split this function up, so that the system +// can process the UDP messages in parallel void asynStreamGeneratorDriver::receiveUDP() { asynStatus status; int isConnected; @@ -170,56 +245,53 @@ void asynStreamGeneratorDriver::receiveUDP() { int eomReason; epicsInt32 val; + epicsInt32 currentStatus; + epicsInt32 countPreset = 0; + epicsInt32 presetChannel = 1; - const uint32_t x_pixels = 128; - const uint32_t y_pixels = 128; + const char *functionName = "receiveUDP"; // TODO epics doesn't seem to support uint64, you would need an array of // uint32. It does support int64 though.. so we start with that epicsInt32 *counts = new epicsInt32[this->num_channels]; while (true) { - // memset doesn't work with epicsInt32 - for (size_t i = 0; i < this->num_channels; ++i) { - counts[i] = 0; + + status = getIntegerParam(this->P_Status, ¤tStatus); + if (!currentStatus || status) { + + epicsEventWait(this->pausedEventId); + + getIntegerParam(this->P_CountPreset, &countPreset); + getIntegerParam(this->P_MonitorChannel, &presetChannel); + + // memset doesn't work with epicsInt32 + for (size_t i = 0; i < this->num_channels; ++i) { + counts[i] = 0; + } + + lock(); + for (size_t i = 0; i < num_channels; ++i) { + setIntegerParam(P_Counts[i], counts[i]); + } + callParamCallbacks(); + unlock(); + + // Clear the input buffer, in case of stray messages + pasynOctetSyncIO->flush(pasynUDPUser); } status = pasynManager->isConnected(pasynUDPUser, &isConnected); - if (status) { + if (!isConnected) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: error calling pasynManager->isConnected, " - "status=%d, error=%s\n", - driverName, "receiveUDP", status, - pasynUDPUser->errorMessage); - // driverName, functionName, status, - // pasynUserIPPort_->errorMessage); - } - asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s: isConnected = %d\n", // - driverName, "receiveUDP", isConnected); + "%s:%s: isConnected = %d\n", driverName, functionName, + isConnected); status = pasynOctetSyncIO->read(pasynUDPUser, buffer, buffer_size, 0, // timeout &received, &eomReason); - // if (status) - // asynPrint( - // pasynUserSelf, ASYN_TRACE_ERROR, - // "%s:%s: error calling pasynOctetSyncIO->read, status=%d\n", - // driverName, "receiveUDP", status); - - // buffer[received] = 0; - if (received) { - // asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: received %f %d - // received\n", - // driverName, "receiveUDP", (double) received / - // 1500., received); - - // asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: received - // %d\n", - // driverName, "receiveUDP", received); - UDPHeader *header = (UDPHeader *)buffer; size_t total_events = (header->BufferLength - 21) / 3; @@ -230,23 +302,19 @@ void asynStreamGeneratorDriver::receiveUDP() { // "%s:%s: received packet %d with %d events (%" // PRIu64 // ")\n", - // driverName, "receiveUDP", + // driverName, functionName, // header->BufferNumber, total_events, // header->nanosecs()); for (size_t i = 0; i < total_events; ++i) { char *event = (buffer + 21 * 2 + i * 6); + if (countPreset && counts[presetChannel] >= countPreset) + break; + if (event[5] & 0x80) { // Monitor Event MonitorEvent *m_event = (MonitorEvent *)event; - // asynPrint( - // pasynUserSelf, ASYN_TRACE_ERROR, - // "%s:%s: event (%03d) on monitor %d (%" PRIu64 - // ")\n", driverName, "receiveUDP", i, - // m_event->DataID, header->nanosecs() + - // (uint64_t)m_event->nanosecs()); - counts[m_event->DataID + 1] += 1; // needs to be freed!!! @@ -264,25 +332,11 @@ void asynStreamGeneratorDriver::receiveUDP() { auto nde = new NormalisedDetectorEvent(); nde->TimeStamp = header->nanosecs() + (uint64_t)d_event->nanosecs(); - nde->PixID = - (header->McpdID - 1) * x_pixels * y_pixels + - x_pixels * (uint32_t)d_event->XPosition + - (uint32_t)d_event->YPosition; + nde->PixID = d_event->pixelId(header->McpdID); this->detectorQueue.push(nde); } } - for (size_t i = 0; i < num_channels; ++i) { - getIntegerParam(P_Counts[i], &val); - counts[i] += val; - } - - // asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - // "%s:%s: det: (%d), mon0: (%d), mon1: (%d), mon2: " - // "(%d), mon3: (%d)\n", - // driverName, "receiveUDP", counts[0], - // counts[1], counts[2], counts[3], counts[4]); - lock(); for (size_t i = 0; i < num_channels; ++i) { setIntegerParam(P_Counts[i], counts[i]); @@ -292,7 +346,15 @@ void asynStreamGeneratorDriver::receiveUDP() { } else { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: invalid UDP packet\n", driverName, - "receiveUDP"); + functionName); + } + + if (countPreset && counts[presetChannel] >= countPreset) { + lock(); + setIntegerParam(P_Status, STATUS_IDLE); + setIntegerParam(P_CountPreset, 0); + callParamCallbacks(); + unlock(); } } diff --git a/src/asynStreamGeneratorDriver.h b/src/asynStreamGeneratorDriver.h index 5aaf664..f3d2ac4 100644 --- a/src/asynStreamGeneratorDriver.h +++ b/src/asynStreamGeneratorDriver.h @@ -36,6 +36,12 @@ struct __attribute__((__packed__)) DetectorEvent { uint16_t Amplitude : 8; uint16_t Id : 1; inline uint32_t nanosecs() { return TimeStamp * 100; } + inline uint64_t pixelId(uint32_t mpcdId) { + const uint32_t x_pixels = 128; + const uint32_t y_pixels = 128; + return (mpcdId - 1) * x_pixels * y_pixels + + x_pixels * (uint32_t)this->XPosition + (uint32_t)this->YPosition; + } }; struct __attribute__((__packed__)) MonitorEvent { @@ -60,12 +66,24 @@ struct __attribute__((__packed__)) NormalisedDetectorEvent { uint32_t PixID; }; +/******************************************************************************* + * Status values that should match the definition in db/daq_common.db + */ +#define STATUS_IDLE 0 +#define STATUS_COUNTING 1 +#define STATUS_LOWRATE 2 +#define STATUS_PAUSED 3 + /******************************************************************************* * Parameters for use in DB records * * i.e.e drvInfo strings that are used to identify the parameters */ +#define P_StatusString "STATUS" +#define P_ResetString "RESET" +#define P_CountPresetString "P_CNT" +#define P_MonitorChannelString "MONITOR" #define P_CountsString "COUNTS%d" /******************************************************************************* @@ -77,16 +95,23 @@ class asynStreamGeneratorDriver : public asynPortDriver { const int numChannels); virtual ~asynStreamGeneratorDriver(); + virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); + void receiveUDP(); void produceMonitor(); void produceDetector(); protected: // Parameter Identifying IDs + int P_Status; + int P_Reset; + int P_CountPreset; + int P_MonitorChannel; int *P_Counts; private: asynUser *pasynUDPUser; + epicsEventId pausedEventId; int num_channels;