PDA

Просмотр полной версии : Связь по ModBus Tcp/ip между двумя ПЛК 100 подключенными в сеть.



lazy
24.05.2012, 10:39
Добрый день.
Нужно связать несколько ПЛК 100 (пока их два) через Ethernet используя ModBus Tcp/ip. Использую сокеты "клиент" - "сервер".

Сервер:

SST_INIT:
IF m_dnServerSocket = SOCKET_INVALID THEN
m_dnServerSocket := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP );
END_IF;
IF m_dnServerSocket <> SOCKET_INVALID THEN
o_eCondition := SST_SETUP;
END_IF;

SST_SETUP:
IF SysSockSetOption( m_dnServerSocket, SOCKET_SOL, SOCKET_SO_REUSEADDR, ADR( m_dnTrue ), SIZEOF( m_dnTrue ) ) THEN (*m_dnTrue=1*)
m_ServerSAddres.sin_family := SOCKET_AF_INET;
m_ServerSAddres.sin_addr := SOCKET_INADDR_ANY;
m_ServerSAddres.sin_port := SysSockHtons( 502 );
IF NOT SysSockBind( m_dnServerSocket, ADR( m_ServerSAddres ), SIZEOF( m_ServerSAddres ) ) THEN
o_eCondition := SST_CLOSE;
ELSIF SysSockIoctl( m_dnServerSocket, SOCKET_FIONBIO, ADR( m_dnTrue ) ) <> 0 THEN
o_eCondition := SST_CLOSE;
ELSE
o_eCondition := SST_LISTEN;
END_IF;
END_IF;

SST_LISTEN:
IF SysSockListen( m_dnServerSocket, 1 ) THEN
o_eCondition := SST_ACCEPT;
ELSE
m_dnError := 0;
m_dwSize := SIZEOF( m_dnError );
m_bRes := SysSockGetOption( m_dnSocket, SOCKET_SOL, SOCKET_SO_ERROR, ADR( m_dnError ), ADR( m_dwSize ) );
IF NOT m_bRes OR m_dnError <> 0 THEN
o_eCondition := CST_CLOSE;
END_IF
END_IF;

дальше "прослушки" дело не идет. m_dnError = 0.


Клиент:

CST_INIT:
IF m_dnSocket = SOCKET_INVALID THEN
m_dnSocket := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP );
END_IF;
IF m_dnSocket <> SOCKET_INVALID THEN
o_eCondition := CST_SETUP;
END_IF;

CST_SETUP:
IF SysSockIoctl( m_dnSocket, SOCKET_FIONBIO, ADR( m_dnTrue ) ) = 0 THEN
m_SAddress.sin_family := SOCKET_AF_INET;
m_SAddress.sin_addr := 16#0A000008; (* IP "сервера" 10.0.0.8 *)
m_SAddress.sin_port := SysSockHtons ( 502 );
o_eCondition := CST_CONNECT;
END_IF;

CST_CONNECT:
IF m_tonExpect.Q OR NOT m_tonExpect.IN THEN
IF SysSockConnect( m_dnSocket, ADR( m_SAddress ), SIZEOF( m_SAddress ) ) THEN
o_eCondition := CST_EXPECT;
ELSE
m_dnError := 0;
m_dwSize := SIZEOF( m_dnError );
m_bRes := SysSockGetOption( m_dnSocket, SOCKET_SOL, SOCKET_SO_ERROR, ADR( m_dnError ), ADR( m_dwSize ) );
IF NOT m_bRes OR m_dnError <> 0 THEN
o_eCondition := CST_CLOSE;
END_IF
END_IF;
m_tonExpect( IN := FALSE );
END_IF;
m_tonExpect( IN := TRUE, PT := T#1s );


SysSockConnect TRUE не возвращает. m_dnError = 0.
манипуляторы сокетов почему то с двух сторон равны единице. SysSockCreate на "сервере" и на "клиенте" возвращает "1". может это и правильно, но связи нет. что я делаю не так? )

