268 lines
7.9 KiB
C++
268 lines
7.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.
|
|
*
|
|
* ecmcCANOpenDevice.cpp
|
|
*
|
|
* Created on: Mar 08, 2021
|
|
* Author: anderssandstrom
|
|
*
|
|
\*************************************************************************/
|
|
|
|
// Needed to get headers in ecmc right...
|
|
#define ECMC_IS_PLUGIN
|
|
|
|
#include <sstream>
|
|
#include "ecmcCANOpenDevice.h"
|
|
#include "ecmcAsynPortDriver.h"
|
|
#include "ecmcPluginClient.h"
|
|
|
|
/**
|
|
* ecmc ecmcCANOpenDevice class
|
|
*/
|
|
ecmcCANOpenDevice::ecmcCANOpenDevice(ecmcSocketCANWriteBuffer* writeBuffer,
|
|
uint32_t nodeId, // 0x580 + CobId
|
|
int exeSampleTimeMs,
|
|
const char* name,
|
|
int heartTimeoutMs,
|
|
int dbgMode) {
|
|
|
|
writeBuffer_ = writeBuffer;
|
|
nodeId_ = nodeId;
|
|
exeSampleTimeMs_ = exeSampleTimeMs;
|
|
exeCounter_ = 0;
|
|
errorCode_ = 0;
|
|
heartTimeoutMs_ = heartTimeoutMs;
|
|
dbgMode_ = dbgMode;
|
|
name_ = strdup(name);
|
|
isMaster_ = false;
|
|
nmtState_ = NMT_NOT_VALID;
|
|
nmtStateOld_ = NMT_NOT_VALID;
|
|
nmtActParam_ = NULL;
|
|
heartBeatCounter_ = 0;
|
|
pdoCounter_ = 0;
|
|
sdoCounter_ = 0;
|
|
sdo1Lock_.test_and_set(); // make sure only one sdo is accessing the bus at the same time
|
|
sdo1Lock_.clear();
|
|
for(int i = 0 ; i<ECMC_CAN_DEVICE_PDO_MAX_COUNT;i++) {
|
|
pdos_[i] = NULL;
|
|
}
|
|
for(int i = 0 ; i<ECMC_CAN_DEVICE_SDO_MAX_COUNT;i++) {
|
|
sdos_[i] = NULL;
|
|
}
|
|
initAsyn();
|
|
}
|
|
|
|
ecmcCANOpenDevice::~ecmcCANOpenDevice() {
|
|
for(int i = 0 ; i<ECMC_CAN_DEVICE_PDO_MAX_COUNT;i++) {
|
|
delete pdos_[i];
|
|
}
|
|
for(int i = 0 ; i<ECMC_CAN_DEVICE_SDO_MAX_COUNT;i++) {
|
|
delete sdos_[i];
|
|
}
|
|
|
|
free(name_);
|
|
}
|
|
|
|
void ecmcCANOpenDevice::execute() {
|
|
|
|
exeCounter_++;
|
|
for(int i=0 ; i<pdoCounter_; i++) {
|
|
if(pdos_[i]) {
|
|
pdos_[i]->execute();
|
|
}
|
|
}
|
|
|
|
for(int i=0 ; i<sdoCounter_; i++) {
|
|
if(sdos_[i]) {
|
|
sdos_[i]->execute();
|
|
}
|
|
}
|
|
|
|
// NMT hearbeat timout
|
|
if (heartBeatCounter_ * exeSampleTimeMs_ >= heartTimeoutMs_) {
|
|
nmtStateOld_ = nmtState_;
|
|
nmtState_ = NMT_NOT_VALID;
|
|
nmtActParam_->refreshParam(1);
|
|
}
|
|
else {
|
|
heartBeatCounter_ ++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// new rx frame recived!
|
|
void ecmcCANOpenDevice::newRxFrame(can_frame *frame) {
|
|
|
|
// only validate if not master
|
|
if (!validateFrame(frame) && !isMaster_) {
|
|
return;
|
|
}
|
|
|
|
// forward to pdos
|
|
for(int i=0 ; i<pdoCounter_; i++) {
|
|
if(pdos_[i]) {
|
|
pdos_[i]->newRxFrame(frame);
|
|
}
|
|
}
|
|
|
|
// forward to sdos
|
|
for(int i=0 ; i<sdoCounter_; i++) {
|
|
if(sdos_[i]) {
|
|
sdos_[i]->newRxFrame(frame);
|
|
}
|
|
}
|
|
|
|
// NMT
|
|
if(!isMaster_ && nmtActParam_) {
|
|
checkNMT(frame);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// r 0x183 [8] 0x00 0x00 0x00 0x00 0x0B 0x40 0x04 0x20
|
|
int ecmcCANOpenDevice::validateFrame(can_frame *frame) {
|
|
|
|
// nodeid is always lower 7bits.. Need to check this calc.. byte order?!
|
|
uint8_t tempNodeId = frame->can_id & 0x7F;
|
|
|
|
if(tempNodeId != nodeId_) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ecmcCANOpenDevice::checkNMT(can_frame *frame) {
|
|
|
|
// check if NMT frame
|
|
if(frame->can_id == (ECMC_CANOPEN_NMT_BASE + nodeId_)) {
|
|
if(frame->can_dlc == 1){
|
|
switch(frame->data[0]) {
|
|
case ECMC_CANOPEN_NMT_BOOT:
|
|
nmtState_ = NMT_BOOT_UP;
|
|
break;
|
|
case ECMC_CANOPEN_NMT_STOP:
|
|
nmtState_ = NMT_STOPPED;
|
|
break;
|
|
case ECMC_CANOPEN_NMT_OP:
|
|
nmtState_ = NMT_OP;
|
|
break;
|
|
case ECMC_CANOPEN_NMT_PREOP:
|
|
nmtState_ = NMT_BOOT_UP;
|
|
break;
|
|
default:
|
|
nmtState_ = NMT_NOT_VALID;
|
|
break;
|
|
}
|
|
}
|
|
heartBeatCounter_ = 0;
|
|
if(nmtState_ != nmtStateOld_) {
|
|
nmtActParam_->refreshParam(1);
|
|
}
|
|
nmtStateOld_ = nmtState_;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ecmcCANOpenDevice::addPDO(uint32_t cobId,
|
|
ecmc_can_direction rw,
|
|
uint32_t ODSize,
|
|
int readTimeoutMs,
|
|
int writeCycleMs, //if <0 then write on demand..
|
|
const char* name) {
|
|
|
|
if(pdoCounter_>= ECMC_CAN_DEVICE_PDO_MAX_COUNT) {
|
|
return ECMC_CAN_PDO_INDEX_OUT_OF_RANGE;
|
|
}
|
|
|
|
pdos_[pdoCounter_] = new ecmcCANOpenPDO(writeBuffer_,
|
|
nodeId_,
|
|
cobId,
|
|
rw,
|
|
ODSize,
|
|
readTimeoutMs,
|
|
writeCycleMs,
|
|
exeSampleTimeMs_,
|
|
name,
|
|
dbgMode_);
|
|
pdoCounter_++;
|
|
return 0;
|
|
}
|
|
|
|
int ecmcCANOpenDevice::addSDO(uint32_t cobIdTx, // 0x580 + CobId
|
|
uint32_t cobIdRx, // 0x600 + Cobid
|
|
ecmc_can_direction rw,
|
|
uint16_t ODIndex, // Object dictionary index
|
|
uint8_t ODSubIndex, // Object dictionary subindex
|
|
uint32_t ODSize,
|
|
int readSampleTimeMs,
|
|
const char* name) {
|
|
|
|
if(sdoCounter_>= ECMC_CAN_DEVICE_SDO_MAX_COUNT) {
|
|
return ECMC_CAN_SDO_INDEX_OUT_OF_RANGE;
|
|
}
|
|
|
|
sdos_[sdoCounter_] = new ecmcCANOpenSDO(writeBuffer_,
|
|
nodeId_,
|
|
cobIdTx,
|
|
cobIdRx,
|
|
rw,
|
|
ODIndex,
|
|
ODSubIndex,
|
|
ODSize,
|
|
readSampleTimeMs,
|
|
exeSampleTimeMs_,
|
|
name,
|
|
&sdo1Lock_,
|
|
sdoCounter_,
|
|
dbgMode_);
|
|
sdoCounter_++;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t ecmcCANOpenDevice::getNodeId() {
|
|
return nodeId_;
|
|
}
|
|
|
|
void ecmcCANOpenDevice::initAsyn() {
|
|
|
|
ecmcAsynPortDriver *ecmcAsynPort = (ecmcAsynPortDriver *)getEcmcAsynPortDriver();
|
|
if(!ecmcAsynPort) {
|
|
printf("ERROR: ecmcAsynPort NULL.");
|
|
throw std::runtime_error( "ERROR: ecmcAsynPort NULL." );
|
|
}
|
|
|
|
// Add resultdata "plugin.can.dev%d.<name>"
|
|
std::string paramName = ECMC_PLUGIN_ASYN_PREFIX + std::string(".dev") +
|
|
to_string(nodeId_) + ".nmtstate";
|
|
|
|
nmtActParam_ = ecmcAsynPort->addNewAvailParam(
|
|
paramName.c_str(), // name
|
|
asynParamInt32, // asyn type
|
|
(uint8_t*)&nmtState_, // pointer to data
|
|
sizeof(nmtState_), // size of data
|
|
ECMC_EC_S32, // ecmc data type
|
|
0); // die if fail
|
|
|
|
if(!nmtActParam_) {
|
|
printf("ERROR: Failed create asyn param for NMT state.");
|
|
throw std::runtime_error( "ERROR: Failed create asyn param for data: " + paramName);
|
|
}
|
|
|
|
nmtActParam_->addSupportedAsynType(asynParamUInt32Digital);
|
|
|
|
nmtActParam_->refreshParam(1); // read once into asyn param lib
|
|
ecmcAsynPort->callParamCallbacks(ECMC_ASYN_DEFAULT_LIST, ECMC_ASYN_DEFAULT_ADDR);
|
|
}
|
|
// Avoid issues with std:to_string()
|
|
std::string ecmcCANOpenDevice::to_string(int value) {
|
|
std::ostringstream os;
|
|
os << value;
|
|
return os.str();
|
|
}
|