/*************************************************************************\ * Copyright (c) 2024 PSI * ecmc is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * ecmcDAQChannelItem.h * * Created on: Mar 01, 2024 * Author: anders sandstrom * \*************************************************************************/ #ifndef ECMC_DAQ_CHANNEL_ITEM_H_ #define ECMC_DAQ_CHANNEL_ITEM_H_ #include #include #include #include "string.h" #include "ecmcDataItem.h" #include "ecmcPluginClient.h" #include "ecmcDAQDefs.h" enum class ecmcDAQDataFormat { raw = 0, time_micro_s = 1, time_micro_s_minus_period = 2, time_ns_minus_period = 3 }; /* Class to store one data item * name : ecmc parameter name, example ec0.s1.positionActual01, ax1.enc.posact, ... * format : Format the data in these ways: * 0=raw : Take raw value (do not apply special format) * 1=time_micro_s : Time: Recalc 64bit nano seconds to 32 bit micro second counter * 2=time_micro_s_minus_period : Time: Recalc 64bit nano seconds to 32 bit micro second counter minus one ec-period. * Useful for oversampling slaves where normally the nextsync time is available. * The calculated time then would correspond to the first data in the array recived. * 3=time_ns_minus_period : Time: Raw value minus one period. * sendOneCycleOldData : Send data from previous cycle (or for arrays elements are shifted one index). To be used by Edwin to trigger on timechange for incremntal timestamped cards.. */ class ecmcDAQChannelItem { public: ecmcDAQChannelItem(const char* name, ecmcDAQDataFormat format, int sendOneCycleOldData) { dataItem_ = NULL; dataItemInfo_ = NULL; name_ = name; cstrName_ = strdup(name); format_ = format; // micro s in int32 sendOneCycleOldData_ = sendOneCycleOldData; // Will only work for scalars (if arrays the elements are shifted once) sampleTimeCompensation_ = 0; dataIndexToReturn_ = 0; dataElementCount_ = 0; bytesPerElement_ = 0; int8Ptr_ = NULL; uint8Ptr_ = NULL; int16Ptr_ = NULL; uint16Ptr_ = NULL; int32Ptr_ = NULL; uint32Ptr_ = NULL; int64Ptr_ = NULL; uint64Ptr_ = NULL; float32Ptr_ = NULL; float64Ptr_ = NULL; dataType_ = ECMC_EC_NONE; dataOld_ = 0; printf("ecmcDAQChannelItem: Created new item %s, format %d\n",name,(int)format); } ~ecmcDAQChannelItem() { free(cstrName_); } void connectToSource() { // Get data item dataItem_ = (ecmcDataItem*) getEcmcDataItem(cstrName_); if(!dataItem_) { printf("ERROR: DataItem %s NULL.\n", name_.c_str()); throw std::runtime_error( "ERROR: DataItem NULL." ); } dataItemInfo_ = dataItem_->getDataItemInfo(); if(!dataItemInfo_) { printf("ERROR: DataItemInfo %s NULL.\n", name_.c_str()); throw std::runtime_error( "ERROR: DataItemInfo NULL." ); } bytesPerElement_ = dataItemInfo_->dataElementSize; dataElementCount_ = dataItemInfo_->dataSize / bytesPerElement_; dataType_= dataItemInfo_->dataType; // Execute here to be sure that startup.cmd of ecmc(cfg) has been executed. switch(format_) { case ecmcDAQDataFormat::time_micro_s_minus_period: sampleTimeCompensation_ = getEcmcSampleTimeMS()*1000; break; case ecmcDAQDataFormat::time_ns_minus_period: sampleTimeCompensation_ = getEcmcSampleTimeMS()*1000000; break; default: sampleTimeCompensation_ = 0; break; } printf("Connected to data item %s (elements %zu)\n",cstrName_,dataElementCount_); } void resetIndex(int reset) { if(reset) { dataIndexToReturn_ = 0; } } /* Return data: * looping with false will return new data for each call untill theres no more data * before call, check if data is available with notEmpty() */ double getData() { double data = 0; uint64_t time = 0; if(dataIndexToReturn_ >= dataElementCount_) { printf("ecmcDAQChannelItem::ERROR: Try to read outside data buffer for data item %s (elements %zu)\n", name_.c_str(),dataElementCount_); return -1; } switch (dataType_) { case ECMC_EC_B1: uint8Ptr_ = (uint8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)(*uint8Ptr_ & 0x01); break; case ECMC_EC_B2: uint8Ptr_ = (uint8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)(*uint8Ptr_ & 0x03); break; case ECMC_EC_B3: uint8Ptr_ = (uint8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)(*uint8Ptr_ & 0x07); break; case ECMC_EC_B4: uint8Ptr_ = (uint8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)(*uint8Ptr_ & 0x0F); break; case ECMC_EC_U8: uint8Ptr_ = (uint8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *uint8Ptr_; data = (double)*uint8Ptr_; break; case ECMC_EC_S8: int8Ptr_ = (int8_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *int8Ptr_; data = (double)*int8Ptr_; break; case ECMC_EC_U16: uint16Ptr_ = (uint16_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *uint16Ptr_; data = (double)*uint16Ptr_; break; case ECMC_EC_S16: int16Ptr_ = (int16_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *int16Ptr_; data = (double)*int16Ptr_; break; case ECMC_EC_U32: uint32Ptr_ = (uint32_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *uint32Ptr_; data = (double)*uint32Ptr_; break; case ECMC_EC_S32: int32Ptr_ = (int32_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *int32Ptr_; data = (double)*int32Ptr_; break; case ECMC_EC_U64: uint64Ptr_ = (uint64_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *uint64Ptr_; data = (double)*uint64Ptr_; break; case ECMC_EC_S64: int64Ptr_ = (int64_t *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; time = *uint64Ptr_; data = (double)*int64Ptr_; break; case ECMC_EC_F32: float32Ptr_ = (float *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)*float32Ptr_; break; case ECMC_EC_F64: float64Ptr_ = (double *)&dataItemInfo_->data[dataIndexToReturn_ * bytesPerElement_]; data = (double)*float64Ptr_; break; default: data = 0; return ERROR_DAQ_MEM_INVALID_DATA_TYPE; break; } dataIndexToReturn_++; double freshData = formatData(data,time); double dataToSend = 0; if(sendOneCycleOldData_) { dataToSend = dataOld_; } else { dataToSend = freshData; } // Store the fresh data here dataOld_ = freshData; return dataToSend; } double formatData(double data, uint64_t time){ // Time format only works for integer values, otherwise a 0 will be returned switch(format_) { case ecmcDAQDataFormat::time_micro_s: time = time / 1000; // micro seconds data = (double)(time & 0xFFFFFFFF); //keep 32bits break; case ecmcDAQDataFormat::time_micro_s_minus_period: time = time / 1000; // micro seconds data = (double)(time & 0xFFFFFFFF); //keep 32bits data = data - sampleTimeCompensation_; break; case ecmcDAQDataFormat::time_ns_minus_period: data = (double)(time - (uint64_t)sampleTimeCompensation_); break; default: ; break; } //printf("DATA %s = %lf\n",cstrName_, data); return data; } /* Returns true if all data have been retruned with getData(). */ bool empty(){ return dataIndexToReturn_ >= dataElementCount_; } size_t getDataElementCount(){ return dataElementCount_; } int validate() { if(dataElementCount_==0) { throw std::runtime_error("Error: DAQ-data item element count 0"); } return 0; } ecmcDataItem* dataItem_; ecmcDataItemInfo* dataItemInfo_; std::string name_; char *cstrName_; ecmcDAQDataFormat format_; double sampleTimeCompensation_; size_t dataIndexToReturn_; size_t dataElementCount_; size_t bytesPerElement_; ecmcEcDataType dataType_; int8_t *int8Ptr_; uint8_t *uint8Ptr_; int16_t *int16Ptr_; uint16_t *uint16Ptr_; int32_t *int32Ptr_; uint32_t *uint32Ptr_; int64_t *int64Ptr_; uint64_t *uint64Ptr_; float *float32Ptr_; double *float64Ptr_; int sendOneCycleOldData_; double dataOld_; }; #endif /* ECMC_DAQ_CHANNEL_ITEM_H_ */