Start from scope plugin

This commit is contained in:
2024-03-01 15:07:38 +01:00
commit a41e8c69b2
15 changed files with 2097 additions and 0 deletions
View File
+90
View File
@@ -0,0 +1,90 @@
# Data source
record(waveform,"$(P)Plugin-Scope${INDEX}-DataSource"){
field(DESC, "Data source name")
field(PINI, "1")
field(DTYP, "asynInt8ArrayIn")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt8ArrayIn/plugin.scope${INDEX}.source?")
field(FTVL, "CHAR")
field(NELM, "1024")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
record(waveform,"$(P)Plugin-Scope${INDEX}-TriggSource"){
field(DESC, "Trigger source name")
field(PINI, "1")
field(DTYP, "asynInt8ArrayIn")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt8ArrayIn/plugin.scope${INDEX}.trigg?")
field(FTVL, "CHAR")
field(NELM, "1024")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
record(waveform,"$(P)Plugin-Scope${INDEX}-NextTimeSource"){
field(DESC, "Trigger source name")
field(PINI, "1")
field(DTYP, "asynInt8ArrayIn")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt8ArrayIn/plugin.scope${INDEX}.nexttime?")
field(FTVL, "CHAR")
field(NELM, "1024")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
# result
record(waveform,"$(P)Plugin-Scope${INDEX}-Data-Act"){
info(asyn:FIFO, "1000")
field(DESC, "Result data")
field(PINI, "1")
field(DTYP, "${RESULT_DTYP}")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=-1)/TYPE=${RESULT_DTYP}/plugin.scope${INDEX}.resultdata?")
field(FTVL, "${RESULT_FTVL}")
field(NELM, "${RESULT_NELM}")
field(SCAN, "I/O Intr")
field(TSE, "0")
}
record(bo,"$(P)Plugin-Scope${INDEX}-Enable"){
field(DESC, "FFT Enable")
field(DTYP,"asynInt32")
field(OUT, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.scope${INDEX}.enable=")
field(ZNAM,"FALSE")
field(ONAM,"TRUE")
field(DOL, "0")
field(VAL, "0")
}
record(ai,"$(P)Plugin-Scope${INDEX}-MissTriggCntAct"){
field(PINI, "1")
field(DESC, "Missed trigger counter")
field(DTYP,"asynInt32")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.scope${INDEX}.missed?")
field(SCAN, "I/O Intr")
}
record(ai,"$(P)Plugin-Scope${INDEX}-ScanToTriggSamples"){
field(PINI, "1")
field(DESC, "Samples between now and trigger []")
field(DTYP,"asynFloat64")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynFloat64/plugin.scope${INDEX}.scantotrigg?")
field(SCAN, "I/O Intr")
}
record(ai,"$(P)Plugin-Scope${INDEX}-TriggCntAct"){
field(PINI, "1")
field(DESC, "Trigger counter")
field(DTYP,"asynInt32")
field(INP, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.scope${INDEX}.count?")
field(SCAN, "I/O Intr")
}
#record(bo,"$(P)Plugin-Scope${INDEX}-Trigg"){
# field(DESC, "FFT Trigg measurement")
# field(DTYP,"asynInt32")
# field(OUT, "@asyn(${PORT},$(ADDR=0),$(TIMEOUT=1000))T_SMP_MS=$(T_SMP_MS=1000)/TYPE=asynInt32/plugin.scope${INDEX}.trigg")
# field(ZNAM,"FALSE")
# field(ONAM,"TRUE")
# field(DOL, "0")
# field(VAL, "0")
#}
View File
+158
View File
@@ -0,0 +1,158 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcPluginScope.cpp
*
* Created on: Sept 21, 2020
* 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 "ecmcScopeDefs.h"
#include "ecmcScopeWrap.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 scopeConstruct(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 createScope(configStr);
}
/** Optional function.
* Will be called once at unload.
**/
void scopeDestruct(void)
{
deleteAllScopes();
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 scopeRealtime(int ecmcError)
{
lastEcmcError = ecmcError;
return executeScopes();
}
/** 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 scopeEnterRT(){
return linkDataToScopes();
}
/** Optional function.
* Will be called once just before leaving realtime mode
* Return value other than 0 will be considered error.
**/
int scopeExitRT(void){
return 0;
}
// // Plc function for clear of buffers
// double scope_clear(double index) {
// return (double)clearScope((int)index);
// }
// Plc function for enable
double scope_enable(double index, double enable) {
return (double)enableScope((int)index, (int)enable);
}
// // Plc function for trigg new measurement (will clear buffers)
// double scope_trigg(double index) {
// return (double)triggScope((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_Scope",
// Description
.desc = "Scope plugin for use with ecmc.",
// Option description
.optionDesc = "\n "ECMC_PLUGIN_DBG_PRINT_OPTION_CMD"<1/0> : Enables/disables printouts from plugin, default = disabled.\n"
" "ECMC_PLUGIN_SOURCE_OPTION_CMD"<source> : Ec source variable (example: ec0.s1.mm.CH1_ARRAY).\n"
" "ECMC_PLUGIN_RESULT_ELEMENTS_OPTION_CMD"<Result buffer size> : Data points to collect, default = 4096.\n"
" "ECMC_PLUGIN_SOURCE_NEXTTIME_OPTION_CMD"<nexttime> : Ec next sync time for source (example: ec0.s1.NEXTTIME)\n"
" "ECMC_PLUGIN_TRIGG_OPTION_CMD"<trigger> : Ec trigg time (example: ec0.s2.LATCH_POS).\n"
" "ECMC_PLUGIN_ENABLE_OPTION_CMD"<1/0> : Enable data acq, defaults to enabled.\n"
,
// Plugin version
.version = ECMC_EXAMPLE_PLUGIN_VERSION,
// Optional construct func, called once at load. NULL if not definded.
.constructFnc = scopeConstruct,
// Optional destruct func, called once at unload. NULL if not definded.
.destructFnc = scopeDestruct,
// Optional func that will be called each rt cycle. NULL if not definded.
.realtimeFnc = scopeRealtime,
// Optional func that will be called once just before enter realtime mode
.realtimeEnterFnc = scopeEnterRT,
// Optional func that will be called once just before exit realtime mode
.realtimeExitFnc = scopeExitRT,
// PLC funcs
.funcs[0] =
{ /*----fft_clear----*/
// Function name (this is the name you use in ecmc plc-code)
.funcName = "scope_enable",
// Function description
.funcDesc = "scope_enable(index,enable) : Enable/disaable scope[index].",
/**
* 12 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 = scope_enable,
.funcArg3 = NULL,
.funcArg4 = NULL,
.funcArg5 = NULL,
.funcArg6 = NULL,
.funcArg7 = NULL,
.funcArg8 = NULL,
.funcArg9 = NULL,
.funcArg10 = NULL,
.funcGenericObj = NULL,
},
.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
+131
View File
@@ -0,0 +1,131 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcScope.h
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_SCOPE_H_
#define ECMC_SCOPE_H_
#include <stdexcept>
#include "ecmcDataItem.h"
#include "ecmcAsynPortDriver.h"
#include "ecmcScopeDefs.h"
#include "inttypes.h"
#include <string>
typedef enum {
ECMC_SCOPE_STATE_INVALID, /**Invalid. */
ECMC_SCOPE_STATE_WAIT_TRIGG, /**Waiting for trigger. */
ECMC_SCOPE_STATE_WAIT_NEXT, /**Waiting analog. (trigger newer than next ai time)*/
ECMC_SCOPE_STATE_COLLECT, /**Filling buffer (waiting for data). */
} ecmcScopeState;
class ecmcScope {
public:
/** ecmc Scope class
* This object can throw:
* - bad_alloc
* - invalid_argument
* - runtime_error
* - out_of_range
*/
ecmcScope(int scopeIndex, // index of this object
char* configStr);
~ecmcScope();
// 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 connectToDataSources();
void setEnable(int enable);
//void clearBuffers();
void triggScope();
void execute();
private:
void parseConfigStr(char *configStr);
void addDataToBuffer(double data);
bool sourceDataTypeSupported(ecmcEcDataType dt);
void initAsyn();
int64_t timeDiff();
asynParamType getResultAsynDTFromEcDT(ecmcEcDataType ecDT);
void setWaitForNextTrigg();
uint8_t* resultDataBuffer_;
uint8_t* lastScanSourceDataBuffer_;
size_t resultDataBufferBytes_;
size_t bytesInResultBuffer_;
ecmcDataItem *sourceDataItem_;
ecmcDataItemInfo *sourceDataItemInfo_;
ecmcDataItem *sourceDataNexttimeItem_;
ecmcDataItemInfo *sourceDataNexttimeItemInfo_;
ecmcDataItem *sourceTriggItem_;
ecmcDataItemInfo *sourceTriggItemInfo_;
int dataSourceLinked_; // To avoid link several times
int objectId_; // Unique object id
int triggOnce_;
int firstTrigg_;
uint64_t triggTime_;
uint64_t oldTriggTime_;
uint64_t sourceNexttime_;
int64_t sourceSampleRateNS_; // nanoseconds
ecmcScopeState scopeState_;
uint64_t ecmcSmapleTimeNS_;
int64_t sourceElementsPerSample_;
size_t elementsInResultBuffer_;
double samplesSinceLastTrigg_;
// Config options
char* cfgDataSourceStr_; // Config: data source string
char* cfgDataNexttimeStr_; // Config: data source string
char* cfgTriggStr_; // Config: trigg string
int cfgDbgMode_; // Config: allow dbg printouts
size_t cfgBufferElementCount_; // Config: Data set size
int cfgEnable_; // Config: Enable data acq./calc.
int missedTriggs_;
int triggerCounter_;
// Asyn
ecmcAsynDataItem *sourceStrParam_;
ecmcAsynDataItem *triggStrParam_;
ecmcAsynDataItem *enbaleParam_;
ecmcAsynDataItem *resultParam_;
ecmcAsynDataItem *sourceNexttimeStrParam_;
ecmcAsynDataItem *asynMissedTriggs_;
ecmcAsynDataItem *asynTriggerCounter_;
ecmcAsynDataItem *asynTimeTrigg2Sample_;
// 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 std::string to_string(int value);
};
#endif /* ECMC_SCOPE_H_ */
+28
View File
@@ -0,0 +1,28 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcScopeDefs.h
*
* Created on: Sept 21, 2020
* Author: anderssandstrom
* Credits to https://github.com/sgreg/dynamic-loading
*
\*************************************************************************/
#ifndef ECMC_SCOPE_DEFS_H_
#define ECMC_SCOPE_DEFS_H_
// Options
#define ECMC_PLUGIN_DBG_PRINT_OPTION_CMD "DBG_PRINT="
#define ECMC_PLUGIN_SOURCE_OPTION_CMD "SOURCE="
#define ECMC_PLUGIN_SOURCE_NEXTTIME_OPTION_CMD "SOURCE_NEXTTIME="
#define ECMC_PLUGIN_TRIGG_OPTION_CMD "TRIGG="
#define ECMC_PLUGIN_RESULT_ELEMENTS_OPTION_CMD "RESULT_ELEMENTS="
#define ECMC_PLUGIN_ENABLE_OPTION_CMD "ENABLE="
// Default size (must be n²)
#define ECMC_PLUGIN_DEFAULT_BUFFER_SIZE 4096
#endif /* ECMC_SCOPE_DEFS_H_ */
+121
View File
@@ -0,0 +1,121 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcScopeWrap.cpp
*
* Created on: Sept 21, 2020
* 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 "ecmcScopeWrap.h"
#include "ecmcScope.h"
#include "ecmcScopeDefs.h"
#define ECMC_PLUGIN_PORTNAME_PREFIX "PLUGIN.SCOPE"
#define ECMC_PLUGIN_SCOPE_ERROR_CODE 1
static std::vector<ecmcScope*> scopes;
static int scopeObjCounter = 0;
int createScope(char* configStr) {
// create new ecmcFFT object
ecmcScope* scope = NULL;
try {
scope = new ecmcScope(scopeObjCounter, configStr);
}
catch(std::exception& e) {
if(scope) {
delete scope;
}
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_SCOPE_ERROR_CODE;
}
scopes.push_back(scope);
scopeObjCounter++;
return 0;
}
void deleteAllScopes() {
for(std::vector<ecmcScope*>::iterator pscope = scopes.begin(); pscope != scopes.end(); ++pscope) {
if(*pscope) {
delete (*pscope);
}
}
}
int linkDataToScopes() {
for(std::vector<ecmcScope*>::iterator pscope = scopes.begin(); pscope != scopes.end(); ++pscope) {
if(*pscope) {
try {
(*pscope)->connectToDataSources();
}
catch(std::exception& e) {
printf("Exception: %s. Plugin will unload.\n",e.what());
return ECMC_PLUGIN_SCOPE_ERROR_CODE;
}
}
}
return 0;
}
int enableScope(int scopeIndex, int enable) {
try {
scopes.at(scopeIndex)->setEnable(enable);
}
catch(std::exception& e) {
printf("Exception: %s. Scope index out of range.\n",e.what());
return ECMC_PLUGIN_SCOPE_ERROR_CODE;
}
return 0;
}
// int clearScope(int scopeIndex) {
// try {
// scopes.at(scopeIndex)->clearBuffers();
// }
// catch(std::exception& e) {
// printf("Exception: %s. Scope index out of range.\n",e.what());
// return ECMC_PLUGIN_SCOPE_ERROR_CODE;
// }
// return 0;
// }
int triggScope(int scopeIndex) {
try {
scopes.at(scopeIndex)->triggScope();
}
catch(std::exception& e) {
printf("Exception: %s. Scope index out of range.\n",e.what());
return ECMC_PLUGIN_SCOPE_ERROR_CODE;
}
return 0;
}
int executeScopes() {
try {
for(std::vector<ecmcScope*>::iterator pscope = scopes.begin(); pscope != scopes.end(); ++pscope) {
if(*pscope) {
(*pscope)->execute();
}
}
}
catch(std::exception& e) {
printf("Exception: %s.\n",e.what());
return ECMC_PLUGIN_SCOPE_ERROR_CODE;
}
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
/*************************************************************************\
* Copyright (c) 2019 European Spallation Source ERIC
* ecmc is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*
* ecmcScopeWrap.h
*
* Created on: Sept 21, 2020
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_SCOPE_WRAP_H_
#define ECMC_SCOPE_WRAP_H_
#include "ecmcScopeDefs.h"
# ifdef __cplusplus
extern "C" {
# endif // ifdef __cplusplus
int createScope(char *configStr);
int enableScope(int scopeIndex, int enable);
//int clearScope(int scopeIndex);
int triggScope(int scopeIndex);
int executeScopes();
/** \brief Link data to _all_ scope objects
*
* This tells the Scope 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 linkDataToScopes();
/** \brief Deletes all created scope objects\n
*
* Should be called when destructs.\n
*/
void deleteAllScopes();
# ifdef __cplusplus
}
# endif // ifdef __cplusplus
#endif /* ECMC_SCOPE_WRAP_H_ */