e3-ecmcPlugin_Advanced ====== ESS Site-specific EPICS module : ecmcPlugin_Advanced Example illustrating how to implement plugins for use with ecmc (https://github.com/icshwi/ecmc). Shows how to implement: * callbacks * custom ecmc plc-functions * custom ecmc plc-constants * access to ecmcAsynPort object to add plugin specific asyn parameter. # Interface The interface is defined in the structure ecmcPluginData in ecmcPluginDefs.h: ``` struct ecmcPluginData { // Name const char *name; // Description const char *desc; // Plugin version int version; // ECMC_PLUG_VERSION_MAGIC int ifVersion; // Optional construct func, called once at load int (*constructFnc)(void); // Optional destruct func, called once at unload void (*destructFnc)(void); // Optional func that will be called once just before enter realtime mode int (*realtimeEnterFnc)(void*); // Optional func that will be called once just before exit realtime mode int (*realtimeExitFnc)(void); // Optional func that will be called each realtime cycle int (*realtimeFnc)(int); // Allow max ECMC_PLUGIN_MAX_PLC_FUNC_COUNT custom plc functions struct ecmcOnePlcFunc funcs[ECMC_PLUGIN_MAX_PLC_FUNC_COUNT]; // Allow max ECMC_PLUGIN_MAX_PLC_CONST_COUNT custom plc constants struct ecmcOnePlcConst consts[ECMC_PLUGIN_MAX_PLC_CONST_COUNT]; }; ``` ## Callbacks: All callbacks are optional. If the callbacks are not used then set the func pointer to NULL ("ecmcPluginData.*Fnc=NULL"). Example: ``` ecmcPluginData.destructFnc=NULL ecmcPluginData.constructFnc=NULL ... ``` ### int constructFnc(), optional This callback is called once when the plugin is loaded into ecmc. This is a good place to put code for any initialization needed in the plugin module. Return value: 0 for success or error code. In this example plugin only a printout is made in this callback. ### void destructFnc(), optional This callback is called once when the plugin is unloaded. This is a good place to put cleanup code needed by the plugin module. In this example plugin only a printout is made in this callback. ### int realtimeFnc(int ecmcErrorId), optional This callback is called once in each realtime loop (sync to ecmc). This is a good place to put any cyclic processing needed by the plugin module. NOTE: This callback is executed by ecmc realtime thread. Take measures to stay as short time as possible in this function. If lots of processing is needed a separate worker thread might be a solution. Parameters: ecmcErrorId: reflects the current errorstate of ecmc. Return value: 0 for success or error code. In this example a counter value is increased for each call and the coresponding asyn parameter is updated. ### int realtimeEnterFnc(void* ecmcRefs), optional This callback is called once just before ecmc enters realtime mode (starts rt-thread). This is a good place to make any prepartions needed before cyclic processing starts. Parameters: ecmcRefs: ref to ecmcdata that can be cast to ecmcPluginDataRefs ``` struct ecmcPluginDataRefs { double sampleTimeMS; ecmcAsynPortDriver *ecmcAsynPort; }; ``` Return value: 0 for success or error code. In this example a asyn parameter called "plugin.adv.counter" is registered. The ecmc realtime samplerate is also determined from the ecmcRefs ### int realtimeExitFnc(), optional This callback is called once just before ecmc exits realtime mode (exits rt-thread). Return value: 0 for success or error code. In this example plugin only a printout is made in this callback. ### Example: ``` // Compile data for lib so ecmc now what to use struct ecmcPluginData pluginDataDef = { // Name .name = "ecmcExamplePlugin", // Description .desc = "Advanced example with use of asynport obj.", // Plugin version .version = ECMC_EXAMPLE_PLUGIN_VERSION, // ECMC_PLUG_VERSION_MAGIC .ifVersion = ECMC_PLUG_VERSION_MAGIC, // Optional construct func, called once at load. NULL if not definded. .constructFnc = adv_exampleConstruct, // Optional destruct func, called once at unload. NULL if not definded. .destructFnc = adv_exampleDestruct, // Optional func that will be called each rt cycle. NULL if not definded. .realtimeFnc = adv_exampleRealtime, // Optional func that will be called once just before enter realtime mode .realtimeEnterFnc = adv_exampleEnterRT, // Optional func that will be called once just before exit realtime mode .realtimeExitFnc = adv_exampleExitRT, ... ... ``` ## PLC functions: Custom ecmc PLC-functions can be implemented in plugins. Currentlly the interface supports implementation of up to 64 plc functions. Each plc function needs to be defined by the struct "ecmcOnePlcFunc": ``` struct ecmcOnePlcFunc { // Function name (this is the name you use in ecmc plc-code) const char *funcName; // Function description const char *funcDesc; /** * 7 different prototypes allowed (only doubles since reg in plc). * Only one funcArg func shall be assigned the rest set to NULL **/ double (*funcArg0)(); double (*funcArg1)(double); double (*funcArg2)(double,double); double (*funcArg3)(double,double,double); double (*funcArg4)(double,double,double,double); double (*funcArg5)(double,double,double,double,double); double (*funcArg6)(double,double,double,double,double,double); }; ``` Example: ``` .funcs[0] = { /*----customPlcFunc1----*/ // Function name (this is the name you use in ecmc plc-code) .funcName = "adv_plugin_func_1", // Function description .funcDesc = "Multiply arg0 with arg1.", /** * 7 different prototypes allowed (only doubles since reg in plc). * Only funcArg one func shall be assigned the rest set to NULL. **/ .funcArg0 = NULL, .funcArg1 = NULL, .funcArg2 = adv_customPlcFunc1, // Func 1 has 2 args .funcArg3 = NULL, .funcArg4 = NULL, .funcArg5 = NULL, .funcArg6 = NULL }, ``` Note: Only the first non NULL function will be used (starting from funcArg0...) ## PLC constants Custom ecmc PLC-constants can be implemented in plugins. Currentlly the interface supports implementation of up to 64 plc constants. Each plc constant needs to be defined by the struct "ecmcOnePlcConst": ``` struct ecmcOnePlcConst{ const char *constName; const char *constDesc; double constValue; }; ``` Example: ``` .consts[0] = { .constName = "adv_CONST_1", .constDesc = "Test constant \"adv_CONST_1\" = 1.234567890", .constValue = 1.234567890, }, ``` ## Dependencies: All needed headers are available in ecmc (https://github.com/icshwi/ecmc) ### Simple plugins Only the "ecmcPluginDefs.h" header is needed. ### Advanced plugins When using the "void* ecmcRefs" param (cast to ecmcPluginDataRefs)in the "realtimeEnterFnc()" these additional headers are needed: * from ecmc: * ecmcAsynPortDriver.h * ecmcAsynDataItem.h * ecmcAsynPortDriverUtils.h * ecmcDefinitions.h * ecmcErrorsList.h * ecmcPluginDataRefs.h * from asyn: * asynPortDriver.h Note: This define is needed in the plugin sources: ``` #define ECMC_IS_PLUGIN ``` ## Plugin info for ecmcPlugin_Advanced ``` Plugin info: Name = ecmcExamplePlugin Description = Advanced example with use of asynport obj. Version = 1 Interface version = 512 (ecmc = 512) max plc funcs = 64 max plc consts = 64 Construct func = @0x7fac4353d190 Enter realtime func = @0x7fac4353d200 Exit realtime func = @0x7fac4353d1c0 Realtime func = @0x7fac4353d1e0 Destruct func = @0x7fac4353d1b0 dlhandle = @0x182e580 Plc functions: funcs[00]: Name = "adv_plugin_func_1(arg0, arg1);" Desc = Multiply arg0 with arg1. Arg count = 2 func = @0x7fac4353d170 funcs[01]: Name = "adv_plugin_func_2(arg0, arg1, arg2);" Desc = Multiply arg0, arg1 and arg2. Arg count = 3 func = @0x7fac4353d180 Plc constants: consts[00]: Name = "adv_CONST_1" = 1.235 Desc = Test constant "adv_CONST_1" = 1.234567890 consts[01]: Name = "adv_CONST_2" = 9.877 Desc = Test constant "adv_CONST_2" = 9.876543210 ``` ## Access to asyn parameter (linked to record IOC_TEST:Plugin-Adv-Counter) ``` camonitor IOC_TEST:Plugin-Adv-Counter IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:21.535547 23 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:21.630954 24 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:21.740799 25 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:21.838110 26 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:21.930354 27 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.034919 28 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.130358 29 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.234041 30 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.334133 31 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.434937 32 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.530212 33 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.634437 34 IOC_TEST:Plugin-Adv-Counter 2020-03-26 15:45:22.734606 35 ```