9 Commits

Author SHA1 Message Date
377a28c95f Adapt to ecmc v11 2025-09-10 13:44:56 +02:00
8d4ea1e87c epics 7.0.9 2025-04-22 12:14:17 +02:00
5ec740b3b0 Update makefile 2025-03-28 09:23:06 +01:00
104875d532 Fix makefile for deb12 2025-03-21 10:47:05 +01:00
69fcd4da36 Default to PLUGIN_ID=0 2025-03-03 15:17:46 +01:00
653d4aac02 Default to PLUGIN_ID=0 2025-03-03 15:17:18 +01:00
0c19350ee5 Update makefile and add some pictures 2025-03-03 15:08:41 +01:00
d47820b543 Prep for ecmc v10 2024-12-16 10:41:02 +01:00
04ea63c8f4 New plugin concept 2024-12-13 16:03:50 +01:00
24 changed files with 67 additions and 250 deletions

2
.gitignore vendored
View File

@@ -20,3 +20,5 @@ tools/bin
tools/lib
tools/lib64
tools/pyvenv.cfg
tools/.*

View File

@@ -1,12 +1,14 @@
include /ioc/tools/driver.makefile
MODULE = ecmc_plugin_motion
# "Transfer" module name to plugin
USR_CFLAGS +=-DECMC_PLUGIN_MODULE_NAME=${MODULE}
BUILDCLASSES = Linux
ARCH_FILTER = deb10%
ARCH_FILTER = deb10% deb12%
# Run 7.0.6 for now
EXCLUDE_VERSIONS+=3 7.0.5 7.0.6
EXCLUDE_VERSIONS+=3 7.0.5 7.0.6 7.0.7
IGNORE_MODULES += asynMotor
IGNORE_MODULES += motorBase
@@ -15,19 +17,28 @@ USR_CXXFLAGS += -std=c++17
OPT_CXXFLAGS_YES = -O3
# dependencies
ECmasterECMC_VERSION = v1.1.0
ecmc_VERSION = 9.6
ecmc_VERSION = v11.0.0_RC1
################################################################################
# THIS RELATES TO THE EtherCAT MASTER LIBRARY
# IT IS OF PARAMOUNT IMPORTANCE TO LOAD THE PROPER KERNEL MODULE
# ################################################################################
USR_LDFLAGS += -lethercat
# ethercat
# debian 12
EC_MASTER_VER = 1.6.3
EC_BASE_PATH = /ioc/NeedfulThings/EtherCAT/4epics/${EC_MASTER_VER}/
USR_CXXFLAGS_deb12-x86_64 += -I${EC_BASE_PATH}${T_A}/include/
USR_CXXFLAGS_deb12-x86_64 += -L${EC_BASE_PATH}${T_A}/lib/
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}
LIB_SYS_LIBS += ethercat
USR_LDFLAGS_deb12-x86_64 += -Wl,-rpath=${EC_BASE_PATH}${T_A}/lib/
USR_LDFLAGS_deb12-x86_64 += -L ${EC_BASE_PATH}${T_A}/lib/
# debian 10
# note: EC_MASTER LIB does not depend on epics version hence use the 7.0.8 build..
USR_LDFLAGS_deb10-x86_64 += -lethercat
EC_MASTER_LIB = /ioc/NeedfulThings/EtherCAT/4epics/v1.1.0/R7.0.8/
USR_LDFLAGS_deb10-x86_64 += -Wl,-rpath=${EC_MASTER_LIB}lib/${T_A}
USR_LDFLAGS_deb10-x86_64 += -L ${EC_MASTER_LIB}lib/${T_A}
USR_CXXFLAGS_deb10-x86_64 += -I${EC_MASTER_LIB}/include/
OPT_CXXFLAGS_YES = -O3
BASE_DIR = .
SRC_DIR = $(BASE_DIR)/src
DB_DIR = $(BASE_DIR)/Db

View File

@@ -31,7 +31,7 @@ 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}"
${SCRIPTEXEC} ${ecmccfg_DIR}loadPlugin.cmd, "PLUGIN_ID=${PLUGIN_ID=0},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}")

View File

