PDA

Просмотр полной версии : PLC 110-30 v2 & сокеты



lazy
14.03.2017, 15:31
PLC model MODEL PLC 110-30 v2
Binary VERSION 0.3.67
Target version = 3.11
Compiled: 12:54:22 Feb 8 2017
STM32 binary version 114

Добрался до сокетов. Пока, поднять TCP IP Не получаецо )

IF m_dnSocket = SOCKET_INVALID THEN

m_dnSocket := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_IP );
END_IF

на 100м и старом 110м при каждом новом создании сокета (если не закрывать старые) SysSockCreate возвращал в m_dnSocket числа от 1 до 15. На новом могу получить 4, затем 8 или 52, затем 128 итд...

Хорошо, создадим сокет и сразу закроем.

IF SysSockClose ( m_dnSocket ) THEN
m_dnSocket := SOCKET_INVALID;
END_IF

Корректный SysSockClose на 100м контроллере возвращал 1 (true), на старом 110м 0 (false).
Новый всегда возвращает 1 (true), что ему не скармливай, реальный сокет или любое число, хоть -1. Сам сокет, похоже не закрывается, т.к после SysSockClose SysSockCreate возвращает любое бОльшее число чем ранее вызванный SysSockCreate. Тогда как старые ПЛК: создал сокет скажем dnSocket = 3, закрыл его, создаешь новый и снова dnSocket = 3.

У кого ни будь сокеты на новом 110м пашут? )

_Pavel_
14.03.2017, 19:34
Сокеты на 110 М02 работают нормально. Дескриптор действительно увеличивается, но это нормально. Есть ньюанс с переводом в неблокирующий режим поищите в теме про новый плк 110.

lazy
15.03.2017, 13:53
Есть ньюанс с переводом в неблокирующий режим поищите в теме про новый плк 110.

Правильно ли я это делаю?

o_dnRes := SysSockIoctl( m_dnSocket, SOCKET_FIONBIO, ADR( m_dwParam ) );

SysSockIoctl постоянно возвращает ноль, может и делает чо... )

Еще вопрос. Что должна возвращать SysSockBind? Все время ведет себя по разному. То true, то false... А связи пока все нет )

_Pavel_
15.03.2017, 22:17
Правильно ли я это делаю?

o_dnRes := SysSockIoctl( m_dnSocket, SOCKET_FIONBIO, ADR( m_dwParam ) );

SysSockIoctl постоянно возвращает ноль, может и делает чо... )




Не, неправильно, теперь это работает так:

SysSockSetOption(m_dnSocket, SOCKET_SOL, SOCK_NBIO, 0, 0);

где: SOCK_NBIO: WORD:=16#1014;

Андрей Шатохин
16.03.2017, 20:11
Не, неправильно, теперь это работает так:

SysSockSetOption(m_dnSocket, SOCKET_SOL, SOCK_NBIO, 0, 0);

где: SOCK_NBIO: WORD:=16#1014;



Представители ОВЕНА че молчат?
Павел вы не из ОВЕНА?

Андрей Шатохин
16.03.2017, 20:17
У меня тоже с этой прошивкой не пашут сокеты,

PLC model MODEL PLC 110-32
Binary VERSION 0.3.52
Need Target version >= 3.07
Compiled: 14:59:00 Mar 22 2016
STM32 binary version 114

а вот с такой прошивкой все ОК

Андрей Шатохин
16.03.2017, 20:19
OwenCloud гасит сокеты похоже!!! =))
Как вернуть прошивку предыдущую????


0.3.67 - Исправлен обмен при групповых запросах RS-485
- Исправлено появление ошибки 81 на RS-485-1 при корректном опросе

0.3.66 - Исправлена работа с TCP сокетами через библиотеку SysLibSocket.lib
- Добавлена поддержка OwenCloud

0.3.65 - Исправлена возможность перезагрузки ПЛК при работе по RS-485 при нестабильном соединении

0.3.64 - Улучшен обмен по RS-485

