2 Commits

Author SHA1 Message Date
d44dbaaf0f clang is too old 2024-10-10 13:19:59 +02:00
501c5badea ran formatter 2024-09-30 17:12:30 +02:00
12 changed files with 609 additions and 1217 deletions

View File

@ -11,16 +11,11 @@ REQUIRED+=SynApps
REQUIRED+=stream
REQUIRED+=scaler
REQUIRED+=asynMotor
REQUIRED+=calc
# Release version
LIBVERSION=2024-ed-dev
LIBVERSION=2024-ed-dev2
# DB files to include in the release
TEMPLATES += sinqEPICSApp/Db/counterbox_common.db
TEMPLATES += sinqEPICSApp/Db/counterbox.db
TEMPLATES += sinqEPICSApp/Db/counterbox_v2.db
TEMPLATES += sinqEPICSApp/Db/counterbox.proto
TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db
TEMPLATES += sinqEPICSApp/Db/spsamor.db
@ -29,22 +24,18 @@ TEMPLATES += sinqEPICSApp/Db/spsamor.db
DBDS += sinqEPICSApp/src/sinq.dbd
# Source files to build
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
SOURCES += sinqEPICSApp/src/SINQAxis.cpp
SOURCES += sinqEPICSApp/src/SINQController.cpp
SOURCES += sinqEPICSApp/src/devScalerEL737.c
SOURCES += sinqEPICSApp/src/SINQController.cpp
SOURCES += sinqEPICSApp/src/SINQAxis.cpp
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
SOURCES += sinqEPICSApp/src/pmacController.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp
SOURCES += sinqEPICSApp/src/CounterBox.cpp
SCRIPTS += sinqEPICSApp/scripts/counterbox.cmd
SCRIPTS += sinqEPICSApp/scripts/counterbox_v2.cmd
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
USR_CFLAGS += -Wall -Wextra # -Werror

View File

