/*************************************************************************\ * 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. * * ecmcCANOpenPDO.cpp * * Created on: Mar 22, 2020 * Author: anderssandstrom * \*************************************************************************/ // Needed to get headers in ecmc right... #define ECMC_IS_PLUGIN #include #include "ecmcCANOpenPDO.h" #include "ecmcAsynPortDriver.h" #include "ecmcPluginClient.h" // Calback function to init write from asyn asynStatus asynWritePDOValue(void* data, size_t bytes, asynParamType asynParType,void *userObj) { // userobj = NULL if(!userObj) { printf("Error: asynWritePDOValue() fail, no user obj defined.\n"); return asynError; } ecmcCANOpenPDO* pdo = (ecmcCANOpenPDO*)userObj; int bytesToCp = bytes; if (bytes > 8) { bytesToCp = 8; } uint64_t tempData = 0; memcpy(&tempData,data,bytesToCp); pdo->setValue(tempData); pdo->writeValue(); return asynSuccess; } /** * ecmc ecmcCANOpenPDO class */ ecmcCANOpenPDO::ecmcCANOpenPDO(ecmcSocketCANWriteBuffer* writeBuffer, uint32_t nodeId, uint32_t cobId, // 0x580 + CobId ecmc_can_direction rw, uint32_t ODSize, int readTimeoutMs, int writeCycleMs, int exeSampleTimeMs, const char* name, int dbgMode) { writeBuffer_ = writeBuffer; nodeId_ = nodeId; cobId_ = cobId; ODSize_ = ODSize; name_ = strdup(name); if(ODSize_ > 8) { ODSize_ = 8; } readTimeoutMs_ = readTimeoutMs; writeCycleMs_ = writeCycleMs; exeSampleTimeMs_ = exeSampleTimeMs; rw_ = rw; exeCounter_ = 0; busy_ = 0; errorCode_ = 0; dataBuffer_ = new uint8_t[ODSize_]; memset(dataBuffer_,0,ODSize_); dbgMode_ = dbgMode; refreshNeeded_ = 0; writeFrame_.can_id = cobId_; writeFrame_.can_dlc = ODSize; // data length writeFrame_.data[0] = 0; // request read cmd writeFrame_.data[1] = 0; writeFrame_.data[2] = 0; writeFrame_.data[3] = 0; writeFrame_.data[4] = 0; writeFrame_.data[5] = 0; writeFrame_.data[6] = 0; writeFrame_.data[7] = 0; dataMutex_ = epicsMutexCreate(); initAsyn(); } ecmcCANOpenPDO::~ecmcCANOpenPDO() { delete[] dataBuffer_; free(name_); } void ecmcCANOpenPDO::execute() { exeCounter_++; if(rw_ == DIR_READ) { if(exeCounter_* exeSampleTimeMs_ >= readTimeoutMs_) { errorCode_ = ECMC_CAN_ERROR_PDO_TIMEOUT; if(dbgMode_) { printf("ECMC_CAN_ERROR_PDO_TIMEOUT (0x%x)\n",errorCode_); } refreshNeeded_ = 1; exeCounter_ = 0; } } else { //DIR_WRITE if(writeCycleMs_<=0) { // Only write on demand if cycle is less than 0 exeCounter_ = 0; return; } if(exeCounter_* exeSampleTimeMs_ >= writeCycleMs_) { writeValue(); // write in defined cycle exeCounter_ = 0; } } // Refresh in sync with ecmc refreshAsynParams(); } // new rx frame recived! void ecmcCANOpenPDO::newRxFrame(can_frame *frame) { // Wait for: if(rw_ == DIR_READ) { if(validateFrame(frame)) { epicsMutexLock(dataMutex_); memset(dataBuffer_,0,ODSize_); memcpy(dataBuffer_, &(frame->data[0]),frame->can_dlc); epicsMutexUnlock(dataMutex_); refreshNeeded_ = 1; errorCode_ = 0; refreshNeeded_ = 1; if(dbgMode_) { printBuffer(); } } } } void ecmcCANOpenPDO::printBuffer() { if(!dataBuffer_) { return; } for(uint32_t i = 0; i < ODSize_; i = i + 2) { uint16_t test; memcpy(&test,&dataBuffer_[i],2); printf("data[%02d]: %u\n",i/2,test); } } // r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20 int ecmcCANOpenPDO::validateFrame(can_frame *frame) { if(frame->can_id != cobId_) { return 0; } if(frame->can_dlc != ODSize_) { return 0; } return 1; } void ecmcCANOpenPDO::setValue(uint64_t data) { epicsMutexLock(dataMutex_); memcpy(dataBuffer_, &data, ODSize_); epicsMutexUnlock(dataMutex_); } int ecmcCANOpenPDO::writeValue() { if(writeFrame_.can_dlc > 0) { epicsMutexLock(dataMutex_); memcpy(&(writeFrame_.data[0]), dataBuffer_ ,writeFrame_.can_dlc); epicsMutexUnlock(dataMutex_); } int errorCode = writeBuffer_->addWriteCAN(&writeFrame_); refreshNeeded_ = 1; return errorCode; } void ecmcCANOpenPDO::initAsyn() { ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver(); if(!ecmcAsynPort) { printf("ERROR: ecmcAsynPort NULL."); throw std::runtime_error( "ERROR: ecmcAsynPort NULL." ); } // Add resultdata "plugin.can.dev%d." std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") + to_string(nodeId_) + ".pdo" /*+ to_string(objIndex_) */ + "." + std::string(name_); dataParam_ = ecmcAsynPort->addNewAvailParam( paramName.c_str(), // name asynParamInt8Array, // asyn type dataBuffer_, // pointer to data ODSize_, // size of data ECMC_EC_U8, // ecmc data type 0); // die if fail if(!dataParam_) { printf("ERROR: Failed create asyn param for data."); throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName); } // Allow different types depending on size if(ODSize_>1){ dataParam_->addSupportedAsynType(asynParamInt16Array); } if(ODSize_>3){ dataParam_->addSupportedAsynType(asynParamInt32Array); dataParam_->addSupportedAsynType(asynParamFloat32Array); dataParam_->addSupportedAsynType(asynParamInt32); } if(ODSize_>7){ dataParam_->addSupportedAsynType(asynParamFloat64Array); dataParam_->addSupportedAsynType(asynParamFloat64); } dataParam_->setAllowWriteToEcmc(rw_ == DIR_WRITE); if(rw_ == DIR_WRITE) { dataParam_->setExeCmdFunctPtr(asynWritePDOValue,this); } dataParam_->refreshParam(1); // read once into asyn param lib ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR); // Add resultdata "plugin.can.dev%d.error" paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") + to_string(nodeId_) + ".pdo" + "." + std::string(name_) + std::string(".error"); errorParam_ = ecmcAsynPort->addNewAvailParam( paramName.c_str(), // name asynParamInt32, // asyn type (uint8_t*)&errorCode_, // pointer to data sizeof(errorCode_), // size of data ECMC_EC_U32, // ecmc data type 0); // die if fail if(!errorParam_) { printf("ERROR: Failed create asyn param for data."); throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName); } errorParam_->setAllowWriteToEcmc(false); // need to callback here errorParam_->refreshParam(1); // read once into asyn param lib ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR); } void ecmcCANOpenPDO::refreshAsynParams() { if(refreshNeeded_) { dataParam_->refreshParamRT(1); // read once into asyn param lib errorParam_->refreshParamRT(1); // read once into asyn param lib } refreshNeeded_ = 0; } // Avoid issues with std:to_string() std::string ecmcCANOpenPDO::to_string(int value) { std::ostringstream os; os << value; return os.str(); }