0.3.57 - Исправлен механизм Network Variables при обмене между ПЛК на Codesys v2 и v3
- Добавлена поддержка функций побитового чтения/записи в Modbus

0.3.56 - Улучшен обмен по RS-485

0.3.55 - Улучшена работа Modbus TCP при работе с несколькими slave при обрывах связи
- Поправлен функционал сетевых переменных (режим приемника)

0.3.53 - Исправлено чтение файлов при использовании 20 функции Modbus

0.3.52 - Исправлена невозможность повторного подключения к ПЛК при отключении опроса OPC сервера по Modbus TCP

0.3.51 - Добавлена поддержка чипов FTDI2322, подключаемых по USB
- Улучшена совместимость с библиотекой UNM

Андрей Шатохин
16.03.2017, 20:21
ОВЕНцы, дайте пример работы с сокета!! И самое главное! исправленная логика будет обратно совместима с предыдущими контроллерами и прошивками???

lazy
17.03.2017, 10:46
Исправленная логика будет обратно совместима с предыдущими контроллерами и прошивками???

Я уже отдельный параметр завел в настройках сокетов - тип ПЛК, ибо они все разные значения возвращают в SysLibSockets'e )

А на твоей прошивке RS485 не работает (http://www.owen.ru/forum/showthread.php?t=26307) )
Хотя, контроллер хороший, быстрый )

Дмитрий Артюховский
17.03.2017, 11:11
ОВЕНцы, дайте пример работы с сокета!! И самое главное! исправленная логика будет обратно совместима с предыдущими контроллерами и прошивками???

не будет

CASE FASE_CONNECT OF

0: (* ОТКРЫТИЕ СОКЕТА *)
sock_UDP := SysSockCreate( SOCKET_AF_INET, SOCKET_DGRAM, SOCKET_IPPROTO_UDP );

sock_adr_in.sin_addr := SOCKET_INADDR_ANY; (* АДРЕС ДЛЯ ПОЛУЧЕНИЯ ПАКЕТОВ - ЛЮБОЙ *)
sock_adr_in.sin_family := SOCKET_AF_INET; (* протокол IPv4 *)
sock_adr_in.sin_port := SysSockHtons( 5555 ); (* НОМЕР ПОРТА ДЛЯ ПОЛУЧЕНИЯ ВХОДНЫХ ПАКЕТОВ. используем функцию привода номера порта к стандарту *)

FASE_CONNECT := 90;

90:
RES_OPT := SysSockSetOption( sock_UDP, SOCKET_SOL, SOCKET_SO_REUSEADDR, 0, 0 );

BIND_RES := SysSockBind( sock_UDP, ADR( sock_adr_in ), SIZEOF( sock_adr_in ) ); (* ПРИВЯЗАТЬ СОКЕТ к портам и адресам ДЛЯ ПРОСЛУШКИ *)

(* поля структуры будут заполнены при приеме пакета *)
sock_adr_out.sin_addr := 16#0A000601; (* АДРЕС ДЛЯ ОТПРАВКИ ПАКЕТОВ, ПОЛЯ МОГУТ БЫТЬ ПРОИЗВОЛЬНЫМИ - БУДУТ ЗАПОЛНЕНЫ ПРИ ПОЛУЕНИИ ДАТАГРАММЫ *)
sock_adr_out.sin_family := SOCKET_AF_INET;
sock_adr_out.sin_port := SysSockHtons( 5555 ); (* НОМЕР ПОРТА НА КОТОРЫЙ ОТВЕЧАЕМ - БУДЕТ ЗАПОЛНЕН ПО ПОЛУЧЕНИЮ ВХОДНОГО ПАКЕТА *)


RES_OPT := SysSockSetOption( sock_UDP, SOCKET_SOL, SOCK_NBIO, 0, 0 ); (* ПЕРЕВЕСТИ СОКЕТ В НЕБЛОКИРУЮЩИЙСЯ РЕЖИМ для М02 *)