@@ -839,22 +839,22 @@ void ecmcMotionPlg::executeMotionObject() {
// protect axis_ if axis object id is changed over asyn
epicsMutexLock(axisMutex_);
ecmcAxisStatusType *tempAxisStat = axis_->getDebugInfoDataPointer();
ecmcAxisDataStatus *tempAxisStat = axis_->getAxisStatusStruct();
// 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);
actPosBuffer_->addData(tempAxisStat->currentPositionActual);
setPosBuffer_->addData(tempAxisStat->currentPositionSetpoint);
diffPosBuffer_->addData(tempAxisStat->cntrlError);
enableBuffer_->addData(tempAxisStat->statusWord_.enable);
enabledBuffer_->addData(tempAxisStat->statusWord_.enabled);
busyBuffer_->addData(tempAxisStat->statusWord_.busy);
executeBuffer_->addData(tempAxisStat->statusWord_.execute);
trajSourceBuffer_->addData(tempAxisStat->statusWord_.trajsource);
encSourceBuffer_->addData(tempAxisStat->statusWord_.encsource);
atTargetBuffer_->addData(tempAxisStat->statusWord_.attarget);
errorIdBuffer_->addData(tempAxisStat->errorCode);
epicsInt32 *temp=(epicsInt32*)&(tempAxisStat->onChangeData.statusWd); // A bit nasty
epicsInt32 *temp=(epicsInt32*)&(tempAxisStat->statusWord_); // A bit nasty
statusWdBuffer_->addData(*temp);
xTime_+=xdt_;
@@ -870,24 +870,6 @@ void ecmcMotionPlg::executeMotionObject() {
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_ ) {

View File

@@ -8,11 +8,16 @@
* Created on: july 10, 2023
* Author: anderssandstrom
*
* Instructions:
* - IMPORTANT: Add "USR_CFLAGS +=-DECMC_PLUGIN_MODULE_NAME=${MODULE}" to GNUMakefile
* - All functions and ecmcPluginData struct must be declared static
*
\*************************************************************************/
// Needed to get headers in ecmc right...
#define ECMC_IS_PLUGIN
#define ECMC_EXAMPLE_PLUGIN_VERSION 2
#define ECMC_PLUGIN_VERSION 2
#ifdef __cplusplus
extern "C" {
@@ -30,15 +35,13 @@ static int lastEcmcError = 0;
static char* lastConfStr = NULL;
/** Optional.
* Will be called once after successfull load into ecmc.
* Will be called once after successful load into ecmc.
* Return value other than 0 will be considered error.
* configStr can be used for configuration parameters.
**/
int motionConstruct(char *configStr)
static int construct(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);
}
@@ -46,31 +49,27 @@ int motionConstruct(char *configStr)
/** Optional function.
* Will be called once at unload.
**/
void motionDestruct(void)
static 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
* Will be called each realtime cycle if defined
* ecmcError: Error code of ecmc. Makes it possible 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)
static int motionRealtime(int ecmcError)
{
executeMotionObjs();
lastEcmcError = ecmcError;
return 0;
}
/** Link to data source here since all sources should be availabe at this stage
/** Link to data source here since all sources should be available at this stage
* (for example ecmc PLC variables are defined only at enter of realtime)
**/
int motionEnterRT(){
static int motionEnterRT(){
return linkDataTomotionObjs();
}
@@ -78,41 +77,17 @@ int motionEnterRT(){
* Will be called once just before leaving realtime mode
* Return value other than 0 will be considered error.
**/
int motionExitRT(void){
static 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 = {
static struct ecmcPluginData pluginDataDef = {
// Allways use ECMC_PLUG_VERSION_MAGIC
.ifVersion = ECMC_PLUG_VERSION_MAGIC,
// Name
.name = "ecmcPlugin_Motion",
.name = "ecmc_plugin_motion",
// Description
.desc = "Motion plugin for commissioning of ecmc motion axes.",
// Option description
@@ -123,171 +98,20 @@ struct ecmcPluginData pluginDataDef = {
" "ECMC_PLUGIN_MODE_OPTION_CMD"<TRIGG/CONT> : 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.
.version = ECMC_PLUGIN_VERSION,
// Optional construct func, called once at load. NULL if not defined.
.constructFnc = construct,
// Optional destruct func, called once at unload. NULL if not defined.
.destructFnc = motionDestruct,
// Optional func that will be called each rt cycle. NULL if not definded.
// Optional func that will be called each rt cycle. NULL if not defined.
.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..
};

View File

@@ -10,10 +10,8 @@
#-###############################################################################
#-
#- Arguments
#- [mandatory]
#- PLUGIN_ID = Plugin instansiation index, must be unique for each call
#-
#- [optional]
#- PLUGIN_ID = Plugin instansiation index, must be unique for each call defaults to 0
#- AX = Axis id, default 1
#- BUFF_SIZE = Buffer size, default 1000
#- DBG = Debug mode, default 1
@@ -27,6 +25,6 @@
# 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}"
${ECMC_PLG_MOTION_INIT=""}${SCRIPTEXEC} $(ecmc_plugin_motion_DIR)addMotionObj.cmd "PLUGIN_ID=${PLUGIN_ID=0},AX=${AX=1},BUFF_SIZE=${BUFF_SIZE=1000},DBG=${DBG=1},ENA=${ENA=1},REPORT=${REPORT=1}"
epicsEnvSet("ECMC_PLG_MOTION_INIT" ,"#")

BIN
tools/cos_ff_trq_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
tools/cos_ff_trq_test_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
tools/delay0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
tools/delay0_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
tools/delay10_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
tools/delay1_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
tools/delay2.5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
tools/delay2.5_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
tools/delay2_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
tools/delay3_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
tools/delay4_kx0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -579,7 +579,7 @@ class ecmcMtnMainGui(QtWidgets.QDialog):
axId = axIdPV.get()
if axId > 0:
self.cmbBxSelectAxis.addItem(str(int(ax)))
self.cmbBxSelectAxis.addItem(str(int(axId)))
def changeAxisIndex(self,xxx):
if self.cmbBxSelectAxis.currentData() is not None:

View File

@@ -19,4 +19,4 @@ fi
echo "Prefix=${P=c6025a-04}"
echo "plugin id=${ID=0}"
python ecmcMotionMainPyQtGraph.py ${P=c6025a-04} ${ID=0}
python3 ecmcMotionMainPyQtGraph.py ${P=c6025a-04} ${ID=0}