/*************************************************************************\ * Copyright (c) 2024 PSI * ecmc is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * ecmcDAQDataArray.cpp * * Created on: March 1, 2024 * Author: anders sandstrom * Credits to https://github.com/sgreg/dynamic-loading * \*************************************************************************/ // Needed to get headers in ecmc right... #define ECMC_IS_PLUGIN #define ECMC_PLUGIN_ASYN_PREFIX "plugin.daq" #define ECMC_PLUGIN_ASYN_ENABLE "enable" #define ECMC_PLUGIN_ASYN_RAWDATA "data" #include #include "ecmcDAQDataArray.h" #include "ecmcPluginClient.h" ecmcDAQDataArray::ecmcDAQDataArray(const char* name, const 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 */ { channelCounter_ = 0; dataElementCount_ = 0; totalElementCount_ = 0; firstDataElementIndex_ = 0; asynEnableId_ = 1; asynRawDataId_ = -1; enablePlugin_ = -1; dataSourcesLinked_ = 0; name_ = name; printf("Created an array with name = %s, asynPortName = %s\n",name, portName); } ecmcDAQDataArray::~ecmcDAQDataArray() { } void ecmcDAQDataArray::addChannel(int type) { dataChannels_.push_back(new ecmcDAQDataChannel(type)); channelCounter_++; } void ecmcDAQDataArray::addDataItemToChannel(const char* name, int format) { // Always add to last added channel dataChannels_.back()->addDataItem(name, format); } void ecmcDAQDataArray::connectToDataSources() { if( dataSourcesLinked_ ) { return; } dataElementCount_ = 0; for(std::vector::iterator pDataCh = dataChannels_.begin(); pDataCh != dataChannels_.end(); ++pDataCh) { if(!(*pDataCh)) { throw std::runtime_error( "Channel empty.."); } (*pDataCh)->connectToDataSources(); dataElementCount_ = dataElementCount_ + (*pDataCh)->getDataElementCount(); } // Header: First element is time stamp then 4 elements per channel totalElementCount_ = dataElementCount_ + channelCounter_ * 4 + 1; // Now we we can finally allocate teh buffer (ecmc is still not in realtime, enterRT) buffer_ = new double [totalElementCount_]; memset(buffer_,0,totalElementCount_*sizeof(double)); // Build header buildArrayHeader(); // Register asyn parameters initAsyn(); dataSourcesLinked_ = 1; } /* Prepare header first in array, 4 elements per channel: * array[0] = Ec timestamp, updated each cycle, the reset of header is static * Channel 1: * array[1] = Channel type * array[2] = Data start index * array[3] = Data element count * array[4] = spare.. * Channel 2: * array[5] = Channel type * array[6] = Data start index * array[7] = Data element count * array[8] = spare.. *.... */ void ecmcDAQDataArray::buildArrayHeader(){ // 4 elements plus first timestamp to first data element, only first element will change in realtime size_t dataStartOffset = channelCounter_* 4 + 1; firstDataElementIndex_ = dataStartOffset; if( totalElementCount_ < dataStartOffset) { throw std::runtime_error( "Array to small, header will not fit (array size must be bigger than total data size plus 4*data_channel_count+1).."); } size_t index = 0; buffer_[index] = 0; // Timestamp, will be set in each loop in execute index++; // Each Data channel takes 4 doubles for(std::vector::iterator pDataCh = dataChannels_.begin(); pDataCh != dataChannels_.end(); ++pDataCh) { if(!(*pDataCh)) { throw std::runtime_error( "Channel empty.."); } // First element: Type buffer_[index] = (double)(*pDataCh)->getType(); index++; // Second element: Data start index in array buffer_[index] = (double)dataStartOffset; index++; // Third element: Data element count (size) buffer_[index] = (double)(*pDataCh)->getDataElementCount(); // Prepare data start index for next data item dataStartOffset = dataStartOffset + (*pDataCh)->getDataElementCount(); index++; // Forth index spare... buffer_[index] = -1234.0; index++; } } void ecmcDAQDataArray::setEnable(int enable) { enablePlugin_ = enable; } void ecmcDAQDataArray::execute() { if(!enablePlugin_) { return; } int first = 1; size_t index = firstDataElementIndex_ ; for(std::vector::iterator pDataCh = dataChannels_.begin(); pDataCh != dataChannels_.end(); ++pDataCh) { first = 1; for(size_t i = 0 ; i < (*pDataCh)->getDataElementCount(); i++) { buffer_[index]=(*pDataCh)->getData(first); first = 0; } } updateAsyn(); } void ecmcDAQDataArray::updateAsyn() { doCallbacksFloat64Array(buffer_, totalElementCount_, asynRawDataId_,0); setIntegerParam(asynEnableId_, enablePlugin_); // Update integers callParamCallbacks(); } void ecmcDAQDataArray::initAsyn() { // Add enable "plugin.daq..enable" std::string paramName =ECMC_PLUGIN_ASYN_PREFIX "."; paramName += name_; paramName += "."; paramName += ECMC_PLUGIN_ASYN_ENABLE; if( createParam(0, paramName.c_str(), asynParamInt32, &asynEnableId_) != asynSuccess ) { throw std::runtime_error("Failed create asyn parameter enable"); } setIntegerParam(asynEnableId_, enablePlugin_); // Add data "plugin.daq..data" paramName =ECMC_PLUGIN_ASYN_PREFIX "."; paramName += name_; paramName += "."; paramName += ECMC_PLUGIN_ASYN_RAWDATA; if( createParam(0, paramName.c_str(), asynParamFloat64Array, &asynRawDataId_ ) != asynSuccess ) { throw std::runtime_error("Failed create asyn parameter data array"); } doCallbacksFloat64Array(buffer_, totalElementCount_, asynRawDataId_,0); // Update integers callParamCallbacks(); return; } int ecmcDAQDataArray::validate() { if(dataElementCount_==0) { throw std::runtime_error("Error: DAQ-Array element count 0"); } if(channelCounter_==0) { throw std::runtime_error("Error: DAQ-Array channel count 0"); } for(std::vector::iterator pDataCh = dataChannels_.begin(); pDataCh != dataChannels_.end(); ++pDataCh) { if(!(*pDataCh)) { throw std::runtime_error( "Channel empty.."); } (*pDataCh)->validate(); } return 0; } size_t ecmcDAQDataArray::getArraySize() { return totalElementCount_; } std::string ecmcDAQDataArray::getName() { return name_; }