Files
ecmc_plugin_motion/src/ecmcDataBuffer.h
2024-04-30 10:00:54 +02:00

302 lines
8.9 KiB
C++

/*************************************************************************\
* 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.
*
* ecmcDataBuffer.h
*
* Created on: Mar 22, 2020
* Author: anderssandstrom
*
\*************************************************************************/
#ifndef ECMC_DATA_BUFFER_WRITE_H_
#define ECMC_DATA_BUFFER_WRITE_H_
#include "ecmcAsynPortDriver.h"
#include <stdexcept>
#include "inttypes.h"
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "asynPortDriver.h"
#include "epicsMutex.h"
#include "ecmcMotionPlgDefs.h"
template <typename T>
struct dataBuffer {
T *data;
int dataCounter;
int id;
};
/*
Implements a data buffer with asyn callbacks.
The buffer uses two memory areas. When one mem area is full,
writes are redirected to the other buffer. The full buffer then
can be transferred to epics.
*/
template <typename T>
class ecmcDataBuffer {
public:
/** ecmc ecmcDataBuffer class
* This object can throw:
* - bad_alloc
* - invalid_argument
* - runtime_error
* - out_of_range
*/
ecmcDataBuffer(int objIndex,
int bufferIndex,
int bufferSize,
int dbgMode,
const char* asynSuffix,
asynPortDriver *asynPort) {
// prepare data buffers
bufferSize_ = bufferSize;
buffer1_.data = new T[bufferSize_];
buffer2_.data = new T[bufferSize_];
memset(&buffer1_.data[0],0,sizeof(T)*bufferSize_);
memset(&buffer2_.data[0],0,sizeof(T)*bufferSize_);
buffer1_.dataCounter = 0;
buffer1_.id = 1;
buffer2_.dataCounter = 0;
buffer2_.id = 2;
// add data to buffer
bufferAdd_ = &buffer1_;
// Buffer ready for writing to epics
bufferWrite_ = &buffer2_;
cfgDbgMode_ = dbgMode;
destructs_ = 0;
bufferSwitchMutex_ = epicsMutexCreate();
bufferIndex_ = bufferIndex;
objIndex_ = objIndex;
asynPort_ = asynPort;
asynSuffix_ = strdup(asynSuffix);
asynParamId_ = -1;
enable_ = true;
initAsyn();
}
~ecmcDataBuffer() {
delete[] buffer1_.data;
delete[] buffer2_.data;
free(asynSuffix_);
destructs_ = 1; // maybe need todo in other way..
}
void addData(T data) {
// Switch buffer if needed
if(bufferAdd_->dataCounter >= bufferSize_) {
switchBuffer();
}
epicsMutexLock(bufferSwitchMutex_);
bufferAdd_->data[bufferAdd_->dataCounter] = data;
bufferAdd_->dataCounter++;
epicsMutexUnlock(bufferSwitchMutex_);
}
void clear() {
//Fill data buffer
memset(&buffer1_.data[0],0,sizeof(T)*bufferSize_);
memset(&buffer2_.data[0],0,sizeof(T)*bufferSize_);
buffer1_.dataCounter = 0;
buffer2_.dataCounter = 0;
}
void setEnable(bool enable) {
enable_ = enable;
}
bool getBufferFull() {
return bufferAdd_->dataCounter == bufferSize_;
}
void switchBuffer(){
// ensure safe buffer switch
epicsMutexLock(bufferSwitchMutex_);
dataBuffer<T> *temp = bufferWrite_;
bufferWrite_ = bufferAdd_;
bufferAdd_ = temp;
// start from 0 in new buffer
bufferAdd_->dataCounter = 0;
epicsMutexUnlock(bufferSwitchMutex_);
}
void writeBuffer();
void initAsyn();
private:
static std::string to_string(int value){
std::ostringstream os;
os << value;
return os.str();
}
int destructs_;
int cfgDbgMode_;
int bufferSize_;
int bufferIndex_;
int objIndex_;
epicsMutexId bufferSwitchMutex_;
dataBuffer<T> buffer1_;
dataBuffer<T> buffer2_;
dataBuffer<T> *bufferAdd_;
dataBuffer<T> *bufferWrite_;
asynPortDriver *asynPort_;
char *asynSuffix_;
int asynParamId_;
bool enable_;
};
// specialized for epicsInt8
template<> inline void ecmcDataBuffer<epicsInt8>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksInt8Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
// specialized for epicsInt16
template<> inline void ecmcDataBuffer<epicsInt16>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksInt16Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
// specialized for epicsInt32
template<> inline void ecmcDataBuffer<epicsInt32>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksInt32Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
// specialized for epicsInt64
template<> inline void ecmcDataBuffer<epicsInt64>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksInt64Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
// specialized for epicsFloat32
template<> inline void ecmcDataBuffer<epicsFloat32>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksFloat32Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
// specialized for epicsFloat64
template<> inline void ecmcDataBuffer<epicsFloat64>::writeBuffer() {
epicsMutexLock(bufferSwitchMutex_);
// Write asyn
asynPort_->lock();
/*asynStatus stat=*/ asynPort_->doCallbacksFloat64Array(&bufferWrite_->data[0], bufferSize_, asynParamId_,0);
asynPort_->unlock();
epicsMutexUnlock(bufferSwitchMutex_);
}
template<> inline void ecmcDataBuffer<epicsInt8>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamInt8Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
template<> inline void ecmcDataBuffer<epicsInt16>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamInt16Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
template<> inline void ecmcDataBuffer<epicsInt32>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamInt32Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
template<> inline void ecmcDataBuffer<epicsInt64>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamInt64Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
template<> inline void ecmcDataBuffer<epicsFloat32>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamFloat32Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
template<> inline void ecmcDataBuffer<epicsFloat64>::initAsyn() {
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX "_" + to_string(objIndex_) +
"." + std::string(asynSuffix_);
if( asynPort_->createParam(0, paramName.c_str(), asynParamFloat64Array, &asynParamId_ ) != asynSuccess ) {
throw std::runtime_error("Failed create asyn parameter rawdata");
}
// use buffer 1 for first callback
writeBuffer();
}
#endif /* ECMC_DATA_BUFFER_WRITE_H_ */