PDA

Просмотр полной версии : Обработка ошибок ModbusTCP



Салихов Ильдар
11.03.2019, 15:08
Добрый день, подскажите пожалуйста.
Задача: логировать ошибки Modbus TCP Slave устройства.
Вот, что пока накидал


VAR
MbState: IoDrvModbusTCP.MB_ErrorCodes;
sMbLog: STRING(255);
sMbLogError: STRING(255);
byMbLog: BYTE;
byMbLogPrev: BYTE;
xLogMbWrite: BOOL;
FB_FIFO_64_STRING_0: FB_FIFO_64_STRING;
END_VAR


MbState:= Modbus_TCP_Slave.byModbusError;
byMbLog := MbState;

IF Modbus_TCP_Slave.xError THEN
xLogMbWrite:= TRUE;
// коды ошибок из IoDrvModbusTCP (enum MB_ErrorCodes)
IF byMbLog = 16#0 THEN sMbLogError := 'RESPONSE_SUCCESS';END_IF
IF byMbLog = 16#1 THEN sMbLogError := 'ILLEGAL_FUNCTION';END_IF
IF byMbLog = 16#2 THEN sMbLogError := 'ILLEGAL_DATA_ADDRESS';END_IF
IF byMbLog = 16#3 THEN sMbLogError := 'ILLEGAL_DATA_VALUE';END_IF
IF byMbLog = 16#4 THEN sMbLogError := 'SLAVE_DEVICE_FAILURE';END_IF
IF byMbLog = 16#5 THEN sMbLogError := 'ACKNOWLEDGE';END_IF
IF byMbLog = 16#6 THEN sMbLogError := 'SLAVE_DEVICE_BUSY';END_IF
IF byMbLog = 16#8 THEN sMbLogError := 'MEMORY_PARITY_ERROR';END_IF
IF byMbLog = 16#A THEN sMbLogError := 'GATEWAY_PATH_UNAVAILABLE';END_IF
IF byMbLog = 16#B THEN sMbLogError := 'GATEWAY_DEVICE_FAILED_TO_RESPOND';END_IF
IF byMbLog = 16#A1 THEN sMbLogError := 'RESPONSE_TIMEOUT';END_IF
IF byMbLog = 16#A2 THEN sMbLogError := 'RESPONSE_CRC_FAIL';END_IF
IF byMbLog = 16#A3 THEN sMbLogError := 'RESPONSE_WRONG_SLAVE';END_IF
IF byMbLog = 16#A4 THEN sMbLogError := 'RESPONSE_WRONG_FUNCTIONCODE';END_IF
IF byMbLog = 16#A5 THEN sMbLogError := 'TCP_COMMUNICATION_ERROR';END_IF
IF byMbLog = 16#A6 THEN sMbLogError := 'RESPONSE_INVALID_DATA';END_IF
IF byMbLog = 16#A7 THEN sMbLogError := 'RESPONSE_INVALID_PROTOCOL';END_IF
IF byMbLog = 16#A8 THEN sMbLogError := 'RESPONSE_INVALID_HEADER';END_IF
IF byMbLog = 16#FF THEN sMbLogError := 'UNDEFINED';END_IF

sMbLog := CONCAT(TSandCYCLES, sMbLogError);
sMbLog := CONCAT(sMbLog, '$N');

FB_FIFO_64_STRING_0 (StrIn:=sMbLog, E:=TRUE, RD:=FALSE, WD:=xLogMbWrite, RST:=FALSE);
END_IF



Сам код работает. У меня возникли такие вопросы:
1. Правильно ли отлавливать ошибки по Modbus_TCP_Slave.xError, не нужна ли дополнительная обработка? (например при обрыве связи постоянно сыплет сообщения "TCP_COMMUNICATION_ERROR").
2. Можно ли уйти от большего количества конструкций "IF byMbLog = 16# THEN sMbLogError := 'ERROR'; END_IF с расшифровками ошибок? (пришлось ручками набивать, не придумал как иначе).

Спасибо.