@ -1,46 +0,0 @@
# EL737 EPICS Database for streamdevice support
# Macros
# P - Prefix
# NAME - just a name, e.g. EL737
# PROTO - Stream device protocol file
# ASYN_PORT - Low level Asyn IP Port to EL737
################################################################################
# Status Variables
record(longin, "$(P):$(NAME):MONITOR-CHANNEL")
{
field(DESC, "PRESET-COUNT Monitors this channel")
field(VAL, 1)
field(DISP, 1)
}
record(longin, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
{
field(DESC, "PRESET-COUNT Monitors this channel")
field(VAL, 1)
field(DISP, 1)
}
################################################################################
# Count Commands
record(longout,"$(P):$(NAME):THRESHOLD-MONITOR")
{
field(DESC, "Channel monitored for minimum rate")
field(VAL, "1") # Monitor
field(DRVL, "1") # Smallest Threshold Channel
field(DRVL, "8") # Largest Threshold Channel
}
################################################################################
# Read all monitors values
record(ai, "$(P):$(NAME):READALL")
{
field(DESC, "Reads monitors and elapsed time")
field(INP, "@$(PROTO) readAll8($(P):$(NAME):) $(ASYN_PORT)")
field(SCAN, ".2 second")
field(DTYP, "stream")
field(FLNK, "$(P):$(NAME):UNSET-COUNTING")
}

View File

@ -1,128 +0,0 @@
#
# Counterbox Protocol File
#
OutTerminator = CR;
InTerminator = CR;
ReadTimeout = 100;
WriteTimeout = 100;
ReplyTimeout = 200;
LockTimeout = 450;
initialise {
out "RMT 1";
in;
out "ECHO 2";
in "%(\$1MsgTxt)s"; # Clear MsgTxt on Init
@mismatch{
exec 'echo "Failed to configure counterbox" && exit(1)';
}
}
fullReset {
out "\%";
wait 5000;
}
################################################################################
# Status Variables
readStatus {
out "RS";
in "%d";
@mismatch{in "%(\$1MsgTxt)s";}
}
readPresetMonitor {
out "PC";
in "%d";
@mismatch{in "%(\$1MsgTxt)s";}
}
writePresetMonitor {
out "PC %d";
@mismatch{in "%(\$1MsgTxt)s";}
}
################################################################################
# Count Commands
startWithCountPreset {
out "MP %d";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
startWithTimePreset {
out "TP %#.2f";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
pauseCount {
out "PS";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
continueCount {
out "CO";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
stopCount {
out "S";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
setMinRate{
out "DL %(\$1THRESHOLD-MONITOR)d %(\$1THRESHOLD)d";
in;
out "DR %(\$1THRESHOLD-MONITOR)d";
in;
@mismatch{in "%(\$1MsgTxt)s";}
}
readMinRate{
out "DR";
in "%(\$1THRESHOLD-MONITOR_RBV)d";
out "DL %(\$1THRESHOLD-MONITOR_RBV)d";
in "%(\$1THRESHOLD_RBV)d";
@mismatch{in "%(\$1MsgTxt)s";}
}
################################################################################
# Read Values From Monitors
readAll8 {
out "RA";
in "%(\$1ELAPSED-TIME)f %(\$1M1)d %(\$1M2)d %(\$1M3)d %(\$1M4)d %(\$1M5)d %(\$1M6)d %(\$1M7)d %(\$1M8)d";
@mismatch{in "%(\$1MsgTxt)s";}
}
readAll10 {
out "RA";
in "%(\$1ELAPSED-TIME)f %(\$1M1)d %(\$1M2)d %(\$1M3)d %(\$1M4)d %(\$1M5)d %(\$1M6)d %(\$1M7)d %(\$1M8)d %(\$1M9)d %(\$1M10)d";
@mismatch{in "%(\$1MsgTxt)s";}
}
readRate {
out "RR \$2";
in "%(\$1R\$2)f";
@mismatch{in "%(\$1MsgTxt)s";}
}
################################################################################
# Testing Commands
switchTestgenOnOff {
out "TG %{off|on}";
@mismatch{in "%(\$1MsgTxt)s";}
}
# Only suppporting test channel 1 at the moment. (The first argument to TG)
setTestSignal {
out "TG 1 %(\$1TESTGEN-HIGHRATE)d %(\$1TESTGEN-LOWRATE)d";
@mismatch{in "%(\$1MsgTxt)s";}
}

View File

@ -1,259 +0,0 @@
# EL737 EPICS Database for streamdevice support
# Macros
# P - Prefix
# NAME - just a name, e.g. EL737
# PROTO - Stream device protocol file
# ASYN_PORT - Low level Asyn IP Port to EL737
# Send initial initialisation commands
record(bo, "$(P):$(NAME):INIT-CONF")
{
field(DESC, "Initialises the Counterbox")
field(OUT, "@$(PROTO) initialise($(P):$(NAME):) $(ASYN_PORT)")
field(PINI, "YES") # Run at init
field(DTYP, "stream")
field(FLNK, "$(P):$(NAME):INIT-BOX")
}
# As we aren't certain of the order that PINI exectutes PVs, we only set it to
# true on INIT-CONF to make sure the box is ready to receive commands, and then
# let INIT-CONF trigger the initialisation of other necessary records
record(fanout, "$(P):$(NAME):INIT-BOX")
{
field(DESC, "Rewrite PVs to Box")
field(SELM, "All")
field(LNK0, "$(P):$(NAME):MONITOR-CHANNEL_RBV PP")
field(LNK1, "$(P):$(NAME):READALL PP")
field(LNK2, "$(P):$(NAME):THRESHOLD_RBV PP")
}
record(longout, "$(P):$(NAME):FULL-RESET")
{
field(DESC, "Reset the Counterbox")
field(OUT, "@$(PROTO) fullReset($(P):$(NAME):) $(ASYN_PORT)")
field(DTYP, "stream")
}
################################################################################
# Status Variables
record(stringin, "$(P):$(NAME):MsgTxt")
{
field(DESC, "Unexpected received response")
field(DTYP, "devCounterBoxStringError")
field(FLNK, "$(P):$(NAME):INVALID-CONFIG")
}
# We want to recognise the invalid config error message, so that we can rerun
# the init if it occurs. This should only happen after turning the box off and
# on again or running a full reset
record(scalcout, "$(P):$(NAME):INVALID-CONFIG")
{
field(DESC, "Has the counterbox been configured?")
field(CALC, "AA[0,2] == '?OF'")
field(INAA, "$(P):$(NAME):MsgTxt")
field(FLNK, "$(P):$(NAME):REINIT-CONF")
}
record(seq, "$(P):$(NAME):REINIT-CONF")
{
field(LNK1, "$(P):$(NAME):INIT-CONF PP")
field(DO1, 1)
field(SELM, "Specified")
field(SELL, "$(P):$(NAME):INVALID-CONFIG.VAL")
}
# The COUNTING PV stays True until Counterbox has switched back to idle mode
# and the monitor counts have been read. Therefore, we know that the monitor
# values have been updated to represent their final values, when this switches
# back to False.
#
# This is accomplished via the explicit SET-COUNTING and UNSET-COUNTING seq
# records, that are triggered by a switch to the counting status
# (RAW-STATUS == 1 || 2) and a read of the monitors respectively.
record(bi, "$(P):$(NAME):COUNTING")
{
field(DESC, "Counterbox is Counting")
field(VAL, 0)
}
record(seq, "$(P):$(NAME):SET-COUNTING")
{
field(LNK1, "$(P):$(NAME):COUNTING PP")
field(DO1, 1)
field(SELM, "Specified")
field(SELL, "$(P):$(NAME):MAP-STATUS.VAL")
field(FLNK, "$(P):$(NAME):STATUS")
}
record(seq, "$(P):$(NAME):UNSET-COUNTING")
{
field(LNK0, "$(P):$(NAME):COUNTING PP")
field(DO0, 0)
field(SELM, "Specified")
field(SELL, "$(P):$(NAME):RAW-STATUS.VAL")
field(FLNK, "$(P):$(NAME):MAP-STATUS")
}
record(longin, "$(P):$(NAME):RAW-STATUS")
{
field(DESC, "Raw returned status value")
field(DTYP, "stream")
field(SCAN, ".2 second")
field(INP, "@$(PROTO) readStatus($(P):$(NAME):) $(ASYN_PORT)")
field(FLNK, "$(P):$(NAME):MAP-STATUS")
}
record(calc, "$(P):$(NAME):MAP-STATUS")
{
field(DESC, "Maps Raw Status to State")
field(INPA, "$(P):$(NAME):RAW-STATUS NPP")
field(INPB, "$(P):$(NAME):INVALID-CONFIG NPP")
field(INPC, "$(P):$(NAME):COUNTING NPP")
field(CALC, "B=1?4:(C=1&&A=0)||A=1||A=2?1:A=0?0:A=5||A=6?2:A=9||A=13||A=10||A=14?3:4")
field(FLNK, "$(P):$(NAME):SET-COUNTING")
}
record(mbbi, "$(P):$(NAME):STATUS")
{
field(DESC, "Counterbox Status")
field(INP, "$(P):$(NAME):MAP-STATUS NPP")
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 counter box reports undocumented statusbits
field(FRVL, "4")
field(FRST, "INVALID")
}
################################################################################
# Count Commands
record(ao,"$(P):$(NAME):PRESET-COUNT")
{
field(DESC, "Count until preset reached")
field(DTYP, "stream")
field(OUT, "@$(PROTO) startWithCountPreset($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, 0)
field(PREC, 2)
}
record(ao,"$(P):$(NAME):PRESET-TIME")
{
field(DESC, "Count for specified time")
field(DTYP, "stream")
field(OUT, "@$(PROTO) startWithTimePreset($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, 0)
field(PREC, 2)
field(EGU, "seconds")
}
record(bo,"$(P):$(NAME):PAUSE")
{
field(DESC, "Pause the current count")
field(DTYP, "stream")
field(OUT, "@$(PROTO) pauseCount($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, "0")
}
record(bo,"$(P):$(NAME):CONTINUE")
{
field(DESC, "Continue with a count that was paused")
field(DTYP, "stream")
field(OUT, "@$(PROTO) continueCount($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, "0")
}
record(bo, "$(P):$(NAME):STOP")
{
field(DESC, "Stop the current counting operation")
field(DTYP, "stream")
field(OUT, "@$(PROTO) stopCount($(P):$(NAME):) $(ASYN_PORT)")
}
# TODO should changing the monitor also set things?
# or only when actually setting a threshold?
record(longout,"$(P):$(NAME):THRESHOLD")
{
field(DESC, "Minimum rate for counting to proceed")
field(VAL, "0") # Rate
field(DRVL, "0") # Minimum Rate
field(DTYP, "stream")
field(OUT, "@$(PROTO) setMinRate($(P):$(NAME):) $(ASYN_PORT)")
}
record(longin,"$(P):$(NAME):THRESHOLD_RBV")
{
field(DESC, "Minimum rate for counting to proceed")
field(DTYP, "stream")
field(INP, "@$(PROTO) readMinRate($(P):$(NAME):) $(ASYN_PORT)")
field(SCAN, "2 second")
}
record(longin,"$(P):$(NAME):THRESHOLD-MONITOR_RBV")
{
field(DESC, "Channel monitored for minimum rate")
}
################################################################################
# Read all monitors values
record(ai,"$(P):$(NAME):ELAPSED-TIME")
{
field(DESC, "Counterbox Measured Time")
field(EGU, "seconds")
}
record(longin, "$(P):$(NAME):M1")
{
field(DESC, "Counterbox CH1")
}
record(longin, "$(P):$(NAME):M2")
{
field(DESC, "Counterbox CH2")
}
record(longin, "$(P):$(NAME):M3")
{
field(DESC, "Counterbox CH3")
}
record(longin, "$(P):$(NAME):M4")
{
field(DESC, "Counterbox CH4")
}
record(longin, "$(P):$(NAME):M5")
{
field(DESC, "Counterbox CH5")
}
record(longin, "$(P):$(NAME):M6")
{
field(DESC, "Counterbox CH6")
}
record(longin, "$(P):$(NAME):M7")
{
field(DESC, "Counterbox CH7")
}
record(longin, "$(P):$(NAME):M8")
{
field(DESC, "Counterbox CH8")
}
# Not yet sure whether we want to support this
# record(longin, "$(P):$(NAME):R1")
# {
# field(DESC, "Counterbox Rate CH1")
# field(INP, "@$(PROTO) readRate($(P):$(NAME):, 1) $(ASYN_PORT)")
# field(SCAN, ".2 second")
# field(DTYP, "stream")
# }

View File

@ -1,88 +0,0 @@
# EL737 EPICS Database for streamdevice support
# Macros
# P - Prefix
# NAME - just a name, e.g. DAQV2
# PROTO - Stream device protocol file
# ASYN_PORT - Low level Asyn IP Port to Counterbox
################################################################################
# Status Variables
record(longout, "$(P):$(NAME):MONITOR-CHANNEL")
{
field(DESC, "PRESET-COUNT Monitors this channel")
field(DTYP, "stream")
field(OUT, "@$(PROTO) writePresetMonitor($(P):$(NAME):) $(ASYN_PORT)")
field(FLNK, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
}
record(longin, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
{
field(DESC, "PRESET-COUNT Monitors this channel")
field(DTYP, "stream")
field(INP, "@$(PROTO) readPresetMonitor($(P):$(NAME):) $(ASYN_PORT)")
field(SCAN, "5 second")
}
################################################################################
# Count Commands
record(longout,"$(P):$(NAME):THRESHOLD-MONITOR")
{
field(DESC, "Channel monitored for minimum rate")
field(VAL, "1") # Monitor
field(DRVL, "1") # Smallest Threshold Channel
field(DRVL, "10") # Largest Threshold Channel
}
################################################################################
# Read all monitors values
record(ai, "$(P):$(NAME):READALL")
{
field(DESC, "Reads monitors and elapsed time")
field(INP, "@$(PROTO) readAll10($(P):$(NAME):) $(ASYN_PORT)")
field(SCAN, ".2 second")
field(DTYP, "stream")
field(FLNK, "$(P):$(NAME):UNSET-COUNTING")
}
record(longin, "$(P):$(NAME):M9")
{
field(DESC, "Counterbox CH9")
}
record(longin, "$(P):$(NAME):M10")
{
field(DESC, "Counterbox CH10")
}
################################################################################
# Testing Commands
# These won't match the values on the machine after a full restart But I chose
# not to force their intialisation as they are only important for testing
record(bo, "$(P):$(NAME):TESTGEN")
{
field(DESC, "Turn on/off Testgen Signal")
field(DTYP, "stream")
field(OUT, "@$(PROTO) switchTestgenOnOff($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, 0)
}
record(longout, "$(P):$(NAME):TESTGEN-LOWRATE")
{
field(DESC, "Set Minimum Testgen Rate")
field(DTYP, "stream")
field(OUT, "@$(PROTO) setTestSignal($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, 1000)
}
record(longout, "$(P):$(NAME):TESTGEN-HIGHRATE")
{
field(DESC, "Set Maximum Testgen Rate")
field(DTYP, "stream")
field(OUT, "@$(PROTO) setTestSignal($(P):$(NAME):) $(ASYN_PORT)")
field(VAL, 1000)
}

View File

@ -1,13 +0,0 @@
require asyn
# Need to be set by user
# epicsEnvSet("CNTBOX_HOST", "testinst-daq1:2000")
# epicsEnvSet("ASYN_PORT", "el737")
# epicsEnvSet("PREFIX", "SQ:SINQTEST")
# epicsEnvSet("NAME", "COUNTERBOX")
epicsEnvSet("PROTO", "$(sinq_DB)counterbox.proto")
drvAsynIPPortConfigure("$(ASYN_PORT)", "$(CNTBOX_HOST)", 0, 0, 0)
dbLoadRecords("$(sinq_DB)counterbox_common.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
dbLoadRecords("$(sinq_DB)counterbox.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")

View File

@ -1,13 +0,0 @@
require asyn
# Need to be set by user
# epicsEnvSet("CNTBOX_HOST", "testinst-daq1:2000")
# epicsEnvSet("ASYN_PORT", "el737")
# epicsEnvSet("PREFIX", "SQ:SINQTEST")
# epicsEnvSet("NAME", "COUNTERBOX")
epicsEnvSet("PROTO", "$(sinq_DB)counterbox.proto")
drvAsynIPPortConfigure("$(ASYN_PORT)", "$(CNTBOX_HOST)", 0, 0, 0)
dbLoadRecords("$(sinq_DB)counterbox_common.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
dbLoadRecords("$(sinq_DB)counterbox_v2.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")

View File

@ -1,60 +0,0 @@
#include <epicsExport.h>
#include <string.h>
#include <string>
#include <stringinRecord.h>
#define update_val(prec, new_val) \
{ \
static_assert(std::char_traits<char>::length((new_val)) < MAX_SIZE, \
"New Val Exceeds Max String Length"); \
strncpy((prec)->val, (new_val), MAX_SIZE); \
}
static long map_raw_failure_message(struct stringinRecord *prec) {
const uint8_t MAX_SIZE = 40; // including null terminator
if (strcmp(prec->val, "?OF") == 0) {
update_val(prec, "?OF: Configuration Error");
} else if (strcmp(prec->val, "?OV") == 0) {
update_val(prec, "?OV: Overflow");
} else if (strcmp(prec->val, "?1") == 0) {
update_val(prec, "?1: Parameter out of range");
} else if (strcmp(prec->val, "?2") == 0) {
update_val(prec, "?2: Bad command");
} else if (strcmp(prec->val, "?3") == 0) {
update_val(prec, "?3: Bad parameter");
} else if (strcmp(prec->val, "?4") == 0) {
update_val(prec, "?4: Bad counter");
} else if (strcmp(prec->val, "?5") == 0) {
update_val(prec, "?5: Parameter missing");
} else if (strcmp(prec->val, "?6") == 0) {
update_val(prec, "?6: Too many counts");
} else if (strcmp(prec->val, "?91") == 0) {
update_val(prec, "?91: Start Failure");
} else if (strcmp(prec->val, "?92") == 0) {
update_val(prec, "?92: Failure while counting");
} else if (strcmp(prec->val, "?93") == 0) {
update_val(prec, "?93: Read Failure");
} else if (strstr(prec->val, "?") != NULL) {
char val_copy[40] = {0};
strncpy(val_copy, prec->val, MAX_SIZE);
snprintf(prec->val, MAX_SIZE - 1, "%s: HW error", val_copy);
} else {
// Leave the message as is
}
return 0; // returns: (-1,0)=>(failure,success)
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_ai;
DEVSUPFUN special_linconv;
} devCounterBoxStringError = {
6, NULL, NULL, NULL, NULL, (DEVSUPFUN)map_raw_failure_message, NULL};
epicsExportAddress(dset, devCounterBoxStringError);

View File

@ -29,7 +29,7 @@ sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp
sinqEPICS_SRCS += NanotecDriver.cpp stptok.cpp
sinqEPICS_SRCS += PhytronDriver.cpp
sinqEPICS_SRCS += EuroMoveDriver.cpp
sinqEPICS_SRCS += CounterBox.cpp
# Build the main IOC entry point on workstation OSs.
sinqEPICS_SRCS_DEFAULT += sinqEPICSMain.cpp

View File

@ -14,59 +14,59 @@
* 2 = threshold monitor
* 3 = threshold monitor count
*
* This is less then ideal. But it is a workaround for the fact that the scalar record
* does not do all the tricks the EL737 knows:
* This is less then ideal. But it is a workaround for the fact that the scalar
* record does not do all the tricks the EL737 knows:
* - Thresholding
* - two count modes
* A better solution would be to extend the scalar record to have a proper status and count
* mode field. And threshold control fields too. But this is much more work, breaks
* compatability with the scalar record completely and thus was not done for now.
* A better solution would be to extend the scalar record to have a proper
* status and count mode field. And threshold control fields too. But this is
* much more work, breaks compatability with the scalar record completely and
* thus was not done for now.
*
* The driver will run a separate thread which does all the
* communication.
*
* Mark Koennecke, February 2013
*
* Enhanced by adding an addtional external pause and status flag in the database and code in here
* to handle this. Moreover an external MsgTxt field in the DB can be filled with an error message.
* Enhanced by adding an addtional external pause and status flag in the
* database and code in here to handle this. Moreover an external MsgTxt field
* in the DB can be filled with an error message.
*
* Mark Koennecke, July 2017
*
* Enhanced with a separate thresholdCounter and threshold field in order to replace the
* hack for threshold handling. If these fields are present, the hack with presets 2 and
* 3 as described above is ignored.
* Enhanced with a separate thresholdCounter and threshold field in order to
* replace the hack for threshold handling. If these fields are present, the
* hack with presets 2 and 3 as described above is ignored.
*
* Mark Koennecke, August 2017
*/
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <asynEpicsUtils.h>
#include <asynOctetSyncIO.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#include <epicsExport.h>
#include <errlog.h>
#include <callback.h>
#include <recSup.h>
#include <devSup.h>
#include <cantProceed.h>
#include <dbAccess.h>
#include <dbDefs.h>
#include <dbFldTypes.h>
#include <dbAccess.h>
#include <errlog.h>
#include <scalerRecord.h>
#include <devScaler.h>
#include <asynEpicsUtils.h>
#include <devSup.h>
#include <epicsEvent.h>
#include <epicsExport.h>
#include <epicsThread.h>
#include <epicsTime.h>
#include <errlog.h>
#include <errno.h>
#include <recSup.h>
#include <scalerRecord.h>
#include <string.h>
#include <unistd.h>
/* dset functions */
static long el737_init_record(struct scalerRecord *psr, CALLBACK *pcallback);
static long el737_reset(scalerRecord *psr);
static long el737_read(scalerRecord *psr, epicsUInt32 *val);
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val);
static long el737_write_preset(scalerRecord *psr, int signal,
unsigned long val);
static long el737_arm(scalerRecord *psr, int val);
static long el737_done(scalerRecord *psr);
@ -82,17 +82,9 @@ static void el737Thread(void *param);
#define COMLEN 132
SCALERDSET devScalerEL737 = {
7,
NULL,
NULL,
el737_init_record,
NULL,
el737_reset,
el737_read,
el737_write_preset,
el737_arm,
el737_done
};
7, NULL, NULL, el737_init_record,
NULL, el737_reset, el737_read, el737_write_preset,
el737_arm, el737_done};
epicsExportAddress(dset, devScalerEL737);
typedef struct {
@ -114,12 +106,9 @@ typedef struct {
unsigned int dbInit;
} EL737priv;
static void dummyAsynCallback([[maybe_unused]] asynUser *pasynUser)
{
}
static void dummyAsynCallback(asynUser *pasynUser) {}
static void connectSlaveRecords(EL737priv *priv)
{
static void connectSlaveRecords(EL737priv *priv) {
char slaveName[PVNAME_SZ + 18], errName[256];
long status;
@ -129,10 +118,12 @@ static void connectSlaveRecords(EL737priv *priv)
status = dbNameToAddr(slaveName, &priv->pause);
if (status != 0) {
errSymLookup(status, errName, sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName,
errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->pause.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",
slaveName, priv->pause.precord->name);
}
snprintf(slaveName, sizeof(slaveName), "%s:MsgTxt", priv->psr->name);
@ -140,10 +131,12 @@ static void connectSlaveRecords(EL737priv *priv)
status = dbNameToAddr(slaveName, &priv->msgTxt);
if (status != 0) {
errSymLookup(status, errName, sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName,
errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->msgTxt.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",
slaveName, priv->msgTxt.precord->name);
}
snprintf(slaveName, sizeof(slaveName), "%s:Status", priv->psr->name);
@ -151,21 +144,26 @@ static void connectSlaveRecords(EL737priv *priv)
status = dbNameToAddr(slaveName, &priv->status);
if (status != 0) {
errSymLookup(status, errName, sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName,
errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",
slaveName, priv->status.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:ThresholdCounter", priv->psr->name);
snprintf(slaveName, sizeof(slaveName), "%s:ThresholdCounter",
priv->psr->name);
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
status = dbNameToAddr(slaveName, &priv->threshCounter);
if (status != 0) {
errSymLookup(status, errName, sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName,
errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshCounter.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",
slaveName, priv->threshCounter.precord->name);
}
snprintf(slaveName, sizeof(slaveName), "%s:Threshold", priv->psr->name);
@ -173,16 +171,17 @@ static void connectSlaveRecords(EL737priv *priv)
status = dbNameToAddr(slaveName, &priv->threshold);
if (status != 0) {
errSymLookup(status, errName, sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName,
errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshold.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",
slaveName, priv->threshold.precord->name);
}
priv->thresholdValue = -999;
}
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
{
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback) {
EL737priv *priv = NULL;
char *port, *userParam;
int signal, status, reason;
@ -204,23 +203,23 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
/*
* private data structure
*/
priv = callocMustSucceed(1,sizeof(EL737priv), "devScalerEL737 init_record()");
priv =
callocMustSucceed(1, sizeof(EL737priv), "devScalerEL737 init_record()");
priv->psr = psr;
priv->wakeUp = epicsEventCreate(epicsEventEmpty);
priv->pcallback = pcallback;
priv->dbInit = 0;
psr->dpvt = priv;
/*
* Hook up with device
*/
dummyUser = pasynManager->createAsynUser(dummyAsynCallback, 0);
status = pasynEpicsUtils->parseLink(dummyUser, &psr->out,
&port, &signal, &userParam);
status = pasynEpicsUtils->parseLink(dummyUser, &psr->out, &port, &signal,
&userParam);
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::init_record %s bad link %s\n",
psr->name, dummyUser->errorMessage);
errlogPrintf("devScalerEL737::init_record %s bad link %s\n", psr->name,
dummyUser->errorMessage);
psr->pact = 1;
return 0;
}
@ -251,18 +250,14 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
/*
start the thread which actually runs the device
*/
epicsThreadCreate("EL737",
epicsThreadPriorityMedium,
epicsThreadStackMedium,
el737Thread,
priv);
epicsThreadCreate("EL737", epicsThreadPriorityMedium,
epicsThreadStackMedium, el737Thread, priv);
// errlogPrintf("EL7373 thread started \n");
return 0;
}
static long el737_reset(scalerRecord *psr)
{
static long el737_reset(scalerRecord *psr) {
unsigned int i;
EL737priv *priv;
@ -273,8 +268,7 @@ static long el737_reset(scalerRecord *psr)
return 0;
}
static long el737_read(scalerRecord *psr, epicsUInt32 *val)
{
static long el737_read(scalerRecord *psr, epicsUInt32 *val) {
unsigned int i;
EL737priv *priv;
@ -286,8 +280,8 @@ static long el737_read(scalerRecord *psr, epicsUInt32 *val)
return 0;
}
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
{
static long el737_write_preset(scalerRecord *psr, int signal,
unsigned long val) {
EL737priv *priv;
// errlogPrintf("EL737: Setting preset %d to %ld\n", signal, val);
@ -300,13 +294,11 @@ static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
priv->sendThreshold = 1;
epicsEventSignal(priv->wakeUp);
// errlogPrintf("EL737: Setting threshold \n");
}
return 0;
}
static long el737_arm(scalerRecord *psr, int val)
{
static long el737_arm(scalerRecord *psr, int val) {
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
@ -319,8 +311,7 @@ static long el737_arm(scalerRecord *psr, int val)
return 0;
}
static long el737_done(scalerRecord *psr)
{
static long el737_done(scalerRecord *psr) {
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
@ -337,8 +328,8 @@ static long el737_done(scalerRecord *psr)
}
}
static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],char reply[COMLEN])
{
static asynStatus el737_transactCommand(EL737priv *priv, char command[COMLEN],
char reply[COMLEN]) {
asynStatus status;
size_t in, out;
int reason, dbStatus;
@ -349,18 +340,22 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
// errlogPrintf("EL737: sending command %s\n", command);
strcpy(message, "");
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
status =
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply, COMLEN, 1., &out, &in, &reason);
if (status == asynSuccess) {
if (strstr(reply, "?OF") != NULL) {
strcpy(myCommand, "RMT 1");
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
status = pasynOctetSyncIO->writeRead(
priv->asynContext, myCommand, strlen(myCommand), reply, COMLEN,
1., &out, &in, &reason);
strcpy(myCommand, "ECHO 2");
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
return pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
status = pasynOctetSyncIO->writeRead(
priv->asynContext, myCommand, strlen(myCommand), reply, COMLEN,
1., &out, &in, &reason);
return pasynOctetSyncIO->writeRead(priv->asynContext, command,
strlen(command), reply, COMLEN,
1., &out, &in, &reason);
} else {
if (strstr(reply, "?OV") != NULL) {
strncpy(message, "Overflow", sizeof(message));
@ -392,7 +387,8 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
if (errno == EAGAIN) {
errlogPrintf("Lost response to %s with EAGAIN\n", command);
} else {
snprintf(message,sizeof(message), "Lost communication with errno %d", errno);
snprintf(message, sizeof(message),
"Lost communication with errno %d", errno);
/* force a reconnect */
pasynOctetSyncIO->disconnect(priv->asynContext);
}
@ -401,33 +397,33 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
dbStatus = dbPutField(&priv->msgTxt, DBR_STRING, message, 1);
if (dbStatus != 0) {
errSymLookup(dbStatus, message, sizeof(message));
errlogPrintf("Setting external count message failed with %s\n", message);
errlogPrintf("Setting external count message failed with %s\n",
message);
}
}
return status;
}
static asynStatus sendStop(EL737priv *priv)
{
static asynStatus sendStop(EL737priv *priv) {
char command[COMLEN], reply[COMLEN];
// errlogPrintf("EL737: Sending stop\n");
strcpy(command, "S");
return el737_transactCommand(priv, command, reply);
}
static void runEvents(EL737priv *priv)
{
static void runEvents(EL737priv *priv) {
char command[COMLEN], reply[COMLEN], errName[256];
int status;
long dbStatus, nElements = 1, options = 0, threshCounter;
long unsigned int myThreshold;
/*
This is the better way to set the threshold rather then the old way below which uses
presets for that function. This one uses separate fields.
This is the better way to set the threshold rather then the old way below
which uses presets for that function. This one uses separate fields.
*/
if (priv->dbInit == 1 && priv->sendThreshold) {
dbStatus = dbGetField(&priv->threshold,DBR_LONG,&myThreshold,&options, &nElements,NULL);
dbStatus = dbGetField(&priv->threshold, DBR_LONG, &myThreshold,
&options, &nElements, NULL);
if (dbStatus != 0) {
errSymLookup(dbStatus, errName, sizeof(errName));
errlogPrintf("Reading threshold failed with %s\n", errName);
@ -436,10 +432,13 @@ static void runEvents(EL737priv *priv)
/*
we have to set the threshold
*/
dbStatus = dbGetField(&priv->threshCounter,DBR_LONG,&threshCounter, &options, &nElements,NULL);
dbStatus =
dbGetField(&priv->threshCounter, DBR_LONG, &threshCounter,
&options, &nElements, NULL);
if (dbStatus != 0) {
errSymLookup(dbStatus, errName, sizeof(errName));
errlogPrintf("Reading thresholdCounter failed with %s\n", errName);
errlogPrintf("Reading thresholdCounter failed with %s\n",
errName);
} else {
if (threshCounter == 0) {
threshCounter = 1;
@ -460,11 +459,13 @@ static void runEvents(EL737priv *priv)
}
if (priv->sendThreshold == 1) {
// fallback when we do not have the DB fields or threshCounter is invalid
// fallback when we do not have the DB fields or threshCounter is
// invalid
if (priv->presets[THRESHMON] > 0) {
sprintf(command, "DL %d %d", (int)priv->presets[THRESHMON],
(int)priv->presets[THRESHVAL]);
// errlogPrintf("Sending threshold from presets, command %s\n", command);
// errlogPrintf("Sending threshold from presets, command %s\n",
// command);
status = el737_transactCommand(priv, command, reply);
sprintf(command, "DR %d", (int)priv->presets[THRESHMON]);
status = el737_transactCommand(priv, command, reply);
@ -496,15 +497,14 @@ static void runEvents(EL737priv *priv)
}
}
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n, errno =%d",
errlogPrintf(
"devScalerEL737::el737Thread communication problem %s\n, errno =%d",
priv->asynContext->errorMessage, errno);
}
}
static void updateValues(EL737priv *priv)
{
static void updateValues(EL737priv *priv) {
char command[COMLEN], reply[COMLEN];
int status;
float fTime;
@ -514,20 +514,24 @@ static void updateValues(EL737priv *priv)
status = el737_transactCommand(priv, command, reply);
if (status != asynSuccess) {
if (errno == EAGAIN) {
errlogPrintf("devScalerEL737::el737Thread lost response to %s with EAGAIN\n", command);
errlogPrintf(
"devScalerEL737::el737Thread lost response to %s with EAGAIN\n",
command);
} else {
errlogPrintf("devScalerEL737::el737Thread communication problem %s, errno = %d\n",
errlogPrintf("devScalerEL737::el737Thread communication problem "
"%s, errno = %d\n",
priv->asynContext->errorMessage, errno);
}
return;
}
status = sscanf(reply, "%f %ld %ld %ld %ld %ld %ld %ld %ld",
&fTime, &m1, &m2, &m3, &m4, &m5, &m6, &m7, &m8);
status = sscanf(reply, "%f %ld %ld %ld %ld %ld %ld %ld %ld", &fTime, &m1,
&m2, &m3, &m4, &m5, &m6, &m7, &m8);
if (status != 9) {
/*
old form with 4 monitors
*/
status = sscanf(reply, "%ld %ld %ld %ld %f", &m1, &m2, &m3, &m4, &fTime);
status =
sscanf(reply, "%ld %ld %ld %ld %f", &m1, &m2, &m3, &m4, &fTime);
if (status != 5) {
errlogPrintf("devScalerEL737::el737Thread bad RA reply %s\n",
reply);
@ -543,11 +547,9 @@ static void updateValues(EL737priv *priv)
priv->values[6] = m6;
priv->values[7] = m7;
priv->values[8] = m8;
}
static void el737Thread(void *param)
{
static void el737Thread(void *param) {
EL737priv *priv = (EL737priv *)param;
epicsEventWaitStatus evStatus;
double timeout = 60.;
@ -581,12 +583,12 @@ static void el737Thread(void *param)
status = sendStop(priv);
}
if (priv->counting) {
strcpy(command, "RS");
status = el737_transactCommand(priv, command, reply);
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
errlogPrintf("devScalerEL737::el737Thread communication "
"problem %s\n",
priv->asynContext->errorMessage);
continue;
}
@ -619,23 +621,28 @@ static void el737Thread(void *param)
dbStatus = dbPutField(&priv->status, DBR_LONG, &ctStatus, 1);
if (dbStatus != 0) {
errSymLookup(dbStatus, errName, sizeof(errName));
errlogPrintf("Setting external count status failed with %s\n", errName);
errlogPrintf(
"Setting external count status failed with %s\n",
errName);
}
/*
check and handle pause flag
*/
dbStatus = dbGetField(&priv->pause, DBR_LONG,&pauseFlag,&options, &nElements, NULL);
dbStatus = dbGetField(&priv->pause, DBR_LONG, &pauseFlag,
&options, &nElements, NULL);
if (dbStatus != 0) {
errSymLookup(dbStatus, errName, sizeof(errName));
errlogPrintf("Reading pauseFlag failed with %s\n", errName);
}
/* errlogPrintf("Successfully read %ld pause flags as %d\n",nElements, pauseFlag); */
/* errlogPrintf("Successfully read %ld pause flags as
* %d\n",nElements, pauseFlag); */
if (pauseFlag == 1 && ctStatus == 1) {
strcpy(command, "PS");
status = el737_transactCommand(priv, command, reply);
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
errlogPrintf("devScalerEL737::el737Thread "
"communication problem %s\n",
priv->asynContext->errorMessage);
continue;
}
@ -643,7 +650,8 @@ static void el737Thread(void *param)
strcpy(command, "CO");
status = el737_transactCommand(priv, command, reply);
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
errlogPrintf("devScalerEL737::el737Thread "
"communication problem %s\n",
priv->asynContext->errorMessage);
continue;
}
@ -658,4 +666,3 @@ static void el737Thread(void *param)
}
}
}

View File

@ -1,16 +1,18 @@
record(bo,"$(P):Pause")
{
field(DTYP,"Soft Channel")
}
record(longin,"$(P):Status")
{
field(DTYP,"Soft Channel")
}
record(stringin,"$(P):MsgTxt")
{
field(DTYP,"Soft Channel")
}
record(scaler,"$(P)")
{
field(DESC,"$(DESC)")

View File

@ -20,7 +20,6 @@ registrar(SINQControllerRegister)
#include "/ioc/modules/scaler/koennecke/R3.14.12/dbd/scaler.dbd"
device(scaler,INST_IO,devScalerEL737,"asynScalerEL737")
device(stringin,INST_IO,devCounterBoxStringError,"devCounterBoxStringError")
#--------- For lakeshore and magnets
#include "/ioc/modules/stream/2.7.9/R3.14.12/dbd/stream.dbd"