Хочу закрыть этот вопрос окончательно.
У OPC Lectus есть возможность добавлять узлы работающие через такие вот ретрансляторы:
Lectus.jpg
А так как он является OPC-сервером, то интегрируется с любой Scada.
Будет полезно новичкам, как пример использования библиотеки UNM.lib и как возможность организации доступа нескольких мастеров в одну RS485-сеть.
Видео испытания кода
ModbusGate.JPG
Код:PROGRAM PLC_PRG VAR Port: DWORD := 255; State: INT := 0; (* Состояние ретранслятора *) Addr: BYTE := 1; (* Адрес ретранслятора - задается перед заливкой в ПЛК *) CRC: WORD; (* Контрольная сумма пакетов *) Timeout: TIME := T#50ms; (* Таймаут ожидания ответного пакета из нижней сети - задается перед заливкой в ПЛК *) pBuf: POINTER TO STRING; pByte: POINTER TO BYTE; IN: POINTER TO RBDATA; (* Принятый байт *) TMR: TON; (* Таймер отсчета таймаутов *) HeadTCP: ARRAY[1..4] OF BYTE; (* Заголовок ModbusTCP *) Buf: ARRAY [1..128] OF BYTE; (* Буфер *) Item: INT; Count: WORD; END_VAR CASE State OF 0: (* Выполняем захват всех UNM-устройств *) IF LockDevice(0) = 1 AND LockDevice(1) = 1 AND LockDevice(2) = 1 THEN Count := 0; State := 1; END_IF 1: (* Прием пакета с верхней сети *) IF Port = 255 THEN (* Пока ждем кто начнет первым *) IN := GetByte(0); (* Слушаем TCP *) IF IN > 0 THEN Port := 0; Count := 1; Buf[Count] := IN^.data; (* Итак TCP был первым *) ELSE IN := GetByte(1); (* Слушаем RS232 *) IF IN > 0 THEN Port := 1; Count := 1; Buf[Count] := IN^.data; (* Итак RS-232 был первым *) END_IF END_IF ELSE (* Здесь кто был первым уже известно *) IN := GetByte(Port); (* Принимаем очередной байт *) IF IN > 0 THEN TMR(IN:=FALSE); Count := Count + 1; Buf[Count] := IN^.data; (* И сохраняем в буфер *) ELSE IF Count > 0 THEN TMR(IN := TRUE, PT := T#1ms); (* Здесь желательно установить PT:= 3.5/(Baudrate*10) *) IF TMR.Q THEN (* Прем пакета завершен *) TMR(IN:=FALSE); IF Count >= 5 THEN (* Длина пакета достаточна для анализа *) CASE Port OF 0: (* Обработка ModbusTCP-пакета *) FOR CRC := 1 TO 4 DO (* Сохраним заголовок ModbusTCP-пакета *) HeadTCP[CRC] := Buf[CRC]; END_FOR pByte := ADR(Buf) + 6; CRC := CalcCRC(pByte, Count - 6); (* Вычислим CRC *) pByte := ADR(Buf) + Count; pByte^ := WORD_TO_BYTE(CRC MOD 256); (* Добавляем младший байт CRC *) pByte := pByte + 1; pByte^ := WORD_TO_BYTE(CRC / 256); (* Добавляем старший байт CRC *) pBuf := ADR(Buf) + 6; SetByte(2, pBuf^, Count - 4); (* Отправляем в порт RS485-1 *) Count := 0; State := 2; (* Переходим к ожиданию ответа *) 1: (* Обработка ModbusRTU-пакета *) pByte := ADR(Buf); CRC := CalcCRC(pByte, Count - 2); (* Вычисляем CRC *) pByte := ADR(Buf) + Count - 2; IF pByte^ = (CRC MOD 256) THEN (* Проверяем младший байт CRC *) pBYte := ADR(Buf) + Count - 1; IF pByte^ = (CRC / 256) THEN (* Проверяем старший байт CRC *) pByte := ADR(Buf); IF pByte^ = Addr THEN (* Проверяем адрес ретранслятора *) pBuf := ADR(Buf) + 1; SetByte(2, pBuf^, Count - 3); (* Отправляем в RS485 встроенный пакет *) Count := 0; State := 2; (* Переходим к ожиданию ответа *) ELSE Count := 0; Port := 255; (* Пакет не нам - ждем следующий *) END_IF ELSE Count :=0; Port := 255; (* Неверный CRC - ждем следующий *) END_IF ELSE Count := 0; Port := 255; (* Неверный CRC - ждем следующий *) END_IF END_CASE ELSE Count := 0; Port := 255; (* Пакет слишком маленький - ждем следующий *) END_IF END_IF (* Ожидаем окончание приема *) END_IF (* Ожидаем входной пакета *) END_IF (* Ожидаем очередной байт *) END_IF (* Ожидаем первый байт для определения первого UNM *) 2: (* Ожидание ответного пакета из нижней сети *) IN := GetByte(2); IF IN > 0 THEN TMR(IN := FALSE); Count := Count + 1; Buf[Count] := IN^.data; ELSE IF Count = 0 THEN TMR(IN := TRUE, PT := Timeout); IF TMR.Q THEN TMR(IN:=FALSE); Count := 0; Port := 255; State := 1; (* Нет ответа - начинаем все с начала*) END_IF ELSE TMR(IN := TRUE, PT := T#1ms); (* Контроль окончания приема *) IF TMR.Q THEN (* Окончание приема *) TMR(IN:=FALSE); pByte := ADR(Buf); CRC := CalcCRC(pByte, Count - 2); pByte := ADR(Buf) + Count - 2; IF pByte^ = (CRC MOD 256) THEN pByte := ADR(Buf) + Count - 1; IF pByte^ = (CRC / 256) THEN CASE Port OF 0: (* Заворачиваем ModbusTCP *) FOR Item := Count + 7 TO 7 BY -1 DO (* Сдвинем буфер на 6 байт вправо*) Buf[Item] := Buf[Item - 6]; END_FOR FOR Item := 1 TO 4 DO (* Вернем заголовок ModbusTCP *) Buf[Item] := HeadTCP[Item]; END_FOR pByte := ADR(Buf) + 4; pByte^ := WORD_TO_BYTE((Count -2) / 256); (* Добавим CRC *) pByte := ADR(Buf) + 5; pByte^ := WORD_TO_BYTE((Count-2) MOD 256); pBuf := ADR(Buf); SetByte(0, pBuf^, Count + 4); (* Вернем верхней сети 6(Head) + Count - 2(CRC) *) Count := 0; Port := 255; State := 1; (* И начнем все с начала *) 1: (* Заворачиваем в ModbusRTU *) FOR Item := Count + 2 TO 2 BY -1 DO (* Сдвигаем буфер на 1 байт вправо *) Buf[Item] := Buf[Item-1]; END_FOR pByte := ADR(Buf); pByte^ := Addr; (* Добавляем адрес ретранслятора *) CRC := CalcCRC(pByte, Count + 1); (* Рассчитаем CRC нового пакета *) pByte := ADR(Buf) + Count + 1; pByte^ := WORD_TO_BYTE(CRC MOD 256); (* Добавим младший байт CRC *) pByte := ADR(Buf) + Count + 2; pByte^ := WORD_TO_BYTE(CRC / 256); (* Добавим старший байт CRC *) pBuf := ADR(Buf); SetByte(1, pBuf^, Count + 3); (* Вернем верхней сети 1(Addr) + Count + 2(CRC) *) Count := 0; Port := 255; State := 1; (* И начнем все с начала *) END_CASE ELSE Count :=0; Port := 255; State := 1; (* Неверна CRC - все с начала *) END_IF ELSE Count :=0; Port := 255; State := 1; (* Неверна CRC - все с начала *) END_IF END_IF (* Ожидание окончания приема *) END_IF (* Ожидаем очередной байт *) END_IF (* Ожидаем входной пакет *) END_CASEКод:FUNCTION CalcCRC : WORD VAR Cnt: BYTE; END_VAR VAR_INPUT pData: POINTER TO BYTE; Size: WORD; END_VAR (* Вычисление контрольной суммы MODBUS RTU CRC *) CalcCRC := 16#FFFF; WHILE Size > 0 DO CalcCRC := CalcCRC XOR pData^; FOR Cnt := 0 TO 7 DO IF CalcCRC.0 = 0 THEN CalcCRC := SHR(CalcCRC, 1); ELSE CalcCRC := SHR(CalcCRC, 1) XOR 16#A001; END_IF END_FOR; pData := pData + 1; Size := Size - 1; END_WHILE


Ответить с цитированием