FASE_CONNECT := 1;

(* TRUE_ADDR := SysSockHtonl( 16#0A000601 );*)

1: (* ОЖИДАЕМ ПРИЕМ *)
READ_BYTES := SysSockRecvFrom( sock_UDP, ADR( BUF_RCV[0] ), 1535, 0, ADR( sock_adr_out ), SIZEOF( sock_adr_out ) ); (* ПРИНЯТЬ ДАННЫЕ, НАКОПЛЕННЫЕ ЗА ИНТЕРВАЛ - поместить в структуру обратный адрес пакета *)
(* read_bytes - количество байт данных
buf_rcv - данные
sock_adr_out - адрес и порт откуда получена датаграмма *)



IF READ_BYTES > 0 THEN
(* ЕСТЬ ПРИНЯТЫЕ ДАННЫЕ В ТЕКУЩЕМ ЦИКЛЕ - ДАННЫЕ ИЗ ПРОИЗВОЛЬНОГО ИСТОЧНИКА *)
(*
TEST_D := TEST_D + READ_BYTES;
*)
PACK_COUNTER := PACK_COUNTER + 1;

(* нужно было в прошивке 0.3.53 *) (* так в прошивке 0.3.66 *)
IF sock_adr_out.sin_addr = (*SysSockHtonl( 16#0A000601 )*) (*TRUE_ADDR*) 16#0A000601 THEN (* используем функцию привода адреса к стандарту *)
(* ДАТАГРАММА ОТ КОМПЬЮТЕРА - ПРАВИЛЬНОГО ХОСТА! *)
FASE_CONNECT := 22;
END_IF
END_IF

22:(* ОБРАБОТАТЬ ПРИНЯТЫЙ ПАКЕТ, ПОДГОТОВИТЬ ОТВЕТ *)
FASE_CONNECT := 33;
(* ПЕРЕДАЕМ БУФЕР В СОКЕТ *)
RES := SysSockSendTo( sock_UDP, ADR( BUF_SND[0] ), LENGHT + 5, 0, ADR( sock_adr_out ), SIZEOF( sock_adr_out ) ); (* ПЕРЕДАТЬ ДАННЫЕ В СОКЕТ *)

(* УСТАНОВИМ ФАЗУ ОЖИДАНИЯ ВХОДЯЩЕГО ПАКЕТА *)
FASE_CONNECT := 1;

END_CASE

Дмитрий Артюховский
17.03.2017, 11:12
задублировалосьь

lazy
17.03.2017, 11:46
Дмитрий, спасибо! А для tcp/ip? Сделал по аналогии, пока не работает )

SysSockBind!!!

Выключаем плк. Включаем, создаем сокет. Доходим до SysSockBind - она все время возвращает false.
Делаем программный сброс ПЛК. Запускаем программу, cоздаем сокет, доходим до SysSockBind - она все время возвращает true!

ЗЫ: похоже, SysSockBind должен возвратить false (0) только нужно несколько раз вызвать.

Дмитрий Артюховский
17.03.2017, 14:28
а он (ответ) вам нужен? вызвали функцию один раз и забыли

lazy
17.03.2017, 16:10
Дык не работает, что с ответами, что без ). Можно пример для тср ip? вы же не головоломки продаете, облегчИте жизнь не легкую )

Андрей Шатохин
17.03.2017, 20:09
Спасибо! Буду разбираться.

Андрей Шатохин
17.03.2017, 20:12
Я уже отдельный параметр завел в настройках сокетов - тип ПЛК, ибо они все разные значения возвращают в SysLibSockets'e )

А на твоей прошивке RS485 не работает (http://www.owen.ru/forum/showthread.php?t=26307) )
Хотя, контроллер хороший, быстрый )

АААААААААААААААААААААА! как так-то? у меня работает RS-485, у меня висят два СМИ2 по модбасу тьфу-тьфу работают. =)))))))))))))
Но вообще треш конечно, я вчера налаживал два изделия, и тут такие новости с прошивками. И уже не первый раз такая тема с сокетами, был у меня уже стресс при переходе с 110-60 =))))