lazy
25.05.2012, 09:58
Вообще на ПЛК 100.K-M можно создать 15 сокетов и SysSockCreate последовательно будет возвращать числа (манипуляторы или дескрипторы) от 1 до 15. Предполагаю, что при удачном завершении SysSockListen, "на сервере" SysSockAccept должен возвратить сокет с манипулятором отличным от сокета созданного SysSockCreate для прослушки, а в структуре SOCKADDRESS должен оказаться ip адрес и порт клиента. Так? Какие причины могут не позволять закончится SysSockListen и SysSockConnect с TRUE?
ПЛК соединены между собой через "хаб".

Малышев Олег
25.05.2012, 10:47
Не совсем понятно зачем городить то что можно задать в конфигурации кликами мышки.
На форуме есть примеры.
Для слушающего сокета WEB сервер.
Для отправляющего тоже есть пример.
Все вполне работают.

lazy
25.05.2012, 11:38
Конфигуратор не подходит. Много переменных, количество клиентов может быть разное итд... Плюс был горький опыт работы с конфигуратором для ModBus по RS485. Связь заработала как надо только после использования библиотеки ModBus.lib. примеры которые есть на форуме читал. Например, в w3_test.pro есть такой кусок:

tcp_adr.sin_family:=SOCKET_AF_INET;
tcp_adr.sin_port:=80;
tcp_adr.sin_addr:=SOCKET_INADDR_ANY;
paddr:=ADR(tcp_adr);
res:=SysSockBind(sock,paddr,SIZEOF(tcp_adr));
res:=SysSockListen(sock,1); (*одно подключение*)
paddr:=ADR(accept_adr);
sock_acc:=sock;
param_ctrl:=1;
rcv_bytes_cnt:=0;
SysSockIoctl(sock_acc,SOCKET_FIONBIO,ADR(param_ctr l));
web_stat:=W3S_RCV;

Здесь SysSockAccept вообще не используется, хотя видно, что ее использовать пытались. И что возвращает SysSockListen тоже не важно. И данные передаются в "слушающий" сокет. Так у меня связь тоже иногда "работала". )

lazy
28.05.2012, 15:55
TCP IP, так и не удалось поднять. Но работает UDP. Теперь вопрос, достаточно на "сервере" одного сокета, для приема сообщений скажем шести-восьми клиентов, или, все таки, лучше на создавать сокетов по числу клиентов? )

lazy
29.05.2012, 09:46
Еще раз повторяю. Если не пользоваться SysSockAccept и передавать данные в "слушающий" сокет, то ТСР "работает". При этом "заявленные" TRUE от SysSockListen и SysSockConnect на ПЛК 100, дождаться не получается... ) Насколько я понимаю, это требует несколько иной организации связи при наличии нескольких клиентов (отличной от "классической").
ЗЫ: Сдается мне, что в функции TcpServerOpenSocket есть ошибки. Что будет если SysSockBind или SysSockListen завершаться с "-1"? )))

lazy
29.05.2012, 10:31
To capzap: Если SysSockBind или SysSockListen в TcpServerOpenSocket завершаться с "-1", то функция возвращает SOCKET_INVALID, не закрыв созданный сокет. Копипасты, они всегда такие копипасты...
ЗЫ: Не работает TCP IP так как хотелось бы. хочется уже услышать мнение техподдержки. Тех кто видел SysLibSocket "изнутри". В какую сторону копать? ))

lazy
29.05.2012, 11:03
Хорошо SysSockAccept присутствует. Но, что она возвращает? Мне интересен манипулятор сокета и адрес клиента возвращенный в структуре SOCKADDRESS.)

lazy
29.05.2012, 11:18
В том то и дело, что SysSockAccept нужен для того чтобы создать новый сокет привязанный к адресу клиента который в свою очередь конектицо к "слушающему" сокету. Это сделано от того что клиентов может быть несколько. И они должны знать только адрес слушающего сокета. И уже созданый SysSockAccept сокет будет использоваться для связи с клиентом. Что будет в вашем проекте если клиентов больше чем один? Как вы узнаете кто приконектилсо и с кем вы ведете "беседу"? На ПЛК 100 SysSockAccept возвращает манипулятор "слушающего" сокета и нули в структуре SOCKADDRESS (кроме sin_family ). Где гарантии, что она, а так же SysSockListen и SysSockConect вообще выполняются? Где гарантии что вы проверяете их корректное исполнение сравнивая возвращенные значения с "-1"? Такой хоккей нам не нужен ))

