Merge branch 'master' of git.psi.ch:epics_ioc_modules/ecmc_plugin_safety
@@ -18,7 +18,7 @@ OPT_CXXFLAGS_YES = -O3
|
||||
ECmasterECMC_VERSION = v1.1.0
|
||||
# motorECMC_VERSION = 7.0.7-ESS
|
||||
#ecmc_VERSION = v9.0.1_RC4
|
||||
ecmc_VERSION = v9.0.1_RC4
|
||||
ecmc_VERSION = 9.1.0
|
||||
|
||||
################################################################################
|
||||
# THIS RELATES TO THE EtherCAT MASTER LIBRARY
|
||||
|
||||
30
README.md
@@ -1,22 +1,24 @@
|
||||
ecmc_plugin_safety
|
||||
======
|
||||
|
||||
IMPORTANT: This plugin or ecmc does NOT offer any safety functionality. This plugin only offers a standard interface to an external safety system / safety PLC / safety relay!!!!
|
||||
IMPORTANT: This plugin or ecmc does NOT offer any safety certified functionality. This plugin only offers a standard interface to an external safety system / safety PLC / safety relay!!!!
|
||||
|
||||
This plugin is designed for interfacing safety systems, see example in below picture, to ecmc motion axes.
|
||||
|
||||
This plugin is designed for interfacing safety systems to ecmc motion axes.
|
||||

|
||||
|
||||
# Loading of plugin
|
||||
# SS1-t
|
||||
The plugin supports stopping axes according to a concept described as SS1-t, see below picture. In SS1-t the STO (or removal of power) from the axes are delayed for a certain defined time allowing a controlled rampdown of velocity. The rampdown of the axis is handled by the non-safe motion controller while the removal of power (or triggering of STO) after the time delay is handled by a safety system, . This makes it possible to stop moving axes in a controlled way and disable the drives before the power is interrupted (or STO triggered). This will result in a safer system and less harware failures and error messages.
|
||||
|
||||
Loading of the plugin is made by require:
|
||||
```
|
||||
# Load safety plugin
|
||||
require ecmc_plugin_safety <optional_version>
|
||||
```
|
||||
After the time delay expires, hopfully all axes are at standstill and power is already removed from the drives.
|
||||
|
||||
# SS1
|
||||
The plugin supports stopping axes accoring to a concept described as SS1. In SS1 the STO (or removal of power) from the axes are delayed for a certain time allowing a controlled rampdown. This makes it possible to stop moving axes in a controlled way and disable of drives before the power is interrupted (or STO triggered). This will result in a safer system and less harware failures and error messages.
|
||||
Basically the safey system is interfaced with binary signals (ethercat I/O). If, for instance, an emergency stop button is pressed the safety system immediately commands this plugin to rampdown velocity of axes (that a configured to stop). When all axes, that are configured to rampdown, have stopped then this plugin will disable the axes and set an ethercat output informing the safety system that the axes are standstill. After a certain timout the safety system will make sure power is removed from the motion axes by STO or removing power. The removal of power or triggering of STO will made regardless if the axes are at rest or not. A reset of the safety system, allowing power to the drives, will only be possible once the safety system gets a confirmation that all axes are at rest.
|
||||