lazy
09.04.2017, 11:40
Ребята, Овен, пример сервера на TCP IP, для 110 v2. Вопрос все еще актуален!
Впрочем, клиента тоже не плохо было бы выложить )

lazy
13.04.2017, 13:07
Все снова и снова поднимаю тему сервера TCP IP, для 110 v2. И снова и снова в ответ тишина. )

_Pavel_
14.04.2017, 08:30
Все снова и снова поднимаю тему сервера TCP IP, для 110 v2. И снова и снова в ответ тишина. )

Я сервер на [M02] не поднимал но TCP-клиент у меня работает отлично. Выложите свой код может удастся помочь...

Трофимов Артем
14.04.2017, 10:07
Прикладываю modbusTCP клиент. оформлен не до конца, делал для себя.
тестировал на Lectus , PLC110_slave и некоторых других устройствах.

обращаю внимание Ваше. для стабильной работы сокетов используйте компилятор версии 2.3.8.1

lazy
16.04.2017, 21:10
Выложите свой код может удастся помочь...

Вот этот код работает на старых ПЛК и не работает на м2. Не работает сервер, клиента не проверял. Переход в неблокирующий режим новым способом в код добавлял. Сервер кагбэ поднимаецо но чтение/запись возвращает -1. В старом варианте бибки это говорит об обрыве связи.

http://www.owen.ru/forum/showthread.php?t=16023&page=2&p=122143#post122143


Прикладываю modbusTCP клиент.

Спасибо! Просто мне ОВЕН обещал пример сервера но чота тихо. )

Мастеренко Иван
17.04.2017, 10:51
Вот этот код работает на старых ПЛК и не работает на м2. Не работает сервер, клиента не проверял. Переход в неблокирующий режим новым способом в код добавлял. Сервер кагбэ поднимаецо но чтение/запись возвращает -1. В старом варианте бибки это говорит об обрыве связи.

http://www.owen.ru/forum/showthread.php?t=16023&page=2&p=122143#post122143



Спасибо! Просто мне ОВЕН обещал пример сервера но чота тихо. )

Прикладываю пример обмена одного сокета ПЛК110_М02 клиента и сервера ниже.
30631

lazy
17.04.2017, 12:36
Прикладываю пример обмена одного сокета ПЛК110_М02 клиента и сервера ниже.

Правильно ли я понимаю, что теперь к серверу может подключаться до четырех клиентов? или все осталось по старому один сокет -одно подключение? И из кода непонятно следующее:

2:
(*готовы "слушать"*)
listen:=SysSockListen(sock,diMaxConnections);
IF listen<0 THEN
state:=0;
ELSE state:=3;
END_IF

то есть, если listen<0 идем создавать новый сокет, а старый закрыть?

Филоненко Владислав
17.04.2017, 14:11
Теперь может подключаться несколько клиентов. Accept работает.
Однако, т.к. ресурсы не резиновые, если есть 2 разных сервера, свободные сокеты между ними распределяются в конкурентном режиме и если на один сервер зайдёт много клиентов - другому ничего не достанется.

Да, если ошибки - всё закрываем и по новой

lazy
17.04.2017, 15:59
Клиент работает, но непонятно, почему SysSockRecv и SysSockSend так часто возвращают -1. На старых ПЛК это однозначно говорило об разрыве связи и при получении -1 во время чтения/записи сокет можно было переоткрывать чтобэ связь восстановить.

Филоненко Владислав
17.04.2017, 16:38
Они и должны возвращать -1 при отсутствии данных.
Варианта 2 - либо считать код посл ошибки, либо таймаут на обрыв связи.
Лучше и то и другое :)

lazy
18.04.2017, 15:21
Они и должны возвращать -1 при отсутствии данных.
Варианта 2 - либо считать код посл ошибки, либо таймаут на обрыв связи.
Лучше и то и другое :)

