Initial commit of Beamline_elements frontend

This commit is contained in:
2026-07-28 14:07:13 +02:00
commit c88fa8bdcc
5 changed files with 904 additions and 0 deletions
+246
View File
@@ -0,0 +1,246 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: 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
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
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
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
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
PenaltyBreakScopeResolution: 500
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
SkipMacroDefinitionBody: false
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
AfterPlacementOperator: true
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: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
---
+1
View File
@@ -0,0 +1 @@
build
+67
View File
@@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.0)
project(Beamline_elements_scfe)
# Check if the required environment variables MIDASSYS and EPICSSYS are available
if (NOT DEFINED ENV{MIDASSYS})
message(SEND_ERROR "MIDASSYS environment variable not defined.")
endif()
if (NOT DEFINED ENV{EPICSSYS})
message(SEND_ERROR "EPICSSYS environment variable not defined.")
endif()
set(CMAKE_CXX_STANDARD 11)
set(MIDASSYS $ENV{MIDASSYS})
set(EPICSSYS $ENV{EPICSSYS})
# Select the correct EPICS library depending on the OS (currently Linus or Darwin are available)
if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
link_directories(${EPICSSYS}/lib/linux-x86_64)
set(LIBS ${LIBS} -lpthread -lutil -lrt -lbsd -ldl)
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES Darwin)
link_directories(${EPICSSYS}/lib/darwin-aarch64)
set(LIBS ${LIBS} -lutil -lca)
endif()
add_executable(Beamline_elements_scfe
frontend/Beamline_elements_scfe.cxx
)
target_compile_options(Beamline_elements_scfe PRIVATE -g)
target_include_directories(Beamline_elements_scfe
PRIVATE
${MIDASSYS}/include
${EPICSSYS}/include
${EPICSSYS}/include/os/Linux
${EPICSSYS}/include/os/Darwin
${EPICSSYS}/include/compiler/gcc
${EPICSSYS}/include/compiler/clang
${CMAKE_CURRENT_SOURCE_DIR}/mxml
)
target_link_libraries(Beamline_elements_scfe
PRIVATE
${EPICSSYS}/lib/$ENV{EPICS_HOST_ARCH}/libca.so
$ENV{MIDASSYS}/lib/libmidas.a
$ENV{MIDASSYS}/lib/libmfe.a
$ENV{MIDASSYS}/lib/libmidas-mfed.a
${LIBS}
)
# This copies the html configuration paths to the Midas installation
install(
FILES
${CMAKE_CURRENT_LIST_DIR}/help.html
${CMAKE_CURRENT_LIST_DIR}/msp_config.html
${CMAKE_CURRENT_LIST_DIR}/msetpoint.html
DESTINATION ${CMAKE_INSTALL_PREFIX}/msetpoint
)
install(
TARGETS
Beamline_elements_scfe
RUNTIME DESTINATION bin
)
+36
View File
@@ -0,0 +1,36 @@
# Beamline elements frontend
This repository contains the "Beamline_elements" frontend, which is used to
control beamline devices such as magnets, slits or beamblockers of indivual
instruments from MIDAS.
This file is almost a verbatim copy of
<https://bitbucket.org/tmidas/midas/src/develop/msetpoint/epics_fe.cxx> from
core MIDAS. The only reason this repository exists is that "epics_fe.cxx" is
written as a C-style frontend, where the equipment and frontend is hardcoded in
the binary. Hence, this copy changes "EPICS Frontend" and "EPICS" to
"Beamline_elements" to match the agreed upon naming convention.
## Requirements
- Environment variable `MIDASSYS` which is a path to the MIDAS shared libraries
and headers. The libraries must be in `$MIDASSYS/lib`, the headers in
`$MIDASSYS/include`.
- Environment variable `EPICSSYS` which is a path to the EPICS shared libraries
and headers. The libraries must be in `$EPICSSYS/lib`, the headers in
`$EPICSSYS/include`.
## Build
Clone this repository, enter the cloned directory, and then build via CMake.
```bash
cmake -S "$(pwd)" -B <directory-to-build-in>
cmake --build <directory-to-build-in> --clean-first -- -j8
```
## Run
```bash
<directory-to-build-in>/Beamline_elements_scfe
```
+554
View File
@@ -0,0 +1,554 @@
/********************************************************************\
Name: epics_fe.c
Created by: Stefan Ritt
Contents: General EPICS frontend used for the MSetPoint system
\********************************************************************/
#include <cstring>
#include <epicsStdlib.h>
#include "cadef.h"
#include "dbDefs.h"
#include "epicsTime.h"
#include "midas.h"
#include "mfe.h"
#include "odbxx.h"
/*-- Globals -------------------------------------------------------*/
// timeout in seconds for caget operations
#define CAGET_TIMEOUT 30.0
// device types, must match msp_config.html:dt_string[]
#define DT_MAGNET 1
#define DT_BEAMBLOCKER 2
#define DT_PSA 3
#define DT_SEPARATOR 4
#define DT_SLIT 5
#define DT_VALUE 6
/* The frontend name (client name) as seen by other MIDAS clients */
const char *frontend_name = "Beamline_elements";
/* The frontend file name, don't change it */
const char *frontend_file_name = __FILE__;
/*-- Equipment list ------------------------------------------------*/
BOOL equipment_common_overwrite = TRUE;
int epics_read(char *pevent, int off);
int epics_loop(void);
int epics_init(void);
int epics_get_demand(int channel);
int epics_get_measured(int channel);
void demandCallback(midas::odb &o);
void commandCallback(midas::odb &o);
EQUIPMENT equipment[] = {
{"Beamline_elements", /* equipment name */
{21, 0, /* event ID, trigger mask */
"SYSTEM", /* event buffer */
EQ_PERIODIC, /* equipment type */
0, /* event source */
"MIDAS", /* format */
TRUE, /* enabled */
RO_ALWAYS,
60000, /* read event every 60 sec */
0, /* readout pause */
0, /* number of sub events */
10, /* log history every 10 seconds */
"", "", ""} ,
epics_read /* readout routine */
},
{""}
};
struct {
int length = 0;
midas::odb settings;
midas::odb variables;
chid *demand = nullptr;
chid *measured = nullptr;
chid *status = nullptr;
chid *command = nullptr;
float *demandCache = nullptr;
unsigned int updateInterval;
bool allowWriteAccess;
} beamline;
/*-- Error dispatcher causing communiction alarm -------------------*/
void epics_fe_error(const char *error)
{
cm_msg(MERROR, "epics_fe_error", "%s", error);
}
/*-- Readout routine -----------------------------------------------*/
int epics_read(char *pevent, int off)
{
float *pdata;
// init bank structure
bk_init(pevent);
// create a bank with demand values
bk_create(pevent, "DMND", TID_FLOAT, (void **)&pdata);
for (int i = 0; i < beamline.length; i++)
*pdata++ = beamline.variables["Demand"][i];
bk_close(pevent, pdata);
// create a bank with measured values
bk_create(pevent, "MSRD", TID_FLOAT, (void **)&pdata);
for (int i = 0; i < beamline.length; i++)
*pdata++ = beamline.variables["Measured"][i];
bk_close(pevent, pdata);
return bk_size(pevent);
}
/*-- Frontend Init -------------------------------------------------*/
int frontend_init()
{
/* set error dispatcher for alarm functionality */
mfe_set_error(epics_fe_error);
// check for Beamline_elements gateway in ODB
if (!midas::odb::exists("/Equipment/Beamline_elements/Settings/EPICS gateway")) {
std::cout << "Missing \"Equipment/Beamline_elements/Settings/EPICS gateway\" in ODB." << std::endl;
std::cout << "Please load beamline configuration file into ODB." << std::endl;
return FE_ERR_ODB;
}
// load EPICS settings from ODB
beamline.settings.connect("/Equipment/Beamline_elements/Settings");
beamline.length = beamline.settings["Names"].size();
beamline.variables.connect("/Equipment/Beamline_elements/Variables");
beamline.updateInterval = beamline.settings["Update interval"];
beamline.allowWriteAccess = beamline.settings["Allow write access"];
// setup environment
std::string gw = beamline.settings["EPICS Gateway"];
setenv("EPICS_CA_ADDR_LIST", gw.c_str(), 1);
setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1);
// create or correct "Variables" in ODB
if (!midas::odb::exists("/Equipment/Beamline_elements/Variables/Demand"))
beamline.variables["Demand"] = std::vector<float>(beamline.length);
else
beamline.variables["Demand"].resize(beamline.length);
if (!midas::odb::exists("/Equipment/Beamline_elements/Variables/Measured"))
beamline.variables["Measured"] = std::vector<float>(beamline.length);
else
beamline.variables["Measured"].resize(beamline.length);
std::string startCommand(__FILE__);
// set start command in ODB if not already set
if (!midas::odb::exists("/Programs/Beamline_elements/Start command")) {
midas::odb efe("/Programs/Beamline_elements");
efe["Start command"].set_string_size(startCommand, 256);
} else {
midas::odb efe("/Programs/Beamline_elements");
if (efe["Start command"] == std::string(""))
efe["Start command"].set_string_size(startCommand, 256);
}
install_frontend_loop(epics_loop);
return epics_init();
}
/*------------------------------------------------------------------*/
int epics_init(void)
{
int status = FE_SUCCESS;
/* initialize driver */
status = ca_task_initialize();
if (!(status & CA_M_SUCCESS)) {
cm_msg(MERROR, "epics_init", "Unable to initialize Beamline_elements EPICS driver");
return FE_ERR_HW;
}
beamline.demand = (chid *) calloc(beamline.length, sizeof(chid));
beamline.measured = (chid *) calloc(beamline.length, sizeof(chid));
beamline.status = (chid *) calloc(beamline.length, sizeof(chid));
beamline.command = (chid *) calloc(beamline.length, sizeof(chid));
beamline.demandCache = (float *) calloc(beamline.length, sizeof(float));
for (int i = 0; i < beamline.length; i++) {
printf("Channel %d\r", i);
fflush(stdout);
std::string demand = beamline.settings["CA Demand"][i];
if (demand != std::string("")) {
std::string name = beamline.settings["CA Name"][i];
name += demand;
status = ca_create_channel(name.c_str(), nullptr, nullptr, 0, &(beamline.demand[i]));
SEVCHK(status, "ca_create_channel");
if (ca_pend_io(5.0) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_init", "Cannot connect to EPICS channel %s (channel %d)", name.c_str(), i);
status = FE_ERR_HW;
break;
}
}
std::string measured = beamline.settings["CA Measured"][i];
if (measured != std::string("")) {
std::string name = beamline.settings["CA Name"][i];
name += measured;
status = ca_create_channel(name.c_str(), nullptr, nullptr, 0, &(beamline.measured[i]));
SEVCHK(status, "ca_create_channel");
if (ca_pend_io(5.0) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_init", "Cannot connect to EPICS channel %s (channel %d)", name.c_str(), i);
status = FE_ERR_HW;
break;
}
}
std::string stat = beamline.settings["CA Status"][i];
if (stat != std::string("")) {
std::string name = beamline.settings["CA Name"][i];
name += stat;
status = ca_create_channel(name.c_str(), nullptr, nullptr, 0, &(beamline.status[i]));
SEVCHK(status, "ca_create_channel");
if (ca_pend_io(5.0) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_init", "Cannot connect to EPICS channel %s (channel %d)", name.c_str(), i);
status = FE_ERR_HW;
break;
}
}
std::string command = beamline.settings["CA Command"][i];
if (command != std::string("")) {
std::string name = beamline.settings["CA Name"][i];
if ((int)beamline.settings["Device type"][i] == DT_SEPARATOR)
name = name.substr(0, name.size() - 1); // Strip 'N' from SEP41VHVN
name += command;
status = ca_create_channel(name.c_str(), nullptr, nullptr, 0, &(beamline.command[i]));
SEVCHK(status, "ca_create_channel");
if (ca_pend_io(5.0) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_init", "Cannot connect to EPICS channel %s (channel %d)", name.c_str(), i);
status = FE_ERR_HW;
break;
}
}
}
// read initial demand values
for (int i = 0; i < beamline.length; i++) {
beamline.demandCache[i] = (float) ss_nan();
epics_get_demand(i);
}
// install callback for demand changes
beamline.variables["Demand"].watch(demandCallback);
// install callback for direct commands
beamline.settings["Command"].watch(commandCallback);
printf("\n");
return status;
}
/*------------------------------------------------------------------*/
int epics_get_demand(int channel)
{
int status;
char str[80];
float d;
// Skip channels which do not support demand values
if (beamline.demand[channel] == nullptr)
return FE_SUCCESS;
std::string name = beamline.settings["Names"][channel];
if ((int)beamline.settings["Device type"][channel] == DT_BEAMBLOCKER) {
status = ca_get(DBR_STRING, beamline.demand[channel], str);
SEVCHK(status, "ca_get");
if (ca_pend_io(CAGET_TIMEOUT) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_get_demand", "Timeout on EPICS channel %s",
name.c_str());
return FE_ERR_HW;
}
d = (str[0] == 'O') ? 1 : 0;
} else {
status = ca_get(DBR_FLOAT, beamline.demand[channel], &d);
SEVCHK(status, "ca_get");
if (ca_pend_io(CAGET_TIMEOUT) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_get_demand", "Timeout on EPICS channel %s",
name.c_str());
return FE_ERR_HW;
}
}
// set demand in ODB only if it differs (avoid loops via hotlink)
if (d != beamline.demandCache[channel]) {
beamline.variables["Demand"][channel] = d;
beamline.demandCache[channel] = d;
}
return FE_SUCCESS;
}
/*------------------------------------------------------------------*/
int epics_get_measured(int channel)
{
int status;
char str[80];
// Skip write-only channels
if (beamline.measured[channel] == nullptr)
return FE_SUCCESS;
std::string name = beamline.settings["Names"][channel];
if ((int)beamline.settings["Device type"][channel] == DT_BEAMBLOCKER ||
(int)beamline.settings["Device type"][channel] == DT_PSA) {
int d;
float f;
status = ca_get(DBR_LONG, beamline.measured[channel], &d);
SEVCHK(status, "ca_get");
if (ca_pend_io(CAGET_TIMEOUT) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_get_measured", "Timeout on EPICS channel %s",
name.c_str());
return FE_ERR_HW;
}
if ((int)beamline.settings["Device type"][channel] == DT_BEAMBLOCKER) {
// bit0: open, bit1: closed, bit2: psa ok
if ((d & 3) == 0)
f = 0.5;
else if (d & 1)
f = 1;
else if (d & 2)
f = 0;
else
f = (float)ss_nan();
} else {
// bit2: PSA Ok
if (d & 4)
f = 1;
else
f = 0;
}
beamline.variables["Measured"][channel] = f;
} else {
float f;
status = ca_get(DBR_FLOAT, beamline.measured[channel], &f);
SEVCHK(status, "ca_get");
if (ca_pend_io(CAGET_TIMEOUT) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_get_measured", "Timeout on EPICS channel %s",
name.c_str());
return FE_ERR_HW;
}
beamline.variables["Measured"][channel] = f;
}
return FE_SUCCESS;
}
/*------------------------------------------------------------------*/
int epics_get_status(int channel)
{
int status;
char s[80];
// Skip channels which do not support status readback
if (beamline.status[channel] == nullptr)
return FE_SUCCESS;
status = ca_get(DBR_STRING, beamline.status[channel], s);
SEVCHK(status, "ca_get");
std::string name = beamline.settings["Names"][channel];
if (ca_pend_io(CAGET_TIMEOUT) == ECA_TIMEOUT) {
cm_msg(MERROR, "epics_get_status", "Timeout on EPICS channel %s",
name.c_str());
return FE_ERR_HW;
}
// translate to Englisch
std::string str(s);
if (str == "Eingeschaltet")
str = "On";
else if (str == "Ausgeschaltet")
str = "Off";
else if (str.compare(1, str.size()-1, "berlast") == 0)
str = "Overload";
beamline.settings["Status"][channel].set_string_size(str, 64);
return FE_SUCCESS;
}
/*------------------------------------------------------------------*/
void epics_set_demand(int channel, float value)
{
int status, channelType;
char s[80];
// skip readonly channels
if (beamline.demand[channel] == nullptr)
return;
channelType = beamline.settings["Device type"][channel];
std::string channelName = beamline.settings["Names"][channel];
if (channelType == DT_BEAMBLOCKER)
status = ca_put(DBR_STRING, beamline.demand[channel], value == 1 ? "OPEN" : "CLOSE");
else if (channelType == DT_SLIT) {
status = ca_put(DBR_FLOAT, beamline.demand[channel], &value);
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_set_demand", "Cannot write to EPICS channel %s", channelName.c_str());
if (beamline.command[channel] == nullptr) {
cm_msg(MERROR, "epics_set_damand", "No \"CA Command\" defined for EPICS slit channel %s", channelName.c_str());
return;
}
status = ca_put(DBR_STRING, beamline.command[channel], "MOVE_TO_SOL");
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_set_demand", "Cannot write to EPICS channel %s", channelName.c_str());
}
else if (channelType == DT_SEPARATOR) {
status = ca_put(DBR_FLOAT, beamline.demand[channel], &value);
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_set_demand", "Cannot write to EPICS channel %s", channelName.c_str());
if (beamline.command[channel] == nullptr) {
cm_msg(MERROR, "epics_set_damand", "No \"CA Command\" defined for EPICS separator channel %s", channelName.c_str());
return;
}
short int d = 3; // HVSET command
status = ca_put(DBR_SHORT, beamline.command[channel], &d);
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_set_demand", "Cannot write to EPICS channel %s", channelName.c_str());
} else
status = ca_put(DBR_FLOAT, beamline.demand[channel], &value);
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_set_demand", "Cannot write to EPICS channel \"%s\"", channelName.c_str());
}
/*------------------------------------------------------------------*/
void epics_send_command(int channel, std::string cmd)
{
int status, channelType;
/* skip readonly channels */
if (beamline.command[channel] == nullptr)
return;
channelType = beamline.settings["Device type"][channel];
std::string channelName = beamline.settings["Names"][channel];
status = ca_put(DBR_STRING, beamline.command[channel], cmd.c_str());
if (status != CA_M_SUCCESS)
cm_msg(MERROR, "epics_send_command", "Cannot send command \"%s\" to EPICS channel \"%s\"",
cmd.c_str(), channelName.c_str());
}
/*------------------------------------------------------------------*/
void demandCallback(midas::odb &o) {
std::vector<float> newDemand = o;
// check write flag
if (!beamline.allowWriteAccess)
return;
for (int i = 0; i < beamline.length; i++) {
if (newDemand[i] != beamline.demandCache[i]) {
epics_set_demand(i, newDemand[i]);
// printf("Set %d to %f\n", i, newDemand[i]);
beamline.demandCache[i] = newDemand[i];
}
}
}
/*------------------------------------------------------------------*/
void commandCallback(midas::odb &o) {
std::vector<std::string> cmd = o;
// check write flag
if (!beamline.allowWriteAccess)
return;
for (int i = 0; i < beamline.length; i++) {
if (!cmd[i].empty()) {
epics_send_command(i, cmd[i]);
// printf("Send command \"%s\" to \"%s\"\n", cmd[i].c_str(),
// beamline.settings["Names"][i].s().c_str());
o[i] = ""; // delete command
}
}
}
/*------------------------------------------------------------------*/
int epics_loop(void)
{
static DWORD last_time_measured = 0;
static DWORD last_time_status = 0;
static DWORD last_time_demand = 0;
// read values once per second
if (ss_millitime() - last_time_measured > beamline.updateInterval) {
for (int i = 0; i < beamline.length; i++)
epics_get_measured(i);
last_time_measured = ss_millitime();
}
// read status once every ten seconds
if (ss_time() >= last_time_status + 10) {
for (int i = 0; i < beamline.length; i++)
epics_get_status(i);
last_time_status = ss_time();
}
if (beamline.allowWriteAccess) {
// read demand values once every five minute (could have been changed by SetPoint)
if (ss_time() >= last_time_demand + 10 * 60 * 5) {
for (int i = 0; i < beamline.length; i++)
epics_get_demand(i);
last_time_demand = ss_time();
}
} else {
// read demand values once every ten seconds (could have been changed by other MSetPoint)
if (ss_time() >= last_time_demand + 10) {
for (int i = 0; i < beamline.length; i++)
epics_get_demand(i);
last_time_demand = ss_time();
}
}
ss_sleep(10); // don't eat all CPU
return CM_SUCCESS;
}