adds a POC preset based count
This commit is contained in:
2
Makefile
2
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
|
||||
|
||||
|
||||
@@ -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")
|
||||
# }
|
||||
|
||||
212
db/daq_common.db
Normal file
212
db/daq_common.db
Normal file
@@ -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")
|
||||
# }
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user