Аллилуйя, сервер заработал )

Тем не менее, можно поподробнее о SysSockGetLastError? Что там за значения в wErrorId и dwLastError могут быть?

Андрей Шатохин
28.04.2017, 22:52
Аллилуйя, сервер заработал )

Тем не менее, можно поподробнее о SysSockGetLastError? Что там за значения в wErrorId и dwLastError могут быть?

Покажи сервер плииииз.

Филоненко Владислав
29.04.2017, 16:59
смотрите msdn.com - хелп оп сокетам

lazy
02.05.2017, 16:27
Покажи сервер плииииз.

CASE o_eCondition OF

ST_EXPECT:
IF i_pOptions^.bEnable THEN
IF m_dnSocket = SOCKET_INVALID THEN
m_dnSocket := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_IP );
ELSE
IF SysSockSetOption( m_dnSocket, SOCKET_SOL, SOCK_NBIO, 0, 0 ) THEN
o_eCondition := ST_SETUP;
ELSE
o_eCondition := ST_CLOSE;
END_IF
END_IF
END_IF

ST_SETUP:
m_y := 255;
o_eCondition := ST_CONECT;
m_SAddress.sin_family := SOCKET_AF_INET;
IF i_pOptions^.bServer THEN
m_SAddress.sin_addr := SOCKET_INADDR_ANY;
m_SAddress.sin_port := SysSockHtons( i_pOptions^.wMyPort );
IF NOT SysSockBind( m_dnSocket, ADR( m_SAddress ), SIZEOF( m_SAddress ) ) THEN
o_eCondition := ST_CLOSE;
END_IF
ELSE
m_SAddress.sin_port := SysSockHtons( i_pOptions^.wOutPort );
m_SAddress.sin_addr := SysSockHtonl( SHL( SHL( SHL(
BYTE_TO_DWORD( i_pOptions^.yOutAddr1 ), 8 )
OR BYTE_TO_DWORD( i_pOptions^.yOutAddr2 ), 8 )
OR BYTE_TO_DWORD( i_pOptions^.yOutAddr3 ), 8 )
OR BYTE_TO_DWORD( i_pOptions^.yOutAddr4 ) );
END_IF

ST_CONECT:
m_y := m_y + 1;
IF ( m_y MOD 50 ) = 0 THEN
IF i_pOptions^.bServer THEN
o_dnRes := BOOL_TO_DINT( SysSockListen( m_dnSocket, 1 ) );
IF i_pOptions^.ePCL = OVEN_110_v2 THEN
IF o_dnRes < 0 THEN
o_eCondition := ST_CLOSE;
ELSE
o_eCondition := ST_ACCEPT;
END_IF
ELSE
IF o_dnRes = 0 THEN
o_eCondition := ST_WORK;
END_IF
END_IF
ELSIF NOT SysSockConnect( m_dnSocket, ADR( m_SAddress ), SIZEOF( m_SAddress ) ) THEN
o_eCondition := ST_WORK;
END_IF
END_IF

ST_ACCEPT:
m_dnClient := SysSockAccept( m_dnSocket, ADR( m_SAddress ), SIZEOF( m_SAddress ) );
IF NOT i_pOptions^.bEnable THEN
o_eCondition := ST_CLOSE;
ELSIF m_dnClient <> SOCKET_INVALID THEN
o_eCondition := ST_WORK;
END_IF

ST_WORK:
IF NOT i_pOptions^.bEnable THEN
o_eCondition := ST_BREAK;
END_IF

ST_BREAK:
o_dnRes := BOOL_TO_DINT ( SysSockShutdown( m_dnSocket, 2 ) );
CASE i_pOptions^.ePCL OF
OVEN_100: m_b := o_dnRes = 1;
OVEN_110: m_b := o_dnRes = 1;
OVEN_110_v2: m_b := o_dnRes = 0;
END_CASE
IF m_b THEN
o_eCondition := ST_CLOSE;
END_IF

