Для тех кто будет искать решение.
У меня тоже возникла задача читать данные с устройства подключенного через 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;




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