Draft Stream Device Rewrite of Counterbox Driver

This commit is contained in:
2024-10-18 13:18:43 +02:00
commit d612f5c565
11 changed files with 930 additions and 0 deletions

236
.clang-format Normal file
View File

@ -0,0 +1,236 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

49
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,49 @@
default:
image: docker.psi.ch:5000/wall_e/sinqepics:latest
stages:
- test
- build
cppcheck:
stage: test
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
- docker
formatting:
stage: test
script:
- clang-format --style=file --Werror --dry-run src/*.cpp
artifacts:
expire_in: 1 week
tags:
- docker
# clangtidy:
# stage: test
# script:
# - curl https://docker.psi.ch:5000/v2/_catalog
# # - dnf update -y
# # - dnf install -y clang-tools-extra
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
# # tags:
# # - docker
build_module:
stage: build
script:
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- make install
- cp -rT "/ioc/modules/counterbox/$(ls -U /ioc/modules/counterbox/ | head -1)" "./counterbox-${CI_COMMIT_SHORT_SHA}"
artifacts:
name: "counterbox-${CI_COMMIT_SHORT_SHA}"
paths:
- "counterbox-${CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- docker

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
# This build the sinq extensions for the PSI EPICS setup
include /ioc/tools/driver.makefile
MODULE=counterbox
BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL%
# additional module dependencies
REQUIRED+=stream
REQUIRED+=calc
# Release version
LIBVERSION=ed-dev
# DB files to include in the release
TEMPLATES += db/counterbox.db
TEMPLATES += db/counterbox.proto
TEMPLATES += db/counterbox_common.db
TEMPLATES += db/counterbox_v2.db
# DBD files to include in the release
DBDS += src/counterbox.dbd
# Source files to build
SOURCES += src/counterbox.cpp
SCRIPTS += scripts/counterbox.cmd
SCRIPTS += scripts/counterbox_v2.cmd
USR_CFLAGS += -Wall -Wextra #-Werror
# MISCS would be the place to keep the stream device template files

46
db/counterbox.db Normal file
View File

@ -0,0 +1,46 @@
# 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")
}

128
db/counterbox.proto Normal file
View File

@ -0,0 +1,128 @@
#
# 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";}
}

259
db/counterbox_common.db Normal file
View File

@ -0,0 +1,259 @@
# 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")
# }

88
db/counterbox_v2.db Normal file
View File

@ -0,0 +1,88 @@
# 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)
}

13
scripts/counterbox.cmd Normal file
View File

@ -0,0 +1,13 @@
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)")

13
scripts/counterbox_v2.cmd Normal file
View File

@ -0,0 +1,13 @@
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)")

60
src/counterbox.cpp Normal file
View File

@ -0,0 +1,60 @@
#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);

5
src/counterbox.dbd Normal file
View File

@ -0,0 +1,5 @@
#---------------------------------------------
# Counterbox specific DB definitions
#---------------------------------------------
device(stringin,INST_IO,devCounterBoxStringError,"devCounterBoxStringError")