ST_CLOSE:
o_bLink := FALSE;
m_tonExpet( IN := o_bLink );
IF m_dnClient <> SOCKET_INVALID THEN
IF SysSockClose( m_dnClient ) = 1 THEN
m_dnClient := SOCKET_INVALID;
END_IF
END_IF
o_dnRes := BOOL_TO_DINT( SysSockClose ( m_dnSocket ) );
CASE i_pOptions^.ePCL OF
OVEN_100: m_b := o_dnRes = 1;
OVEN_110: m_b := o_dnRes = 0;
OVEN_110_v2: m_b := o_dnRes = 1;
END_CASE
IF m_b
AND m_dnClient = SOCKET_INVALID THEN
m_dnSocket := SOCKET_INVALID;
o_eCondition := ST_EXPECT;
END_IF

END_CASE

здесь и сервер и клиент для всех ПЛК. Но на новом 110 все еще остаюцо траблы. после программного сброса сокеты не работают. это не так страшно, но неудобно.

Филоненко Владислав
03.05.2017, 10:31
После программного сброса, если нет обработчика события Reset, в котором ресурсы освобождаются, порты, сокеты и пр. ресурсы, выделяемые в программе и не должны работать.

DmitriiAnyushin
03.05.2017, 16:25
Прикладываю пример обмена одного сокета ПЛК110_М02 клиента и сервера ниже.
30631
Вопрос по серверу. Сейчас сервер работает только на передачу данных клиенту. Подскажите пожалуйста, каким образом я могу осуществить запись данных в сервер (в уже имеющийся массив my_data)? Просто заменить используемую функцию (3 на 16), я так полагаю, недостаточно. Спасибо!

Филоненко Владислав
04.05.2017, 08:50
не путайте транспортный уровень (клиент-сервер) и уровень прикладной (мастер-slave ModBus). Мухи отдельно, котлеты отдельно.
Сервер по ничего о модбасе не знает. добавляйте прикладной уровень для требуемых функций (16 там или 6) и получайте доступ на запись.

А лучше воспользуйтесь готовыми решениями.

DmitriiAnyushin
04.05.2017, 08:52
не путайте транспортный уровень (клиент-сервер) и уровень прикладной (мастер-slave ModBus). Мухи отдельно, котлеты отдельно.
Сервер по ничего о модбасе не знает. добавляйте прикладной уровень для требуемых функций (16 там или 6) и получайте доступ на запись.

А лучше воспользуйтесь готовыми решениями.
А можно посмотреть на готовое решение?

krug2000
20.11.2017, 11:24
А можно посмотреть на готовое решение?

Мой пример, если еще актуально.
http://www.owen.ru/forum/showthread.php?t=16814&p=263012&viewfull=1#post263012

Serhioromano
04.11.2018, 16:54
Для тех кто будет искать решение.

У меня тоже возникла задача читать данные с устройства подключенного через TCP. По сути просто TCP клиент. Я пробовал библиотеку ОСКАТ, но так и не смог ее запустить. Пришлось разбираться с сокетами. Ваня помог, показал пример основного принципа работы с сокетами, плюс на РНР работал с сокетами. В результате получил рабочий код, который опрашивает устройство по заданному адресу и читает заданное число регистров начиная с заданного номера. Проверял на МУ210-401.

В примерах что я видел тут, сокет сбрасывается после каждого цикла и его дескриптор меняется, так как по сути каждый раз после получения данных создается новый сокет. Я не сбрасываю сокет, а после чтения, сразу отправляю новый запрос, а потом опять читаю.

Так как весь смысл моей задачи перейти на опрос через код а не через конфигурации, было увеличить скорость опроса, что бы не было задержек. Так что ни какой паузы после чтения нет, вы можете сами вставить дополнительный шаг, если будет нужно.

Как материал для создания этой функции я использовал статью
https://www.rtaautomation.com/technologies/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;

monteg
04.11.2018, 17:38
xResult := SysSockConnect(diSock, ADR(stSocAddr), SIZEOF(stSocAddr));

