Для тех кто будет искать решение.
У меня тоже возникла задача читать данные с устройства подключенного через TCP. По сути просто TCP клиент. Я пробовал библиотеку ОСКАТ, но так и не смог ее запустить. Пришлось разбираться с сокетами. Ваня помог, показал пример основного принципа работы с сокетами, плюс на РНР работал с сокетами. В результате получил рабочий код, который опрашивает устройство по заданному адресу и читает заданное число регистров начиная с заданного номера. Проверял на МУ210-401.
В примерах что я видел тут, сокет сбрасывается после каждого цикла и его дескриптор меняется, так как по сути каждый раз после получения данных создается новый сокет. Я не сбрасываю сокет, а после чтения, сразу отправляю новый запрос, а потом опять читаю.
Так как весь смысл моей задачи перейти на опрос через код а не через конфигурации, было увеличить скорость опроса, что бы не было задержек. Так что ни какой паузы после чтения нет, вы можете сами вставить дополнительный шаг, если будет нужно.
Как материал для создания этой функции я использовал статью
https://www.rtaautomation.com/techno.../modbus-tcpip/
Код:
FUNCTION_BLOCK SR_MB_TCP_CLIENT
VAR
xResult: BOOL; (* Результат функции *)
fbEnableFall: F_TRIG;
bStep: BYTE := 0; (* Текущий шаг *)
diSock: DINT;
diResult: DINT; (* Результат коммуникации *)
diCount: DINT; (* Считаем сколько байтов получено *)
fbTimeoutTON: TON;
stSocAddr: SOCKADDRESS;
uiTryCount: UINT; (* Количество ошибок подключения сокета перед сбросом *)
(* 0, 1 - Номер транзакции,
2, 3 - Протокол. 00 00 - Modbus,
4, 5 - Длина всего что ниже
6 - Адрес устройства Modbus *)
arbData: ARRAY[0..11] OF BYTE := 00, 01, 00, 00, 00, 06, 01;
arbBuffer: ARRAY[0..24] OF BYTE;
uiCount: WORD;
END_VAR
VAR_INPUT
ENABLE: BOOL; (* Блок работает *)
PORT: UINT := 502; (* Порт*)
IP: DWORD; (* IP Адрес в HEX для пример 192.168.1.99 будет 16#C0A80163 *)
TIMEOUT: TIME; (* Время ожидания при переподключении *)
REG_START: WORD; (* Начальный регистр *)
REG_NUM: WORD; (* Количество регистров *)
FC: BYTE; (* функция 03, 04*)
RESET: BOOL; (* Сброс ошибки и переподключение *)
END_VAR
VAR_OUTPUT
_ERR_CODE: BYTE; (* Код ошибки *)
_DATA: ARRAY[0..255] OF WORD; (* Выходной массив с данными *)
END_VAR
fbEnableFall(CLK := ENABLE);
IF fbEnableFall.Q THEN
bStep := 100;
END_IF;
IF RESET THEN
_ERR_CODE := 0;
bStep := 100;
END_IF;
CASE bStep OF
(* Ожидание включения *)
0:
IF ENABLE AND NOT RESET THEN
bStep := 10;
END_IF;
(* Создаем сокет *)
10:
diSock := SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP);
IF diSock <> SOCKET_INVALID THEN
bStep := 20;
IF _ERR_CODE = 5 THEN
_ERR_CODE := 0;
END_IF;
ELSE (* если нет создается пробуем еще 3 раза потом сброс *)
uiTryCount := uiTryCount + 1;
IF uiTryCount > 3 THEN
bStep := 100;
_ERR_CODE := 5;
END_IF
END_IF
(* Подключаемся *)
20:
stSocAddr.sin_family := SOCKET_AF_INET;
stSocAddr.sin_port := SysSockHtons(PORT);
stSocAddr.sin_addr := SysSockHtonl(IP);
SysSockSetOption(diSock, SOCKET_SOL, 16#1014, 0, 0) ;
xResult := SysSockConnect(diSock, ADR(stSocAddr), SIZEOF(stSocAddr));
IF NOT xResult THEN
bStep := 30;
IF _ERR_CODE = 10 THEN
_ERR_CODE := 0;
END_IF;
ELSE
bStep := 100;
_ERR_CODE := 10;
END_IF;
(* Отправляем запрос *)
30:
arbData[7] := FC; (* 7 - функция чтения*)
arbData[8] := WORD_TO_BYTE(SHR(REG_START, 8)); (* 8, 9 - начальный регистр *)
arbData[9] := WORD_TO_BYTE(REG_START);
arbData[10] := WORD_TO_BYTE(SHR(REG_NUM, 8)); (* 10, 11 - количество регистров для чтения *)
arbData[11] := WORD_TO_BYTE(REG_NUM);
diResult := SysSockSend(diSock, ADR(arbData), SIZEOF(arbData), SOCKET_MSG_OOB);
IF _ERR_CODE = 15 THEN
_ERR_CODE := 0;
END_IF;
IF diResult = -1 THEN
bStep := 100;
_ERR_CODE := 15;
END_IF
diCount := diCount + diResult;
IF diCount >= SIZEOF(arbData) THEN
diCount := 0;
bStep := 40;
END_IF;
(* Получаем данные *)
40:
diResult := SysSockRecv(diSock, ADR(arbBuffer), SIZEOF(arbBuffer), SOCKET_MSG_OOB);
IF _ERR_CODE = 41 THEN
_ERR_CODE := 0;
END_IF;
IF diResult = -1 THEN
_ERR_CODE := 41;
bStep := 100;
END_IF
IF diResult > 0 THEN
bStep := 30;
REPEAT
_DATA[uiCount] := BYTE_TO_WORD(arbBuffer[10 + (uiCount * 2)]) OR SHL(BYTE_TO_WORD(arbBuffer[9 + (uiCount * 2)]),8);
uiCount := uiCount + 1;
UNTIL
uiCount >= REG_NUM
END_REPEAT;
uiCount := 0;
END_IF
(* Сбрасываем сокет *)
100:
IF diSock > 0 THEN
SysSockShutdown(diSock, 2);
SysSockClose(diSock);
END_IF;
uiTryCount := 0;
diSock := 0;
bStep := 101;
(* Задержка перед повторным подключением *)
101:
fbTimeoutTON(IN := TRUE, PT := TIMEOUT);
IF fbTimeoutTON.Q THEN
fbTimeoutTON(IN := FALSE);
bStep := 0;
END_IF;
END_CASE;