diff --git a/db/daq_soft_proton.db b/db/daq_soft_proton.db index a9de733..4cdad2e 100644 --- a/db/daq_soft_proton.db +++ b/db/daq_soft_proton.db @@ -194,15 +194,19 @@ record(aSub, "$(INSTR)$(NAME):EMULATION") field(FTC, "DOUBLE") field(OUTD, "$(INSTR)$(NAME):COMMAND-TRIG") field(FTD, "ULONG") - field(INPE, "$(INSTR)$(NAME):COMMAND-TRIG") - field(FTE, "ULONG") + field(OUTE, "$(INSTR)$(NAME):THRESHOLD") + field(FTE, "DOUBLE") # Address the PV which are mapped as input backwards + field(INPF, "$(INSTR)$(NAME):COUNT-TYPE") + field(FTF, "ULONG") field(INPG, "$(INSTR)$(NAME):PRESET-COUNT") field(FTG, "INT64") field(INPH, "$(INSTR)$(NAME):PRESET-TIME") field(FTH, "DOUBLE") # L is last input before EPICS 7.0.10 + field(INPJ, "$(INSTR)$(NAME):R0-PREV") + field(FTJ, "DOUBLE") field(INPL, "$(INSTR)$(NAME):R0 PP") field(FTL, "DOUBLE") @@ -215,6 +219,8 @@ record(aSub, "$(INSTR)$(NAME):EMULATION") field(FTVC, "DOUBLE") field(OUTD, "$(INSTR)$(NAME):COMMAND-TRIG PP") field(FTVD, "ULONG") + field(INPE, "$(INSTR)$(NAME):R0-PREV PP") + field(FTE, "DOUBLE") } ####################### @@ -241,6 +247,11 @@ record(calc, "$(INSTR)$(NAME):R0") field(EGU, "cts/sec") } +# Store previous rate value, so we can average over the period +record(ai, "$(INSTR)$(NAME):R0-PREV") { + field(DESC, "Previous rate of DAQ CH0 proton current") +} + # Clear channel, has to be calcout due to client # writing 1 to it. record(calcout, "$(INSTR)$(NAME):C0") diff --git a/src/daq_soft_proton.c b/src/daq_soft_proton.c index 121cae6..bffe38f 100644 --- a/src/daq_soft_proton.c +++ b/src/daq_soft_proton.c @@ -14,6 +14,103 @@ static const epicsFloat64 soft_proton_sample_rate = 0.1; static int softProtonDebug=0; epicsExportAddress(int, softProtonDebug); +struct spc_internal { + epicsUInt32 status; + epicsInt64 monitor_count; + epicsFloat64 elapsed_time; + epicsUInt32 command_trig; + epicsFloat64 threshold; + epicsFloat64 proton_rate; + epicsFloat64 prev_proton_rate; + epicsUInt32 count_type; + epicsInt64 preset_count; + epicsFloat64 preset_time; +}; + +/* Enum with values for all commands */ +enum commands { + NONE = 0, + COUNT_PRESET = 1, + TIME_PRESET = 2, + PAUSE = 3, + CONTINUE = 4 + STOP = 5, + FULL_RESET = 6 +}; + +/* Enum with the possible statuses/states */ +enum status { + IDLE = 0, + COUNTING = 1, + LOW_RATE = 2, + PAUSED = 3, + INVALID = 4 +}; + + +int handleNoop(struct spc_internal* spc_int, int* exit_status) { + const char[] funcstr = "handleNoop"; + /* This shouldn't happen, but let's handle it just in case */ + if (spc_int->status >= INVALID || spc->command_trig > FULL_RESET) { + errlogPrintf("%s: Status and/or command triggers are invalid \n" + "Status has value %d, \n Command trigger has value %d\n", + funcstr, spc_int->status, spc_int->command_trig); + *exit_status = -1; + return 1; + } + + /* Determine if we are idle or paused and have not received a command */ + if ((spc_int->status == IDLE || spc_int->status == PAUSED) && + spc_int->command_trig == NONE) { + /* Just return as quickly as possible if there is nothing going on */ + *exit_status = 0; + return 1; + } + + /* Determine if we are idle and have received a noop command */ + if (spc_int->status == IDLE) { + switch (spc_int->command_trig) { + case PAUSE: + case CONTINUE: + case STOP: + errlogPrintf("%s: Can not PAUSE/CONTINUE/STOP during IDLE\n" + "Status has value %d, \n" + "Command trigger has value %d\n", + funcstr, spc_int->status, spc_int->command_trig); + *exit_status = -1; + return 1; + } + } + + /* Determine if we are counting, low_rate or paused; + * and have received a noop command */ + if (spc_int->status == COUNTING || spc_int->status == LOW_RATE || + spc_int -> PAUSED) { + switch (spc_int->command_trig) { + case COUNT_PRESET: + case TIME_PRESET: + errlogPrintf("%s: Already counting can not start a new count\n" + "Status has value %d, \n" + "Command trigger has value %d\n", + funcstr, spc_int->status, spc_int->command_trig); + *exit_status = -1; + return 1; + } + } + + /* Determine if we are paused and received a command */ + if (spc_int->status == PAUSED && spc_int->command_trig == PAUSE) { + errlogPrintf("%s: Already paused\n" + "Status has value %d, \n" + "Command trigger has value %d\n", + funcstr, spc_int->status, spc_int->command_trig); + *exit_status = -1; + return 1; + } + /* None of the noop cases detected */ + return 0; +} + /* * This function is called a IOC init */ @@ -30,57 +127,39 @@ static long initEmulatedCounter(struct subRecord *psub) */ static long processEmulatedCounter(struct aSubRecord *psub) { - enum commands { - NONE = 0, - COUNT_PRESET = 1, - TIME_PRESET = 2, - PAUSE = 3, - CONTINUE = 4 - STOP = 5, - FULL_RESET = 6 - }; - - enum status { - IDLE = 0, - COUNTING = 1, - LOW_RATE = 2, - PAUSED = 3, - INVALID = 4 - }; const char[] funcstr = "processEmulatedCounter"; if (softProtonDebug) printf("%s was called\n", funcstr); - - /* We copy input values to the stack, - but take the pointer to output values. + + /* Declare internal variable */ + struct spc_internal spc_int; + int exit_status = 0; + + /* Copy input values to a struct on the stack + * to simplify creation of functions */ + spc_int.status = psub->a[0]; + monitor_count = psub->b[0]; + elapsed_time = psub->c[0]; + command_trig = psub->d[0]; + threshold = psub->e[0]; + count_type = psub->f[0]; + preset_count = psub->g[0]; + preset_time = psub->h[0]; + prev_proton_rate = psub->j[0]; + proton_rate = psub->l[0]; + + /* Get the pointer to output values. This to increase readability. */ - /* First all inputs that have a matching output */ - /* Status input and output pointer */ - epicsUInt32 status = psub->a[0]; - epicsUInt32* status_out = psub->vala; - /* Monitor count input and output pointer */ - epicsInt64 monitor_count = psub->b[0]; - epicsInt64* monitor_count_out = psub->valb; - /* Elapsed time input and output pointer */ - epicsFloat64 elapsed_time = psub->c[0]; - epicsFloat64* elaped_time_out = psub->valc; - /* Command trigger input and output pointer */ - epicsUInt32* command_trig = psub->d[0]; - epicsUInt32* command_trig_out = psub->vald; - /* Remaining only inputs */ - epicsUInt32 preset_count = psub->f[0]; - epicsInt64 preset_count = psub->g[0]; - epicsFloat64 preset_time = psub->h[0]; - epicsFloat64 proton_rate = psub->l[0]; + epicsUInt32* status_out = psub->vala; + epicsInt64* monitor_count_out = psub->valb; + epicsFloat64* elaped_time_out = psub->valc; + epicsUInt32* command_trig_out = psub->vald; + epicsFloat64* prev_proton_rate_out = psub->vale; - if (status >= INVALID || command_trig > FULL_RESET) { - /* This shouldn't happen, but let's handle it just in case */ - errlogPrintf("%s: Status and/or command triggers are invalid \n" - "Status has value %d, \n Command trigger has value %d\n", - funcstr, status, command_trig); - return -1; - } + /* Handle noop situations both valid and invalid */ + if (handle_noop(&spc_int, &exit_status)) + return exit_status; - if (command_trig == FULL_RESET) { + if (spc_int->command_trig == FULL_RESET) { *status_out = IDLE; *monitor_count_out = 0; *elapsed_time_out = 0.0; @@ -89,27 +168,69 @@ static long processEmulatedCounter(struct aSubRecord *psub) return 0; } - /* Determine if we are idle and have not received a command */ - if (status == 0 && command_trig == 0) { - /* Just return as quickly as possible if there is nothing going on */ - return 0; + if ((spc_int->status == COUNTING || spc_int->status == LOW_RATE) && + spc_int->command_trig == PAUSE)) { + /* We are counting or at low rate and will pause */ + *status_out = PAUSED; + } /* Determine if we are idle but received a count command */ - if (status == 0 && (command_trig == 1 || command_trig == 2)) { + if (status == IDLE && + (command_trig == COUNT_PRESET || command_trig == TIME_PRESET)) { if (softProtonDebug) printf("%s: Starting Count!\n", funcstr); - /* Check that count type is properly stored */ + /* Sanity check that count type is properly stored */ if ( command_trig != 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; + *elapsed_time_out = 0; + *status_out = COUNTING; + *command_trig_out = NONE; + *prev_proton_rate_out = spc_int->proton_rate; + goto FINISH_ONGOING_COUNT; } + /* Resume from paused */ + if (spc_int->status == PAUSED && spc_int->command_trig == CONTINUE) { + *prev_proton_rate_out = spc_int->proton_rate; + *status_out = COUNTING; + *command_trig_out = NONE; + /* continue cycle doesn't increment the counter */ + goto FINISH_ONGOING_COUNT; + } - /* Placeholder output setting */ - *status_out = status; - *monitor_count_out = monitor_count; - if ( first - *elapsed_time_out = elapsed_time + soft_proton_sample_rate; + /* Counting */ + if (spc_int->status == COUNTING && spc_int->command_trig == NONE) { + /* Take the average proton rate of the sampling period */ + *monitor_count_out = + (spc_int->proton_rate + spc_int->prev_proton_rate) / 2; + *elapsed_time_out += soft_proton_sample_rate; + *command_trig_out = NONE; + *prev_proton_rate_out = spc_int->proton_rate; + } + + /* Special cases */ + + /* Count finished normally */ + if (/* Time based count finished */ + (*elapsed_time_out >= spc_int->preset_time && + spc_int->count_type = TIME_PRESET) || + /* Monitor based count finished */ + (*monitor_count_out >= spc_int->preset_time && + spc_int->count_type = COUNT_PRESET)) { + *status_out = IDLE; + return 0; + } + + FINISH_ONGOING_COUNT: + if (spc_int->proton_rate < spc_int->threshold) + *status_out = LOW_RATE; + else + *status_out = COUNTING; return 0; }