Start with motion plugin as base (just minor cleanup and renaming made) WIP

This commit is contained in:
2024-01-29 10:37:22 +01:00
commit 0dd0161ffb
37 changed files with 2945 additions and 0 deletions
View File
+298
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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"<axis id> : Sets default source axis id.\n"
" "ECMC_PLUGIN_BUFFER_SIZE_OPTION_CMD"<size> : Data points to collect, default = 4096.\n"
" "ECMC_PLUGIN_RATE_OPTION_CMD"<rate hz> : Sampling rate in Hz"
" "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.
.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
File diff suppressed because it is too large Load Diff
+168
View File
@@ -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 <stdexcept>
#include "ecmcDataItem.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcMotionPlgDefs.h"
#include "inttypes.h"
#include <string>
#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<double>* fftBuff,
size_t elements,
int objId);
static std::string to_string(int value);
ecmcDataBuffer<epicsFloat64> *actPosBuffer_;
ecmcDataBuffer<epicsFloat64> *setPosBuffer_;
ecmcDataBuffer<epicsFloat64> *diffPosBuffer_;
ecmcDataBuffer<epicsFloat64> *xPosBuffer_;
ecmcDataBuffer<epicsInt8> *enableBuffer_;
ecmcDataBuffer<epicsInt8> *enabledBuffer_;
ecmcDataBuffer<epicsInt8> *busyBuffer_;
ecmcDataBuffer<epicsInt8> *executeBuffer_;
ecmcDataBuffer<epicsInt8> *trajSourceBuffer_;
ecmcDataBuffer<epicsInt8> *encSourceBuffer_;
ecmcDataBuffer<epicsInt8> *atTargetBuffer_;
ecmcDataBuffer<epicsInt32> *errorIdBuffer_;
ecmcDataBuffer<epicsInt32> *statusWdBuffer_;
bool bTriggInProgress_;
double xdt_;
double xTime_;
epicsEvent doWriteEvent_;
int writeBusy_;
timespec writePauseTime_;
};
#endif /* ECMC_MOTION_PLG_H_ */
+39
View File
@@ -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_ */
+126
View File
@@ -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 <vector>
#include <stdexcept>
#include <string>
#include "ecmcMotionPlgWrap.h"
#include "ecmcMotionPlg.h"
#define ECMC_PLUGIN_MAX_PORTNAME_CHARS 64
#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.MOTION"
static std::vector<ecmcMotionPlg*> 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<ecmcMotionPlg*>::iterator pMotionObj = motionObjs.begin(); pMotionObj != motionObjs.end(); ++pMotionObj) {
if(*pMotionObj) {
delete (*pMotionObj);
}
}
}
int linkDataTomotionObjs() {
for(std::vector<ecmcMotionPlg*>::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<ecmcMotionPlg*>::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;
}
+109
View File
@@ -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=<data 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_ */