commit 0dd0161ffb05202cfe925a65b48ebc070d0d24b8 Author: Anders Sandström Date: Mon Jan 29 10:37:22 2024 +0100 Start with motion plugin as base (just minor cleanup and renaming made) WIP diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02bf308 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*~ +*-dev +modules.order +Module.symvers +Mkfile.old +core.* +#* +.#* +\#* +*.local +\#* +.cvsignore +*_old/ +*PVs.list +*-loc/*.Makefile +ecmc_plugin_motion/*.Makefile +*__* +O* \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1e2a5ca --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "string_view": "cpp", + "stdexcept": "cpp", + "iosfwd": "cpp" + } +} \ No newline at end of file diff --git a/Db/.keep b/Db/.keep new file mode 100644 index 0000000..e69de29 diff --git a/Db/ecmcPluginMotion.template b/Db/ecmcPluginMotion.template new file mode 100644 index 0000000..700eec7 --- /dev/null +++ b/Db/ecmcPluginMotion.template @@ -0,0 +1,258 @@ +# Act pos +record(waveform,"$(P)Plg-Mtn${INDEX}-PosAct-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Pos Act") + field(PINI, "1") + field(DTYP, "asynFloat64ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.actpos_arr") + field(FTVL, "DOUBLE") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") + field(EGU, "${RAW_EGU= }") +} + +# Set pos +record(waveform,"$(P)Plg-Mtn${INDEX}-PosSet-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Pos Set") + field(PINI, "1") + field(DTYP, "asynFloat64ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.setpos_arr") + field(FTVL, "DOUBLE") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") + field(EGU, "${RAW_EGU= }") +} + +# Position Error +record(waveform,"$(P)Plg-Mtn${INDEX}-PosErr-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Pos Error (diff)") + field(PINI, "1") + field(DTYP, "asynFloat64ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.diffpos_arr") + field(FTVL, "DOUBLE") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") + field(EGU, "${RAW_EGU= }") +} + +# X axis time (reltive within buffer) +record(waveform,"$(P)Plg-Mtn${INDEX}-Time-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Rel. time within buffer") + field(PINI, "1") + field(DTYP, "asynFloat64ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.x_arr") + field(FTVL, "DOUBLE") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Enable +record(waveform,"$(P)Plg-Mtn${INDEX}-Ena-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Enable") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.enable_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Enabled +record(waveform,"$(P)Plg-Mtn${INDEX}-EnaAct-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Enabled") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.enabled_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Busy +record(waveform,"$(P)Plg-Mtn${INDEX}-Bsy-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Busy") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.busy_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Execute +record(waveform,"$(P)Plg-Mtn${INDEX}-Exe-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Execute") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.execute_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Traj source +record(waveform,"$(P)Plg-Mtn${INDEX}-TrjSrc-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Traj source") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.trajsrc_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Enc source +record(waveform,"$(P)Plg-Mtn${INDEX}-EncSrc-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Enc source") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.encsrc_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis At target +record(waveform,"$(P)Plg-Mtn${INDEX}-AtTrg-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "At target") + field(PINI, "1") + field(DTYP, "asynInt8ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.attarget_arr") + field(FTVL, "CHAR") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Error Id +record(waveform,"$(P)Plg-Mtn${INDEX}-ErrId-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Error Id") + field(PINI, "1") + field(DTYP, "asynInt32ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.error_arr") + field(FTVL, "LONG") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +# Axis Error Id +record(waveform,"$(P)Plg-Mtn${INDEX}-Stat-Arr"){ + info(asyn:FIFO, "1000") + field(DESC, "Status wd") + field(PINI, "1") + field(DTYP, "asynInt32ArrayIn") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.statuswd_arr") + field(FTVL, "LONG") + field(NELM, "$(NELM)") + field(SCAN, "I/O Intr") + field(TSE, "0") +} + +record(bo,"$(P)Plg-Mtn${INDEX}-EnaCmd-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Plg Enable") + field(DTYP,"asynInt32") + field(OUT, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.enable") + field(ZNAM,"FALSE") + field(ONAM,"TRUE") + field(DOL, "0") + field(VAL, "0") +} + +record(ao,"$(P)Plg-Mtn${INDEX}-AxCmd-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Axis") + field(DTYP,"asynInt32") + field(OUT, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.axis_id") + field(DOL, "0") + field(VAL, "1") +} + +record(ai,"$(P)Plg-Mtn${INDEX}-SmpHz-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Sample rate") + field(DTYP,"asynFloat64") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.samplerate") + field(VAL, "1") + field(EGU, "Hz") + field(SCAN, "I/O Intr") +} + +record(ai,"$(P)Plg-Mtn${INDEX}-BuffSze"){ + info(asyn:READBACK,"1") + field(DESC, "Buffer Size") + field(DTYP,"asynInt32") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.buff_size") + field(VAL, "1") +} + +record(ai,"$(P)Plg-Mtn${INDEX}-ElmCnt"){ + info(asyn:READBACK,"1") + field(DESC, "Elements in buffer count") + field(DTYP,"asynInt32") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.elem_count") + field(VAL, "1") +} + +record(bo,"$(P)Plg-Mtn${INDEX}-TrgCmd-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Trigg command") + field(DTYP,"asynInt32") + field(OUT, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.trigg") + field(ZNAM,"IDLE") + field(ONAM,"TRIGG") + field(VAL, "0") + field(HIGH, 0.001) +} + +record(longout,"$(P)Plg-Mtn${INDEX}-Mde-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Mode selection") + field(PINI, "1") + field(TSE, "0") + field(DTYP, "asynInt32") + field(OUT, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.mode") + field(SCAN, "Passive") +} + +record(longout,"$(P)Plg-Mtn${INDEX}-Cmd-RB"){ + info(asyn:READBACK,"1") + field(DESC, "Command") + field(PINI, "1") + field(TSE, "0") + field(DTYP, "asynInt32") + field(OUT, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.cmd") + field(SCAN, "Passive") +} + +record(longin,"$(P)Plg-Mtn${INDEX}-Stat"){ + info(asyn:READBACK,"1") + field(DESC, "Status") + field(PINI, "1") + field(TSE, "0") + field(DTYP, "asynInt32") + field(INP, "@asyn(PLUGIN.MOTION_${INDEX},$(ADDR=0),$(TIMEOUT=1000))plugin.motion_${INDEX}.status") + field(SCAN, "I/O Intr") +} diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..35114a9 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,46 @@ +include /ioc/tools/driver.makefile + +MODULE = ecmc_plugin_safety + +BUILDCLASSES = Linux +ARCH_FILTER = deb10% + +# Run 7.0.6 for now +EXCLUDE_VERSIONS+=3 7.0.5 7.0.6 + +IGNORE_MODULES += asynMotor +IGNORE_MODULES += motorBase + +USR_CXXFLAGS += -std=c++17 +OPT_CXXFLAGS_YES = -O3 + +# dependencies +# ECmasterECMC_VERSION = v1.1.0 +# motorECMC_VERSION = 7.0.7-ESS +ecmc_VERSION = v9.0.1_RC4 + +################################################################################ +# THIS RELATES TO THE EtherCAT MASTER LIBRARY +# IT IS OF PARAMOUNT IMPORTANCE TO LOAD THE PROPER KERNEL MODULE +# ################################################################################ +USR_LDFLAGS += -lethercat + +EC_MASTER_LIB = ${EPICS_MODULES}/ECmasterECMC/${ECmasterECMC_VERSION}/R${EPICSVERSION}/lib/${T_A} +USR_LDFLAGS += -Wl,-rpath=${EC_MASTER_LIB} +USR_LDFLAGS += -L ${EC_MASTER_LIB} + +BASE_DIR = . +SRC_DIR = $(BASE_DIR)/src +DB_DIR = $(BASE_DIR)/Db + +SOURCES += $(SRC_DIR)/ecmcPluginSafety.c +SOURCES += $(SRC_DIR)/ecmcSafetyPlgWrap.cpp +SOURCES += $(SRC_DIR)/ecmcSafetyPlg.cpp + +#SOURCES += $(foreach d,${SRC_DIR}, $(wildcard $d/*.c) $(wildcard $d/*.cpp)) +HEADERS += $(foreach d,${SRC_DIR}, $(wildcard $d/*.h)) +DBDS += $(foreach d,${SRC_DIR}, $(wildcard $d/*.dbd)) +SCRIPTS += $(BASE_DIR)/startup.cmd +SCRIPTS += $(BASE_DIR)/addMotionObj.cmd +TEMPLATES += $(wildcard $(DB_DIR)/*.template) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..68f08c0 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +ecmc_plugin_safety +====== +Plugin designed for interfacing safety plc:s + +IMPORTANT: this plugin does not offer any safety functionality. Its just a standard interface to an external safety systtem. + + + + + + diff --git a/cmds/.keep b/cmds/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configure/CONFIG b/configure/CONFIG new file mode 100644 index 0000000..2286cf9 --- /dev/null +++ b/configure/CONFIG @@ -0,0 +1,11 @@ +# +VARS_EXCLUDES := $(.VARIABLES) + +include $(TOP)/configure/RELEASE +include $(TOP)/configure/CONFIG_MODULE + +E3_REQUIRE_LOCATION := $(EPICS_BASE)/$(E3_REQUIRE_NAME)/$(E3_REQUIRE_VERSION) +REQUIRE_CONFIG := $(E3_REQUIRE_LOCATION)/configure + +include $(REQUIRE_CONFIG)/CONFIG + diff --git a/configure/CONFIG_MODULE b/configure/CONFIG_MODULE new file mode 100644 index 0000000..79e36b5 --- /dev/null +++ b/configure/CONFIG_MODULE @@ -0,0 +1,40 @@ +# +EPICS_MODULE_NAME:=ecmc_plugin_motion + +EPICS_MODULE_TAG:=master +# +E3_MODULE_VERSION:=master + +# DEPENDENT MODULE VERSION +# For Example, + +ECMC_DEP_VERSION:=v9.0.1_RC1 +ASYN_DEP_VERSION:=4.41.0 +EXPRTK_DEP_VERSION:=addCompFunc +MOTOR_DEP_VERSION:=7.0.6 +RUCKIG_DEP_VERSION:=1.0.0 + +#DEVLIB2_DEP_VERSION:=2.9.0 +#PCRE_DEP_VERSION:=8.41.0 +#ADCORE_DEP_VERSION:=3.7.0 +#ADSUPPORT_DEP_VERSION:=1.9.0 +#LOKI_DEP_VERSION=1.0.0 +#NDS_DEP_VERSION=2.3.3 +#SIS8300DRV_DEP_VERSION=4.3.1 +#SEQUENCER_DEP_VERSION=2.2.7 +# +# +#E3_KMOD_SRC_PATH:=$(E3_MODULE_SRC_PATH) +# +# In most case, we don't need to touch the following variables. +# + +E3_MODULE_NAME:=$(EPICS_MODULE_NAME) +E3_MODULE_SRC_PATH:=ecmc_plugin_motion +E3_MODULE_MAKEFILE:=$(EPICS_MODULE_NAME).Makefile + + +-include $(TOP)/configure/CONFIG_OPTIONS +# The definitions shown below can also be placed in an untracked CONFIG_MODULE.local +-include $(TOP)/configure/CONFIG_MODULE.local + diff --git a/configure/CONFIG_OPTIONS b/configure/CONFIG_OPTIONS new file mode 100644 index 0000000..2e022c1 --- /dev/null +++ b/configure/CONFIG_OPTIONS @@ -0,0 +1,6 @@ +# +# WITH_PVA:=NO +# +# The definitions shown below can also be placed in an untracked CONFIG_OPTIONS.local +-include $(TOP)/configure/CONFIG_OPTIONS.local + diff --git a/configure/RELEASE b/configure/RELEASE new file mode 100644 index 0000000..ddfdd1c --- /dev/null +++ b/configure/RELEASE @@ -0,0 +1,11 @@ +# +EPICS_BASE:=${HOME}/epics/base-7.0.5 + +E3_REQUIRE_NAME:=require +E3_REQUIRE_VERSION:=3.4.0 + +# The definitions shown below can also be placed in an untracked RELEASE.local +-include $(TOP)/../../RELEASE.local +-include $(TOP)/../RELEASE.local +-include $(TOP)/configure/RELEASE.local + diff --git a/configure/RULES b/configure/RULES new file mode 100644 index 0000000..56e9524 --- /dev/null +++ b/configure/RULES @@ -0,0 +1,12 @@ + +include $(REQUIRE_CONFIG)/RULES_E3 +include $(REQUIRE_CONFIG)/DEFINES_FT +include $(REQUIRE_CONFIG)/RULES_PATCH +include $(REQUIRE_CONFIG)/RULES_E3_SITELIBS + +include $(REQUIRE_CONFIG)/RULES_VLIBS +include $(REQUIRE_CONFIG)/RULES_VARS + +include $(TOP)/configure/module/RULES_MODULE +-include $(TOP)/configure/module/RULES_DKMS_L + diff --git a/configure/module/RULES_DKMS_L b/configure/module/RULES_DKMS_L new file mode 100644 index 0000000..c66bf32 --- /dev/null +++ b/configure/module/RULES_DKMS_L @@ -0,0 +1,38 @@ + +# KMOD_NAME := mrf + +# .PHONY: dkms_add + +# dkms_add: conf +# $(MSI) -M name="$(E3_MODULE_NAME)" -M version="$(E3_MODULE_VERSION)" -M kmod_name="$(KMOD_NAME)" $(TOP)/dkms/dkms_with_msi.conf.in > $(TOP)/dkms/dkms_with_msi.conf +# $(QUIET) cat $(TOP)/dkms/dkms_with_msi.conf $(TOP)/dkms/dkms_without_msi.conf > $(TOP)/dkms/dkms.conf +# $(QUIET) install -m 644 $(TOP)/dkms/dkms.conf $(E3_KMOD_SRC_PATH)/ +# $(SUDO) install -d /usr/src/$(E3_MODULE_NAME)-$(E3_MODULE_VERSION) +# $(SUDO) cp -r $(TOP)/$(E3_KMOD_SRC_PATH)/* /usr/src/$(E3_MODULE_NAME)-$(E3_MODULE_VERSION)/ +# $(SUDO) $(DKMS) add $(DKMS_ARGS) + + +# setup: +# $(QUIET) $(SUDO2) 'echo KERNEL==\"uio*\", ATTR{name}==\"mrf-pci\", MODE=\"0666\" | tee /etc/udev/rules.d/99-$(KMOD_NAME).rules' +# $(QUIET) $(SUDO) /bin/udevadm control --reload-rules +# $(QUIET) $(SUDO) /bin/udevadm trigger +# $(QUIET) $(SUDO2) 'echo $(KMOD_NAME) | tee /etc/modules-load.d/$(KMOD_NAME).conf' +# $(QUIET) $(SUDO) depmod --quick +# $(QUIET) $(SUDO) modprobe -rv $(KMOD_NAME) +# $(QUIET) $(SUDO) modprobe -v $(KMOD_NAME) +# $(QUIET) echo "" +# $(QUIET) echo "" +# $(QUIET) echo "It is OK to see \"E3/RULES_DKMS:37: recipe for target 'setup' failed\"" +# $(QUIET) echo "---------------------------------------------------------------------" +# $(QUIET) -ls -l /dev/uio* 2>/dev/null +# $(QUIET) echo "---------------------------------------------------------------------" + + +# setup_clean: +# $(QUIET) $(SUDO) modprobe -rv $(KMOD_NAME) +# $(SUDO) rm -f /etc/modules-load.d/$(KMOD_NAME).conf +# $(SUDO) rm -f /etc/udev/rules.d/99-$(KMOD_NAME).rules + + +# .PHONY: setup setup_clean + diff --git a/configure/module/RULES_MODULE b/configure/module/RULES_MODULE new file mode 100644 index 0000000..775070a --- /dev/null +++ b/configure/module/RULES_MODULE @@ -0,0 +1,20 @@ +# +.PHONY: db hdrs + +db: conf + $(QUIET) $(E3_MODULE_MAKE_CMDS) db + +hdrs: +# $(SUDO) install -m 755 -d $(E3_MODULES_INSTALL_LOCATION_INC)/pv +# cd $(E3_MODULES_INSTALL_LOCATION_INC) && $(SUDO) mv *.h pv/ + +#.PHONY: epics +#epics: +# $(QUIET)echo "EPICS_BASE:=$(EPICS_BASE)" > $(TOP)/$(E3_MODULE_SRC_PATH)/configure/RELEASE +# $(QUIET)echo "ASYN:=$(M_ASYN)" > $(TOP)/$(E3_MODULE_SRC_PATH)/configure/RELEASE +# $(QUIET)echo "SSCAN:=$(M_SSCAN)" >> $(TOP)/$(E3_MODULE_SRC_PATH)/configure/RELEASE +# $(QUIET)echo "SNCSEQ:=$(M_SNCSEQ)" >> $(TOP)/$(E3_MODULE_SRC_PATH)/configure/RELEASE +# $(QUIET)echo "CHECK_RELEASE:=YES" > $(TOP)/$(E3_MODULE_SRC_PATH)/configure/CONFIG_SITE +# $(QUIET)echo "INSTALL_LOCATION:=$(M_DEVLIB2)" >> $(TOP)/$(E3_MODULE_SRC_PATH)/configure/CONFIG_SITE +# $(SUDOBASH) "$(MAKE) -C $(E3_MODULE_SRC_PATH)" + diff --git a/docs/.MODULE_LOG b/docs/.MODULE_LOG new file mode 100644 index 0000000..b60b012 --- /dev/null +++ b/docs/.MODULE_LOG @@ -0,0 +1,28 @@ +>> +Script is used : e3TemplateGenerator.bash +Script Path : /home/anderssandstrom/plugin_ecmc/e3-tools/e3TemplateGenerator +Script Version : 1.0.8 +Script Run Time : 2020Mar22-1607-33CET +User : anderssandstrom +e3-tools Hash : bf03d40 +>> +>> git diff + +>> +>> git diff --cached + +>> +>> git diff HEAD + + +>> +>> Your sources are located in e3-ecmcPlugin_Simple. +>> +EPICS_MODULE_NAME : ecmcPlugin_Simple +E3_MODULE_SRC_PATH : ecmcPlugin_Simple +E3_TARGET_URL : https://github.com/anderssandstrom +>> +e3 module name : e3-ecmcPlugin_Simple +e3 target url full : https://github.com/anderssandstrom/e3-ecmcPlugin_Simple.git +>> +e3 module is located in siteMods diff --git a/docs/ecmcPLC5HzFFT.png b/docs/ecmcPLC5HzFFT.png new file mode 100644 index 0000000..70cdaa9 Binary files /dev/null and b/docs/ecmcPLC5HzFFT.png differ diff --git a/docs/ecmcPLC5HzRAW.png b/docs/ecmcPLC5HzRAW.png new file mode 100644 index 0000000..866cea7 Binary files /dev/null and b/docs/ecmcPLC5HzRAW.png differ diff --git a/docs/gui/ecmcArrayGui.png b/docs/gui/ecmcArrayGui.png new file mode 100644 index 0000000..feba6bf Binary files /dev/null and b/docs/gui/ecmcArrayGui.png differ diff --git a/docs/gui/ecmcFFTGui.png b/docs/gui/ecmcFFTGui.png new file mode 100644 index 0000000..f53c601 Binary files /dev/null and b/docs/gui/ecmcFFTGui.png differ diff --git a/docs/gui/ecmcFFTMainGui.png b/docs/gui/ecmcFFTMainGui.png new file mode 100644 index 0000000..fcbffe2 Binary files /dev/null and b/docs/gui/ecmcFFTMainGui.png differ diff --git a/iocsh/.keep b/iocsh/.keep new file mode 100644 index 0000000..e69de29 diff --git a/iocsh/addSafetyAxis.cmd b/iocsh/addSafetyAxis.cmd new file mode 100644 index 0000000..956d416 --- /dev/null +++ b/iocsh/addSafetyAxis.cmd @@ -0,0 +1,40 @@ + +#============================================================================== +# addMotionObj.cmd +#-------------- Information: +#- Description: ecmc_plugin_motion startup.cmd +#- +#- by Anders Sandström, Paul Scherrer Institute, 2023 +#- email: anders.sandstroem@psi.ch +#- +#-############################################################################### +#- +#- Arguments +#- [mandatory] +#- PLUGIN_ID = Plugin instansiation index, must be unique for each call +#- +#- [optional] +#- AX = Axis id, default 1 +#- BUFF_SIZE = Buffer size, default 1000 +#- DBG = Debug mode, default 1 +#- ENA = Enable operation, default 1 +#- REPORT = Printout plugin details, default 1 + +################################################################################# + +#- Load plugin: MOTION + +#- Note: ECMC_PLG_MOTION_OBJ_INDEX is the index of motion object in motion plugin and not PLUGIN_ID. +#- First loaded object will therefore have index +epicsEnvSet(ECMC_PLG_MOTION_OBJ_INDEX,${ECMC_PLG_MOTION_OBJ_INDEX=0}) + +# Might need differet paths for PSI and ESS.. must check +epicsEnvSet(ECMC_PLUGIN_FILNAME,"$(ecmc_plugin_motion_DIR)/lib/${EPICS_HOST_ARCH=linux-x86_64}/libecmc_plugin_motion.so") +epicsEnvSet(ECMC_PLUGIN_CONFIG,"AXIS=${AX};BUFFER_SIZE=${BUFF_SIZE};DBG_PRINT=${DBG=1};ENABLE=${ENA=1};") +${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=${PLUGIN_ID},FILE=${ECMC_PLUGIN_FILNAME},CONFIG='${ECMC_PLUGIN_CONFIG}', REPORT=${REPORT=1}" + +dbLoadRecords(${ecmc_plugin_motion_TEMPLATES}ecmcPluginMotion.template,"P=$(IOC):,INDEX=${ECMC_PLG_MOTION_OBJ_INDEX=0},NELM=${BUFF_SIZE=1000}") + +#- Increase of index (need to keep track in order to load correct db) +ecmcEpicsEnvSetCalc("ECMC_PLG_MOTION_OBJ_INDEX" ,${ECMC_PLG_MOTION_OBJ_INDEX=0}+1) + diff --git a/iocsh/cfg/el7031.ax b/iocsh/cfg/el7031.ax new file mode 100644 index 0000000..230ffd9 --- /dev/null +++ b/iocsh/cfg/el7031.ax @@ -0,0 +1,93 @@ +#General +epicsEnvSet("ECMC_MOTOR_NAME", "Axis1") +epicsEnvSet("ECMC_R", "Axis1-") +epicsEnvSet("ECMC_AXIS_NO", "1") +epicsEnvSet("ECMC_DESC", "Test EL7037") +epicsEnvSet("ECMC_EGU", "deg") # Motor Record Unit +epicsEnvSet("ECMC_PREC", "3") # Motor Record Precision +epicsEnvSet("ECMC_AXISCONFIG", "") # Extra parameters to driver +epicsEnvSet("ECMC_EC_AXIS_HEALTH", "") # Entry for axis health output (example: ec0.s1.binaryOutput01.0) +epicsEnvSet("ECMC_MOD_RANGE" , "0") # Modulo range (traj setpoints and encoder values will be in range 0..ECMC_MOD_RANGE) +epicsEnvSet("ECMC_MOD_TYPE", "0") # For positioning and MOD_RANGE is larger than 0: 0 = Normal, 1 = Always Fwd, 2 = Always Bwd, 3 = Closest Distance + +#Encoder +epicsEnvSet("ECMC_ENC_SCALE_NUM" "360") +epicsEnvSet("ECMC_ENC_SCALE_DENOM" "12800") +epicsEnvSet("ECMC_ENC_TYPE" "0") # Type: 0=Incremental, 1=Absolute +epicsEnvSet("ECMC_ENC_BITS" "16") # Total bit count of encoder raw data +epicsEnvSet("ECMC_ENC_ABS_BITS", "0") # Absolute bit count (for absolute encoders) always least significant part of ECMC_ENC_BITS +epicsEnvSet("ECMC_ENC_ABS_OFFSET" "0") # Encoder offset in eng units (for absolute encoders) +epicsEnvSet("ECMC_EC_ENC_ACTPOS", "ec0.s$(DRV_ID).positionActual01") # Ethercat entry for actual position input (encoder) +epicsEnvSet("ECMC_EC_ENC_RESET", "") # Reset (if no encoder reset bit then leave empty) +epicsEnvSet("ECMC_EC_ENC_ALARM_0", "") # Error 0 (if no encoder error bit then leave empty) +epicsEnvSet("ECMC_EC_ENC_ALARM_1", "") # Error 1 (if no encoder error bit then leave empty) +epicsEnvSet("ECMC_EC_ENC_ALARM_2", "") # Error 2 (if no encoder error bit then leave empty) +epicsEnvSet("ECMC_EC_ENC_WARNING", "") # Warning (if no encoder warning bit then leave empty) + +#Drive +epicsEnvSet("ECMC_DRV_TYPE" "0") # Stepper: 0. DS402: 1 (DS402 = servos and advanced stepper drives) +epicsEnvSet("ECMC_DRV_SCALE_NUM" "3600") # Fastest speed in engineering units +epicsEnvSet("ECMC_DRV_SCALE_DENOM" "32768") # I/O range for ECMC_EC_ALIAS_DRV_VELO_SET +epicsEnvSet("ECMC_EC_DRV_CONTROL", "ec0.s$(DRV_ID).driveControl01.0") # Ethercat entry for control word or bit output +epicsEnvSet("ECMC_EC_DRV_STATUS", "ec0.s$(DRV_ID).driveStatus01.1") # Ethercat entry for status word or bit input +epicsEnvSet("ECMC_EC_DRV_VELOCITY", "ec0.s$(DRV_ID).velocitySetpoint01") # Ethercat entry for velocity setpoint output +epicsEnvSet("ECMC_EC_DRV_REDUCE_TORQUE", "ec0.s$(DRV_ID).driveControl01.2") # Ethercat entry for reduce torque output +epicsEnvSet("ECMC_EC_DRV_BRAKE", "ec0.s$(DO_ID).binaryOutput01.0") # Ethercat entry for brake output +epicsEnvSet("ECMC_DRV_BRAKE_OPEN_DLY_TIME", "1000") # Brake timing parameter in cycles (default 1kHz) +epicsEnvSet("ECMC_DRV_BRAKE_CLOSE_AHEAD_TIME", "2000") # Brake timing parameter in cycles (default 1kHz) +epicsEnvSet("ECMC_EC_DRV_RESET", "ec0.s$(DRV_ID).driveControl01.1") # Reset +epicsEnvSet("ECMC_EC_DRV_ALARM_0", "ec0.s$(DRV_ID).driveStatus01.3") # Error +epicsEnvSet("ECMC_EC_DRV_ALARM_1", "ec0.s$(DRV_ID).driveStatus01.7") # Stall +epicsEnvSet("ECMC_EC_DRV_ALARM_2", "ec0.s$(DRV_ID).driveStatus01.14") # Sync error +epicsEnvSet("ECMC_EC_DRV_WARNING", "ec0.s$(DRV_ID).driveStatus01.2") # Warning + +#Trajectory +epicsEnvSet("ECMC_VELO", "360.0") +epicsEnvSet("ECMC_JOG_VEL", "360.0") +epicsEnvSet("ECMC_JAR", "0.0") # JAR defaults to VELO/ACCL +epicsEnvSet("ECMC_ACCS_EGU_PER_S2", "360") +epicsEnvSet("ECMC_EMERG_DECEL", "1000") # Emergency deceleration + +#Homing +epicsEnvSet("ECMC_HOME_PROC", "1") +epicsEnvSet("ECMC_HOME_POS", "0.0") +epicsEnvSet("ECMC_HOME_VEL_TO", "5") +epicsEnvSet("ECMC_HOME_VEL_FRM", "4") +epicsEnvSet("ECMC_HOME_ACC", "21") +epicsEnvSet("ECMC_HOME_DEC", "100") +epicsEnvSet("ECMC_HOME_POS_MOVE_ENA", "0") # Enable move to position after successfull homing +epicsEnvSet("ECMC_HOME_POS_MOVE_TARG_POS","0") # Target position to go to after successfull homing + +#Controller +epicsEnvSet("ECMC_CNTRL_KP", "5.0") +epicsEnvSet("ECMC_CNTRL_KI", "0.02") +epicsEnvSet("ECMC_CNTRL_KD", "0.0") +epicsEnvSet("ECMC_CNTRL_KFF", "1.0") + +#Monitoring +# Switches +epicsEnvSet("ECMC_EC_MON_LOWLIM", "ec0.s$(DRV_ID).ONE.0") # Ethercat entry for low limit switch input +epicsEnvSet("ECMC_EC_MON_HIGHLIM", "ec0.s$(DRV_ID).ONE.0") # Ethercat entry for high limit switch inpuit +epicsEnvSet("ECMC_EC_MON_HOME_SWITCH", "ec0.s$(DRV_ID).ONE.0") # Ethercat entry for home switch input +epicsEnvSet("ECMC_EC_MON_EXT_INTERLOCK", "ec0.s$(DRV_ID).ONE.0") # Ethercat entry for external interlock input + +# Softlimits (disable with 0,0,0) +epicsEnvSet("ECMC_SOFT_LOW_LIM", "$(SM_DLLM=0)") +epicsEnvSet("ECMC_SOFT_HIGH_LIM", "$(SM_DHLM=0)") +epicsEnvSet("ECMC_DXLM_ENABLE", "0") + +# Position lag +epicsEnvSet("ECMC_MON_LAG_MON_TOL", "5") +epicsEnvSet("ECMC_MON_LAG_MON_TIME", "100") +epicsEnvSet("ECMC_MON_LAG_MON_ENA", "0") + +# At target +epicsEnvSet("ECMC_MON_AT_TARGET_TOL", "0.1") +epicsEnvSet("ECMC_MON_AT_TARGET_TIME", "100") +epicsEnvSet("ECMC_MON_AT_TARGET_ENA", "1") + +# Velocity +epicsEnvSet("ECMC_MON_VELO_MAX", "21000.0") +epicsEnvSet("ECMC_MON_VELO_MAX_TRAJ_TIME","100") +epicsEnvSet("ECMC_MON_VELO_MAX_DRV_TIME", "200") +epicsEnvSet("ECMC_MON_VELO_MAX_ENA", "0") diff --git a/iocsh/plc/plc_no_ec_fft_sin.plc b/iocsh/plc/plc_no_ec_fft_sin.plc new file mode 100644 index 0000000..3491427 --- /dev/null +++ b/iocsh/plc/plc_no_ec_fft_sin.plc @@ -0,0 +1,9 @@ +############################################################################################### +# For help on syntax, variables and functions, please read the file: "plcSyntaxHelp.plc" +# +# PLC Functionality Demo: +# No hardware related variables +# + +static.time:=ec_get_time()/1E9; +static.sineval:=sin(2*pi*${FREQ=10}*static.time); diff --git a/iocsh/pvs.log b/iocsh/pvs.log new file mode 100644 index 0000000..eeab59d --- /dev/null +++ b/iocsh/pvs.log @@ -0,0 +1,345 @@ +IOC_TEST:m0s001-Drv01-Cmd-RB +IOC_TEST:m0s001-Drv01-Spd-RB +IOC_TEST:m0s001-Enc01-PosAct +IOC_TEST:m0s001-Enc01-LtchPosAct +IOC_TEST:m0s001-Enc01-Cmd-RB +IOC_TEST:m0s001-Enc01-PosCmd-RB +IOC_TEST:Axis1-Vel-RB +IOC_TEST:Axis1-Acc-RB +IOC_TEST:Axis1-EncAct +IOC_TEST:Axis1-CfgSREV-RB +IOC_TEST:Axis1-CfgUREV-RB +IOC_TEST:Axis1-CfgPMIN-RB +IOC_TEST:Axis1-CfgPMAX-RB +IOC_TEST:Axis1-CfgSPDB-RB +IOC_TEST:Axis1-CfgRDBD-RB +IOC_TEST:Axis1-CfgRDBD-Tim-RB +IOC_TEST:Axis1-CfgPOSLAG-RB +IOC_TEST:Axis1-CfgPOSLAG-Tim-RB +IOC_TEST:Axis1-CfgDHLM-RB +IOC_TEST:Axis1-CfgDLLM-RB +IOC_TEST:Axis1-CfgVELO-RB +IOC_TEST:Axis1-CfgVMAX-RB +IOC_TEST:Axis1-CfgJVEL-RB +IOC_TEST:Axis1-CfgACCS-RB +IOC_TEST:Axis1-HomPos-RB +IOC_TEST:Axis1-PosAct +IOC_TEST:Axis1-VelAct +IOC_TEST:Axis1-PosSet +IOC_TEST:Axis1-PosErr +IOC_TEST:Axis1-PLC-Err +IOC_TEST:Plg-Mtn0-SmpHz-RB +IOC_TEST:Plg-Mtn0-BuffSze +IOC_TEST:Plg-Mtn0-ElmCnt +IOC_TEST:MCU-AppMode +IOC_TEST:MCU-ErrId +IOC_TEST:MCU-ThdLatMin +IOC_TEST:MCU-ThdLatMax +IOC_TEST:MCU-ThdPrdMin +IOC_TEST:MCU-ThdPrdMax +IOC_TEST:MCU-ThdExeMin +IOC_TEST:MCU-ThdExeMax +IOC_TEST:MCU-ThdSndMin +IOC_TEST:MCU-ThdSndMax +IOC_TEST:m0s001-Drv01-WrnAlrm +IOC_TEST:m0s001-Drv01-ErrAlrm +IOC_TEST:m0s001-Drv01-StlAlrm +IOC_TEST:m0s001-Drv01-SyncErrAlrm +IOC_TEST:m0s001-Enc01-ExtLtchOK +IOC_TEST:m0s001-Enc01-OpnCrctAlrm +IOC_TEST:m0s001-Enc01-WrnAlrm +IOC_TEST:m0s001-Enc01-SyncErrAlrm +IOC_TEST:m0s001-Online +IOC_TEST:m0s001-Operational +IOC_TEST:m0s001-Alstate-Init +IOC_TEST:m0s001-Alstate-Preop +IOC_TEST:m0s001-Alstate-Safeop +IOC_TEST:m0s001-Alstate-Op +IOC_TEST:m0s002-BO01-RB +IOC_TEST:m0s002-BO02-RB +IOC_TEST:m0s002-BO03-RB +IOC_TEST:m0s002-BO04-RB +IOC_TEST:m0s002-BO05-RB +IOC_TEST:m0s002-BO06-RB +IOC_TEST:m0s002-BO07-RB +IOC_TEST:m0s002-BO08-RB +IOC_TEST:m0s002-BO09-RB +IOC_TEST:m0s002-BO10-RB +IOC_TEST:m0s002-BO11-RB +IOC_TEST:m0s002-BO12-RB +IOC_TEST:m0s002-BO13-RB +IOC_TEST:m0s002-BO14-RB +IOC_TEST:m0s002-BO15-RB +IOC_TEST:m0s002-BO16-RB +IOC_TEST:m0s002-BO01-OvrTmpAlrm +IOC_TEST:m0s002-BO01-OpnLdAlrm +IOC_TEST:m0s002-BO01-OvrCurrAlrm +IOC_TEST:m0s002-BO01-ShrtCircAlrm +IOC_TEST:m0s002-BO02-OvrTmpAlrm +IOC_TEST:m0s002-BO02-OpnLdAlrm +IOC_TEST:m0s002-BO02-OvrCurrAlrm +IOC_TEST:m0s002-BO02-ShrtCircAlrm +IOC_TEST:m0s002-BO03-OvrTmpAlrm +IOC_TEST:m0s002-BO03-OpnLdAlrm +IOC_TEST:m0s002-BO03-OvrCurrAlrm +IOC_TEST:m0s002-BO03-ShrtCircAlrm +IOC_TEST:m0s002-BO04-OvrTmpAlrm +IOC_TEST:m0s002-BO04-OpnLdAlrm +IOC_TEST:m0s002-BO04-OvrCurrAlrm +IOC_TEST:m0s002-BO04-ShrtCircAlrm +IOC_TEST:m0s002-BO05-OvrTmpAlrm +IOC_TEST:m0s002-BO05-OpnLdAlrm +IOC_TEST:m0s002-BO05-OvrCurrAlrm +IOC_TEST:m0s002-BO05-ShrtCircAlrm +IOC_TEST:m0s002-BO06-OvrTmpAlrm +IOC_TEST:m0s002-BO06-OpnLdAlrm +IOC_TEST:m0s002-BO06-OvrCurrAlrm +IOC_TEST:m0s002-BO06-ShrtCircAlrm +IOC_TEST:m0s002-BO07-OvrTmpAlrm +IOC_TEST:m0s002-BO07-OpnLdAlrm +IOC_TEST:m0s002-BO07-OvrCurrAlrm +IOC_TEST:m0s002-BO07-ShrtCircAlrm +IOC_TEST:m0s002-BO08-OvrTmpAlrm +IOC_TEST:m0s002-BO08-OpnLdAlrm +IOC_TEST:m0s002-BO08-OvrCurrAlrm +IOC_TEST:m0s002-BO08-ShrtCircAlrm +IOC_TEST:m0s002-BO09-OvrTmpAlrm +IOC_TEST:m0s002-BO09-OpnLdAlrm +IOC_TEST:m0s002-BO09-OvrCurrAlrm +IOC_TEST:m0s002-BO09-ShrtCircAlrm +IOC_TEST:m0s002-BO10-OvrTmpAlrm +IOC_TEST:m0s002-BO10-OpnLdAlrm +IOC_TEST:m0s002-BO10-OvrCurrAlrm +IOC_TEST:m0s002-BO10-ShrtCircAlrm +IOC_TEST:m0s002-BO11-OvrTmpAlrm +IOC_TEST:m0s002-BO11-OpnLdAlrm +IOC_TEST:m0s002-BO11-OvrCurrAlrm +IOC_TEST:m0s002-BO11-ShrtCircAlrm +IOC_TEST:m0s002-BO12-OvrTmpAlrm +IOC_TEST:m0s002-BO12-OpnLdAlrm +IOC_TEST:m0s002-BO12-OvrCurrAlrm +IOC_TEST:m0s002-BO12-ShrtCircAlrm +IOC_TEST:m0s002-BO13-OvrTmpAlrm +IOC_TEST:m0s002-BO13-OpnLdAlrm +IOC_TEST:m0s002-BO13-OvrCurrAlrm +IOC_TEST:m0s002-BO13-ShrtCircAlrm +IOC_TEST:m0s002-BO14-OvrTmpAlrm +IOC_TEST:m0s002-BO14-OpnLdAlrm +IOC_TEST:m0s002-BO14-OvrCurrAlrm +IOC_TEST:m0s002-BO14-ShrtCircAlrm +IOC_TEST:m0s002-BO15-OvrTmpAlrm +IOC_TEST:m0s002-BO15-OpnLdAlrm +IOC_TEST:m0s002-BO15-OvrCurrAlrm +IOC_TEST:m0s002-BO15-ShrtCircAlrm +IOC_TEST:m0s002-BO16-OvrTmpAlrm +IOC_TEST:m0s002-BO16-OpnLdAlrm +IOC_TEST:m0s002-BO16-OvrCurrAlrm +IOC_TEST:m0s002-BO16-ShrtCircAlrm +IOC_TEST:m0s002-Online +IOC_TEST:m0s002-Operational +IOC_TEST:m0s002-Alstate-Init +IOC_TEST:m0s002-Alstate-Preop +IOC_TEST:m0s002-Alstate-Safeop +IOC_TEST:m0s002-Alstate-Op +IOC_TEST:Axis1-EnaCmd-RB +IOC_TEST:Axis1-EnaAct +IOC_TEST:Axis1-ExeCmd-RB +IOC_TEST:Axis1-Busy +IOC_TEST:Axis1-AtTarget +IOC_TEST:Axis1-Moving +IOC_TEST:Axis1-LimFwd +IOC_TEST:Axis1-LimBwd +IOC_TEST:Axis1-HomeSwitch +IOC_TEST:Axis1-Homed +IOC_TEST:Axis1-InRT +IOC_TEST:Axis1-TrjSrcTyp-RB +IOC_TEST:Axis1-EncSrcTyp-RB +IOC_TEST:Axis1-CmdFrmPLCCmd-RB +IOC_TEST:Axis1-SftLimFwdEna-RB +IOC_TEST:Axis1-SftLimBwdEna-RB +IOC_TEST:Axis1-PLC-EnaCmd-RB +IOC_TEST:Axis1-PLC-FirstScan +IOC_TEST:Axis1-Err +IOC_TEST:Axis1-Wrn +IOC_TEST:MCU-ThdRTPrioOK +IOC_TEST:MCU-ThdMemLocked +IOC_TEST:m0-LinkUp +IOC_TEST:m0-AlStates-Init +IOC_TEST:m0-AlStates-Preop +IOC_TEST:m0-AlStates-Safeop +IOC_TEST:m0-AlStates-Op +IOC_TEST:m0-Dom-RedunActive +IOC_TEST:m0-Dom-WC-Zero +IOC_TEST:m0-Dom-WC-Incomplete +IOC_TEST:m0-Dom-WC-Complete +IOC_TEST:m0-Stat-OK +REQMOD:raspberrypi-10406:exit +REQMOD:raspberrypi-10406:MODULES +REQMOD:raspberrypi-10406:VERSIONS +REQMOD:raspberrypi-10406:MOD_VER +IOC_TEST:Axis1-Arr-Stat +IOC_TEST:Axis1-PLC-Expr-RB +IOC_TEST:Plg-Mtn0-PosAct-Arr +IOC_TEST:Plg-Mtn0-PosSet-Arr +IOC_TEST:Plg-Mtn0-PosErr-Arr +IOC_TEST:Plg-Mtn0-Time-Arr +IOC_TEST:Plg-Mtn0-Ena-Arr +IOC_TEST:Plg-Mtn0-EnaAct-Arr +IOC_TEST:Plg-Mtn0-Bsy-Arr +IOC_TEST:Plg-Mtn0-Exe-Arr +IOC_TEST:Plg-Mtn0-TrjSrc-Arr +IOC_TEST:Plg-Mtn0-EncSrc-Arr +IOC_TEST:Plg-Mtn0-AtTrg-Arr +IOC_TEST:Plg-Mtn0-ErrId-Arr +IOC_TEST:MCU-ErrMsg +IOC_TEST:MCU-Updated +IOC_TEST:m0s001-Enc01-LtchCmd +IOC_TEST:Axis1-MtnCmd +IOC_TEST:Axis1-movVelCmd +IOC_TEST:Axis1-movRelCmd +IOC_TEST:Axis1-movAbsCmd +IOC_TEST:Axis1-movHomCmd +IOC_TEST:Axis1-HomProc-RB +IOC_TEST:Axis1-Type +IOC_TEST:Axis1-DrvType +IOC_TEST:Axis1-TrajType +IOC_TEST:m0s001-One +IOC_TEST:m0s001-Zero +IOC_TEST:m0s002-One +IOC_TEST:m0s002-Zero +IOC_TEST:Axis1-DIR_ +IOC_TEST:Axis1-ErrRst +IOC_TEST:Axis1-HomProc +IOC_TEST:Axis1-MtnCmdData +IOC_TEST:Plg-Mtn0-Mde-RB +IOC_TEST:Plg-Mtn0-Cmd-RB +IOC_TEST:m0s001-Stat +IOC_TEST:m0s002-Stat +IOC_TEST:Axis1-MR-ErrId +IOC_TEST:Axis1-CfgRDBD-En-RB +IOC_TEST:Axis1-CfgPOSLAG-En-RB +IOC_TEST:Axis1-CfgDHLM-En-RB +IOC_TEST:Axis1-CfgDLLM-En-RB +IOC_TEST:Axis1-Stat +IOC_TEST:Axis1-ErrId +IOC_TEST:Axis1-WrnId +IOC_TEST:Plg-Mtn0-Stat +IOC_TEST:m0-Stat +IOC_TEST:m0-SlvCntr +IOC_TEST:m0-MemmapCntr +IOC_TEST:m0-DomFailCntrTot +IOC_TEST:m0-EntryCntr +IOC_TEST:m0-Dom-Stat +IOC_TEST:MCU-Cfg-Info +IOC_TEST:MCU-Cfg-Naming +IOC_TEST:MCU-Cfg-Mode +IOC_TEST:MCU-Cfg-PVA +IOC_TEST:Axis1-DbgStrToLOG +IOC_TEST:MCU-Cfg-AX1-Pfx +IOC_TEST:MCU-Cfg-AX1-Nam +IOC_TEST:MCU-Cfg-AX1-PfxNam +IOC_TEST:m0s001-EntryCntr +IOC_TEST:m0s002-EntryCntr +IOC_TEST:Axis1-SeqState +IOC_TEST:Axis1-LastIlock +IOC_TEST:m0-SlvRsp +IOC_TEST:m0-Dom-WC +IOC_TEST:m0s001-Enc01-LtchRst +IOC_TEST:Axis1-Cmd_ +REQMOD:raspberrypi-10406:BaseVersion +REQMOD:raspberrypi-10406:require_VER +REQMOD:raspberrypi-10406:ecmccfg_VER +REQMOD:raspberrypi-10406:asyn_VER +REQMOD:raspberrypi-10406:exprtk_VER +REQMOD:raspberrypi-10406:motor_VER +REQMOD:raspberrypi-10406:ruckig_VER +REQMOD:raspberrypi-10406:ecmc_VER +IOC_TEST:m0s001-HWType +IOC_TEST:m0s002-HWType +IOC_TEST:Axis1-MsgTxt +REQMOD:raspberrypi-10406:ecmc_plugin_motion_VER +IOC_TEST:m0s001-Drv01-Stat +IOC_TEST:m0s001-Enc01-Stat +IOC_TEST:m0s001-Stat_ +IOC_TEST:m0s002-BO01-Stat +IOC_TEST:m0s002-BO02-Stat +IOC_TEST:m0s002-BO03-Stat +IOC_TEST:m0s002-BO04-Stat +IOC_TEST:m0s002-BO05-Stat +IOC_TEST:m0s002-BO06-Stat +IOC_TEST:m0s002-BO07-Stat +IOC_TEST:m0s002-BO08-Stat +IOC_TEST:m0s002-BO09-Stat +IOC_TEST:m0s002-BO10-Stat +IOC_TEST:m0s002-BO11-Stat +IOC_TEST:m0s002-BO12-Stat +IOC_TEST:m0s002-BO13-Stat +IOC_TEST:m0s002-BO14-Stat +IOC_TEST:m0s002-BO15-Stat +IOC_TEST:m0s002-BO16-Stat +IOC_TEST:m0s002-Stat_ +IOC_TEST:Axis1-Stat_ +IOC_TEST:ThdRTStat_ +IOC_TEST:m0-Stat_ +IOC_TEST:m0-Dom-Stat_ +IOC_TEST:m0s001-Enc01-LtchAutRst +IOC_TEST:Axis1-MtnCmd_ +IOC_TEST:MCU-Cfg-EC-Mst +IOC_TEST:MCU-Cfg-Rate +IOC_TEST:MCU-Cfg-Time +IOC_TEST:MCU-Cfg-PV-Time +IOC_TEST:m0s001-Drv01-Cmd +IOC_TEST:m0s001-Drv01-Spd +IOC_TEST:m0s001-Enc01-Cmd +IOC_TEST:m0s001-Enc01-PosCmd +IOC_TEST:m0s001-NxtObjId +IOC_TEST:MCU-Cfg-EC-FrstObjId +IOC_TEST:m0s002-NxtObjId +IOC_TEST:Axis1-OFF_ +IOC_TEST:Axis1-MRES_ +IOC_TEST:Axis1-HomPos +IOC_TEST:Axis1-VelToHom +IOC_TEST:Axis1-VelFrmHom +IOC_TEST:Axis1-AccHom +IOC_TEST:Axis1-TgtPosCmd +IOC_TEST:Axis1-TgtVelCmd +IOC_TEST:Axis1-Id +IOC_TEST:MCU-Cfg-AX1-NxtObjId +IOC_TEST:MCU-Cfg-AX-FrstObjId +IOC_TEST:MCU-Cfg-PLG{Index}-NxtObjId +IOC_TEST:MCU-Cfg-PLG-FrstObjId +IOC_TEST:Plg-Mtn0-AxCmd-RB +IOC_TEST:MCU-Cfg-Eng-Mode +IOC_TEST:m0s001-Enc01-LchAutRstSp +IOC_TEST:m0s002-BO01 +IOC_TEST:m0s002-BO02 +IOC_TEST:m0s002-BO03 +IOC_TEST:m0s002-BO04 +IOC_TEST:m0s002-BO05 +IOC_TEST:m0s002-BO06 +IOC_TEST:m0s002-BO07 +IOC_TEST:m0s002-BO08 +IOC_TEST:m0s002-BO09 +IOC_TEST:m0s002-BO10 +IOC_TEST:m0s002-BO11 +IOC_TEST:m0s002-BO12 +IOC_TEST:m0s002-BO13 +IOC_TEST:m0s002-BO14 +IOC_TEST:m0s002-BO15 +IOC_TEST:m0s002-BO16 +IOC_TEST:Axis1-EnaCmd +IOC_TEST:Axis1-ExeCmd +IOC_TEST:Axis1-StpCmd +IOC_TEST:Axis1-RstCmd +IOC_TEST:Axis1-EncSrcTyp-Cmd +IOC_TEST:Axis1-TrjSrcTyp-Cmd +IOC_TEST:Axis1-PLC-EnaCmd +IOC_TEST:Axis1-CmdFrmPLCCmd +IOC_TEST:Axis1-SftLimBwdEna +IOC_TEST:Axis1-SftLimFwdEna +IOC_TEST:Plg-Mtn0-EnaCmd-RB +IOC_TEST:Plg-Mtn0-TrgCmd-RB +IOC_TEST:MCU-ErrRst +IOC_TEST:Axis1-MCU1-asyn +IOC_TEST:MCU-Cmd +IOC_TEST:Axis1 diff --git a/iocsh/test_plugin_motion.script b/iocsh/test_plugin_motion.script new file mode 100644 index 0000000..bc22a5b --- /dev/null +++ b/iocsh/test_plugin_motion.script @@ -0,0 +1,62 @@ +############################################################################## +## Example: Configuraftion for running ecmc motion plugin +############################################################################## + +## Initiation: +epicsEnvSet("IOC" ,"$(IOC="IOC_TEST")") +epicsEnvSet("SCRIPTEXEC" ,"$(SCRIPTEXEC="iocshLoad")") + +require ecmccfg "9.0.1_RC1" + +# run module startup.cmd (only needed at ESS PSI auto call at require) +$(ECMCCFG_INIT="")$(SCRIPTEXEC) ${ecmccfg_DIR}startup.cmd, "IOC=$(IOC),ECMC_VER=v9.0.1_RC1, EC_RATE=500" + + +############################################################################## +## Configure hardware + +epicsEnvSet("ECMC_EC_SLAVE_NUM", "1") +${SCRIPTEXEC} ${ecmccfg_DIR}configureSlave.cmd, "SLAVE_ID=$(ECMC_EC_SLAVE_NUM), HW_DESC=EL7031, CONFIG=-Motor-Trinamic-QMot-QSH4218-41-10-035" +epicsEnvSet("DRV_ID", "${ECMC_EC_SLAVE_NUM}") +${SCRIPTEXEC} ${ecmccfg_DIR}addSlave.cmd, "HW_DESC=EL2819" +epicsEnvSet("DO_ID", "${ECMC_EC_SLAVE_NUM}") + +# Configure drv input 1 as drv enable +ecmcConfigOrDie "Cfg.EcAddSdo(${DRV_ID},0x8012,0x32,1,1)" +OK +# Control external drv enable +ecmcConfigOrDie "Cfg.WriteEcEntryIDString(${DO_ID},binaryOutput02,1)" + +#Apply hardware configuration +ecmcConfigOrDie "Cfg.EcApplyConfig(1)" + +############################################################################## +## AXIS 1 +# +epicsEnvSet("DEV", "$(IOC)") +$(SCRIPTEXEC) ($(ecmccfg_DIR)configureAxis.cmd, CONFIG=./cfg/el7031.ax) + +############################################################################## +## Load plugin: MOTION +# +epicsEnvSet(ECMC_PLUGIN_CONFIG,"PLUGIN_ID=1,AX=1,BUFF_SIZE=200,DBG=0,ENA=1") +require ecmc_plugin_motion master ${ECMC_PLUGIN_CONFIG} +# below needed at ESS but not PSI: +${SCRIPTEXEC} ${ecmc_plugin_motion_DIR}startup.cmd "${ECMC_PLUGIN_CONFIG}" + +############################################################################## +############# Configure diagnostics: + +ecmcConfigOrDie "Cfg.EcSetDiagnostics(1)" +ecmcConfigOrDie "Cfg.EcEnablePrintouts(0)" +ecmcConfigOrDie "Cfg.EcSetDomainFailedCyclesLimit(100)" +ecmcConfigOrDie "Cfg.SetDiagAxisIndex(1)" +ecmcConfigOrDie "Cfg.SetDiagAxisFreq(2)" +ecmcConfigOrDie "Cfg.SetDiagAxisEnable(0)" + +############################################################################## +############# go active: +$(SCRIPTEXEC) ($(ecmccfg_DIR)setAppMode.cmd) + +iocInit +dbl > pvs.log diff --git a/iocsh/test_plugin_motion_psi.script b/iocsh/test_plugin_motion_psi.script new file mode 100644 index 0000000..38a13b3 --- /dev/null +++ b/iocsh/test_plugin_motion_psi.script @@ -0,0 +1,54 @@ +############################################################################## +## Example: Configuraftion for running ecmc motion plugin +############################################################################## + +## Initiation: +epicsEnvSet("IOC" ,"$(IOC="IOC_TEST")") +epicsEnvSet("ECMCCFG_INIT" ,"") #Only run startup once (auto at PSI, need call at ESS), variable set to "#" in startup.cmd +epicsEnvSet("SCRIPTEXEC" ,"$(SCRIPTEXEC="iocshLoad")") + +require ecmccfg "sandst_a_v9.0.1_RC1" "ECMC_VER=v9.0.1_RC1" + +############################################################################## +## Configure hardware + +epicsEnvSet("ECMC_EC_SLAVE_NUM", "4") +${SCRIPTEXEC} ${ecmccfg_DIR}configureSlave.cmd, "SLAVE_ID=$(ECMC_EC_SLAVE_NUM), HW_DESC=EL7031, CONFIG=-Motor-Trinamic-QMot-QSH4218-41-10-035" +epicsEnvSet("DRV_ID", "${ECMC_EC_SLAVE_NUM}") + +${SCRIPTEXEC} ${ecmccfg_DIR}addSlave.cmd, "SLAVE_ID=6,HW_DESC=EL2008" +epicsEnvSet(DO_ID,${ECMC_EC_SLAVE_NUM}) + +#Apply hardware configuration +ecmcConfigOrDie "Cfg.EcApplyConfig(1)" + +############################################################################## +## AXIS 1 +# +epicsEnvSet("DEV", "$(IOC)") +$(SCRIPTEXEC) ($(ecmccfg_DIR)configureAxis.cmd, CONFIG=./cfg/el7031.ax) + +############################################################################## +## Load plugin: MOTION +# +epicsEnvSet(ECMC_PLUGIN_CONFIG,"PLUGIN_ID=1,AX=1,BUFF_SIZE=200,DBG=0,ENA=1") +require ecmc_plugin_motion sandst_a "${ECMC_PLUGIN_CONFIG}" +# below needed at ESS but not PSI: +#${SCRIPTEXEC} ${ecmc_plugin_motion_DIR}startup.cmd "${ECMC_PLUGIN_CONFIG}" + +############################################################################## +############# Configure diagnostics: + +ecmcConfigOrDie "Cfg.EcSetDiagnostics(1)" +ecmcConfigOrDie "Cfg.EcEnablePrintouts(0)" +ecmcConfigOrDie "Cfg.EcSetDomainFailedCyclesLimit(100)" +ecmcConfigOrDie "Cfg.SetDiagAxisIndex(1)" +ecmcConfigOrDie "Cfg.SetDiagAxisFreq(2)" +ecmcConfigOrDie "Cfg.SetDiagAxisEnable(0)" + +############################################################################## +############# go active: +$(SCRIPTEXEC) ($(ecmccfg_DIR)setAppMode.cmd) + +iocInit +dbl > pvs.log diff --git a/qt/.keep b/qt/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/.keep b/src/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/ecmcPluginSafety.c b/src/ecmcPluginSafety.c new file mode 100644 index 0000000..e240feb --- /dev/null +++ b/src/ecmcPluginSafety.c @@ -0,0 +1,298 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcPluginExample.cpp +* +* Created on: july 10, 2023 +* Author: anderssandstrom +* +\*************************************************************************/ + +// Needed to get headers in ecmc right... +#define ECMC_IS_PLUGIN +#define ECMC_EXAMPLE_PLUGIN_VERSION 2 + +#ifdef __cplusplus +extern "C" { +#endif // ifdef __cplusplus + +#include +#include +#include + +#include "ecmcPluginDefs.h" +#include "ecmcMotionPlgDefs.h" +#include "ecmcMotionPlgWrap.h" + +static int lastEcmcError = 0; +static char* lastConfStr = NULL; + +/** Optional. + * Will be called once after successfull load into ecmc. + * Return value other than 0 will be considered error. + * configStr can be used for configuration parameters. + **/ +int motionConstruct(char *configStr) +{ + //This module is allowed to load several times so no need to check if loaded + + // create FFT object and register data callback + lastConfStr = strdup(configStr); + return createMotionObj(configStr); +} + +/** Optional function. + * Will be called once at unload. + **/ +void motionDestruct(void) +{ + deleteAllMotionObjs(); + if(lastConfStr){ + free(lastConfStr); + } +} + +/** Optional function. + * Will be called each realtime cycle if definded + * ecmcError: Error code of ecmc. Makes it posible for + * this plugin to react on ecmc errors + * Return value other than 0 will be considered to be an error code in ecmc. + **/ +int motionRealtime(int ecmcError) +{ + executeMotionObjs(); + lastEcmcError = ecmcError; + return 0; +} + +/** Link to data source here since all sources should be availabe at this stage + * (for example ecmc PLC variables are defined only at enter of realtime) + **/ +int motionEnterRT(){ + return linkDataTomotionObjs(); +} + +/** Optional function. + * Will be called once just before leaving realtime mode + * Return value other than 0 will be considered error. + **/ +int motionExitRT(void){ + return 0; +} + +//// Plc function for clear of buffers +//double fft_clear(double index) { +// return (double)clearFFT((int)index); +//} +// +//// Plc function for enable +//double fft_enable(double index, double enable) { +// return (double)enableFFT((int)index, (int)enable); +//} +// +//// Plc function for trigg new measurement (will clear buffers) +//double fft_trigg(double index) { +// return (double)triggFFT((int)index); +//} +// +//// Plc function for enable +//double fft_mode(double index, double mode) { +// return (double)modeFFT((int)index, (FFT_MODE)((int)mode)); +//} +// +//// Plc function for enable +//double fft_stat(double index) { +// return (double)statFFT((int)index); +//} + +// Register data for plugin so ecmc know what to use +struct ecmcPluginData pluginDataDef = { + // Allways use ECMC_PLUG_VERSION_MAGIC + .ifVersion = ECMC_PLUG_VERSION_MAGIC, + // Name + .name = "ecmcPlugin_Motion", + // Description + .desc = "Motion plugin for commissioning of ecmc motion axes.", + // Option description + .optionDesc = "\n "ECMC_PLUGIN_DBG_PRINT_OPTION_CMD"<1/0> : Enables/disables printouts from plugin, default = disabled.\n" + " "ECMC_PLUGIN_AXIS_OPTION_CMD" : Sets default source axis id.\n" + " "ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD" : Data points to collect, default = 4096.\n" + " "ECMC_PLUGIN_RATE_OPTION_CMD" : Sampling rate in Hz" + " "ECMC_PLUGIN_MODE_OPTION_CMD" : Sampling rate in Hz" + , + // Plugin version + .version = ECMC_EXAMPLE_PLUGIN_VERSION, + // Optional construct func, called once at load. NULL if not definded. + .constructFnc = motionConstruct, + // Optional destruct func, called once at unload. NULL if not definded. + .destructFnc = motionDestruct, + // Optional func that will be called each rt cycle. NULL if not definded. + .realtimeFnc = motionRealtime, + // Optional func that will be called once just before enter realtime mode + .realtimeEnterFnc = motionEnterRT, + // Optional func that will be called once just before exit realtime mode + .realtimeExitFnc = motionExitRT, + // PLC funcs +// .funcs[0] = +// { /*----fft_clear----*/ +// // Function name (this is the name you use in ecmc plc-code) +// .funcName = "fft_clear", +// // Function description +// .funcDesc = "double fft_clear(index) : Clear/reset fft[index].", +// /** +// * 7 different prototypes allowed (only doubles since reg in plc). +// * Only funcArg${argCount} func shall be assigned the rest set to NULL. +// **/ +// .funcArg0 = NULL, +// .funcArg1 = fft_clear, +// .funcArg2 = NULL, +// .funcArg3 = NULL, +// .funcArg4 = NULL, +// .funcArg5 = NULL, +// .funcArg6 = NULL, +// .funcArg7 = NULL, +// .funcArg8 = NULL, +// .funcArg9 = NULL, +// .funcArg10 = NULL, +// .funcGenericObj = NULL, +// }, +// .funcs[1] = +// { /*----fft_enable----*/ +// // Function name (this is the name you use in ecmc plc-code) +// .funcName = "fft_enable", +// // Function description +// .funcDesc = "double fft_enable(index, enable) : Set enable for fft[index].", +// /** +// * 7 different prototypes allowed (only doubles since reg in plc). +// * Only funcArg${argCount} func shall be assigned the rest set to NULL. +// **/ +// .funcArg0 = NULL, +// .funcArg1 = NULL, +// .funcArg2 = fft_enable, +// .funcArg3 = NULL, +// .funcArg4 = NULL, +// .funcArg5 = NULL, +// .funcArg6 = NULL, +// .funcArg7 = NULL, +// .funcArg8 = NULL, +// .funcArg9 = NULL, +// .funcArg10 = NULL, +// .funcGenericObj = NULL, +// }, +// .funcs[2] = +// { /*----fft_trigg----*/ +// // Function name (this is the name you use in ecmc plc-code) +// .funcName = "fft_trigg", +// // Function description +// .funcDesc = "double fft_trigg(index) : Trigg new measurement for fft[index]. Will clear buffers.", +// /** +// * 7 different prototypes allowed (only doubles since reg in plc). +// * Only funcArg${argCount} func shall be assigned the rest set to NULL. +// **/ +// .funcArg0 = NULL, +// .funcArg1 = fft_trigg, +// .funcArg2 = NULL, +// .funcArg3 = NULL, +// .funcArg4 = NULL, +// .funcArg5 = NULL, +// .funcArg6 = NULL, +// .funcArg7 = NULL, +// .funcArg8 = NULL, +// .funcArg9 = NULL, +// .funcArg10 = NULL, +// .funcGenericObj = NULL, +// }, +// .funcs[3] = +// { /*----fft_mode----*/ +// // Function name (this is the name you use in ecmc plc-code) +// .funcName = "fft_mode", +// // Function description +// .funcDesc = "double fft_mode(index, mode) : Set mode Cont(1)/Trigg(2) for fft[index].", +// /** +// * 7 different prototypes allowed (only doubles since reg in plc). +// * Only funcArg${argCount} func shall be assigned the rest set to NULL. +// **/ +// .funcArg0 = NULL, +// .funcArg1 = NULL, +// .funcArg2 = fft_mode, +// .funcArg3 = NULL, +// .funcArg4 = NULL, +// .funcArg5 = NULL, +// .funcArg6 = NULL, +// .funcArg7 = NULL, +// .funcArg8 = NULL, +// .funcArg9 = NULL, +// .funcArg10 = NULL, +// .funcGenericObj = NULL, +// }, +// .funcs[4] = +// { /*----fft_stat----*/ +// // Function name (this is the name you use in ecmc plc-code) +// .funcName = "fft_stat", +// // Function description +// .funcDesc = "double fft_stat(index) : Get status of fft (NO_STAT, IDLE, ACQ, CALC) for fft[index].", +// /** +// * 7 different prototypes allowed (only doubles since reg in plc). +// * Only funcArg${argCount} func shall be assigned the rest set to NULL. +// **/ +// .funcArg0 = NULL, +// .funcArg1 = fft_stat, +// .funcArg2 = NULL, +// .funcArg3 = NULL, +// .funcArg4 = NULL, +// .funcArg5 = NULL, +// .funcArg6 = NULL, +// .funcArg7 = NULL, +// .funcArg8 = NULL, +// .funcArg9 = NULL, +// .funcArg10 = NULL, +// .funcGenericObj = NULL, +// }, + .funcs[0] = {0}, // last element set all to zero.. + // PLC consts + /* CONTINIOUS MODE = 1 */ +// .consts[0] = { +// .constName = "fft_CONT", +// .constDesc = "FFT Mode: Continious", +// .constValue = CONT +// }, +// /* TRIGGERED MODE = 2 */ +// .consts[1] = { +// .constName = "fft_TRIGG", +// .constDesc = "FFT Mode :Triggered", +// .constValue = TRIGG +// }, +// /* TRIGGERED MODE = 2 */ +// .consts[2] = { +// .constName = "fft_NO_STAT", +// .constDesc = "FFT Status: Invalid state", +// .constValue = NO_STAT, +// }, +// /* TRIGGERED MODE = 2 */ +// .consts[3] = { +// .constName = "fft_IDLE", +// .constDesc = "FFT Status: Idle state (waiting for trigger)", +// .constValue = IDLE +// }, +// /* TRIGGERED MODE = 2 */ +// .consts[4] = { +// .constName = "fft_ACQ", +// .constDesc = "FFT Status: Acquiring data", +// .constValue = ACQ +// }, +// /* TRIGGERED MODE = 2 */ +// .consts[5] = { +// .constName = "fft_CALC", +// .constDesc = "FFT Status: Calculating result", +// .constValue = CALC +// }, + .consts[0] = {0}, // last element set all to zero.. +}; + +ecmc_plugin_register(pluginDataDef); + +# ifdef __cplusplus +} +# endif // ifdef __cplusplus diff --git a/src/ecmcSafetyPlg.cpp b/src/ecmcSafetyPlg.cpp new file mode 100644 index 0000000..5cf7fd7 --- /dev/null +++ b/src/ecmcSafetyPlg.cpp @@ -0,0 +1,1064 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcMotionPlg.cpp +* +* Created on: July 10, 2023 +* Author: anderssandstrom +* Credits to https://github.com/sgreg/dynamic-loading +* +\*************************************************************************/ + +// Needed to get headers in ecmc right... +#define ECMC_IS_PLUGIN + +#define ECMC_PLUGIN_ASYN_ENABLE "enable" +#define ECMC_PLUGIN_ASYN_MOTION_COMMAND "cmd" +#define ECMC_PLUGIN_ASYN_MOTION_STAT "status" +#define ECMC_PLUGIN_ASYN_MOTION_TRIGG "trigg" +#define ECMC_PLUGIN_ASYN_MOTION_MODE "mode" +#define ECMC_PLUGIN_ASYN_AXIS_ID "axis_id" +#define ECMC_PLUGIN_ASYN_RATE "samplerate" +#define ECMC_PLUGIN_ASYN_X_DATA "x_arr" +#define ECMC_PLUGIN_ASYN_Y_ACTPOS_DATA "actpos_arr" +#define ECMC_PLUGIN_ASYN_Y_SETPOS_DATA "setpos_arr" +#define ECMC_PLUGIN_ASYN_Y_DIFFPOS_DATA "diffpos_arr" +#define ECMC_PLUGIN_ASYN_ENABLE_DATA "enable_arr" +#define ECMC_PLUGIN_ASYN_ENABLED_DATA "enabled_arr" +#define ECMC_PLUGIN_ASYN_BUSY_DATA "busy_arr" +#define ECMC_PLUGIN_ASYN_EXECUTE_DATA "execute_arr" +#define ECMC_PLUGIN_ASYN_TRAJSOURCE_DATA "trajsrc_arr" +#define ECMC_PLUGIN_ASYN_ENCSOURCE_DATA "encsrc_arr" +#define ECMC_PLUGIN_ASYN_ATTARGET_DATA "attarget_arr" +#define ECMC_PLUGIN_ASYN_ERROR_DATA "error_arr" +#define ECMC_PLUGIN_ASYN_BUFF_SIZE "buff_size" +#define ECMC_PLUGIN_ASYN_ELEMENTS_IN_BUFF "elem_count" +#define ECMC_PLUGIN_ASYN_STATUSWD_DATA "statuswd_arr" + +#include + +#include "ecmcMotionPlg.h" +#include "ecmcPluginClient.h" +#include "ecmcAsynPortDriver.h" +#include "ecmcAsynPortDriverUtils.h" +#include "epicsThread.h" +#include "ecmcMotion.h" + +// Breaktable +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbAccess.h" +#include "epicsVersion.h" +#include "cvtTable.h" +#ifdef BASE_VERSION +#define EPICS_3_13 +extern DBBASE *pdbbase; +#endif + +// Start worker for asyn write +void f_worker_write(void *obj) { + if(!obj) { + printf("%s/%s:%d: Error: Worker write thread ecmcMotionPlg object NULL..\n", + __FILE__, __FUNCTION__, __LINE__); + return; + } + ecmcMotionPlg *mtnobj = (ecmcMotionPlg*)obj; + mtnobj->doWriteWorker(); +} + +/** ecmc Motion Commissioning class + * This object can throw: + * - bad_alloc + * - invalid_argument + * - runtime_error +*/ +ecmcMotionPlg::ecmcMotionPlg(int objIndex, // index of this object (if several is created) + char* configStr, + char* portName) + : asynPortDriver(portName, + 1, /* maxAddr */ + asynInt32Mask | asynFloat64Mask | asynFloat32ArrayMask | + asynFloat64ArrayMask | asynEnumMask | asynDrvUserMask | + asynOctetMask | asynInt8ArrayMask | asynInt16ArrayMask | + asynInt32ArrayMask | asynUInt32DigitalMask, /* Interface mask */ + asynInt32Mask | asynFloat64Mask | asynFloat32ArrayMask | + asynFloat64ArrayMask | asynEnumMask | asynDrvUserMask | + asynOctetMask | asynInt8ArrayMask | asynInt16ArrayMask | + asynInt32ArrayMask | asynUInt32DigitalMask, /* Interrupt mask */ + ASYN_CANBLOCK , /*NOT ASYN_MULTI_DEVICE*/ + 1, /* Autoconnect */ + 0, /* Default priority */ + 0) /* Default stack size */ + { + + axis_ = NULL; + + command_ = 0; + status_ = 0; + + destructs_ = 0; + callbackHandle_ = -1; + objectId_ = objIndex; + triggOnce_ = 0; + cycleCounter_ = 0; + ignoreCycles_ = 0; + dataSourceLinked_ = 0; + + bTriggInProgress_ = false; + + asynEnableId_ = -1; // Enable/disable acq./calcs + asynYActPosArrayId_ = -1; + asynYSetPosArrayId_ = -1; // FFT amplitude array (double) + asynYDiffPosArrayId_ = -1; // FFT mode (cont/trigg) + asynTriggId_ = -1; // Trigg new measurement + asynXAxisArrayId_ = -1; // FFT X-axis frequencies + asynSRateId_ = -1; // Sample rate Hz + asynElementsInBufferId_ = -1; + asynCommandId_ = -1; + asynStatusId_ = -1; + asynBufferSizeId_ = -1; + asynModeId_ = -1; + + writeBusy_ = 0; + writePauseTime_.tv_sec = 0; + writePauseTime_.tv_nsec = 0.5e6; // 0.5ms + + axisMutex_ = epicsMutexCreate(); + + ecmcSampleRateHz_ = getEcmcSampleRate(); + cfgSampleRateHz_ = ecmcSampleRateHz_; + cfgDataSampleRateHz_ = ecmcSampleRateHz_; + cfgAxisIndex_ = 1; + + // Config defaults + cfgDbgMode_ = 0; + cfgArraySize_ = ECMC_PLUGIN_DEFAULT_ARRAY_SIZE; // samples in fft (must be n^2) + cfgEnable_ = 0; // start disabled (enable over asyn) + + parseConfigStr(configStr); // Assigns all configs + // Check valid nfft + if(cfgArraySize_ <= 0) { + throw std::out_of_range("Array size must be > 0 (default 4096)."); + } + + // Check valid sample rate + if(cfgSampleRateHz_ <= 0) { + throw std::out_of_range("Invalid sample rate"); + } + if(cfgSampleRateHz_ > ecmcSampleRateHz_) { + printf("Warning selected sample rate faster than ecmc rate, rate will be set to ecmc rate.\n"); + cfgSampleRateHz_ = ecmcSampleRateHz_; + } + + // Se if any data update cycles should be ignored + // example ecmc 1000Hz, fft 100Hz then ignore 9 cycles (could be strange if not multiples) + ignoreCycles_ = ecmcSampleRateHz_ / cfgSampleRateHz_ -1; + + // Allocate buffers + actPosBuffer_ = new ecmcDataBuffer(objectId_,0, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_Y_ACTPOS_DATA, + this); + setPosBuffer_ = new ecmcDataBuffer(objectId_,1, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_Y_SETPOS_DATA, + this); + diffPosBuffer_ = new ecmcDataBuffer(objectId_,2, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_Y_DIFFPOS_DATA, + this); + xPosBuffer_ = new ecmcDataBuffer(objectId_,3, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_X_DATA, + this); + enableBuffer_ = new ecmcDataBuffer(objectId_,4, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_ENABLE_DATA, + this); + enabledBuffer_ = new ecmcDataBuffer(objectId_,5, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_ENABLED_DATA, + this); + busyBuffer_ = new ecmcDataBuffer(objectId_,6, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_BUSY_DATA, + this); + executeBuffer_ = new ecmcDataBuffer(objectId_,7, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_EXECUTE_DATA, + this); + trajSourceBuffer_ = new ecmcDataBuffer(objectId_,8, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_TRAJSOURCE_DATA, + this); + encSourceBuffer_ = new ecmcDataBuffer(objectId_,9, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_ENCSOURCE_DATA, + this); + atTargetBuffer_ = new ecmcDataBuffer(objectId_,10, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_ATTARGET_DATA, + this); + errorIdBuffer_ = new ecmcDataBuffer(objectId_,11, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_ERROR_DATA, + this); + statusWdBuffer_ = new ecmcDataBuffer(objectId_,12, + cfgArraySize_, + cfgDbgMode_, + ECMC_PLUGIN_ASYN_STATUSWD_DATA, + this); + + axis_ = (ecmcAxisBase*) getAxisPointer(cfgAxisIndex_); + clearBuffers(); + + // init x axis (after clear) + xdt_ = 1 / cfgSampleRateHz_; + xTime_ = 0; + for(size_t i=0;iaddData(xTime_); + xTime_+=xdt_; + } + + xTime_ = 0; + initAsyn(); + + // Create worker thread for writing socket + std::string threadname = "ecmc." ECMC_PLUGIN_ASYN_PREFIX ".writer"; + if(epicsThreadCreate(threadname.c_str(), 0, 32768, f_worker_write, this) == NULL) { + throw std::runtime_error("Error: Failed create worker thread for ecmc." ECMC_PLUGIN_ASYN_PREFIX ".writer"); + } + doWriteEvent_.signal(); +} + +ecmcMotionPlg::~ecmcMotionPlg() { + // kill worker + destructs_ = 1; // maybe need todo in other way.. + delete actPosBuffer_; + + // De register callback when unload + //if(callbackHandle_ >= 0) { + // dataItem_->deregDataUpdatedCallback(callbackHandle_); + //} +} + +void ecmcMotionPlg::parseConfigStr(char *configStr) { + + // check config parameters + if (configStr && configStr[0]) { + char *pOptions = strdup(configStr); + char *pThisOption = pOptions; + char *pNextOption = pOptions; + + while (pNextOption && pNextOption[0]) { + pNextOption = strchr(pNextOption, ';'); + if (pNextOption) { + *pNextOption = '\0'; /* Terminate */ + pNextOption++; /* Jump to (possible) next */ + } + + // ECMC_PLUGIN_DBG_PRINT_OPTION_CMD (1/0) + if (!strncmp(pThisOption, ECMC_PLUGIN_DBG_PRINT_OPTION_CMD, strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_DBG_PRINT_OPTION_CMD); + cfgDbgMode_ = atoi(pThisOption); + } + + // ECMC_PLUGIN_AXIS_OPTION_CMD (number) + else if (!strncmp(pThisOption, ECMC_PLUGIN_AXIS_OPTION_CMD, strlen(ECMC_PLUGIN_AXIS_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_AXIS_OPTION_CMD); + cfgAxisIndex_ = atoi(pThisOption); + } + + // ECMC_PLUGIN_ENABLE_OPTION_CMD (1/0) + else if (!strncmp(pThisOption, ECMC_PLUGIN_ENABLE_OPTION_CMD, strlen(ECMC_PLUGIN_ENABLE_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_ENABLE_OPTION_CMD); + cfgEnable_ = atoi(pThisOption); + } + + // ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD (number) + else if (!strncmp(pThisOption, ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD, strlen(ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD); + cfgArraySize_ = atoi(pThisOption); + } + + // ECMC_PLUGIN_RATE_OPTION_CMD (number) + else if (!strncmp(pThisOption, ECMC_PLUGIN_RATE_OPTION_CMD, strlen(ECMC_PLUGIN_RATE_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_RATE_OPTION_CMD); + cfgSampleRateHz_ = atof(pThisOption); + } + + // ECMC_PLUGIN_MODE_OPTION_CMD CONT/TRIGG + else if (!strncmp(pThisOption, ECMC_PLUGIN_MODE_OPTION_CMD, strlen(ECMC_PLUGIN_MODE_OPTION_CMD))) { + pThisOption += strlen(ECMC_PLUGIN_MODE_OPTION_CMD); + if(!strncmp(pThisOption, ECMC_PLUGIN_MODE_CONT_OPTION,strlen(ECMC_PLUGIN_MODE_CONT_OPTION))){ + cfgMode_ = CONT; + } + if(!strncmp(pThisOption, ECMC_PLUGIN_MODE_TRIGG_OPTION,strlen(ECMC_PLUGIN_MODE_TRIGG_OPTION))){ + cfgMode_ = TRIGG; + } + } + + pThisOption = pNextOption; + } + free(pOptions); + } +} + +void ecmcMotionPlg::connectToDataSource() { + ///* Check if already linked (one call to enterRT per loaded FFT lib (FFT object)) + // But link should only happen once!!*/ + //if( dataSourceLinked_ ) { + // return; + //} + // + //// Get dataItem + //dataItem_ = (ecmcDataItem*) getEcmcDataItem(cfgDataSourceStr_); + //if(!dataItem_) { + // throw std::runtime_error( "Data item NULL." ); + //} + // + //dataItemInfo_ = dataItem_->getDataItemInfo(); +// + //// Register data callback + //callbackHandle_ = dataItem_->regDataUpdatedCallback(f_dataUpdatedCallback, this); + //if (callbackHandle_ < 0) { + // throw std::runtime_error( "Failed to register data source callback."); + //} +// + //// Check data source + //if( !dataTypeSupported(dataItem_->getEcmcDataType()) ) { + // throw std::invalid_argument( "Data type not supported." ); + //} +// + //// Add oversampling + //cfgDataSampleRateHz_ = cfgSampleRateHz_ * dataItem_->getEcmcDataSize()/dataItem_->getEcmcDataElementSize(); + //setDoubleParam(asynSRateId_, cfgDataSampleRateHz_); + //callParamCallbacks(); +// + // dataSourceLinked_ = 1; + // updateStatus(IDLE); +} + +void ecmcMotionPlg::dataUpdatedCallback(uint8_t* data, + size_t size, + ecmcEcDataType dt) { + + //if(fftWaitingForCalc_) { + // return; + //} + //// No buffer or full or not enabled + //if(!rawDataBuffer_ || !cfgEnable_) { + // return; + //} +// + //// See if data should be ignored + //if(cycleCounter_ < ignoreCycles_) { + // cycleCounter_++; + // return; // ignore this callback + //} +// + //cycleCounter_ = 0; +// + //if (cfgMode_ == TRIGG && !triggOnce_ ) { + // updateStatus(IDLE); + // return; // Wait for trigger from plc or asyn + //} +// + //if(cfgDbgMode_) { + // printEcDataArray(data, size, dt, objectId_); +// + // if(elementsInBuffer_ == cfgArraySize_) { + // printf("Buffer full (%zu elements appended).\n",elementsInBuffer_); + // } + //} + // + //if(elementsInBuffer_ >= cfgArraySize_) { + // //Buffer full + // if(!fftWaitingForCalc_){ + // // Perform calcs + // updateStatus(CALC); + // fftWaitingForCalc_ = 1; + // doCalcEvent_.signal(); // let worker start + // } + // return; + //} +// + //updateStatus(ACQ); +// + //size_t dataElementSize = getEcDataTypeByteSize(dt); +// + //uint8_t *pData = data; + //for(unsigned int i = 0; i < size / dataElementSize; ++i) { + // //printf("dataElementSize=%d, size=%d\n",dataElementSize,size); + // switch(dt) { + // case ECMC_EC_U8: + // addDataToBuffer((double)getUint8(pData)); + // break; + // case ECMC_EC_S8: + // addDataToBuffer((double)getInt8(pData)); + // break; + // case ECMC_EC_U16: + // addDataToBuffer((double)getUint16(pData)); + // break; + // case ECMC_EC_S16: + // addDataToBuffer((double)getInt16(pData)); + // break; + // case ECMC_EC_U32: + // addDataToBuffer((double)getUint32(pData)); + // break; + // case ECMC_EC_S32: + // addDataToBuffer((double)getInt32(pData)); + // break; + // case ECMC_EC_U64: + // addDataToBuffer((double)getUint64(pData)); + // break; + // case ECMC_EC_S64: + // addDataToBuffer((double)getInt64(pData)); + // break; + // case ECMC_EC_F32: + // addDataToBuffer((double)getFloat32(pData)); + // break; + // case ECMC_EC_F64: + // addDataToBuffer((double)getFloat64(pData)); + // break; + // default: + // break; + // } + // + // pData += dataElementSize; + //} +}// + +//void ecmcMotionPlg::addDataToBuffer(double data) { +// +// if(rawDataBuffer_ && (elementsInBuffer_ < cfgArraySize_) ) { +// +// if( cfgBreakTableStr_ && interruptAccept ) { +// double breakData = data; +// // Supply a breaktable (init=0, LINR must be > 1 but only used if init > 0) +// if (cvtRawToEngBpt(&breakData, 2, 0, &breakTable_, &lastBreakPoint_)!=0) { +// //TODO: What does status here mean.. +// //throw std::runtime_error("Breaktable conversion failed.\n"); +// } +// //printf("Index %d: Before %lf, after %lf\n",objectId_,data,breakData); +// data = breakData * cfgScale_; +// } else { +// data = data * cfgScale_; +// } +// +// rawDataBuffer_[elementsInBuffer_] = data; +// prepProcDataBuffer_[elementsInBuffer_] = data; +// } +// elementsInBuffer_ ++; +//} + +void ecmcMotionPlg::clearBuffers() { + actPosBuffer_->clear(); + setPosBuffer_->clear(); + diffPosBuffer_->clear(); + enableBuffer_->clear(); + enabledBuffer_->clear(); + busyBuffer_->clear(); + executeBuffer_->clear(); + trajSourceBuffer_->clear(); + encSourceBuffer_->clear(); + atTargetBuffer_->clear(); + errorIdBuffer_->clear(); + xPosBuffer_->clear(); + statusWdBuffer_->clear(); +} + +void ecmcMotionPlg::printEcDataArray(uint8_t* data, + size_t size, + ecmcEcDataType dt, + int objId) { + printf("motion obj id: %d, data: ",objId); + + size_t dataElementSize = getEcDataTypeByteSize(dt); + + uint8_t *pData = data; + for(unsigned int i = 0; i < size / dataElementSize; ++i) { + switch(dt) { + case ECMC_EC_U8: + printf("%hhu\n",getUint8(pData)); + break; + case ECMC_EC_S8: + printf("%hhd\n",getInt8(pData)); + break; + case ECMC_EC_U16: + printf("%hu\n",getUint16(pData)); + break; + case ECMC_EC_S16: + printf("%hd\n",getInt16(pData)); + break; + case ECMC_EC_U32: + printf("%u\n",getUint32(pData)); + break; + case ECMC_EC_S32: + printf("%d\n",getInt32(pData)); + break; + case ECMC_EC_U64: + printf("%" PRIu64 "\n",getInt64(pData)); + break; + case ECMC_EC_S64: + printf("%" PRId64 "\n",getInt64(pData)); + break; + case ECMC_EC_F32: + printf("%f\n",getFloat32(pData)); + break; + case ECMC_EC_F64: + printf("%lf\n",getFloat64(pData)); + break; + default: + break; + } + + pData += dataElementSize; + } +} + +int ecmcMotionPlg::dataTypeSupported(ecmcEcDataType dt) { + + switch(dt) { + case ECMC_EC_NONE: + return 0; + break; + case ECMC_EC_B1: + return 0; + break; + case ECMC_EC_B2: + return 0; + break; + case ECMC_EC_B3: + return 0; + break; + case ECMC_EC_B4: + return 0; + break; + default: + return 1; + break; + } + return 1; +} + +uint8_t ecmcMotionPlg::getUint8(uint8_t* data) { + return *data; +} + +int8_t ecmcMotionPlg::getInt8(uint8_t* data) { + int8_t* p=(int8_t*)data; + return *p; +} + +uint16_t ecmcMotionPlg::getUint16(uint8_t* data) { + uint16_t* p=(uint16_t*)data; + return *p; +} + +int16_t ecmcMotionPlg::getInt16(uint8_t* data) { + int16_t* p=(int16_t*)data; + return *p; +} + +uint32_t ecmcMotionPlg::getUint32(uint8_t* data) { + uint32_t* p=(uint32_t*)data; + return *p; +} + +int32_t ecmcMotionPlg::getInt32(uint8_t* data) { + int32_t* p=(int32_t*)data; + return *p; +} + +uint64_t ecmcMotionPlg::getUint64(uint8_t* data) { + uint64_t* p=(uint64_t*)data; + return *p; +} + +int64_t ecmcMotionPlg::getInt64(uint8_t* data) { + int64_t* p=(int64_t*)data; + return *p; +} + +float ecmcMotionPlg::getFloat32(uint8_t* data) { + float* p=(float*)data; + return *p; +} + +double ecmcMotionPlg::getFloat64(uint8_t* data) { + double* p=(double*)data; + return *p; +} + +size_t ecmcMotionPlg::getEcDataTypeByteSize(ecmcEcDataType dt){ + switch(dt) { + case ECMC_EC_NONE: + return 0; + break; + + case ECMC_EC_B1: + return 1; + break; + + case ECMC_EC_B2: + return 1; + break; + + case ECMC_EC_B3: + return 1; + break; + + case ECMC_EC_B4: + return 1; + break; + + case ECMC_EC_U8: + return 1; + break; + + case ECMC_EC_S8: + return 1; + break; + + case ECMC_EC_U16: + return 2; + break; + + case ECMC_EC_S16: + return 2; + break; + + case ECMC_EC_U32: + return 4; + break; + + case ECMC_EC_S32: + return 4; + break; + + case ECMC_EC_U64: + return 8; + break; + + case ECMC_EC_S64: + return 8; + break; + + case ECMC_EC_F32: + return 4; + break; + + case ECMC_EC_F64: + return 8; + break; + + default: + return 0; + break; + } + + return 0; +} + +void ecmcMotionPlg::initAsyn() { + + // Add enable "plugin.motion%d.enable" + std::string paramName =ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_ENABLE; + int *paramId = &asynEnableId_; + if( createParam(0, paramName.c_str(), asynParamInt32, paramId) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter enable"); + } + setIntegerParam(*paramId, cfgEnable_); + +// Add xdata "plugin.motion%d.x_arr" +// paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) + +// "." + ECMC_PLUGIN_ASYN_X_DATA; +// paramId = &asynXAxisArrayId_; +// if( createParam(0, paramName.c_str(), asynParamFloat64Array, paramId ) != asynSuccess ) { +// throw std::runtime_error("Failed create asyn parameter rawdata"); +// } +// doCallbacksFloat64Array(xAxisArray_, cfgArraySize_, *paramId,0); +// +// // Add actpos "plugin.motion%d.actpos_arr" +// paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) + +// "." + ECMC_PLUGIN_ASYN_Y_ACTPOS_DATA; +// paramId = &asynYActPosArrayId_; +// if( createParam(0, paramName.c_str(), asynParamFloat64Array, paramId ) != asynSuccess ) { +// throw std::runtime_error("Failed create asyn parameter rawdata"); +// } +// doCallbacksFloat64Array(yActPosArray_, cfgArraySize_, *paramId,0); +// +// // Add actpos "plugin.motion%d.setpos_arr" +// paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) + +// "." + ECMC_PLUGIN_ASYN_Y_SETPOS_DATA; +// paramId = &asynYSetPosArrayId_; +// if( createParam(0, paramName.c_str(), asynParamFloat64Array, paramId ) != asynSuccess ) { +// throw std::runtime_error("Failed create asyn parameter rawdata"); +// } +// doCallbacksFloat64Array(ySetPosArray_, cfgArraySize_, *paramId,0); +// +// // Add diffpos "plugin.motion%d.diffpos_arr" +// paramName =ECMC_PLUGIN_ASYN_PREFIX + to_string(objectId_) + +// "." + ECMC_PLUGIN_ASYN_Y_DIFFPOS_DATA; +// paramId = &asynYDiffPosArrayId_; +// if( createParam(0, paramName.c_str(), asynParamFloat64Array, paramId ) != asynSuccess ) { +// throw std::runtime_error("Failed create asyn parameter rawdata"); +// } +// doCallbacksFloat64Array(yDiffPosArray_, cfgArraySize_, *paramId,0); +// + // Add motion "plugin.motion%d.command" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_MOTION_COMMAND; + paramId = &asynCommandId_; + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter mode"); + } + setIntegerParam(*paramId, (epicsInt32)command_); + + // Add motion "plugin.motion%d.status" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_MOTION_STAT; + paramId = &asynStatusId_; + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter mode"); + } + setIntegerParam(*paramId, (epicsInt32)status_); + + // Add motion "plugin.motion%d.trigg" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_MOTION_TRIGG; + paramId = &asynTriggId_; + + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter trigg"); + } + setIntegerParam(*paramId, (epicsInt32)triggOnce_); + + // Add motion plugin.motion%d.buff_size" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_BUFF_SIZE; + paramId = &asynBufferSizeId_; + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter nfft"); + } + setIntegerParam(*paramId, (epicsInt32)cfgArraySize_); + + // Add motion "plugin.motion%d.rate" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_RATE; + paramId = &asynSRateId_; + + if( createParam(0, paramName.c_str(), asynParamFloat64, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter rate"); + } + setDoubleParam(*paramId, cfgSampleRateHz_); + + // Add motion "plugin.motion%d.elem_count" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_ELEMENTS_IN_BUFF; + paramId = &asynElementsInBufferId_; + + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter trigg"); + } + setIntegerParam(*paramId, (epicsInt32)elementsInBuffer_); + + // Add motion "plugin.motion%d.axis" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_AXIS_ID; + paramId = &asynAxisId_; + + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter axis id"); + } + setIntegerParam(*paramId, (epicsInt32)cfgAxisIndex_); + + // Add motion "plugin.motion%d.mode" + paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objectId_) + + "." + ECMC_PLUGIN_ASYN_MOTION_MODE; + paramId = &asynModeId_; + + if( createParam(0, paramName.c_str(), asynParamInt32, paramId ) != asynSuccess ) { + throw std::runtime_error("Failed create asyn parameter mode"); + } + setIntegerParam(*paramId, (epicsInt32)cfgMode_); + + // Update integers + callParamCallbacks(); +} + +// Avoid issues with std:to_string() +std::string ecmcMotionPlg::to_string(int value) { + std::ostringstream os; + os << value; + return os.str(); +} + +void ecmcMotionPlg::setEnable(int enable) { + cfgEnable_ = enable; + setIntegerParam(asynEnableId_, enable); + actPosBuffer_->setEnable(enable); + setPosBuffer_->setEnable(enable); + diffPosBuffer_->setEnable(enable); + xPosBuffer_->setEnable(enable); +} + +void ecmcMotionPlg::triggMotionObject() { + clearBuffers(); + triggOnce_ = 1; + setIntegerParam(asynTriggId_,0); +} + + +// Executed by ecmc rt thread. +void ecmcMotionPlg::executeMotionObject() { + + if(!axis_) { + return; + } + + if(cfgMode_==TRIGG && !bTriggInProgress_) { + return; + } + + // protect axis_ if axis object id is changed over asyn + epicsMutexLock(axisMutex_); + ecmcAxisStatusType *tempAxisStat = axis_->getDebugInfoDataPointer(); + + // Fill the buffers + actPosBuffer_->addData(tempAxisStat->onChangeData.positionActual); + setPosBuffer_->addData(tempAxisStat->onChangeData.positionSetpoint); + diffPosBuffer_->addData(tempAxisStat->onChangeData.positionError); + enableBuffer_->addData(tempAxisStat->onChangeData.statusWd.enable); + enabledBuffer_->addData(tempAxisStat->onChangeData.statusWd.enabled); + busyBuffer_->addData(tempAxisStat->onChangeData.statusWd.busy); + executeBuffer_->addData(tempAxisStat->onChangeData.statusWd.execute); + trajSourceBuffer_->addData(tempAxisStat->onChangeData.statusWd.trajsource); + encSourceBuffer_->addData(tempAxisStat->onChangeData.statusWd.encsource); + atTargetBuffer_->addData(tempAxisStat->onChangeData.statusWd.attarget); + errorIdBuffer_->addData(tempAxisStat->onChangeData.error); + + epicsInt32 *temp=(epicsInt32*)&(tempAxisStat->onChangeData.statusWd); // A bit nasty + statusWdBuffer_->addData(*temp); + + xTime_+=xdt_; + xPosBuffer_->addData(xTime_); // Always relative within one buffer + + // If buffers full the (all buffers syncronous so all should be full if one is full) + if(actPosBuffer_->getBufferFull()) { + bTriggInProgress_ = false; + xTime_ = 0; //reset x axis + switchBuffers(); // unfortenately needed here + doWriteEvent_.signal(); + } + epicsMutexUnlock(axisMutex_); +} + +//void ecmcMotionPlg::setModeFFT(FFT_MODE mode) { +// cfgMode_ = mode; +// setIntegerParam(asynFFTModeId_,(epicsInt32)mode); +//} +// +//FFT_STATUS ecmcMotionPlg::getStatusFFT() { +// return status_; +//} + +//void ecmcMotionPlg::updateStatus(FFT_STATUS status) { +// status_ = status; +// setIntegerParam(asynFFTStatId_,(epicsInt32) status); +// +// setIntegerParam(asynElementsInBuffer_, (epicsInt32)elementsInBuffer_); +// +// callParamCallbacks(); +//} + +asynStatus ecmcMotionPlg::writeInt32(asynUser *pasynUser, epicsInt32 value) { + int function = pasynUser->reason; + if( function == asynEnableId_ ) { + cfgEnable_ = value; + return asynSuccess; + } else if( function == asynTriggId_){ + triggOnce_ = value > 0; + return asynSuccess; + } else if( function == asynCommandId_){ + command_ = value > 0; + //also exe something? + return asynSuccess; + } else if( function == asynEnableId_){ + setEnable(value > 0); + return asynSuccess; + } else if( function == asynAxisId_){ + return setAxis(value) > 0 ? asynSuccess : asynError; + } else if( function == asynModeId_){ + return setMode((TRIGG_MODE)value) > 0 ? asynSuccess : asynError; + } else if( function == asynModeId_){ + return setTrigg(value) ? asynSuccess :asynError; + } + + return asynError; +} + +asynStatus ecmcMotionPlg::readInt32(asynUser *pasynUser, epicsInt32 *value) { + int function = pasynUser->reason; + if( function == asynEnableId_ ) { + *value = cfgEnable_; + return asynSuccess; + } else if( function == asynAxisId_){ + *value = cfgAxisIndex_; + return asynSuccess; + } else if( function == asynTriggId_ ){ + *value = triggOnce_; + return asynSuccess; + }else if( function == asynStatusId_ ){ + *value = (epicsInt32)status_; + return asynSuccess; + }else if( function == asynBufferSizeId_ ){ + *value = (epicsInt32)cfgArraySize_; + return asynSuccess; + }else if( function == asynElementsInBufferId_){ + *value = (epicsInt32)elementsInBuffer_; + return asynSuccess; + } else if( function == asynEnableId_){ + *value =cfgEnable_; + return asynSuccess; + } + + return asynError; +} + +// These callbacks are moved to ecmcdataBuffer class +asynStatus ecmcMotionPlg::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, + size_t nElements, size_t *nIn) { + *nIn = 0; + return asynError; +} + +asynStatus ecmcMotionPlg::readFloat64(asynUser *pasynUser, epicsFloat64 *value) { + int function = pasynUser->reason; + if( function == asynSRateId_ ) { + *value = cfgDataSampleRateHz_; + return asynSuccess; + } + + return asynError; +} + +int ecmcMotionPlg::setAxis(int axisId) { + ecmcAxisBase *temp= (ecmcAxisBase*) getAxisPointer(axisId); + if(!temp) { + printf("Warning selected axis index out of range.\n"); + //set old value again + setParamAlarmStatus(asynAxisId_,1); + setParamAlarmSeverity(asynAxisId_,1); + setIntegerParam(asynAxisId_, (epicsInt32)cfgAxisIndex_); + callParamCallbacks(); + return ECMC_PLUGIN_MOTION_ERROR_AXIS_OUT_OF_RANGE; + } + + epicsMutexLock(axisMutex_); + clearBuffers(); + axis_ = temp; + cfgAxisIndex_ = axisId; + epicsMutexUnlock(axisMutex_); + + setParamAlarmStatus(asynAxisId_,0); + setParamAlarmSeverity(asynAxisId_,0); + setIntegerParam(asynAxisId_, (epicsInt32)cfgAxisIndex_); + callParamCallbacks(); + return 0; +} + +int ecmcMotionPlg::setTrigg(int trigg) { + if(cfgMode_==TRIGG) { + epicsMutexLock(axisMutex_); + clearBuffers(); + bTriggInProgress_ = true; + epicsMutexUnlock(axisMutex_); + } + return 0; +} + +int ecmcMotionPlg::setMode(TRIGG_MODE mode) { + cfgMode_ = mode; + return 0; +} + +// Write socket worker thread (switch between two buffers) +void ecmcMotionPlg::doWriteWorker() { + while(true) { + if(destructs_) { + return; + } + + nanosleep(&writePauseTime_,NULL); + + if(writeBusy_ || !cfgEnable_ ) { + continue; + } + + if(destructs_) { + return; + } + + doWriteEvent_.wait(); + writeBusy_ = 1; + writeBuffers(); + writeBusy_ = 0; + } +} + +// triggered by low prio work thread only +void ecmcMotionPlg::writeBuffers() { + + //Write all buffers + actPosBuffer_->writeBuffer(); + setPosBuffer_->writeBuffer(); + diffPosBuffer_->writeBuffer(); + enableBuffer_->writeBuffer(); + enabledBuffer_->writeBuffer(); + busyBuffer_->writeBuffer(); + executeBuffer_->writeBuffer(); + trajSourceBuffer_->writeBuffer(); + encSourceBuffer_->writeBuffer(); + atTargetBuffer_->writeBuffer(); + errorIdBuffer_->writeBuffer(); + statusWdBuffer_->writeBuffer(); + + // Always write x last if triggering is needed + xPosBuffer_->writeBuffer(); +} + +// triggered by ecmc RT thread +void ecmcMotionPlg::switchBuffers() { + // switch in new empty buffer while data is stored in the other. + actPosBuffer_->switchBuffer(); + setPosBuffer_->switchBuffer(); + diffPosBuffer_->switchBuffer(); + enableBuffer_->switchBuffer(); + enabledBuffer_->switchBuffer(); + busyBuffer_->switchBuffer(); + executeBuffer_->switchBuffer(); + trajSourceBuffer_->switchBuffer(); + encSourceBuffer_->switchBuffer(); + atTargetBuffer_->switchBuffer(); + errorIdBuffer_->switchBuffer(); + statusWdBuffer_->switchBuffer(); + xPosBuffer_->switchBuffer(); +} + diff --git a/src/ecmcSafetyPlg.h b/src/ecmcSafetyPlg.h new file mode 100644 index 0000000..f13e1e8 --- /dev/null +++ b/src/ecmcSafetyPlg.h @@ -0,0 +1,168 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcMotionPlg.h +* +* Created on: july 10, 2023 +* Author: anderssandstrom +* +\*************************************************************************/ +#ifndef ECMC_MOTION_PLG_H_ +#define ECMC_MOTION_PLG_H_ + +#include +#include "ecmcDataItem.h" +#include "ecmcAsynPortDriver.h" +#include "ecmcMotionPlgDefs.h" +#include "inttypes.h" +#include +#include "dbBase.h" +#include "ecmcAxisBase.h" +#include "ecmcDataBuffer.h" +#include "epicsMutex.h" +#include "epicsEvent.h" + +#define ECMC_PLUGIN_MOTION_ERROR_AXIS_OUT_OF_RANGE 1 + +typedef enum TRIGG_MODE{ + NO_MODE = 0, + CONT = 1, + TRIGG = 2, +} TRIGG_MODE; + +class ecmcMotionPlg : public asynPortDriver { + public: + + /** ecmc Motion Plg class + * This object can throw: + * - bad_alloc + * - invalid_argument + * - runtime_error + * - out_of_range + */ + ecmcMotionPlg(int objIndex, // index of this object + char* configStr, + char* portName); + ~ecmcMotionPlg(); + + // Add data to buffer (called from "external" callback) + void dataUpdatedCallback(uint8_t* data, + size_t size, + ecmcEcDataType dt); + // Call just before realtime because then all data sources should be available + void connectToDataSource(); + void doWriteWorker(); // low prio worker thread + void setEnable(int enable); + void clearBuffers(); + void triggMotionObject(); + void executeMotionObject(); + virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); + virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value); + virtual asynStatus readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, + size_t nElements, size_t *nIn); + virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value); + + + private: + void parseConfigStr(char *configStr); + void addDataToBuffer(double data); + void initAsyn(); + static int dataTypeSupported(ecmcEcDataType dt); + int setAxis(int axisId); + int setMode(TRIGG_MODE mode); + int setTrigg(int trigg); + void writeBuffers(); + void switchBuffers(); + + + epicsMutexId axisMutex_; + + double* xAxisArray_; // FFT x axis with freqs + size_t elementsInBuffer_; + double ecmcSampleRateHz_; + int dataSourceLinked_; // To avoid link several times + int command_; + int status_; + // ecmc callback handle for use when deregister at unload + int callbackHandle_; + int destructs_; + int objectId_; // Unique object id + int triggOnce_; + int cycleCounter_; + int ignoreCycles_; + + // Config options + int cfgDbgMode_; // Config: allow dbg printouts + size_t cfgArraySize_; // Config: Data set size + int cfgEnable_; // Config: Enable data acq./calc. + double cfgSampleRateHz_; // Config: Sample rate (defaults to ecmc rate) + double cfgDataSampleRateHz_;// Config: Sample for data + int cfgAxisIndex_; // Config: Enable data acq./calc. + TRIGG_MODE cfgMode_; // Config: Mode continous or triggered. + + // Asyn + int asynEnableId_; // Enable/disable acq./calcs + int asynYActPosArrayId_; + int asynYSetPosArrayId_; + int asynYDiffPosArrayId_; + int asynXAxisArrayId_; + int asynTriggId_; // Trigg new measurement + int asynAxisId_; // motion axis id + int asynSRateId_; // Sample rate + int asynElementsInBufferId_; // Current buffer index + int asynBufferSizeId_; // Current buffer index + int asynCommandId_; + int asynStatusId_; + int asynModeId_; + + ecmcAxisBase *axis_; + // Thread related + //epicsEvent doCalcEvent_; + + + // Some generic utility functions + static uint8_t getUint8(uint8_t* data); + static int8_t getInt8(uint8_t* data); + static uint16_t getUint16(uint8_t* data); + static int16_t getInt16(uint8_t* data); + static uint32_t getUint32(uint8_t* data); + static int32_t getInt32(uint8_t* data); + static uint64_t getUint64(uint8_t* data); + static int64_t getInt64(uint8_t* data); + static float getFloat32(uint8_t* data); + static double getFloat64(uint8_t* data); + static size_t getEcDataTypeByteSize(ecmcEcDataType dt); + static void printEcDataArray(uint8_t* data, + size_t size, + ecmcEcDataType dt, + int objId); + static void printComplexArray(std::complex* fftBuff, + size_t elements, + int objId); + static std::string to_string(int value); + + ecmcDataBuffer *actPosBuffer_; + ecmcDataBuffer *setPosBuffer_; + ecmcDataBuffer *diffPosBuffer_; + ecmcDataBuffer *xPosBuffer_; + ecmcDataBuffer *enableBuffer_; + ecmcDataBuffer *enabledBuffer_; + ecmcDataBuffer *busyBuffer_; + ecmcDataBuffer *executeBuffer_; + ecmcDataBuffer *trajSourceBuffer_; + ecmcDataBuffer *encSourceBuffer_; + ecmcDataBuffer *atTargetBuffer_; + ecmcDataBuffer *errorIdBuffer_; + ecmcDataBuffer *statusWdBuffer_; + + bool bTriggInProgress_; + double xdt_; + double xTime_; + epicsEvent doWriteEvent_; + int writeBusy_; + timespec writePauseTime_; +}; + +#endif /* ECMC_MOTION_PLG_H_ */ diff --git a/src/ecmcSafetyPlgDefs.h b/src/ecmcSafetyPlgDefs.h new file mode 100644 index 0000000..1adcac1 --- /dev/null +++ b/src/ecmcSafetyPlgDefs.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcMotionPlgDefs.h +* +* Created on: july 10, 2023 +* Author: anderssandstrom +* Credits to https://github.com/sgreg/dynamic-loading +* +\*************************************************************************/ + +#ifndef ECMC_MOTION_PLG_DEFS_H_ +#define ECMC_MOTION_PLG_DEFS_H_ + +#define ECMC_PLUGIN_ASYN_PREFIX "plugin.motion" + +// Options +#define ECMC_PLUGIN_DBG_PRINT_OPTION_CMD "DBG_PRINT=" +#define ECMC_PLUGIN_AXIS_OPTION_CMD "AXIS=" +#define ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD "BUFFER_SIZE=" +#define ECMC_PLUGIN_RATE_OPTION_CMD "RATE=" +#define ECMC_PLUGIN_ENABLE_OPTION_CMD "ENABLE=" +#define ECMC_PLUGIN_MODE_OPTION_CMD "MODE=" + +// CONT, TRIGG +#define ECMC_PLUGIN_MODE_CONT_OPTION "CONT" +#define ECMC_PLUGIN_MODE_TRIGG_OPTION "TRIGG" + + +/** Just one error code in "c" part of plugin +(error handled with exceptions i c++ part) */ +#define ECMC_PLUGIN_MOTION_ERROR_CODE 1 + +// Default size (must be n²) +#define ECMC_PLUGIN_DEFAULT_ARRAY_SIZE 4096 + +#endif /* ECMC_MOTION_PLG_DEFS_H_ */ diff --git a/src/ecmcSafetyPlgWrap.cpp b/src/ecmcSafetyPlgWrap.cpp new file mode 100644 index 0000000..96571f8 --- /dev/null +++ b/src/ecmcSafetyPlgWrap.cpp @@ -0,0 +1,126 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcMotionPlgWrap.cpp +* +* Created on: july 10, 2023 +* Author: anderssandstrom +* Credits to https://github.com/sgreg/dynamic-loading +* +\*************************************************************************/ + +// Needed to get headers in ecmc right... +#define ECMC_IS_PLUGIN + +#include +#include +#include +#include "ecmcMotionPlgWrap.h" +#include "ecmcMotionPlg.h" + +#define ECMC_PLUGIN_MAX_PORTNAME_CHARS 64 +#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.MOTION" + +static std::vector motionObjs; +static int motionObjCounter = 0; +static char portNameBuffer[ECMC_PLUGIN_MAX_PORTNAME_CHARS]; + +int createMotionObj(char* configStr) { + + // create new ecmcMotionPlg object + ecmcMotionPlg* motionObj = NULL; + + // create asynport name for new object () + memset(portNameBuffer, 0, ECMC_PLUGIN_MAX_PORTNAME_CHARS); + snprintf (portNameBuffer, ECMC_PLUGIN_MAX_PORTNAME_CHARS, + ECMC_PLUGIN_PORTNAME_PREFIX "_%d", motionObjCounter); + try { + motionObj = new ecmcMotionPlg(motionObjCounter, configStr, portNameBuffer); + } + catch(std::exception& e) { + if(motionObj) { + delete motionObj; + } + printf("Exception: %s. Plugin will unload.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + + motionObjs.push_back(motionObj); + motionObjCounter++; + + return 0; +} + +void deleteAllMotionObjs() { + for(std::vector::iterator pMotionObj = motionObjs.begin(); pMotionObj != motionObjs.end(); ++pMotionObj) { + if(*pMotionObj) { + delete (*pMotionObj); + } + } +} + +int linkDataTomotionObjs() { + for(std::vector::iterator pMotionObj = motionObjs.begin(); pMotionObj != motionObjs.end(); ++pMotionObj) { + if(*pMotionObj) { + try { + (*pMotionObj)->connectToDataSource(); + } + catch(std::exception& e) { + printf("Exception: %s. Plugin will unload.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + } + } + return 0; +} + +int enableMotionObj(int objIndex, int enable) { + try { + motionObjs.at(objIndex)->setEnable(enable); + } + catch(std::exception& e) { + printf("Exception: %s. Motion object index out of range.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + return 0; +} + +int clearMotionObj(int objIndex) { + try { + motionObjs.at(objIndex)->clearBuffers(); + } + catch(std::exception& e) { + printf("Exception: %s. Motion object index out of range.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + return 0; +} + +int triggMotionObj(int objIndex) { + try { + motionObjs.at(objIndex)->triggMotionObject(); + } + catch(std::exception& e) { + printf("Exception: %s. Motion object index out of range.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + return 0; +} + +int executeMotionObjs() { + + for(std::vector::iterator pMotionObj = motionObjs.begin(); pMotionObj != motionObjs.end(); ++pMotionObj) { + if(*pMotionObj) { + try { + (*pMotionObj)->executeMotionObject(); + } + catch(std::exception& e) { + printf("Exception: %s. Plugin will unload.\n",e.what()); + return ECMC_PLUGIN_MOTION_ERROR_CODE; + } + } + } + return 0; +} diff --git a/src/ecmcSafetyPlgWrap.h b/src/ecmcSafetyPlgWrap.h new file mode 100644 index 0000000..240be88 --- /dev/null +++ b/src/ecmcSafetyPlgWrap.h @@ -0,0 +1,109 @@ +/*************************************************************************\ +* Copyright (c) 2023 Paul Scherrer Institute +* ecmc is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +* +* ecmcMotionPlgWrap.h +* +* Created on: july 10, 2023 +* Author: anderssandstrom +* +\*************************************************************************/ +#ifndef ECMC_MOTION_PLG_WRAP_H_ +#define ECMC_MOTION_PLG_WRAP_H_ +#include "ecmcMotionPlgDefs.h" + +# ifdef __cplusplus +extern "C" { +# endif // ifdef __cplusplus + +/** \brief Create new Motion object + * + * The plugin supports creation of multiple Motion objects\n + * (if loaded several times).\n + * The different fft are adressed by fftindex (in other functions below).\n + * The first loaded fft get index 0 and then increases for each load.\n + * This function call will create the custom asynparameters dedicated for this plugin.\ + * The configuration string needs to define a data source by:\n + * "SOURCE=;"\n + * Example:\n + * "SOURCE=ec0.s1.AI_1";\n + * \param[in] configStr Configuration string.\n + * + * \return 0 if success or otherwise an error code.\n + */ +int createMotionObj(char *configStr); + +/** \brief Enable/disable Motion object + * + * Enable/disable Motion object. If disabled no data will be acquired\n + * and no calculations will be made.\n + * \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n + * \param[in] enable enable/disable (1/0).\n + * + * \return 0 if success or otherwise an error code.\n + */ +int enableMotionObj(int objIndex, int enable); + +/** \brief Clear FFT object\n + * + * Clears buffers. After this command the acquistion can start from scratch.\n + * \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n + * + * \return 0 if success or otherwise an error code.\n + */ +int clearMotionObj(int objIndex); + +/** \brief Set mode of FFT object + * + * The FFT object can measure in two differnt modes:\n + * CONT(1) : Continious measurement (Acq data, calc, then Acq data ..)\n + * TRIGG(2): Measurements are triggered from plc or over asyn and is only done once (untill next trigger)\n + * \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n + * \param[in] mode Mode CONT(1) or TRIGG(2)\n + * + * \return 0 if success or otherwise an error code.\n + */ +//int modeMotionObj(int objIndex, FFT_MODE mode); + +/** \brief Trigger FFT object\n + * + * If in triggered mode a new measurment cycle is initiated (fft will be cleared first).\n + * \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n + * + * \return 0 if success or otherwise an error code.\n + */ +int triggMotionObj(int objIndex); + + +/** \brief execute FFT object\n + * + * If in triggered mode a new measurment cycle is initiated (fft will be cleared first).\n + * \param[in] fftIndex Index of fft (first loaded fft have index 0 then increases)\n + * + * \return 0 if success or otherwise an error code.\n + */ +int executeMotionObjs(); + +/** \brief Link data to _all_ fft objects + * + * This tells the FFT lib to connect to ecmc to find it's data source.\n + * This function should be called just before entering realtime since then all\n + * data sources in ecmc will be definded (plc sources are compiled just before runtime\n + * so are only fist accesible now).\n + * \return 0 if success or otherwise an error code.\n + */ +int linkDataTomotionObjs(); + +/** \brief Deletes all created fft objects\n + * + * Should be called when destructs.\n + */ + +void deleteAllMotionObjs(); + +# ifdef __cplusplus +} +# endif // ifdef __cplusplus + +#endif /* ECMC_MOTION_PLG_WRAP_H_ */ diff --git a/startup.cmd b/startup.cmd new file mode 100644 index 0000000..37fb6f6 --- /dev/null +++ b/startup.cmd @@ -0,0 +1,32 @@ + +#============================================================================== +# startup.cmd +#-------------- Information: +#- Description: ecmc_plugin_motion startup.cmd +#- +#- by Anders Sandström, Paul Scherrer Institute, 2023 +#- email: anders.sandstroem@psi.ch +#- +#-############################################################################### +#- +#- Arguments +#- [mandatory] +#- PLUGIN_ID = Plugin instansiation index, must be unique for each call +#- +#- [optional] +#- AX = Axis id, default 1 +#- BUFF_SIZE = Buffer size, default 1000 +#- DBG = Debug mode, default 1 +#- ENA = Enable operation, default 1 +#- REPORT = Printout plugin details, default 1 + +################################################################################# + +#- Load plugin: MOTION + +# Only allow call startup.cmd once. if more objects are needed then use addMotionObj.cmd directlly. + +#- add One motion plugin object, only run startup once +${ECMC_PLG_MOTION_INIT=""}${SCRIPTEXEC} $(ecmc_plugin_motion_DIR)addMotionObj.cmd "PLUGIN_ID=${PLUGIN_ID},AX=${AX=1},BUFF_SIZE=${BUFF_SIZE=1000},DBG=${DBG=1},ENA=${ENA=1},REPORT=${REPORT=1}" + +epicsEnvSet("ECMC_PLG_MOTION_INIT" ,"#") diff --git a/template/.keep b/template/.keep new file mode 100644 index 0000000..e69de29