Евгений Кислов
11.03.2019, 17:05
1. Правильно, при TCP_COMMUNICATION_ERROR xError тоже должна быть в TRUE.
2. Можно оформить код в функцию, где через СASE формировать строку.
Интересный момент - если вы попали в условие xError, то в теории (byMbLog = 16#0) у вас не может быть.

capzap
11.03.2019, 17:17
по второму вопросу, насколько помню в КДС есть библиотека ошибок, там вроде должна быть функция или я её сам делал, смысл как раз в том и заключается, на вход подать цифровое значение в ответ получить текстовое определение ошибки

Евгений Кислов
11.03.2019, 17:26
по второму вопросу, насколько помню в КДС есть библиотека ошибок, там вроде должна быть функция или я её сам делал, смысл как раз в том и заключается, на вход подать цифровое значение в ответ получить текстовое определение ошибки

Вы, видимо, про CmpErrors, но это несколько из другой оперы и функций получения текста ошибок там все равно нет.
В свежих версиях CODESYS можно просто выполнить конверсию TO_STRING от ENUM и получить текст (если разработчики не забыли поставить нужный атрибут в ENUM).

Салихов Ильдар
11.03.2019, 18:00
Конверсия ENUM сразу в STRING прям в точку.... А что за атрибут должен быть выставлен у ENUM?

Евгений Кислов
11.03.2019, 19:12
Конверсия ENUM сразу в STRING прям в точку.... А что за атрибут должен быть выставлен у ENUM?

{attribute 'to_string'}
https://help.codesys.com/api-content/2/codesys/3.5.14.0/en/_cds_pragma_attribute_to_string/

Но это в свежих версиях CODESYS появилось, в SP5 Patch 5 работать не будет.

Салихов Ильдар
12.03.2019, 10:04
Вот, что получилось в итоге у меня.
Устранил замечания по теме. Убрал запись повторяющихся статусов ошибок, чтобы не разрастался лог. Добавил разовую запись лога 'RESPONSE_SUCCESS' для определения времени конца ошибки.
Покритикуйте ...



// отлавливаем статус ошибки UNDEFINED
IF (Modbus_TCP_Slave.byModbusError <> 16#FF) THEN
MbState:= Modbus_TCP_Slave.byModbusError;
END_IF

// обработка ошибки
IF Modbus_TCP_Slave.xError THEN

// преобразовать код ошибки в строку
CASE MbState OF
16#1: sMbLogError := 'ILLEGAL_FUNCTION';
16#2: sMbLogError := 'ILLEGAL_DATA_ADDRESS';
16#3: sMbLogError := 'ILLEGAL_DATA_VALUE';
16#4: sMbLogError := 'SLAVE_DEVICE_FAILURE';
16#5: sMbLogError := 'ACKNOWLEDGE';
16#6: sMbLogError := 'SLAVE_DEVICE_BUSY';
16#8: sMbLogError := 'MEMORY_PARITY_ERROR';
16#A: sMbLogError := 'GATEWAY_PATH_UNAVAILABLE';
16#B: sMbLogError := 'GATEWAY_DEVICE_FAILED_TO_RESPOND';
16#A1:sMbLogError := 'RESPONSE_TIMEOUT';
16#A2:sMbLogError := 'RESPONSE_CRC_FAIL';
16#A3:sMbLogError := 'RESPONSE_WRONG_SLAVE';
16#A4:sMbLogError := 'RESPONSE_WRONG_FUNCTIONCODE';
16#A5:sMbLogError := 'TCP_COMMUNICATION_ERROR';
16#A6:sMbLogError := 'RESPONSE_INVALID_DATA';
16#A7:sMbLogError := 'RESPONSE_INVALID_PROTOCOL';
16#A8:sMbLogError := 'RESPONSE_INVALID_HEADER';
END_CASE

// если статус ошибки поменялся, то в лог
IF MbState <> MbStatePREV THEN
sMbLog := CONCAT(TSandCYCLES, sMbLogError);
sMbLog := CONCAT(sMbLog, '$N');
FB_FIFO_64_STRING_0 (StrIn:=sMbLog, E:=TRUE, RD:=FALSE, WD:=TRUE, RST:=FALSE);
END_IF

END_IF

// для разового лога RESPONSE_SUCCESS
IF (MbState = 16#0) AND (MbStatePREV <> 16#0) THEN
sMbLog := CONCAT(TSandCYCLES, 'MODBUS OK: RESPONSE_SUCCESS');
sMbLog := CONCAT(sMbLog, '$N');
FB_FIFO_64_STRING_0 (StrIn:=sMbLog, E:=TRUE, RD:=FALSE, WD:=TRUE, RST:=FALSE);
END_IF

// индикация ошибки
IF (MbState <> 16#0) THEN xLedError:= TRUE;
ELSE xLedError:= FALSE;
END_IF

// сохраняем текущий статус ошибки
MbStatePREV := MbState;

Салихов Ильдар
15.03.2019, 10:11
Допиливаю свой логер...
Вот, что сейчас накидал:
41740
ФБ FB_MB_CREATE_LOG_0 формирует строку с расшифровкой ошибки и с меткой времени. Метка времени TimeStamp формируется каждый цикл отдельным ФБ (нужна и для других задач). Решил метку времени дополнить номером цикла работы ПЛК (мне кажется секундный TimeStamp + цикл вполне будет удобно для анализа).
ФБ FB_MB_LOG_TO_FILE_0 готовую строку по сигналу xMsgWRITE пишет в файл. Внутри этого блока: ФБ FIFO буфер, логика записи в файл, и ФБ записи в файл. Строка сначала записывается в FIFO буфер, потом по логике (у меня сигнал "буфер не пуст") записывается в файл.
У меня собственно 2 вопроса:
1) как Вам решение?
2) логеров будет несколько, каждый будет писать в свой файл. Как правильно инициализировать каждый экземпляр ФБ FB_MSG_TO_FILE при объявлении, своим значением имени файла для записи? Сейчас имя жестко прошито в коде ФБ.

Спасибо.

Салихов Ильдар
15.03.2019, 13:20
2) логеров будет несколько, каждый будет писать в свой файл. Как правильно инициализировать каждый экземпляр ФБ FB_MSG_TO_FILE при объявлении, своим значением имени файла для записи?
Почитал. Метод FB_init то, что нужно.