Files
sinqDAQ/db/daq_soft_proton.db
T
wall_e 7e795504aa
Example Action / Lint (push) Successful in 2s
Example Action / BuildAndTest (push) Successful in 38s
updates soft proton current to functions with updated nicos device
2026-06-09 13:14:16 +02:00

437 lines
12 KiB
Plaintext

# EPICS database for counterbox-like interface using proton current,
# 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
################################################################################
# Count Config
################################################################################
record(longin, "$(INSTR)$(NAME):CHANNELS")
{
field(DESC, "Total Supported Channels")
field(VAL, 1)
field(DISP, 1)
}
record(waveform, "$(INSTR)$(NAME):MsgTxt")
{
field(DESC, "Miscellanous messages")
field(FTVL, "CHAR")
field(NELM, 40)
}
record(bi, "$(INSTR)$(NAME):IS_LOWRATE")
{
field(ZNAM, "LOW RATE")
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(seq, "$(INSTR)$(NAME):START")
{
field(DESC, "Start a count")
field(SELM, "All")
field(DO0, 1)
field(LNK0, "$(INSTR)$(NAME):COMMAND-TRIG PP")
}
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")
}
# 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)
}
# Identical to FULL-RESET
record(seq, "$(INSTR)$(NAME):RESET")
{
field(SELM, "All")
field(DO0, 1)
field(LNK0, "$(INSTR)$(NAME):FULL-RESET.PROC PP")
}
################################################################################
# Poll Loop
################################################################################
# TODO need to reset the presets to 0 after the count
record(dfanout, "$(INSTR)$(NAME):DB_POLL")
{
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(OUTC, "$(INSTR)$(NAME):STATUS.PROC")
field(OUTD, "$(INSTR)$(NAME):T_PRESET_CLEAR.PROC")
field(OUTE, "$(INSTR)$(NAME):PRESET_CLEAR.PROC")
}
# 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):CH1")
field(FTB, "INT64")
field(INPC, "$(INSTR)$(NAME):T")
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):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")
field(FTJ, "DOUBLE")
field(INPL, "$(INSTR)$(NAME):CH1_RATE PP")
field(FTL, "DOUBLE")
# The first 4 outputs are also mapped as the first 4 inputs
field(OUTA, "$(INSTR)$(NAME):RAW_STATUS PP")
field(FTVA, "ULONG")
field(OUTB, "$(INSTR)$(NAME):CH1 PP")
field(FTVB, "INT64")
field(OUTC, "$(INSTR)$(NAME):T PP")
field(FTVC, "DOUBLE")
field(OUTD, "$(INSTR)$(NAME):COMMAND-TRIG PP")
field(FTVD, "ULONG")
field(OUTE, "$(INSTR)$(NAME):CH1_RATE-PREV PP")
field(FTVE, "DOUBLE")
field(OUTF, "$(INSTR)$(NAME):IS_LOWRATE PP")
field(FTVF, "ULONG")
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")
field(PREC, 1)
}
record(calcout, "$(INSTR)$(NAME):T_PRESET_CLEAR")
{
field(INPA, "$(INSTR)$(NAME):STATUS NPP")
field(CALC, "A")
field(OOPT, "Transition To Zero")
field(DOPT, "Use OCAL")
field(OCAL, "0")
field(OUT, "$(INSTR)$(NAME):T_PRESET PP")
}
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):RAW_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")
field(EGU, "$(COUNTER_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):CH1_RATE")
{
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)")
# The proton current signal routed to real counterboxes
# transfer the current to frequency modulated signal.
# The relation ship 1uA -> 100Hz, meaning that nominal 1500uA -> 150kHz
field(INPF, "$(SCALE_FACTOR=100)")
# If either shutter is closed we have no rate
field(CALC, "B != C && D != E ? A * F : 0")
field(EGU, "$(RATE_EGU=cts/sec)")
}
# Store previous rate value, so we can average over the period
record(ai, "$(INSTR)$(NAME):CH1_RATE-PREV") {
field(DESC, "Previous rate of DAQ CH0 proton current")
}
record(calcout, "$(INSTR)$(NAME):PRESET_CLEAR")
{
field(INPA, "$(INSTR)$(NAME):STATUS NPP")
field(CALC, "A")
field(OOPT, "Transition To Zero")
field(DOPT, "Use OCAL")
field(OCAL, "0")
field(OUT, "$(INSTR)$(NAME):CH1_PRESET PP")
}
record(int64in, "$(INSTR)$(NAME):CH1_PRESET")
{
field(DESC, "Monitor Count Preset")
field(VAL, 0)
field(FLNK, "$(INSTR)$(NAME):CH1_SETTING")
}
record(calcout, "$(INSTR)$(NAME):CH1_SETTING")
{
field(INPA, "$(INSTR)$(NAME):CH1_PRESET NPP")
field(CALC, "A")
field(OOPT, "Transition To Non-zero")
field(DOPT, "Use OCAL")
field(OCAL, "1")
field(OUT, "$(INSTR)$(NAME):CH1_SET PP")
}
record(bo, "$(INSTR)$(NAME):CH1_SET")
{
field(DESC, "are hw counts being cleared")
field(ZNAM, "Set")
field(ONAM, "Setting")
field(HIGH, 0.1) # Emulate Waiting for HW setting preset
field(FLNK, "$(INSTR)$(NAME):CH1_STS_RAW")
}
record(calc, "$(INSTR)$(NAME):CH1_STS_RAW")
{
field(DESC, "Raw Channel Status")
field(INPA, "$(INSTR)$(NAME):RAW_STATUS NPP MS")
field(INPB, "$(INSTR)$(NAME):CH1_CLEARED NPP")
field(INPC, "$(INSTR)$(NAME):CH1_SET NPP")
field(CALC, "(0 < A && A < 4) || B || C")
field(FLNK, "$(INSTR)$(NAME):CH1_STS")
}
# Current Status of Channel
# This is only to satify the interface.
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, "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)
}