Часто на ПНР приходится сталкиваться с крайне неблагоприятной электромагнитной обстановкой. Не на конкретном предприятии, а вообще повсеместно. Можно сказать, в 50% случаев — или сеть перегружена потребителями, либо отсутствует нормальное заземление и т.п. факторы. В ряде случаев у управляющего ПЛК могут возникать трудности с подключением к приводам по цифровой шине.
Поскольку я в своей работе ориентируюсь на EtherCAT, как на самую гибкую, быструю и надёжную промышленную сеть, то рассмотрю вполне реальный случай, где проблемы со связью были решены только программными методами. Можно сказать, «на коленке».
Итак, описание проблемы: после включения системы управления, привода, находящиеся в сети EtherCAT, зависают в состоянии
Для наглядности привожу схему т.н. «EtherCAT State Machine» (если кому интересно — можно заглянуть на www.ethercat.org в раздел описания технологии), которая универсальна для всех устройств, обслуживаемых в EtherCAT-сети:
С задачей установки/смены состояния ведомого устройства в EtherCAT-сети вполне успешно (как показала практика) справляется функциональный блок
Сначала описываем свой функциональный блок
Теперь пишем тело блока:
Вышеприведённый код, в принципе, работает. Но в некоторых случаях, если это, конечно, не противоречит выполнению технологического процесса, можно и вовсе обойтись и простым перезапуском системы исполнения TwinCAT на управляющем ПЛК. Делается это при помощи функционального блока
Поскольку я в своей работе ориентируюсь на EtherCAT, как на самую гибкую, быструю и надёжную промышленную сеть, то рассмотрю вполне реальный случай, где проблемы со связью были решены только программными методами. Можно сказать, «на коленке».
Итак, описание проблемы: после включения системы управления, привода, находящиеся в сети EtherCAT, зависают в состоянии
INIT
(инициализация) и отказываются автоматически переходить в состояние OP
(эксплуатация). Соответственно, никак не реагируют на управляющие команды ПЛК. Ситуация осложнялась тем, что проблема эта носила случайный характер: два раза подряд система могла включиться без ошибок, затем, раз десять зависнуть на этапе инициализации.Для наглядности привожу схему т.н. «EtherCAT State Machine» (если кому интересно — можно заглянуть на www.ethercat.org в раздел описания технологии), которая универсальна для всех устройств, обслуживаемых в EtherCAT-сети:
FB_EcSetSlaveState
, реализованный под TwinCAT (см. TcEtherCAT.lib
). В паре с ним хорошо работает блок FB_EcGetSlaveState
, читающий фактическое состояние ведомого. На их основе и была написана небольшая программа, переводящая привод из INIT
в OP
. Все комментарии на английском, т.к. мне часто приходится обмениваться кусками программ с забугорными коллегами. Однако, там и без слов всё понятно.Сначала описываем свой функциональный блок
FB_ETHERCAT_STATUS
:FUNCTION_BLOCK FB_ETHERCAT_STATUS
VAR_INPUT
bEnable: BOOL; (*Enabling FB on rising edge*)
sNetId: STRING; (*Master's EtherCAT NetId*)
nSlaveAddr: UINT; (*Slave's EtherCAT address*)
END_VAR
VAR_OUTPUT
bDone: BOOL; (*Successfully done*)
iMessage: STRING (10); (*String message*)
bError: BOOL; (*Error detected*)
nErrId: UDINT; (*ADS error code*)
stActualState: ST_EcSlaveState; (*Actual slave status*)
END_VAR
VAR
iStep: INT;
fbTrigger: R_TRIG;
fbGetSlaveState: FB_EcGetSlaveState;
fbSetSlaveState: FB_EcSetSlaveState;
END_VAR
Теперь пишем тело блока:
CASE iStep OF
0: (*Starting*)
fbTrigger(CLK := bEnable);
IF fbTrigger.Q THEN
iMessage := 'PROCESSING'; (*Status message*)
bError := FALSE; (*Resetting error*)
iStep := iStep + 1;
END_IF;
1: (*Reading slave's EtherCAT status*)
fbGetSlaveState(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
bExecute := TRUE);
IF fbGetSlaveState.bBusy THEN
iStep := iStep + 1;
END_IF;
2: (*Still reading slave's EtherCAT status*)
fbGetSlaveState(bExecute := TRUE);
IF NOT(fbGetSlaveState.bBusy) THEN
(*Successful*)
IF NOT(fbGetSlaveState.bError) THEN
stActualState := fbGetSlaveState.state; (*Actual slave's status*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := iStep + 1; (*Next*)
(*Error*)
ELSE
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
nErrId := fbGetSlaveState.nErrId; (*Saving error ID*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := 0;
END_IF;
END_IF;
3: (*Checking if slave's EtherCAT status equals 'OP'*)
IF stActualState.deviceState = EC_DEVICE_STATE_OP THEN (*Slave is in 'OP' status*)
iMessage := 'OP'; (*Status message*)
bDone := TRUE; (*Done*)
iStep := 0;
ELSE (*Slave isn't in 'OP' status*)
iStep := iStep + 1;
END_IF;
4: (*Setting slave into 'INIT' status*)
fbSetSlaveState(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
bExecute := TRUE,
reqState := EC_DEVICE_STATE_INIT);
IF fbSetSlaveState.bBusy THEN
iStep := iStep + 1;
END_IF;
5: (*Still setting slave into 'INIT' status*)
fbSetSlaveState(bExecute := TRUE);
IF NOT(fbSetSlaveState.bBusy) THEN
(*Successful*)
IF NOT(fbSetSlaveState.bError) THEN
fbSetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := iStep + 1;
(*Error*)
ELSE
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
nErrId := fbSetSlaveState.nErrId; (*Saving error ID*)
fbSetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := 0;
END_IF;
END_IF;
6: (*Reading slave's EtherCAT status*)
fbGetSlaveState(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
bExecute := TRUE);
IF fbGetSlaveState.bBusy THEN
iStep := iStep + 1;
END_IF;
7: (*Still reading slave's EtherCAT status*)
fbGetSlaveState(bExecute := TRUE);
IF NOT(fbGetSlaveState.bBusy) THEN
(*Successful*)
IF NOT(fbGetSlaveState.bError) THEN
stActualState := fbGetSlaveState.state; (*Actual slave's status*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := iStep + 1;
(*Error*)
ELSE
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
nErrId := fbGetSlaveState.nErrId; (*Saving error ID*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := 0;
END_IF;
END_IF;
8: (*Checking if slave's EtherCAT status equals 'INIT'*)
IF stActualState.deviceState = EC_DEVICE_STATE_INIT THEN (*Slave is in 'INIT' status*)
iMessage := 'INIT'; (*Status message*)
iStep := iStep + 1;
ELSE (*Slave isn't in 'INIT' status*)
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
iStep := 0;
END_IF;
9: (*Setting slave into 'OP' status*)
fbSetSlaveState(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
bExecute := TRUE,
reqState := EC_DEVICE_STATE_OP);
IF fbSetSlaveState.bBusy THEN
iStep := iStep + 1;
END_IF;
10: (*Still setting slave into 'OP' status*)
fbSetSlaveState(bExecute := TRUE);
IF NOT(fbSetSlaveState.bBusy) THEN
(*Successful*)
IF NOT(fbSetSlaveState.bError) THEN
fbSetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := iStep + 1;
(*Error*)
ELSE
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
nErrId := fbSetSlaveState.nErrId; (*Saving error ID*)
fbSetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := 0;
END_IF;
END_IF;
11: (*Reading slave's EtherCAT status*)
fbGetSlaveState(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
bExecute := TRUE);
IF fbGetSlaveState.bBusy THEN
iStep := iStep + 1;
END_IF;
12: (*Still reading slave's EtherCAT status*)
fbGetSlaveState(bExecute := TRUE);
IF NOT(fbGetSlaveState.bBusy) THEN
(*Successful*)
IF NOT(fbGetSlaveState.bError) THEN
stActualState := fbGetSlaveState.state; (*Actual slave's status*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := iStep + 1;
(*Error*)
ELSE
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
nErrId := fbGetSlaveState.nErrId; (*Saving error ID*)
fbGetSlaveState(bExecute := FALSE); (*Resetting FB*)
iStep := 0;
END_IF;
END_IF;
13: (*Checking if slave's EtherCAT status equals 'OP'*)
IF stActualState.deviceState = EC_DEVICE_STATE_OP THEN (*Slave is in 'OP' status*)
iMessage := 'OP'; (*Status message*)
iStep := iStep + 1;
ELSE (*Slave isn't in 'OP' status*)
iMessage := 'ERROR'; (*Status message*)
bError := TRUE; (*Error detected*)
iStep := 0;
END_IF;
14: (*Setting 'OP' status successfully done*)
bDone := TRUE; (*Setting 'OP' status done*)
iStep := 0; (*Done*)
END_CASE;
Вышеприведённый код, в принципе, работает. Но в некоторых случаях, если это, конечно, не противоречит выполнению технологического процесса, можно и вовсе обойтись и простым перезапуском системы исполнения TwinCAT на управляющем ПЛК. Делается это при помощи функционального блока
TC_Restart
(см. TcUtilities.lib
), приводить пример использования которого нет смысла ввиду его простоты.
Комментариев нет:
Отправить комментарий