diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e2a5ca..ed02d40 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "files.associations": { "string_view": "cpp", "stdexcept": "cpp", - "iosfwd": "cpp" + "iosfwd": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/Db/ss1.template b/Db/ss1.template index fab1e6f..da1dab2 100644 --- a/Db/ss1.template +++ b/Db/ss1.template @@ -51,4 +51,15 @@ record(bi,"${P}SS1-${NAME}-AxsStndStllAct"){ field(ONAM, "Standstill") field(ZSV, "NO_ALARM") field(OSV, "NO_ALARM") + field(FLNK, "${P}SS1-${NAME}-RedVeloAct.PROC") +} + +# // bit 3 reduce velo active +record(bi,"${P}SS1-${NAME}-RedVeloAct"){ + field(DESC, "SS1-${NAME}: Reduce velo active") + field(INP, "${P}SS1-${NAME}-Stat_.B3") + field(ZNAM, "Not Active") + field(ONAM, "Active") + field(ZSV, "NO_ALARM") + field(OSV, "NO_ALARM") } diff --git a/GNUmakefile b/GNUmakefile index 47aaf94..35e791c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -16,9 +16,8 @@ OPT_CXXFLAGS_YES = -O3 # dependencies ECmasterECMC_VERSION = v1.1.0 -# motorECMC_VERSION = 7.0.7-ESS -#ecmc_VERSION = v9.0.1_RC4 -ecmc_VERSION = 9.1.0 +# ecmc_VERSION = 9.2.0 +ecmc_VERSION = test ################################################################################ # THIS RELATES TO THE EtherCAT MASTER LIBRARY diff --git a/README.md b/README.md index a6410ac..d312c8f 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,12 @@ The configuration is made by two commands: The ecmcAddSS1SafetyGroup() adds a SS1 safety group. The command takes the following parameters: ``` -ecmcAddSS1SafetyGroup(, , ,) - : Name of group. - : Ethercat entry input for rampdown cmd. - : Ethercat entry output for group standstill status. - : Time delay of STO [ms]. +ecmcAddSS1SafetyGroup(, , ,,) + : Name of group + : Ethercat entry input for rampdown cmd + : Ethercat entry output for group standstill status + : Ethercat entry input for activation of maximum velo limitation (set to "empty" to disable) + : Time delay of STO [ms] ``` Each group is interfacing the safety system through the same I/O (ethercat). Axes that needs the SS1 fucntionality can be added to this group with the ecmcAddAxisToSafetyGroup() command. @@ -41,19 +42,21 @@ Axes that needs the SS1 fucntionality can be added to this group with the ecmcAd This command is also wrapped in a snippet addSS1Group.cmd with the follwoing parameters: * NAME : Name of group * EC_RAMP_DOWN : Ethercat input of ramp down signal from safety PLC -* EC_AXES_STANDSTILL : Ethercat output for signaling safetysystem that all axes are at rest. +* EC_AXES_REST : Ethercat entry for signaling that all axes in group are at rest, output from ecmc (feedback to safety PLC/system) +* EC_AXES_MAX_VELO : Ethercat entry for reducing velocity, input to ecmc (command from safety PLC/system) * DELAY_MS : Safety system delay time of STO or removal of power Example: ``` # Create SS1 group epicsEnvSet(EC_RAMP_DOWN,"ec${ECMC_EC_MASTER_ID}.s${BI_SLAVE}.binaryInput08.0") -epicsEnvSet(EC_AXES_STANDSTILL,"ec${ECMC_EC_MASTER_ID}.s${BO_SLAVE}.binaryOutput07.0") +epicsEnvSet(EC_AXES_REST,"ec${ECMC_EC_MASTER_ID}.s${BO_SLAVE}.binaryOutput07.0") epicsEnvSet(SAFETY_TIMEOUT,500) -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addSS1Group.cmd "NAME=first,EC_RAMP_DOWN=${EC_RAMP_DOWN},EC_AXES_STANDSTILL=${EC_AXES_STANDSTILL},DELAY_MS=${SAFETY_TIMEOUT}" +${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addSS1Group.cmd "NAME=first,EC_RAMP_DOWN=${EC_RAMP_DOWN},EC_AXES_REST=${EC_AXES_REST},DELAY_MS=${SAFETY_TIMEOUT}" ``` ## ecmcAddAxisToSafetyGroup() + With the "ecmcAddAxisToSafetyGroup()" command an ecmc axis can be added to a safety group. All axes linked to a certain group will act on command from the safety system (initiation of velocity rampdown). @@ -62,7 +65,8 @@ The ecmcAddAxisToSafetyGroup() command takes the following parameters: ecmcAddAxisToSafetyGroup(, , , ) : Name of safety group to add axis to. : Axis index to add (ecmc axis index). - : Axis standstill velo limit [unit of axis]. + : Axis at rest velo limit [unit of axis]. + : Axis max velo limit [unit of axis]. : NOT USED (for future implemenation). Time for axis to be below velo limit [ms]. ``` Note: The "filter_time" parameter is not used right now. As soon as the axis is below teh velo_limit it will be considered to stand still and will then be disabled. @@ -70,13 +74,14 @@ Note: The "filter_time" parameter is not used right now. As soon as the axis is Note: The plugin checks the trajectory generated velocity setpoint and not the actual velocity. This command is also wrapped in a snippet addAxisToSafetyGroup.cmd with the following parameters: -* NAME : Name of group to add axis to (group must be created first with addSS1Group.cmd) -* AX_ID : ecmc axis index of axis to add -* VELO_LIM : Velocity limit [EGU of axis], default 0. If setpoint and actual velocity is lower or equal than VELO_LIM the axis is considered to be at rest +* NAME : Name of group to add axis to (group must be created first with addSS1Group.cmd) +* AX_ID : ecmc axis index of axis to add +* VELO_REST_LIM : Velocity at rest limit [unit same as EGU of axis] +* VELO_MAX_LIM : Velocity maximum limit, -1 to disable [unit same as EGU of axis] Example: ``` -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd "NAME=first,AX_ID=1,VELO_LIM=1" +${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd "NAME=first,AX_ID=1,VELO_REST_LIM=1,VELO_MAX_LIM=100" ``` # Example of startup script: @@ -84,17 +89,26 @@ ${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd "NAME=first,AX_I ############################################################################## ## Load safety plugin # + require ecmc_plugin_safety sandst_a -# Create SS1 group -epicsEnvSet(RAMP_DOWN_CMD,"ec${ECMC_EC_MASTER_ID}.s${DRV_SLAVE}.ZERO.0") -epicsEnvSet(STANDSTILL_STAT,"ec${ECMC_EC_MASTER_ID}.s${DRV_SLAVE}.ZERO.1") -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addSS1Group.cmd "NAME=first,EC_RAMP_DOWN_CMD=${RAMP_DOWN_CMD},EC_STANDSTILL_STAT=${STANDSTILL_STAT},DELAY_MS=500" -# Add axes to group "first" -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd.cmd "NAME=first,AX_ID=1,VELO_LIM=1" -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd.cmd "NAME=first,AX_ID=2,VELO_LIM=1" -${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd.cmd "NAME=first,AX_ID=3,VELO_LIM=1" +# Create SS1 group +#- EC_RAMP_DOWN : Ethercat entry for ramp down command, input to ecmc (command from safety PLC/system) +#- EC_AXES_REST : Ethercat entry for signaling that all axes in group are at rest, output from ecmc (feedback to safety PLC/system) +#- EC_AXES_MAX_VELO : Ethercat entry for reducing velocity, input to ecmc (command from safety PLC/system) +#- DELAY_MS : Time between rampdown command and STO +epicsEnvSet(EC_RAMP_DOWN,"ec${ECMC_EC_MASTER_ID}.s${BI_SLAVE}.binaryInput08.0") +epicsEnvSet(EC_AXES_REST,"ec${ECMC_EC_MASTER_ID}.s${BO_SLAVE}.binaryOutput07.0") +epicsEnvSet(EC_AXES_MAX_VELO,"ec${ECMC_EC_MASTER_ID}.s${BO_SLAVE}.ONE.0") +epicsEnvSet(SAFETY_TIMEOUT,500) +${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addSS1Group.cmd "NAME=first,EC_RAMP_DOWN=${EC_RAMP_DOWN},EC_AXES_REST=${EC_AXES_REST},${EC_AXES_MAX_VELO},DELAY_MS=${SAFETY_TIMEOUT}" + +#- Add axis +#- AX_ID : Axis ID +#- VELO_REST_LIM : Velocity at rest limit [unit same as EGU of axis] +#- VELO_MAX_LIM : Velocity maximum limit, -1 to disable [unit same as EGU of axis] +${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToSafetyGroup.cmd "NAME=first,AX_ID=1,VELO_REST_LIM=1,VELO_MAX_LIM=100" .... ``` diff --git a/scripts/addAxisToSafetyGroup.cmd b/scripts/addAxisToSafetyGroup.cmd index e6c6ef2..c845745 100644 --- a/scripts/addAxisToSafetyGroup.cmd +++ b/scripts/addAxisToSafetyGroup.cmd @@ -17,14 +17,16 @@ #- Arguments: #- NAME : Name of safety group #- AX_ID : Axis ID -#- VELO_LIM : Velocity standstill limit [unit same as EGU of axis] +#- VELO_REST_LIM : Velocity at rest limit [unit same as EGU of axis] +#- VELO_MAX_LIM : Velocity maximum limit, -1 to disable [unit same as EGU of axis] #- ################################################################################# #- ecmcAddAxisToSafetyGroup(, , , ) #- : Name of safety group to add axis to. #- : Axis index to add (ecmc axis index). -#- : Axis standstill velo limit [unit of axis]. +#- : Axis at rest velo limit [unit of axis]. +#- : Axis max velo limit [unit of axis]. #- : NOT USED (for future implemenation). Time for axis to be below velo limit [ms]. -ecmcAddAxisToSafetyGroup("${NAME}",${AX_ID},${VELO_LIM=0},0) +ecmcAddAxisToSafetyGroup("${NAME}",${AX_ID},${VELO_REST_LIM=0},0,${VELO_MAX_LIM=0.0}) diff --git a/scripts/addSS1Group.cmd b/scripts/addSS1Group.cmd index 5e65031..b677a4e 100644 --- a/scripts/addSS1Group.cmd +++ b/scripts/addSS1Group.cmd @@ -16,18 +16,21 @@ #- #- Arguments: #- NAME : Name of safety group -#- EC_RAMP_DOWN : Ethercat entry for rampd down command, input to ecmc (command from safety PLC/system) -#- EC_AXES_STANDSTILL : Ethercat entry for all axes in group at standstill, output from ecmc (feedback to safety PLC/system) +#- EC_RAMP_DOWN : Ethercat entry for ramp down command, input to ecmc (command from safety PLC/system) +#- EC_AXES_REST : Ethercat entry for signaling that all axes in group are at rest, output from ecmc (feedback to safety PLC/system) +#- EC_AXES_MAX_VELO : Ethercat entry for reducing velocity, input to ecmc (command from safety PLC/system) #- DELAY_MS : Time between rampdown command and STO #- ################################################################################# -#- ecmcAddSS1SafetyGroup(, , ,) -#- : Name of group. -#- : Ethercat entry input for rampdown cmd. -#- : Ethercat entry output for group standstill status. -#- : Time delay of STO [ms]. -ecmcAddSS1SafetyGroup("${NAME}","${EC_RAMP_DOWN}","${EC_AXES_STANDSTILL}",${DELAY_MS=0}) +#-Use ecmcAddSS1SafetyGroup(, , ,) +#- : Name of group +#- : Ethercat entry input for rampdown cmd +#- : Ethercat entry output for group standstill status +#- : Ethercat entry input for activation of maximum velo limitation (set to "empty" to disable) +#- : Time delay of STO [ms] + +ecmcAddSS1SafetyGroup("${NAME}","${EC_RAMP_DOWN}","${EC_AXES_REST}","${EC_AXES_MAX_VELO=empty}",${DELAY_MS=0}) #- Load SS1 group records dbLoadRecords("ss1.template","P=${ECMC_PREFIX},NAME=${NAME}") diff --git a/src/ecmcSS1SafetyGroup.cpp b/src/ecmcSS1SafetyGroup.cpp index ef0f24a..4b3a321 100644 --- a/src/ecmcSS1SafetyGroup.cpp +++ b/src/ecmcSS1SafetyGroup.cpp @@ -37,6 +37,7 @@ extern DBBASE *pdbbase; ecmcSS1SafetyGroup::ecmcSS1SafetyGroup(const char *name, const char *ec_rampdown_cmd, const char *ec_standstill_status, + const char *ec_limit_velo, int time_delay_ms, const char *cfg_string, char* portName) @@ -55,49 +56,59 @@ ecmcSS1SafetyGroup::ecmcSS1SafetyGroup(const char *name, 0, /* Default priority */ 0) /* Default stack size */ { - sName_ = strdup(name); - sEcRampDownCmdNameOrg_ = strdup(ec_rampdown_cmd); - sEcAxesStandStillStatOrg_= strdup(ec_standstill_status); - sEcRampDownCmdNameStrip_ = strdup(ec_rampdown_cmd); - sEcAxesStandStillStatStrip_ = strdup(ec_standstill_status); - sConfig_ = strdup(cfg_string); - delayMs_ = time_delay_ms; + sName_ = strdup(name); + sEcRampDownCmdNameOrg_ = strdup(ec_rampdown_cmd); + sEcAxesStandStillStatOrg_ = strdup(ec_standstill_status); + sEcLimitVeloOrg_ = strdup(ec_limit_velo); + limitMaxVeloEnable_ = 1; + + if(strcmp(sEcLimitVeloOrg_, "empty") == 0) { + limitMaxVeloEnable_ = 0; + } + + sConfig_ = strdup(cfg_string); + delayMs_ = time_delay_ms; + ptrStatus_ = (int*)&status_; + asynStatusId_ = -1; + axesCounter_ = 0; + ecmcSampleRateHz_ = getEcmcSampleRate(); + dataSourcesLinked_ = 0; + ecMaster_ = NULL; + ecEntryRampDown_ = NULL; + ecEntryStandstill_ = NULL; + ecEntryLimitVelo_ = NULL; + masterId_ = 0; + slaveIdRampDown_ = 0; + bitRampDown_ = 0; + slaveIdStandStill_ = 0; + bitStandStill_ = 0; + aliasRampDown_[0] = 0; + aliasStandStill_[0] = 0; + aliasLimitVelo_[0] = 0; + rampDownCmd_ = 0; + axesAreStandstill_ = 0; + rampDownCmdOld_ = 0; + axesAreStandstillOld_ = 0; + printEnableStatus_ = 1; + limitVeloCmdOld_ = 0; + limitVeloCmd_ = 0; + cfgDbgMode_ = 0; memset(&status_,0,sizeof(status_)); - ptrStatus_ = (int*)&status_; - asynStatusId_ = -1; - axesCounter_ = 0; - ecmcSampleRateHz_ = getEcmcSampleRate(); - dataSourcesLinked_ = 0; - ecMaster_ = NULL; - ecEntryRampDown_ = NULL; - ecEntryStandstill_ = NULL; - masterId_ = 0; - slaveIdRampDown_ = 0; - bitRampDown_ = 0; - slaveIdStandStill_ = 0; - bitStandStill_ = 0; - aliasRampDown_[0] = 0; - aliasStandStill_[0] = 0; - rampDownCmd_ = 0; - axesAreStandstill_ = 0; - rampDownCmdOld_ = 0; - axesAreStandstillOld_ = 0; - printEnableStatus_ = 1; - // Config defaults - cfgDbgMode_ = 0; parseConfigStr(cfg_string); initAsyn(); if(cfgDbgMode_) { - printf("Safety %s: Safety group created:\n" - " Type: SS1\n" - " Name: %s\n" - " I/O for rampdown command from saftey PLC: %s\n" - " I/O for axes standstill status: %s\n" - " STO delay [ms]: %d\n" - " Configuration string: %s\n", + printf("Safety %s: Group created:\n" + " Type: SS1-t\n" + " Name: %s\n" + " I/O for rampdown command from saftey PLC: %s\n" + " I/O for axes standstill status: %s\n" + " I/O for velo limit command from safety PLC: %s\n" + " STO delay [ms]: %d\n" + " Configuration string: %s\n", sName_,sName_,sEcRampDownCmdNameOrg_, - sEcAxesStandStillStatOrg_,delayMs_,sConfig_); + sEcAxesStandStillStatOrg_,sEcLimitVeloOrg_, + delayMs_,sConfig_); } } @@ -109,9 +120,8 @@ ecmcSS1SafetyGroup::~ecmcSS1SafetyGroup() { free(sName_); free(sEcRampDownCmdNameOrg_); free(sEcAxesStandStillStatOrg_); - free(sEcRampDownCmdNameStrip_); - free(sEcAxesStandStillStatStrip_); free(sConfig_); + free(sEcLimitVeloOrg_); } void ecmcSS1SafetyGroup::parseConfigStr(const char *configStr) { @@ -168,7 +178,16 @@ void ecmcSS1SafetyGroup::validateCfgs() { throw std::runtime_error( "Safety: Parse error: Data source for standstill status."); } - if(masterIdStandStill != masterIdRampDown ) { + int masterIdLimitVelo=-1; + if(parseEcPath(sEcLimitVeloOrg_, + &masterIdLimitVelo, + &slaveIdLimitVelo_, + aliasLimitVelo_, + &bitLimitVelo_)) { + throw std::runtime_error( "Safety: Parse error: Data source for limit velo."); + } + + if(masterIdStandStill != masterIdRampDown || masterIdLimitVelo != masterIdRampDown) { throw std::runtime_error( "Safety: Parse error: Master id for datasources different."); } masterId_ = masterIdStandStill; @@ -180,6 +199,10 @@ void ecmcSS1SafetyGroup::validateCfgs() { if(bitStandStill_ < 0) { throw std::runtime_error( "Safety: Parse error: Standstill status, bit invalid."); } + + if(bitLimitVelo_ < 0) { + throw std::runtime_error( "Safety: Parse error: Limit velo, bit invalid."); + } } void ecmcSS1SafetyGroup::validateAxes() { @@ -226,6 +249,22 @@ void ecmcSS1SafetyGroup::validateAxes() { printf("Safety %s: Error, group empty (axis count zero)\n",sName_); throw std::runtime_error( "Safety: Error, empty group not allowed."); } + + // Check if max velo enabled in axis + int axesMaxVeloEnabled = 0; + for(std::vector::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) { + axesMaxVeloEnabled = (*saxis)->veloMaxLimitEnabled_ || axesMaxVeloEnabled; + } + + if(limitMaxVeloEnable_ && !axesMaxVeloEnabled) { + printf("Safety %s: Error, Limit max velo enabled but no axis configured with max velo\n",sName_); + throw std::runtime_error( "Safety: Error, Limit max velo enabled but no axis configured with max velo"); + } + + if(!limitMaxVeloEnable_ && axesMaxVeloEnabled) { + printf("Safety %s: Error, Limit max velo disabled but axis configured with a max velo\n",sName_); + throw std::runtime_error( "Safety: Error, Limit max velo disabled but axis configured with a max velo"); + } } void ecmcSS1SafetyGroup::connectToDataSources() { @@ -255,30 +294,24 @@ void ecmcSS1SafetyGroup::connectToDataSources() { ecEntryStandstill_ = slave->findEntry(aliasStandStill_); if(!ecEntryStandstill_) { - throw std::runtime_error( "Safety: EtherCAT entry for rampdown I/O NULL."); + throw std::runtime_error( "Safety: EtherCAT entry for standstill I/O NULL."); }; - + + + // standstill + slave = ecMaster_->findSlave(slaveIdLimitVelo_); + if(!slave) { + throw std::runtime_error( "Safety: EtherCAT slave limit velo I/O NULL."); + } + + ecEntryLimitVelo_ = slave->findEntry(aliasLimitVelo_); + if(!ecEntryLimitVelo_) { + throw std::runtime_error( "Safety: EtherCAT entry for limit velo I/O NULL."); + }; + dataSourcesLinked_ = 1; return; - - //// Get dataItem for rampdown command - //dataItemRampDownCmd_ = (ecmcDataItem*) getEcmcDataItem(sEcRampDownCmdNameStrip_); - //if(!dataItemRampDownCmd_) { - // throw std::runtime_error( "Safety: Data item for ramp down command NULL."); - //} - // - //// Get dataItem for axes standstill status - //dataItemStandStillStat_ = (ecmcDataItem*) getEcmcDataItem(sEcAxesStandStillStatStrip_); - //if(!dataItemStandStillStat_) { - // throw std::runtime_error( "Safety: Data item for axes standstill status NULL."); - //} - // - //if(cfgDbgMode_) { - // printf("Safety: Safety group \"%s\"\": Data sources linked.\n",sName_); - //} - // - //dataSourcesLinked_ = 1; } void ecmcSS1SafetyGroup::refreshAsyn() { @@ -318,9 +351,8 @@ std::string ecmcSS1SafetyGroup::to_string(int value) { return os.str(); } -// Executed by ecmc rt thread. -void ecmcSS1SafetyGroup::execute() { - +// Ramp down and disable if safety interlock +void ecmcSS1SafetyGroup::exeRampDown() { uint64_t data = 0; // Read ramp down command from safety plc if(ecEntryRampDown_->readBit(bitRampDown_, @@ -333,7 +365,7 @@ void ecmcSS1SafetyGroup::execute() { } rampDownCmdOld_ = rampDownCmd_; - rampDownCmd_ = data == 0; + rampDownCmd_ = data == 0; if(rampDownCmdOld_ != rampDownCmd_) { @@ -353,6 +385,7 @@ void ecmcSS1SafetyGroup::execute() { // set safety interlock in ecmc setAxesSafetyInterlocks(rampDownCmd_); + // check if axes are standstill to safety PLC axesAreStandstill_ = checkAxesStandstillAndDisableIfNeeded(); setAxesStandstillStatus(axesAreStandstill_); @@ -365,6 +398,56 @@ void ecmcSS1SafetyGroup::execute() { } } +// Limit velo if needed +void ecmcSS1SafetyGroup::exeLimitVelo() { + + if(!limitMaxVeloEnable_) { + return; + } + + uint64_t data = 0; + // Read ramp down command from safety plc + if(ecEntryLimitVelo_->readBit(bitLimitVelo_, + &data)) { + // Disable all axes + setAxesDisable(); // disable + setAxesSafetyInterlocks(0); // stop + setAxesStandstillStatus(0); // set output + throw std::out_of_range("Safety: Read limit velo cmd failed"); + } + + limitVeloCmdOld_ = limitVeloCmd_; + limitVeloCmd_ = data == 0; + + if(limitVeloCmdOld_ != limitVeloCmd_) { + // Update asyn status wd + status_.limitVeloCmdActive = limitVeloCmd_; + refreshAsyn(); + + resetPrintoutStatus(); + if(cfgDbgMode_) { + if(limitVeloCmd_) { + printf("Safety %s: Limit velo cmd active\n",sName_); + } else { + printf("Safety %s: Limit velo cmd not active\n",sName_); + } + } + } + + if(limitVeloCmd_) { + checkAxesMaxVeloAndDisableIfNeeded(); + } + + // Write velo limit and activation to ecmc axis object + setAxesMaxVelo(); +} + +// Executed by ecmc rt thread. +void ecmcSS1SafetyGroup::execute() { + exeRampDown(); + exeLimitVelo(); +} + void ecmcSS1SafetyGroup::resetPrintoutStatus() { for(std::vector::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) { (*saxis)->printEnableStat_ = 1; @@ -396,11 +479,55 @@ void ecmcSS1SafetyGroup::setAxesStandstillStatus(int standstill) { bool ecmcSS1SafetyGroup::checkAxesStandstill() { bool standstill = 1; for(std::vector::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) { - standstill= standstill && checkAxisStandstill((*saxis)); + standstill= standstill && checkAxisStandstill((*saxis)); } return standstill; } +// Check max velo violation +void ecmcSS1SafetyGroup::checkAxesMaxVeloAndDisableIfNeeded() { + if(!limitVeloCmd_) { + return; + } + + for(std::vector::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) { + if(!(*saxis)->veloMaxLimitEnabled_) { + break; + } + if(checkAxisMaxVelo((*saxis))) { + printf("Safety %s: Axis %d, velo too high, disabling axis.\n", sName_, (*saxis)->axisId_); + setAxisEnable((*saxis)->axisId_,0); + } + } + return; +} + +// Set max velo in axis object +void ecmcSS1SafetyGroup::setAxesMaxVelo() { + for(std::vector::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) { + if( (*saxis)->veloMaxLimitEnabled_ ) { + setAxisExtMaxVelo((*saxis)->axisId_,0.95*(*saxis)->veloMaxLimit_,limitVeloCmd_); + } + } +} + +bool ecmcSS1SafetyGroup::checkAxisMaxVelo(safetyAxis* axis) { + double traj = 1; + + int err = getAxisTrajVelo(axis->axisId_, &traj); + if(err) { + return 0; + } + + double enc = 1; + err = getAxisEncVelo(axis->axisId_, &enc); + if(err) { + return 0; + } + + return std::abs(traj) >= axis->veloMaxLimit_ || std::abs(enc) >= axis->veloMaxLimit_; +} + // Check standstill axis bool ecmcSS1SafetyGroup::checkAxisStandstill(safetyAxis* axis) { double traj = 1; @@ -416,7 +543,7 @@ bool ecmcSS1SafetyGroup::checkAxisStandstill(safetyAxis* axis) { return 0; } - return std::abs(traj) <= axis->veloLimit_ && std::abs(enc) <= axis->veloLimit_; + return std::abs(traj) <= axis->veloStandstillLimit_ && std::abs(enc) <= axis->veloStandstillLimit_; } // Check standstill and disable @@ -447,7 +574,7 @@ void ecmcSS1SafetyGroup::setAxesDisable() { if(!*saxis) { throw std::runtime_error("Safety: Axis object NULL."); } - setAxisEnable((*saxis)->axisId_,0); + setAxisEnable((*saxis)->axisId_,0); } if(printEnableStatus_ && cfgDbgMode_) { @@ -466,19 +593,47 @@ void ecmcSS1SafetyGroup::setAxesSafetyInterlocks(int stop) { } } -void ecmcSS1SafetyGroup::addAxis(int axisId, double veloLimit,int standStillTimeMs) { +void ecmcSS1SafetyGroup::addAxis(int axisId, + double veloStandstillLimit, + int standStillTimeMs) { if(!getAxisValid(axisId)) { throw std::out_of_range("Safety: Invalid axis id"); } - axes_.push_back(new safetyAxis(axisId, veloLimit, standStillTimeMs)); + axes_.push_back(new safetyAxis(axisId, veloStandstillLimit, + standStillTimeMs)); axesCounter_++; + if(cfgDbgMode_) { + printf("Safety %s: Added axis %d to safety group.\n" + " velo stand still limit = %lf,\n" + " standstill filter time = %d\n" + ,sName_,axisId,veloStandstillLimit,standStillTimeMs); + } + + return; +} + +void ecmcSS1SafetyGroup::addAxis(int axisId, + double veloStandstillLimit, + int standStillTimeMs, + double veloMaxLimit) { + + if(!getAxisValid(axisId)) { + throw std::out_of_range("Safety: Invalid axis id"); + } + + axes_.push_back(new safetyAxis(axisId, veloStandstillLimit, + standStillTimeMs, veloMaxLimit)); + axesCounter_++; if(cfgDbgMode_) { - printf("Safety %s: Added axis %d to safety group (velo limit = %lf, standstill filter : %d)\n" - ,sName_,axisId,veloLimit,standStillTimeMs); + printf("Safety %s: Added axis %d to safety group.\n" + " velo stand still limit = %lf,\n" + " velo max limit = %lf,\n" + " standstill filter time = %d\n" + ,sName_,axisId,veloStandstillLimit,veloMaxLimit,standStillTimeMs); } return; diff --git a/src/ecmcSS1SafetyGroup.h b/src/ecmcSS1SafetyGroup.h index 3a43a2c..a90616b 100644 --- a/src/ecmcSS1SafetyGroup.h +++ b/src/ecmcSS1SafetyGroup.h @@ -25,26 +25,45 @@ typedef struct { bool error : 1; bool rampDownCmdActive : 1; bool axesAtStandstill : 1; - int dummy : 29; + bool limitVeloCmdActive : 1; + int dummy : 28; } ecmcSafetyStatusWd; class safetyAxis { public: safetyAxis(int axisId, - double veloLimit, - int standStillTimeMs) { - veloLimit_ = veloLimit; - axisId_ = axisId; - standStillTimeMs_ = standStillTimeMs; - printEnableStat_ = 1; + double velostandstillLimit, + int standStillTimeMs, + double veloMaxLimit) { + veloStandstillLimit_ = velostandstillLimit; + axisId_ = axisId; + standStillTimeMs_ = standStillTimeMs; + veloMaxLimit_ = veloMaxLimit; + veloMaxLimitEnabled_ = veloMaxLimit > 0; + printEnableStat_ = 1; } - double veloLimit_; + safetyAxis(int axisId, + double velostandstillLimit, + int standStillTimeMs) { + veloStandstillLimit_ = velostandstillLimit; + axisId_ = axisId; + standStillTimeMs_ = standStillTimeMs; + veloMaxLimit_ = -1; + veloMaxLimitEnabled_ = 0; + printEnableStat_ = 1; + } + + double veloStandstillLimit_; + double veloMaxLimit_; // disable with -1 int axisId_; int standStillTimeMs_; int printEnableStat_; + int veloMaxLimitEnabled_; }; + + class ecmcSS1SafetyGroup : public asynPortDriver { public: @@ -54,17 +73,24 @@ class ecmcSS1SafetyGroup : public asynPortDriver { * - out_of_range */ ecmcSS1SafetyGroup(const char *name, - const char *ec_rampdown_cmd, - const char *ec_standstill_status, - int time_delay_ms, - const char *cfg_string, - char* portName); + const char *ec_rampdown_cmd, + const char *ec_standstill_status, + const char *ec_limit_velo, + int time_delay_ms, + const char *cfg_string, + char* portName); ~ecmcSS1SafetyGroup(); // Call just before realtime because then all data sources should be available void validate(); - void addAxis(int axisId, double veloLimit,int standStillTimeMs); + void addAxis(int axisId, + double veloLimit, + int standStillTimeMs); + void addAxis(int axisId, + double veloLimit, + int standStillTimeMs, + double veloMaxLimit); void execute(); virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value); std::string getName(); @@ -80,16 +106,23 @@ class ecmcSS1SafetyGroup : public asynPortDriver { bool checkAxisStandstill(safetyAxis* axis); bool checkAxisStandstillAndDisableIfNeeded(safetyAxis* axis); bool checkAxesStandstillAndDisableIfNeeded(); + void checkAxesMaxVeloAndDisableIfNeeded(); + void setAxesMaxVelo(); + bool checkAxisMaxVelo(safetyAxis* axis); void resetPrintoutStatus(); void parseConfigStr(const char *configStr); void initAsyn(); void refreshAsyn(); + void exeRampDown(); + void exeLimitVelo(); double ecmcSampleRateHz_; int dataSourcesLinked_; // To avoid link several times int objectId_; // Unique object id int cycleCounter_; int rampDownCmd_; int rampDownCmdOld_; + int limitVeloCmdOld_; + int limitVeloCmd_; // Config options int cfgDbgMode_; // Config: allow dbg printouts @@ -105,8 +138,7 @@ class ecmcSS1SafetyGroup : public asynPortDriver { char* sName_; char* sEcRampDownCmdNameOrg_; char* sEcAxesStandStillStatOrg_; - char* sEcRampDownCmdNameStrip_; - char* sEcAxesStandStillStatStrip_; + char* sEcLimitVeloOrg_; char* sConfig_; int delayMs_; @@ -115,16 +147,23 @@ class ecmcSS1SafetyGroup : public asynPortDriver { int bitRampDown_; int slaveIdStandStill_; int bitStandStill_; + int slaveIdLimitVelo_; + int bitLimitVelo_; + + int limitMaxVeloEnable_; + int axesAreStandstill_; int axesAreStandstillOld_; int printEnableStatus_; char aliasRampDown_[EC_MAX_OBJECT_PATH_CHAR_LENGTH]; char aliasStandStill_[EC_MAX_OBJECT_PATH_CHAR_LENGTH]; - + char aliasLimitVelo_[EC_MAX_OBJECT_PATH_CHAR_LENGTH]; + ecmcEc *ecMaster_; ecmcEcEntry *ecEntryRampDown_; ecmcEcEntry *ecEntryStandstill_; + ecmcEcEntry *ecEntryLimitVelo_; static std::string to_string(int value); }; diff --git a/src/ecmcSafetyPlgWrap.cpp b/src/ecmcSafetyPlgWrap.cpp index 3239f2f..b18976e 100644 --- a/src/ecmcSafetyPlgWrap.cpp +++ b/src/ecmcSafetyPlgWrap.cpp @@ -51,6 +51,7 @@ ecmcSS1SafetyGroup* getGroupFromName(const char *name) { int createSafetyGroup(const char *name, const char *ec_rampdown_cmd, const char *ec_standstill_status, + const char *ec_max_velo_cmd, int time_delay_ms) { // ensure group does not already exist @@ -71,6 +72,7 @@ int createSafetyGroup(const char *name, safetyGroup = new ecmcSS1SafetyGroup(name, ec_rampdown_cmd, ec_standstill_status, + ec_max_velo_cmd, time_delay_ms, configString, portNameBuffer); @@ -90,7 +92,8 @@ int createSafetyGroup(const char *name, int addAxisToSafetyGroup(const char *groupName, int axisId, double veloLimit, - int standStillTimeMs) { + int standStillTimeMs, + double maxVeloLimit) { ecmcSS1SafetyGroup* grp = getGroupFromName(groupName); @@ -99,7 +102,7 @@ int addAxisToSafetyGroup(const char *groupName, throw std::runtime_error( "Safety: Error, group not found."); } - grp->addAxis(axisId,veloLimit,standStillTimeMs); + grp->addAxis(axisId,veloLimit,standStillTimeMs,maxVeloLimit); return asynSuccess; } @@ -160,15 +163,20 @@ int executeSafetyGroups() { */ void ecmcAddSS1SafetyGroupPrintHelp() { printf("\n"); - printf(" Use ecmcAddSS1SafetyGroup(, , ,)\n"); + printf(" Use ecmcAddSS1SafetyGroup(, , ,,)\n"); printf(" : Name of group.\n"); printf(" : Ethercat entry input for rampdown cmd.\n"); printf(" : Ethercat entry output for group standstill status.\n"); + printf(" : Ethercat entry input for activation of maximum velo limitation (set to \"empty\" to disable).\n"); printf(" : Time delay of STO [ms].\n"); printf("\n"); } -int ecmcAddSS1SafetyGroup(const char* name, const char* ec_rampdown_cmd,const char* ec_standstill_status,int time_delay_ms) { +int ecmcAddSS1SafetyGroup(const char* name, + const char* ec_rampdown_cmd, + const char* ec_standstill_status, + const char* ec_max_velo_cmd, + int time_delay_ms) { if(!name) { ecmcAddSS1SafetyGroupPrintHelp(); return asynError; @@ -190,13 +198,20 @@ int ecmcAddSS1SafetyGroup(const char* name, const char* ec_rampdown_cmd,const ch ecmcAddSS1SafetyGroupPrintHelp(); return asynError; } + + if(!ec_max_velo_cmd) { + printf("Error: ec_max_velo_cmd ethercat entry not defined.\n"); + ecmcAddSS1SafetyGroupPrintHelp(); + return asynError; + } + if(time_delay_ms <= 0) { printf("Error: time_delay invalid.\n"); exit (EXIT_FAILURE); } try { - createSafetyGroup(name,ec_rampdown_cmd,ec_standstill_status, time_delay_ms); + createSafetyGroup(name,ec_rampdown_cmd,ec_standstill_status,ec_max_velo_cmd,time_delay_ms); } catch(std::exception& e) { printf("Exception: %s. Add safety group failed.\n",e.what()); @@ -213,16 +228,19 @@ static const iocshArg initArg1_1 = static const iocshArg initArg2_1 = { "ec entry output axes standstill status", iocshArgString }; static const iocshArg initArg3_1 = +{ "ec entry input activate max velo", iocshArgString }; +static const iocshArg initArg4_1 = { "STO delay [ms]", iocshArgInt }; static const iocshArg *const initArgs_1[] = { &initArg0_1, &initArg1_1, &initArg2_1, - &initArg3_1}; + &initArg3_1, + &initArg4_1,}; -static const iocshFuncDef initFuncDef_1 = { "ecmcAddSS1SafetyGroup", 4, initArgs_1 }; +static const iocshFuncDef initFuncDef_1 = { "ecmcAddSS1SafetyGroup", 5, initArgs_1 }; static void initCallFunc_1(const iocshArgBuf *args) { - ecmcAddSS1SafetyGroup(args[0].sval, args[1].sval, args[2].sval, args[3].ival); + ecmcAddSS1SafetyGroup(args[0].sval, args[1].sval, args[2].sval, args[3].sval, args[4].ival); } /** @@ -231,14 +249,19 @@ static void initCallFunc_1(const iocshArgBuf *args) { void ecmcAddAxisToSafetyGroupPrintHelp() { printf("\n"); printf(" Use ecmcAddAxisToSafetyGroup(, )\n"); - printf(" : Name of safety group.\n"); - printf(" : Axis index to add.\n"); - printf(" : Axis standstill velo limit [unit of axis].\n"); - printf("