не стоит доверять результату SysSockConnect

Serhioromano
04.11.2018, 18:00
xResult := SysSockConnect(diSock, ADR(stSocAddr), SIZEOF(stSocAddr));

не стоит доверять результату SysSockConnect

Да это я уже понял. Долго не мог понять почему у меня ошибка 10 все время. По этому у меня в коде IF NOT xResult THEN потому что возврата TRUE из этой функции я так и не добился, хотя все работает.

monteg
04.11.2018, 18:11
Это разработчики Овен так SysSockConnect реализовали. Наличие соединения определяется по SysSockSend. А если пытаетесь установить соединение с ПЛК М01, лучше после SysSockRecv.

ufaman
05.11.2018, 01:54
xResult := SysSockConnect(diSock, ADR(stSocAddr), SIZEOF(stSocAddr));

не стоит доверять результату SysSockConnect

Угу. Не стоит. (http://www.owen.ru/forum/showthread.php?t=29703)

Serhioromano
05.11.2018, 16:15
Блин, счастье длилось не долго. Это функция работает в проекте с тестовой программой. Если сохраняю ее как библиотеку .lib тоже работает если залить на ПЛК, а если подключаю ее к другому проекту, то там ни фига не работает. Хотя код вызова ФБ одинаковый. Что может быть?

monteg
05.11.2018, 22:13
Блин, счастье длилось не долго. Это функция работает в проекте с тестовой программой. Если сохраняю ее как библиотеку .lib тоже работает если залить на ПЛК, а если подключаю ее к другому проекту, то там ни фига не работает. Хотя код вызова ФБ одинаковый. Что может быть?

Сокеты у Овена своеобразные, но вроде работают
1. создать сокет ( SysSockCreate )
2. настроить неблокирующий режим ( SysSockSetOption )
3. попытка соединения с сервером ( SysSockConnect )
4. немного подождать
5. отправка сообщения серверу ( SysSockSend ), если отправка удалась, то переходим (6) иначе (3)
6. можно совсем немного подождать
7. прием ответа ( SysSockRecv )

Замечания:
1. ( bStep = 10 ), чтобы нечаянно не потерять сокет, я бы сделать так


IF ( handle = SOCKET_INVALID ) THEN
answer := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP );
if ( answer = SOCKET_INVALID ) then
....
else
handle := answer;
....
end_if
else
SysSockShutdown( handle, 2 );
SysSockClose( handle );
handle := SOCKET_INVALID;
end_if

2. ( bStep = 20 ) – не нужно анализировать возврат SysSockConnect, можно сразу переходить к отправке, можно немного подождать, и потом переходить к отправке
3. ( bStep = 20 ) – вызов SysSockSetOption, наверно, лучше перенести в ( bStep = 10 )
4. ( bStep = 30 ) – я не уверен, что нужно использовать флаги в SysSockSend, попробуйте 0
5. ( bStep = 30 ) – я не уверен, что SysSockSend может вернуть отрицательное значение
6. ( bStep = 30 ) – в Modbus Tcp «маленькие» пакеты, не нужно пытаться отправлять запрос по частям, т.е


answer := SysSockSend( handle, adr( data ), sizesof( data ), 0 );
if ( answer = sizeof( data ) ) then
переход на прием
else
...
end_if

7. ( bStep = 40 ) – я не уверен, что нужно использовать флаги в SysSockRecv, попробуйте 0
8. ( bStep = 40 ) – я не уверен, что SysSockRecv может вернуть отрицательное значение
9. ( bStep = 40 ) – нужно проверять ответ, а не просто


IF diResult > 0 THEN

10. ( bStep = 40 ) - размер буфера для чтения, наверно, должен быть больше 25 байт


arbBuffer: ARRAY[0..24] OF BYTE;

наверно для FC = 3 или FC = 4 размер должен быть не меньше 259 ( 9 + REG_NUM * 2, если читать до 125 регистров ), или 263 ( 9 + REG_NUM * 2, если читать до 127 регистров )