|
||||
|
||||
## Interface
|
||||
Basically the safey system is interfaced with two binary signals (ethercat I/O):
|
||||
* Ramp down command (from safety system to ecmc)
|
||||
* Axis stand still status (to safety system from ecmc)
|
||||
If, for instance, an safety event is triggerd by the safety system, it will immediately command this plugin to rampdown velocity of all axes (that a configured to stop). When all axes, that are configured to rampdown, have stopped then this plugin will disable the axes and set an ethercat output informing the safety system that the axes are standstill. After a certain timout the safety system will make sure power is removed from the motion axes by triggering an STO or removing power. The removal of power or triggering of STO will made regardless if the axes are at rest or not. A reset of the safety system, allowing power to the drives, will only be possible once the safety system gets a confirmation that all axes are at rest.
|
||||
|
||||
The configuration is made by two commands:
|
||||
1. ecmcAddSS1SafetyGroup()
|
||||
@@ -28,8 +30,8 @@ The ecmcAddSS1SafetyGroup() adds a SS1 safety group. The command takes the follo
|
||||
```
|
||||
ecmcAddSS1SafetyGroup(<name>, <ec_rampdown_cmd>, <ec_standstill_status>,<time_delay_ms>)
|
||||
<name> : Name of group.
|
||||
<ec_rampdown_cmd> : Ethercat entry input for rampdown cmd.
|
||||
<ec_standstill_status> : Ethercat entry output for group standstill status.
|
||||
<ec_rampdown> : Ethercat entry input for rampdown cmd.
|
||||
<ec_axes_at_rest> : Ethercat entry output for group standstill status.
|
||||
<time_delay_ms> : Time delay of STO [ms].
|
||||
```
|
||||
Each group is interfacing the safety system through the same I/O (ethercat).
|
||||
@@ -69,7 +71,7 @@ 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
|
||||
# Add axes to group "first"
|
||||
${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToGroup.cmd "NAME=first,AX_ID=1,VELO_LIM=1"
|
||||
${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToGroup.cmd "NAME=first,AX_ID=2,VELO_LIM=1"
|
||||
${SCRIPTEXEC} ${ecmc_plugin_safety_DIR}addAxisToGroup.cmd "NAME=first,AX_ID=3,VELO_LIM=1"
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
>>
|
||||
Script is used : e3TemplateGenerator.bash
|
||||
Script Path : /home/anderssandstrom/plugin_ecmc/e3-tools/e3TemplateGenerator
|
||||
Script Version : 1.0.8
|
||||
Script Run Time : 2020Mar22-1607-33CET
|
||||
User : anderssandstrom
|
||||
e3-tools Hash : bf03d40
|
||||
>>
|
||||
>> git diff
|
||||
|
||||
>>
|
||||
>> git diff --cached
|
||||
|
||||
>>
|
||||
>> git diff HEAD
|
||||
|
||||
|
||||
>>
|
||||
>> Your sources are located in e3-ecmcPlugin_Simple.
|
||||
>>
|
||||
EPICS_MODULE_NAME : ecmcPlugin_Simple
|
||||
E3_MODULE_SRC_PATH : ecmcPlugin_Simple
|
||||
E3_TARGET_URL : https://github.com/anderssandstrom
|
||||
>>
|
||||
e3 module name : e3-ecmcPlugin_Simple
|
||||
e3 target url full : https://github.com/anderssandstrom/e3-ecmcPlugin_Simple.git
|
||||
>>
|
||||
e3 module is located in siteMods
|
||||
BIN
docs/SS1-t.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 57 KiB |
BIN
docs/safetyplc.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
@@ -16,18 +16,18 @@
|
||||
#-
|
||||
#- Arguments:
|
||||
#- NAME : Name of safety group
|
||||
#- EC_RAMP_DOWN_CMD : Ethercat entry for rampd down command (cmd from safety PLC/system)
|
||||
#- EC_STANDSTILL_STAT : Ethercat entry for axes at standstill status (feedback to safety PLC/system)
|
||||
#- 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)
|
||||
#- DELAY_MS : Time between rampdown command and STO
|
||||
#-
|
||||
#################################################################################
|
||||
|
||||
#- ecmcAddSS1SafetyGroup(<name>, <ec_rampdown_cmd>, <ec_standstill_status>,<time_delay_ms>)
|
||||
#- <name> : Name of group.
|
||||
#- <ec_rampdown_cmd> : Ethercat entry input for rampdown cmd.
|
||||
#- <ec_standstill_status> : Ethercat entry output for group standstill status.
|
||||
#- <ec_rampdown> : Ethercat entry input for rampdown cmd.
|
||||
#- <ec_standstill> : Ethercat entry output for group standstill status.
|
||||
#- <time_delay_ms> : Time delay of STO [ms].
|
||||
ecmcAddSS1SafetyGroup("${NAME}","${EC_RAMP_DOWN_CMD}","${EC_STANDSTILL_STAT}",${DELAY_MS=0})
|
||||
ecmcAddSS1SafetyGroup("${NAME}","${EC_RAMP_DOWN}","${EC_AXES_STANDSTILL}",${DELAY_MS=0})
|
||||
|
||||
#- Load SS1 group records
|
||||
dbLoadRecords("ss1.template","P=${ECMC_PREFIX},NAME=${NAME}")
|
||||
|
||||
@@ -182,8 +182,7 @@ void ecmcSS1SafetyGroup::validateCfgs() {
|
||||
}
|
||||
}
|
||||
|
||||
void ecmcSS1SafetyGroup::validateAxes() {
|
||||
|
||||
void ecmcSS1SafetyGroup::validateAxes() {
|
||||
// Ensure that axis is not linked twice to group
|
||||
for(std::vector<safetyAxis*>::iterator iaxis = axes_.begin(); iaxis != axes_.end(); ++iaxis) {
|
||||
int axisLinkedCounter = 0;
|
||||
@@ -213,11 +212,19 @@ void ecmcSS1SafetyGroup::validateAxes() {
|
||||
}
|
||||
}
|
||||
|
||||
int axisCounter = 0;
|
||||
// Check axis object exists and valid
|
||||
for(std::vector<safetyAxis*>::iterator saxis = axes_.begin(); saxis != axes_.end(); ++saxis) {
|
||||
if(!getAxisValid((*saxis)->axisId_)) {
|
||||
throw std::runtime_error( "Safety: Ecmc Axis object not valid.");
|
||||
}
|
||||
axisCounter++;
|
||||
}
|
||||
|
||||
// do not allow empty group
|
||||
if(axisCounter == 0) {
|
||||
printf("Safety %s: Error, group empty (axis count zero)\n",sName_);
|
||||
throw std::runtime_error( "Safety: Error, empty group not allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,13 +37,31 @@ int setCfgString(const char* cfgString) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ecmcSS1SafetyGroup* getGroupFromName(const char *name) {
|
||||
// Find group by name
|
||||
for(std::vector<ecmcSS1SafetyGroup*>::iterator psafetyGroup = safetyGroups.begin(); psafetyGroup != safetyGroups.end(); ++psafetyGroup) {
|
||||
bool found = strcmp(name, (*psafetyGroup)->getName().c_str()) == 0;
|
||||
if(found) {
|
||||
return (*psafetyGroup);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int createSafetyGroup(const char *name,
|
||||
const char *ec_rampdown_cmd,
|
||||
const char *ec_standstill_status,
|
||||
int time_delay_ms) {
|
||||
|
||||
// ensure group does not already exist
|
||||
ecmcSS1SafetyGroup* safetyGroup = getGroupFromName(name);
|
||||
if( safetyGroup ) {
|
||||
printf("Safety: Error, group %s already defined. Plugin will unload.\n",name);
|
||||
throw std::runtime_error( "Safety: Error, group already defined.");
|
||||
}
|
||||
|
||||
// create new ecmcSS1SafetyGroup object
|
||||
ecmcSS1SafetyGroup* safetyGroup = NULL;
|
||||
safetyGroup = NULL;
|
||||
|
||||
// create asynport name for new object ()
|
||||
memset(portNameBuffer, 0, ECMC_PLUGIN_MAX_PORTNAME_CHARS);
|
||||
@@ -61,7 +79,6 @@ int createSafetyGroup(const char *name,
|
||||
if(safetyGroup) {
|
||||
delete safetyGroup;
|
||||
}
|
||||
return ECMC_PLUGIN_SAFETY_ERROR_CODE;
|
||||
}
|
||||
|
||||
safetyGroups.push_back(safetyGroup);
|
||||
@@ -74,16 +91,17 @@ int addAxisToSafetyGroup(const char *groupName,
|
||||
int axisId,
|
||||
double veloLimit,
|
||||
int standStillTimeMs) {
|
||||
// Find group by name
|
||||
for(std::vector<ecmcSS1SafetyGroup*>::iterator psafetyGroup = safetyGroups.begin(); psafetyGroup != safetyGroups.end(); ++psafetyGroup) {
|
||||
bool found = strcmp(groupName, (*psafetyGroup)->getName().c_str()) == 0;
|
||||
if(found) {
|
||||
(*psafetyGroup)->addAxis(axisId,veloLimit,standStillTimeMs);
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
ecmcSS1SafetyGroup* grp = getGroupFromName(groupName);
|
||||
|
||||
if( !grp ) {
|
||||
printf("Safety: Error, group %s not found. Plugin will unload.\n",groupName);
|
||||
throw std::runtime_error( "Safety: Error, group not found.");
|
||||
}
|
||||
|
||||
return asynError;
|
||||
grp->addAxis(axisId,veloLimit,standStillTimeMs);
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
void deleteAllSafetyGroups() {
|
||||
@@ -98,17 +116,26 @@ void deleteAllSafetyGroups() {
|
||||
}
|
||||
|
||||
int validate() {
|
||||
int groupCount = 0;
|
||||
for(std::vector<ecmcSS1SafetyGroup*>::iterator psafetyGroup = safetyGroups.begin(); psafetyGroup != safetyGroups.end(); ++psafetyGroup) {
|
||||
if(*psafetyGroup) {
|
||||
try {
|
||||
(*psafetyGroup)->validate();
|
||||
groupCount++;
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
printf("Exception: %s. Plugin will unload.\n",e.what());
|
||||
printf("Safety: %s. Plugin will unload.\n",e.what());
|
||||
return ECMC_PLUGIN_SAFETY_ERROR_CODE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow loaded plugin with no group configured
|
||||
if(groupCount == 0) {
|
||||
printf("Saftey: Error, group count 0. Plugin will unload.\n");
|
||||
return ECMC_PLUGIN_SAFETY_ERROR_CODE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -120,7 +147,7 @@ int executeSafetyGroups() {
|
||||
(*psafetyGroup)->execute();
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
printf("Exception: %s. Plugin will unload.\n",e.what());
|
||||
printf("Safety: %s. Plugin will unload.\n",e.what());
|
||||
return ECMC_PLUGIN_SAFETY_ERROR_CODE;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +192,7 @@ int ecmcAddSS1SafetyGroup(const char* name, const char* ec_rampdown_cmd,const ch
|
||||
}
|
||||
if(time_delay_ms <= 0) {
|
||||
printf("Error: time_delay invalid.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -173,7 +200,7 @@ int ecmcAddSS1SafetyGroup(const char* name, const char* ec_rampdown_cmd,const ch
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
printf("Exception: %s. Add safety group failed.\n",e.what());
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return asynSuccess;
|
||||
@@ -224,24 +251,24 @@ int ecmcAddAxisToSafetyGroup(const char* name, int axis_id, double velo_lim, int
|
||||
|
||||
if(axis_id <= 0) {
|
||||
printf("Error: Invalid axis id.\n");
|
||||
exit (1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(velo_lim < 0) {
|
||||
printf("Error: Invalid velocity limit.\n");
|
||||
exit (1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(stand_still_time < 0) {
|
||||
printf("Error: Invalid stand still filter time.\n");
|
||||
exit (1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
try {
|
||||
return addAxisToSafetyGroup(name,axis_id, velo_lim, stand_still_time);
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
printf("Exception: %s. Add axis to safety group failed.\n",e.what());
|
||||
exit (1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return asynSuccess;
|
||||
|
||||