lazy
29.05.2012, 11:43
Если на все пять компов отправлялась одинаковая информация, то ничего удивительного в том что ваш проект работал нет. Если нет (т.е. разная) мне хотелось бы узнать (хотя бы на словах) как вы решали какому компу и по какому сокету что отправлять. ))
ЗЫ: в библиотеке oscat "не самоучки" проверяют корректность исполнения "тех самых" функций на TRUE. )

lazy
29.05.2012, 12:01
ужс...
Представители "Овен" что либо могут пояснить по поводу SysLibSocket? )

PS: Это сообщение ответ на:

одинаковая информация, это температура из модуля статистики, само значение этой температуры как понимаете ни когда одинаковым не бывает. Я ни чего не решал, библиотека сама решает, какой клиент запросил инфу и ответит на запрос именно ему, в этом и отличие TCP от UDP

а не по поводу версий библиотек. )

Малышев Олег
29.05.2012, 13:32
1) По поводу работы библиотеки.
Не нужно испытывать иллюзий. Стек TCP/IP в ПЛК очень ограниченный - это не виндовс и не линукс. Шаг влево-шаг вправо расстрел. Прыжок на месте провокация. Соответственно есть очень большие ограничения - один клиент - один сокет, числу открытых сокетов и т.д. Кривизна в библиотеке есть. В основном это связанно с тем что в новой версии поддержаны дополнительные фичи, а в старом заголовочном файле все было не совсем так.
2) OSCAT oscat network на Овеновских плк как после кодесис 2 , так и 3 заработала после допиливания напильником. Ничего особенного - но пришлось поработать. Библиотеку не могу дать - т.к. она часть проекта - я накопировал блоки в проект.
3) SocketAccept - не получится взять удаленный адрес клиент - ее можно вообще не использовать.
Если работать только с нужными IP используйте ...RecvFrom ...SendTo
4) Кусочек кода для клиента
CASE STATE_SEND OF
0: SOCK_OUT := SysSockCreate( SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP );
TCP_ADR_OUT.sin_family := SOCKET_AF_INET;
TCP_ADR_OUT.sin_port := port; TCP_ADR_OUT.sin_addr := server; (* 192.168.200.207 *)

PADDR_OUT := ADR( TCP_ADR_OUT );
OUT_TON( IN := FALSE, PT := T#50MS ); STATE_SEND := 1;
1: RES_OUT := SysSockConnect( SOCK_OUT, PADDR_OUT, SIZEOF( PADDR_OUT) ); OUT_TON( IN := TRUE, PT := T#2S ); (* ?????? ?? 20 ??? *)
IF OUT_TON.Q THEN
STATE_SEND := 2;
END_IF

5) Для сервера
Настойчиво рекомендую посмотреть повнимательнее пример Web сервера.

lazy
07.06.2012, 18:01
Может кому понадобицо. Вобщем, ТСР удалось поднять. SysSockListen и SysSockConnect завершившись корректно возвращают ноль. То есть код нужно поправить приблизительно так:

для сервера:
IF BOOL_TO_INT( SysSockListen( m_dnSocket, 1 ) ) = 0 THEN
(* законектились *);
END_IF;

для клиента
IF BOOL_TO_INT( SysSockConnect( m_dnSocket, ADR( m_SAddress ), SIZEOF( m_SAddress ) ) ) = 0 THEN
(* законектились *);
END_IF;

Соответственно, вызывать их нужно по таймеру. пока не будет конекта, а не один раз... ))
Причем сокет должен быть настроен для блокирующего режима. где то так:
m_dwVal := 1;
SysSockSetOption( m_dnSocket, SOCKET_IPPROTO_TCP, SOCKET_TCP_NODELAY, ADR( m_dwVal ), SIZEOF( m_dwVal ) );