11. ( bStep = 40 ) - вашему FB очень не хватает таймера (watchdog)

Serhioromano
06.11.2018, 10:04
Я учел некоторые замечания. Но проблема ведь не в том. Код то рабочий. Если он не как библиотека подключен то он работает, а если подключить как библиотека, то перестает.


Сокеты у Овена своеобразные, но вроде работают
5. ( bStep = 30 ) – я не уверен, что SysSockSend может вернуть отрицательное значение


Возвращает,проверено и send и recv


11. ( bStep = 40 ) - вашему FB очень не хватает таймера (watchdog)

Зачем этот таймер? Мне не нужна задержка между опросами. Весь смысл перехода на сокеты был скорость работы. Даже 200мс задержки может отразиться на конечном результате.

monteg
06.11.2018, 10:27
Зачем этот таймер? Мне не нужна задержка между опросами. Весь смысл перехода на сокеты был скорость работы. Даже 200мс задержки может отразиться на конечном результате.
Таймер нужен не для задержки, а для контроля.
Почему Вы уверены, что получите ответ? Все может повиснуть в ( bStep = 40 )

monteg
06.11.2018, 10:41
Код то рабочий. Если он не как библиотека подключен то он работает, а если подключить как библиотека, то перестает.

Чудес не бывает, дело не в lib-е, а в вашем коде и коде Овена.

Slev
20.11.2018, 19:48
После программного сброса, если нет обработчика события Reset, в котором ресурсы освобождаются, порты, сокеты и пр. ресурсы, выделяемые в программе и не должны работать.

А как освободить ресурсы не по программному сбросу, а при заливке в контроллер другой программы, тоже использующей эти же ресурсы? Иначе новая программа не сможет ими воспользоваться.
У Wago, например, есть событие before_download.
А как быть здесь?

monteg
20.11.2018, 21:12
А как освободить ресурсы не по программному сбросу, а при заливке в контроллер другой программы, тоже использующей эти же ресурсы? Иначе новая программа не сможет ими воспользоваться.
У Wago, например, есть событие before_download.
А как быть здесь?

Наверно, никак. Только перезагрузка ПЛК.
Если работаете удаленно и ПЛК М02, то в ПЛК-Броузер есть команда rebooT

Slev
22.11.2018, 14:04
Наверно, никак. Только перезагрузка ПЛК.
Если работаете удаленно и ПЛК М02, то в ПЛК-Броузер есть команда rebooT

Похоже, очистку надо вешать на событие СТОП. При заливке новой программы. старую система сначала останавливает, что вполне разумно.
Так, по крайней мере, у меня всё заработало.

Филоненко Владислав
22.11.2018, 15:34
А как освободить ресурсы не по программному сбросу, а при заливке в контроллер другой программы, тоже использующей эти же ресурсы? Иначе новая программа не сможет ими воспользоваться.
У Wago, например, есть событие before_download.
А как быть здесь?
И у нас есть события, используйте их

monteg
23.11.2018, 14:51
Похоже, очистку надо вешать на событие СТОП. При заливке новой программы. старую система сначала останавливает, что вполне разумно.
Так, по крайней мере, у меня всё заработало.

Хорошо, что заработало. Я пробовал это через SysLibCallback сделать, ничего не вышло. Поэтому остановился на варианте с перезапуском.

Slev
23.11.2018, 15:26
Хорошо, что заработало. Я пробовал это через SysLibCallback сделать, ничего не вышло. Поэтому остановился на варианте с перезапуском.

Перезапустить можно, когда есть возможность подключиться. А у меня - связь зависала напрочь. Контроллер находится черт знает где, поэтому, нужен был автоматический метод восстановления связи. Сначала делал через watchdog, но это плохо влияет на работу системы. Сейчас очистку сделал в СТОПе, а watchdog - последний рубеж обороны). Теперь связь почти не теряется.