Initial commit of Beamline_elements frontend
This commit is contained in:
+246
@@ -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
|
||||
---
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
build
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
```
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user