# as retrieved via network from HIPA. # # This is created to be able reuse the SinqDAQ interface # in Nicos, to avoid having more code to maintain there. # # Commands that has no actual relevant value that needs to be written to them: # * Pause # * Continue # * Stop # * Full reset # * Reset elapsed time # * Reset the counter/monitor value # Are all implemented as seq records. Due to how the existing # interface worked, they were being written 1 from nicos. # It just so turns out that seq record's VAL field triggers processing, # but a calcout records VAL field doesn't. seq record was the only option # to emulate these features with only 1 record per command. # # This requires following macros to specified: # INSTR: Instrument prefix, e.g. "SQAMOR:" # NAME: Name of the DAQ, e.g. "PROTONDAQ" # SHUTTER1_PV: PV of a shutter state will be taken as a gate signal for the protoon current # SHUTTER1_CLOSED_VAL: Value of the shutter PV when the shutter is closed # 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) } record(longin, "$(INSTR)$(NAME):CHANNELS") { field(DESC, "Total Supported Channels") field(VAL, 1) field(DISP, 1) } record(stringin, "$(INSTR)$(NAME):MsgTxt") { field(DESC, "Miscellanous messages") } record(bi, "$(INSTR)$(NAME):IS_LOWRATE") { field(ZNAM, "LOW RATE") field(ONAM, "GOOD RATE") } ################################################################################ # Commands ################################################################################ record(ao, "$(INSTR)$(NAME):PRESET-COUNT") { 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") } record(seq, "$(INSTR)$(NAME):PAUSE") { field(DESC, "Pause the current count") field(SELM, "All") field(DO0, 3) field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP") } record(seq, "$(INSTR)$(NAME):CONTINUE") { field(DESC, "Continue with a count that was paused") field(SELM, "All") field(DO0, 4) field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP") } record(seq, "$(INSTR)$(NAME):STOP") { field(DESC, "Stop the current counting operation") field(SELM, "All") field(DO0, 5) field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP") } 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):CT") { field(SELM, "All") field(LNK0, "$(INSTR)$(NAME):ETS PP") field(DO0, 1) field(LNK1, "$(INSTR)$(NAME):ELAPSED-TIME PP") field(DO1, 0) field(LNK2, "$(INSTR)$(NAME):ETS PP") field(DO2, 0) } # Record is to signal command given by the client to the emulated counter. # It's set back to no command from the emulated counter subroutine record record(mbbi, "$(INSTR)$(NAME):COMMAND-TRIG") { field(DESC, "Command type") field(VAL, 0) field(ZRST, "No command") field(ZRVL, 0) field(ONST, "Count preset command") field(ONVL, 1) field(TWST, "Time preset command") field(TWVL, 2) field(THST, "Pause command") field(THVL, 3) field(FRST, "Continue command") field(FRVL, 4) field(FVST, "Stop command") field(FVVL, 5) field(SXST, "Full reset command") 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) } 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(ai,"$(INSTR)$(NAME):ELAPSED-TIME") { field(DESC, "DAQ Measured Time") field(EGU, "sec") } # Current Status of elapsed time record(bi, "$(INSTR)$(NAME):ETS") { field(DESC, "Channel Status") field(VAL, 0) field(ZNAM, "OK") field(ONAM, "CLEARING") } record(longout, "$(INSTR)$(NAME):MONITOR-CHANNEL") { alias("$(INSTR)$(NAME):MONITOR-CHANNEL_RBV") field(DESC, "PRESET-COUNT Monitors this channel") field(VAL, 1) } # Array Subroutine record which emulates the counterbox functionality # Use an aSub because it allows specifying the type. record(aSub, "$(INSTR)$(NAME):EMULATION") { # Scan rate determines how often we sample the rate # and how often the counter value updates. field(SCAN, ".1 second") field(SNAM, "processEmulatedCounter") # The first 4 inputs are also mapped as the first 4 outputs field(INPA, "$(INSTR)$(NAME):STATUS") field(FTA, "ULONG") field(INPB, "$(INSTR)$(NAME):M1") field(FTB, "INT64") field(INPC, "$(INSTR)$(NAME):ELAPSED-TIME") field(FTC, "DOUBLE") field(INPD, "$(INSTR)$(NAME):COMMAND-TRIG") field(FTD, "ULONG") # Address the PV which are mapped as input backwards field(INPE, "$(INSTR)$(NAME):THRESHOLD-MONITOR") 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(FTI, "DOUBLE") # L is last input before EPICS 7.0.10 field(INPJ, "$(INSTR)$(NAME):R1-PREV") field(FTJ, "DOUBLE") field(INPL, "$(INSTR)$(NAME):R1 PP") field(FTL, "DOUBLE") # The first 4 outputs are also mapped as the first 4 inputs field(OUTA, "$(INSTR)$(NAME):STATUS PP") field(FTVA, "ULONG") field(OUTB, "$(INSTR)$(NAME):M1 PP") field(FTVB, "INT64") field(OUTC, "$(INSTR)$(NAME):ELAPSED-TIME PP") field(FTVC, "DOUBLE") field(OUTD, "$(INSTR)$(NAME):COMMAND-TRIG PP") field(FTVD, "ULONG") field(OUTE, "$(INSTR)$(NAME):R1-PREV PP") field(FTVE, "DOUBLE") field(OUTF, "$(INSTR)$(NAME):IS_LOWRATE PP") field(FTVF, "ULONG") field(OUTF, "$(INSTR)$(NAME):MsgTxt PP") field(FTVF, "CHAR") field(NEVF, 40) } ####################### # Channel interface ####################### record(int64in, "$(INSTR)$(NAME):M1") { field(DESC, "DAQ CH0, proton current") field(EGU, "cts") } # The proton rate take by a PV over the network, PV named indicated by $(REMOTE_RATE_PV) macro # It emulates Zero rate if either shutter is closed. record(calc, "$(INSTR)$(NAME):R1") { field(DESC, "Rate of DAQ CH0 proton current") field(INPA, "$(REMOTE_RATE_PV) CA") field(INPB, "$(SHUTTER1_PV=0)") field(INPC, "$(SHUTTER1_CLOSED_VAL=1)") field(INPD, "$(SHUTTER2_PV=0)") field(INPE, "$(SHUTTER2_CLOSED_VAL=1)") # If either shutter is closed we have no rate field(CALC, "B != C && D != E ? A : 0") field(EGU, "cts/sec") } # Store previous rate value, so we can average over the period record(ai, "$(INSTR)$(NAME):R1-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):C1") { field(SELM, "All") field(LNK0, "$(INSTR)$(NAME):S1 PP") field(DO0, 1) field(LNK1, "$(INSTR)$(NAME):M1 PP") field(DO1, 0) field(LNK2, "$(INSTR)$(NAME):S1 PP") field(DO2, 0) } # Current Status of Channel # This is only to satify the interface. record(bi, "$(INSTR)$(NAME):S1") { field(DESC, "Channel Status") field(VAL, 0) field(ZNAM, "OK") field(ONAM, "CLEARING") }