can somewhat count on time with soft proton
Example Action / Lint (push) Successful in 3s
Example Action / BuildAndTest (push) Successful in 37s

This commit is contained in:
2026-06-08 15:44:34 +02:00
parent d216a3ac42
commit d6f0dd54eb
4 changed files with 213 additions and 152 deletions
+190 -131
View File
@@ -25,23 +25,9 @@
# SHUTTER2_PV: PV of a second shutter state will be taken as a gate signal for the protoon current
# SHUTTER2_CLOSED_VAL: Value of the shutter PV when the shutter is closed
record(mbbi, "$(INSTR)$(NAME):STATUS")
{
field(DESC, "DAQ 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")
# We start in idle
field(VAL, 0)
}
################################################################################
# Count Config
################################################################################
record(longin, "$(INSTR)$(NAME):CHANNELS")
{
@@ -63,43 +49,44 @@ record(bi, "$(INSTR)$(NAME):IS_LOWRATE")
field(ONAM, "GOOD RATE")
}
record(longout,"$(INSTR)$(NAME):THRESHOLD-MONITOR")
{
# Alias to RBV to be compatible with higher level interface
alias("$(INSTR)$(NAME):THRESHOLD-MONITOR_RBV")
field(DESC, "Channel monitored for minimum rate")
field(VAL, "1") # Monitor
field(DRVL, "0") # Smallest Threshold Channel (0 is off)
field(DRVH, "1") # Largest Threshold Channel
}
record(ao,"$(INSTR)$(NAME):THRESHOLD")
{
# Alias to RBV to be compatible with higher level interface
alias("$(INSTR)$(NAME):THRESHOLD_RBV")
field(DESC, "Minimum rate for counting to proceed")
field(VAL, "1") # Default Rate
field(DRVL, "1") # Minimum Rate
field(DRVH, "100000") # Maximum Rate
field(OMSL, "supervisory")
}
record(longout, "$(INSTR)$(NAME):MONITOR-CHANNEL")
{
alias("$(INSTR)$(NAME):MONITOR-CHANNEL_RBV")
field(DESC, "Count preset channel")
field(VAL, 1)
}
################################################################################
# Commands
################################################################################
record(ao, "$(INSTR)$(NAME):PRESET_COUNT")
record(seq, "$(INSTR)$(NAME):START")
{
field(DESC, "Count until preset reached")
field(VAL, 0)
field(PREC, 2)
field(FLNK, "$(INSTR)$(NAME):PRESET_COUNT-TRIG")
}
record(longout, "$(INSTR)$(NAME):PRESET_COUNT-TRIG")
{
field(DESC, "Count until preset reached")
# Signal count preset as command
field(VAL, 1)
field(OUT, "$(INSTR)$(NAME):COMMAND-TRIG PP")
field(FLNK, "$(INSTR)$(NAME):COUNT-TYPE")
}
record(ao, "$(INSTR)$(NAME):PRESET_TIME")
{
field(DESC, "Count for specified time")
field(VAL, 0)
field(PREC, 2)
field(EGU, "seconds")
field(FLNK, "$(INSTR)$(NAME):PRESET_TIME-TRIG")
}
record(longout, "$(INSTR)$(NAME):PRESET_TIME-TRIG")
{
field(DESC, "Count until preset time reached")
# Signal count preset as command
field(VAL, 2)
field(OUT, "$(INSTR)$(NAME):COMMAND-TRIG PP")
field(FLNK, "$(INSTR)$(NAME):COUNT-TYPE")
field(DESC, "Start a count")
field(SELM, "All")
field(DO0, 1)
field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP")
}
record(seq, "$(INSTR)$(NAME):PAUSE")
@@ -131,22 +118,7 @@ record(seq, "$(INSTR)$(NAME):FULL-RESET")
field(DESC, "Perform full reset")
field(SELM, "All")
field(DO0, 6)
field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP")
}
# Emulate Reset elapsed time
# 0. set status to busy
# 1. set elapsed time to 0
# 2. set status to OK
record(seq, "$(INSTR)$(NAME):T_CLEAR")
{
field(SELM, "All")
field(LNK0, "$(INSTR)$(NAME):T_STS PP")
field(DO0, 1)
field(LNK1, "$(INSTR)$(NAME):T PP")
field(DO1, 0)
field(LNK2, "$(INSTR)$(NAME):T_STS PP")
field(DO2, 0)
field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP")
}
# Record is to signal command given by the client to the emulated counter.
@@ -159,8 +131,8 @@ record(mbbi, "$(INSTR)$(NAME):COMMAND-TRIG")
field(ZRVL, 0)
field(ONST, "Count preset command")
field(ONVL, 1)
field(TWST, "Time preset command")
field(TWVL, 2)
# field(TWST, "Time preset command")
# field(TWVL, 2)
field(THST, "Pause command")
field(THVL, 3)
field(FRST, "Continue command")
@@ -171,53 +143,31 @@ record(mbbi, "$(INSTR)$(NAME):COMMAND-TRIG")
field(SXVL, 6)
}
# Copy COMMAND-TRIG to memorize what type of count is on-going
record(longin, "$(INSTR)$(NAME):COUNT-TYPE") {
field(INP, "$(INSTR)$(NAME):COMMAND-TRIG.VAL NPP")
field(VAL, 0)
# Identical to FULL-RESET
record(seq, "$(INSTR)$(NAME):RESET")
{
field(SELM, "All")
field(DO0, 1)
field(LNK0, "$(INSTR)$(NAME):FULL-RESET.PROC PP")
}
record(longout,"$(INSTR)$(NAME):THRESHOLD-MONITOR")
{
# Alias to RBV to be compatible with higher level interface
alias("$(INSTR)$(NAME):THRESHOLD-MONITOR_RBV")
field(DESC, "Channel monitored for minimum rate")
field(VAL, "1") # Monitor
field(DRVL, "0") # Smallest Threshold Channel (0 is off)
field(DRVH, "1") # Largest Threshold Channel
}
################################################################################
# Poll Loop
################################################################################
record(ao,"$(INSTR)$(NAME):THRESHOLD")
{
# Alias to RBV to be compatible with higher level interface
alias("$(INSTR)$(NAME):THRESHOLD_RBV")
field(DESC, "Minimum rate for counting to proceed")
field(VAL, "1") # Default Rate
field(DRVL, "1") # Minimum Rate
field(DRVH, "100000") # Maximum Rate
field(OMSL, "supervisory")
}
# TODO need to reset the presets to 0 after the count
record(ai,"$(INSTR)$(NAME):T")
record(dfanout, "$(INSTR)$(NAME):DB_POLL")
{
field(DESC, "DAQ Measured Time")
field(EGU, "sec")
}
# Current Status of elapsed time
record(bi, "$(INSTR)$(NAME):T_STS")
{
field(DESC, "Channel Status")
field(VAL, 0)
field(ZNAM, "Idle")
field(ONAM, "Busy")
}
record(longout, "$(INSTR)$(NAME):MONITOR-CHANNEL")
{
alias("$(INSTR)$(NAME):MONITOR-CHANNEL_RBV")
field(DESC, "PRESET_COUNT Monitors this channel")
field(VAL, 1)
field(SELM, "All")
field(OMSL, "closed_loop")
field(DOL, 1)
# This order is important, to ensure that the status
# can't change whichout having first updated the time
# and count records to their final values
field(OUTA, "$(INSTR)$(NAME):T_STS_RAW.PROC")
field(OUTB, "$(INSTR)$(NAME):CH1_STS_RAW.PROC")
field(OUTB, "$(INSTR)$(NAME):STATUS.PROC")
}
# Array Subroutine record which emulates the counterbox functionality
@@ -244,11 +194,11 @@ record(aSub, "$(INSTR)$(NAME):EMULATION")
field(FTE, "LONG")
field(INPF, "$(INSTR)$(NAME):THRESHOLD")
field(FTF, "DOUBLE")
field(INPG, "$(INSTR)$(NAME):COUNT-TYPE")
field(FTG, "ULONG")
field(INPH, "$(INSTR)$(NAME):PRESET_COUNT")
field(FTH, "DOUBLE")
field(INPI, "$(INSTR)$(NAME):PRESET_TIME")
# field(INPG, "$(INSTR)$(NAME):COUNT-TYPE")
# field(FTG, "ULONG")
field(INPH, "$(INSTR)$(NAME):CH1_PRESET")
field(FTH, "INT64")
field(INPI, "$(INSTR)$(NAME):T_PRESET")
field(FTI, "DOUBLE")
# L is last input before EPICS 7.0.10
field(INPJ, "$(INSTR)$(NAME):CH1_RATE-PREV")
@@ -257,7 +207,7 @@ record(aSub, "$(INSTR)$(NAME):EMULATION")
field(FTL, "DOUBLE")
# The first 4 outputs are also mapped as the first 4 inputs
field(OUTA, "$(INSTR)$(NAME):STATUS PP")
field(OUTA, "$(INSTR)$(NAME):RAW_STATUS PP")
field(FTVA, "ULONG")
field(OUTB, "$(INSTR)$(NAME):CH1 PP")
field(FTVB, "INT64")
@@ -272,11 +222,96 @@ record(aSub, "$(INSTR)$(NAME):EMULATION")
field(OUTG, "$(INSTR)$(NAME):MsgTxt PP")
field(FTVG, "CHAR")
field(NOVG, 40)
field(FLNK, "$(INSTR)$(NAME):DB_POLL")
}
#######################
record(mbbi, "$(INSTR)$(NAME):RAW_STATUS")
{
field(DESC, "Raw returned status value")
}
record(mbbi, "$(INSTR)$(NAME):STATUS")
{
field(DESC, "DAQ Status")
field(INP, "$(INSTR)$(NAME):RAW_STATUS NPP MS")
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")
# We start in idle
field(VAL, 0)
}
################################################################################
# Time Channel interface
################################################################################
record(ai,"$(INSTR)$(NAME):T")
{
field(DESC, "DAQ Measured Time")
field(EGU, "sec")
}
record(ai, "$(INSTR)$(NAME):T_PRESET")
{
field(DESC, "Time Preset")
field(VAL, 0)
}
record(calc, "$(INSTR)$(NAME):T_STS_RAW")
{
field(DESC, "Raw time channel status")
field(INPA, "$(INSTR)$(NAME):STATUS NPP MS")
field(INPB, "$(INSTR)$(NAME):T_CLEARED NPP")
field(CALC, "(0 < A && A < 4) || B")
field(FLNK, "$(INSTR)$(NAME):T_STS")
}
# Current Status of elapsed time
record(bi, "$(INSTR)$(NAME):T_STS")
{
field(DESC, "Channel Status")
field(INP, "$(INSTR)$(NAME):T_STS_RAW NPP MS")
field(ZNAM, "Idle")
field(ONAM, "Busy")
}
record(bi, "$(INSTR)$(NAME):T_CLEARED")
{
field(DESC, "is being cleared")
field(VAL, 0)
field(ZNAM, "Cleared")
field(ONAM, "Clearing")
field(FLNK, "$(INSTR)$(NAME):T_STS_RAW")
}
# Emulate Reset elapsed time
# 0. set status to busy
# 1. set elapsed time to 0
# 2. set status to OK
record(seq, "$(INSTR)$(NAME):T_CLEAR")
{
field(SELM, "All")
field(LNK0, "$(INSTR)$(NAME):T_CLEARED PP")
field(DO0, 1)
field(LNK1, "$(INSTR)$(NAME):T PP")
field(DO1, 0)
field(LNK2, "$(INSTR)$(NAME):T_CLEARED PP")
field(DO2, 0)
}
################################################################################
# Channel interface
#######################
################################################################################
record(int64in, "$(INSTR)$(NAME):CH1")
{
field(DESC, "DAQ CH0, proton current")
@@ -307,19 +342,19 @@ record(ai, "$(INSTR)$(NAME):CH1_RATE-PREV") {
field(DESC, "Previous rate of DAQ CH0 proton current")
}
# Emulate clearing channel
# 0. set status to busy
# 1. set elapsed time to 0
# 2. set status to OK
record(seq, "$(INSTR)$(NAME):CH1_CLEAR")
record(int64in, "$(INSTR)$(NAME):CH1_PRESET")
{
field(SELM, "All")
field(LNK0, "$(INSTR)$(NAME):CH1_STS PP")
field(DO0, 1)
field(LNK1, "$(INSTR)$(NAME):CH1 PP")
field(DO1, 0)
field(LNK2, "$(INSTR)$(NAME):CH1_STS PP")
field(DO2, 0)
field(DESC, "Monitor Count Preset")
field(VAL, 0)
}
record(calc, "$(INSTR)$(NAME):CH1_STS_RAW")
{
field(DESC, "Raw Channel Status")
field(INPA, "$(INSTR)$(NAME):STATUS NPP MS")
field(INPB, "$(INSTR)$(NAME):CH1_CLEARED NPP")
field(CALC, "(0 < A && A < 4) || B")
field(FLNK, "$(INSTR)$(NAME):CH1_STS")
}
# Current Status of Channel
@@ -327,7 +362,31 @@ record(seq, "$(INSTR)$(NAME):CH1_CLEAR")
record(bi, "$(INSTR)$(NAME):CH1_STS")
{
field(DESC, "Channel Status")
field(INP, "$(INSTR)$(NAME):CH1_STS_RAW NPP MS")
field(ZNAM, "Idle")
field(ONAM, "Busy")
}
record(bi, "$(INSTR)$(NAME):CH1_CLEARED")
{
field(DESC, "are hw counts being cleared")
field(VAL, 0)
field(ZNAM, "OK")
field(ONAM, "CLEARING")
field(ZNAM, "Cleared")
field(ONAM, "Clearing")
field(FLNK, "$(INSTR)$(NAME):CH1_STS_RAW")
}
# Emulate clearing channel
# 0. set status to busy
# 1. set elapsed time to 0
# 2. set status to OK
record(seq, "$(INSTR)$(NAME):CH1_CLEAR")
{
field(SELM, "All")
field(LNK0, "$(INSTR)$(NAME):CH1_CLEARED PP")
field(DO0, 1)
field(LNK1, "$(INSTR)$(NAME):CH1 PP")
field(DO1, 0)
field(LNK2, "$(INSTR)$(NAME):CH1_CLEARED PP")
field(DO2, 0)
}
+1
View File
@@ -100,6 +100,7 @@ record(mbbi, "$(INSTR)$(NAME):T_STS")
# Time Channel Preset
# If set, stop count once preset reached
# TODO type
record(int64in, "$(INSTR)$(NAME):T_PRESET")
{
field(DESC, "Time Preset")
+1 -3
View File
@@ -1,8 +1,6 @@
#var softProtonDebug 1
var softProtonDebug 1
$(SET_SIM_MODE=#)dbLoadRecords("$(sinqDAQ_DB)sim_proton_current.db", "INSTR=$(INSTR), NAME=$(NAME)")
$(SET_SIM_MODE=#)epicsEnvSet("REMOTE_RATE_PV", "$(INSTR)$(NAME):SIM-PROTON-CURR")
dbLoadRecords("$(sinqDAQ_DB)daq_soft_proton.db", "INSTR=$(INSTR), NAME=$(NAME), REMOTE_RATE_PV=$(REMOTE_RATE_PV=MHC6:IST:2), RATE_EGU=$(RATE_EGU=cts/sec), COUNTER_EGU=$(COUNTER_EGU=cts), SCALE_FACTOR=$(SCALE_FACTOR=100)")
iocInit
+21 -18
View File
@@ -31,9 +31,9 @@ struct spc_internal {
epicsFloat64 threshold;
epicsFloat64 proton_rate;
epicsFloat64 prev_proton_rate;
epicsUInt32 count_type;
epicsFloat64 preset_count;
epicsFloat64 preset_time;
//epicsUInt32 count_type;
epicsInt64 ch1_preset;
epicsFloat64 t_preset;
epicsFloat64 average_rate;
};
@@ -134,9 +134,9 @@ static long processEmulatedCounter(struct aSubRecord *psub) {
spc->command_trig = *(epicsUInt32 *)psub->d;
spc->threshold_ch = *(epicsInt32 *)psub->e;
spc->threshold = *(epicsFloat64 *)psub->f;
spc->count_type = *(epicsUInt32 *)psub->g;
spc->preset_count = *(epicsFloat64 *)psub->h;
spc->preset_time = *(epicsFloat64 *)psub->i;
// spc->count_type = *(epicsUInt32 *)psub->g;
spc->ch1_preset = *(epicsInt64 *)psub->h;
spc->t_preset = *(epicsFloat64 *)psub->i;
spc->prev_proton_rate = *(epicsFloat64 *)psub->j;
spc->proton_rate = *(epicsFloat64 *)psub->l;
@@ -218,13 +218,14 @@ static long processEmulatedCounter(struct aSubRecord *psub) {
spc->command_trig == TIME_PRESET)) {
/* Determine if we are idle but received a count command */
if (softProtonDebug)
printf("%s: Starting Count!\n", funcstr);
printf("%s: Starting Count! (time <= %f, count <= %d)\n",
funcstr, spc->t_preset, spc->ch1_preset);
/* Sanity check that count type is properly stored */
if (spc->command_trig != spc->count_type) {
if (softProtonDebug)
printf("%s: Count type not stored!\n", funcstr);
return -1;
}
// if (spc->command_trig != spc->count_type) {
// if (softProtonDebug)
// printf("%s: Count type not stored!\n", funcstr);
// return -1;
// }
/* Starting a new count
* Reset counter and time */
*monitor_count_out = 0;
@@ -281,12 +282,14 @@ static long processEmulatedCounter(struct aSubRecord *psub) {
/* Check if count is finished normally.
* Higher priority than low rate */
if (/* Time based count finished */
(*elapsed_time_out >= spc->preset_time &&
spc->count_type == TIME_PRESET) ||
/* Monitor based count finished */
(*monitor_count_out >= spc->preset_count &&
spc->count_type == COUNT_PRESET)) {
if (
(spc->t_preset > 0 && *elapsed_time_out >= spc->t_preset) ||
(spc->ch1_preset > 0 && *monitor_count_out >= spc->ch1_preset)
) {
if (softProtonDebug)
printf("%s: Case COUNT preset reached (time: %f >= %f, count: %lld >= %lld)\n",
funcstr, *elapsed_time_out, spc->t_preset,
*monitor_count_out, spc->ch1_preset);
*status_out = IDLE;
}