August 20, 2020

Сокеты не реального времени

Из ПЛК задачи можно сделать TCP/UDP клиент или даже сервер. Это позволяет работать с данными современной периферии без разработки промежуточных слоев. У нас есть выбор между TF6310 (обычная и давно практикуемая) и TF6311 (модная, современная, риалтаймовая). Обе-две работают как на PC/CX (x86), так и на CX (ARM). В этом посте будет практика работы с обычной библиотекой 6310, а с новой разберемся как-нибудь позже.

Изображение: Beckhoff Automation

TF6311 TCP/UDP (realtime)

Полное описание доступно в инфосисе. Обе библиотеки доступны из ПЛК задачи, но 6311 работает "рядом" с ядром TwinCAT 3, на том же системном уровне.

Здесь заострю внимание на особенностях, плюсах и минусах.

  • Только TwinCAT 3.
  • Функционал не требует установки, все компоненты уже встроены в TwinCAT 3.
  • Требуется лицензия TC3 TCP/UDP RT.
  • Есть возможность использовать временную (trial) лицензию на время разработки.
  • Работает напрямую с сетевой картой, минуя большинство механизмов операционной системы.
  • TF6311 настраивается в проекте через TCP/UDP RT TcCom Parameter. Это требует отдельного рассмотрения.

Минусы

  • Не рассчитан на большие и незнакомые сети. Вероятно подразумевается интернет, либо большой интранет.
  • Не для больших объемов данных.
  • Не поддерживает мультикаст в UDP.
  • Windows Compact CE только начиная с версии 7.
  • Windows Firewall отсутствует в цепочке передачи пакетов (менее защищенные соединения).
  • Только TwinCAT-совместимые карты. Список доступен в Supported Network Controller by Beckhoff Ethernet Driver.
  • Нет связи с локальным, стандартным сетевым интерфейсом Windows. Можно реализовать через стороннего посредника.
  • Сетевые коммутаторы (эзернет свичи) EL6601 и EL6614 не могут использоваться совместно с этой библиотекой.

Плюсы

  • Очень детерминированный и предсказуем (подтвердить не могу).
  • Поддержка С++ (похоже, что это основное назначение библиотеки).
  • Поддерживает ARP/Ping.


TF6310 TCP/IP


Работает через специальный ADS-сервер, а дальше через стандартный WinSock, по сути копируя стандартный функционал сетевого ввода-вывода. Как и что устанавливать вменяемо описано в справке, и проблем это обычно не вызывает.

Для TwinCAT 2 мы устанавливаем TS6310. Он приносит с собой следующие библиотеки в каталог TwinCAT\Plc\Lib:
  • TcpIp.lib — базовые функции TCP/IP и UDP;
  • TcSocketHelper.lib — вспомогательные функции TCP/IP, упрощающие жизнь разработчика. Содержит готовые ФБ с полным циклом клиент-сервер и сервер-клиент.
  • TcSnmp.lib — протокол SNMPv1, вспомогательные функции.
  • TwinCAT TCP/IP Connection Server — по сути это мост ADS ↔ TCP/IP.


Практика 6310


TCP-клиент рассчитан на передачу больших объемов данных в виде непрерывных потоков. Данные текут непрерывно, но ничто не мешает разбить их на пакеты. Перед началом работы устанавливается соединения, которое по окончании работы разрывается. Длительность передачи данных после соединения не оговаривается: минуты, часы, дни, года. Так было задумано и это обычная практика работы с TCP/IP протоколом.

Минимальная реализация TCP-клиента:
  • FB_SocketConnect и FB_SocketClose — для подключения и разрыва сессии.
  • FB_ClientServerConnection — включает в себя оба предыдущих блока и упрощает работу с ними.
  • FB_SocketSend и/или FB_SocketReceive — для обмена данными.

Минимальная реализация TCP-сервера:
  • FB_SocketListen — открывает сокет на прослушивание в режиме ожидания клиента.
  • FB_SocketAccept и FB_SocketClose — открывают и соответственно закрывают соединение.
  • FB_ServerClientConnection — умеет все вышеперечисленное вместе и упрощает работу.
  • FB_SocketSend и/или FB_SocketReceive — для обмена данными.

UDP-клиент пакетный, работает без установки постоянного соединения: отправил пакет данных и забыл. Пакет может и не дойти, а может дойти фрагментированным, то есть разбитым на несколько частей. Иногда задняя часть может придти раньше передней (если ползли разными маршрутами). И так тоже было задумано, во славу производительности.

Минимальная реализация UDP-клиента:
  • FB_SocketUdpCreate и FB_SocketClose — открыть/закрыть сокет.
  • FB_SocketUdpSendTo и/или FB_SocketUdpReceiveFrom — прием-отправка данных.

Для UDP-пакета ограничен максимальный размер отправляемых данных. По умолчанию он равен 8192 байт (это число задается константой TCPADS_MAXUDP_BUFFSIZE). Поэтому стоит обратить внимание на аргумент cbLen функции FB_SocketUdpSendTo. Ограничение служит для экономии памяти.

Для всех типов связи FB_SocketCloseAll закрыть всё открытое и закончить любые работы в пределах текущего рантайма. Это который имеет определенный порт, например, 801 для первого рантайма TwinCAT 2.
FB_SocketAccept, FB_SocketReceive, FB_SocketUdpReceiveFrom — вызываются циклически (polling), то есть каждый цикл. Остальные блоки вызываются по мере необходимости.


Производительность


TwinCAT 2


CX8090 (TC2, WinCE 7, ARMv4) — идеально когда происходит один вызов сетевой функции за цикл. 4 соединения за цикл работают нормально, при большем числе идут таймауты.

CX9020 (TC2, WinCE 7, Arm Cortex™-A8) — 4 соединения одновременно за цикл работают нормально, а дальше идут таймауты. Соединения-подключения все равно необходимо выполнять последовательно: сначала устанавливается одно соединение и только затем выполняем следующее подключение.


TwinCAT 3


CX8190 (TC3, WinCE 7, ARM Cortex™-A9) — 10 соединений одновременно за цикл работают нормально. Соединения-подключения необходимо выполнять последовательно.

CX2030 (TC3, Windows 7 Emb Std, Core-i7) — 10 соединений одновременно за цикл работают нормально. Соединения-подключения необходимо выполнять последовательно.


Не больше 10 клиентов

  • Не больше 10 клиентов. Ограничение в 10 клиентских подключений на рантайм, а возможно и на весь ПЛК, но это не точно.
  • Разные циклы — разные подключения. Открывать соединение лучше друг за другом по одному за цикл.
  • Функции всегда работают как минимум один цикл. После первого вызова, в последующих циклах можно спокойно вызывать ФБ с bExecute := FALSE, ожидая когда функция переключится в NOT bBusy, что означает поступление данных либо ошибку.
  • При обрыве связи возвращает bError = FALSE и nRecByte = 0. Для определения обрыва необходимо самостоятельно использовать собственный таймер для контроля таймаута. Великая вещь, что функции здесь неблокирующие (разработчики на C++ и другие поймут).
  • Протокол поточный, если читаете данные пачками, то полученные данные необходимо накапливать в буфере, десериализовать или просто сканировать этот буфер на предмет специального тэга или какой-либо другой ситуации предусмотренной протоколом (это уже зависит от специфики протокола).

Мне стало интересно откуда это волшебное и круглое десятичное число — десять. И почему нельзя взять и подключаться ко всему и сразу. Я начал следить за количеством соединений и количеством системных потоков (threads).
Соединения - потоки
 2 - 8
 3 - 10
 4 - 12
 . . ..
 6 - 16
Просматривается явная зависимость — на каждое новое соединение создается два новых потока. Зачем два? Заглянем в лог TcpIpServer.log:

Видно, что сначала создается ADS-сокет CTcpAdsSocket::CTcpAdsSocket(); Он будет принимать команды и данные из ФБ ПЛК-задачи, а затем создается требуемый TCP-сокет CTcpSocket::Create(); теперь уже для непосредственной передачи данных. Поэтому каждый цикл можно открыть только одно новое соединение — на запрос создания сокета создается только одна связка ADS ↔ TCP|UDP. Такая вот архитектура, упрощенно.


Не больше 10, но можно меньше


Под Windows CE можно поиграть с ключами реестра: Start → Run... → regedit. Создать ключ Registry → New → Key: HKLM\SOFTWARE\Beckhoff\TwinCAT TcpIp Server. Внутри раздела доступны несколько значений-параметров типа DWORD. Что удалось выяснить:

MaxTcpSocketCount
0 = вероятно стандартные 10 сокетов-соединений.
1 = запретить вообще все подключения. Теперь функции FB_SocketListen и FB_SocketConnect возвращают код ошибки TCPADSERROR_NOMOREENTRIES (0x0000800132769).
2 = 1 доступное подключение.
3 = 2 доступных подключения.
[...]
11 = 10 максимально доступных подключений. Всё, больше нельзя.

MaxUdpSocketCount — аналогично MaxTcpSocketCount, но для UDP протокола.
AdsServerCommTimeout — возможно таймаут ADS-сервера. Единицы измерения вероятно миллисекунды.
DisableKeepAlive — запретить постоянные KeepAlive подключения?
ThreadPriority — приоритет системного потока? Значения не известны.

LogLevel
0 = отключен.
1 = включен. Логировать будет сюда: \Hard Disk\TwinCAT\TcpIpServer.log


TcSocketHelper


TcSocketHelper.lib выполняет за нас все трудоемкие операции по клиентским подключениям к серверу и в обратную сторону — отслеживает клиентские подключения к серверу. Доступные примеры лежат в справочной системе.

FB_ServerClientConnection — выполняет функции TCP-сервера. Внутри себя содержит и выполняет FB_SocketListen, FB_SocketAccept и FB_SocketClose. На выходе выдает идентификатор сокета hSocket для подключившегося клиента. Дальше передаем его в FB_SocketSend и/или FB_SocketReceive.

FB_ClientServerConnection и FB_ConnectionlessSocket — первый служит для создания TCP-клиентов, а второй для UDP. Оба умеют создавать и закрывать соединения. При успешном соединении выдают на выходе hSocket для передачи в FB_SocketSend и/или FB_SocketReceive.

Из интересного все функции, связанные с получением данных (Receive), внутри себя содержат механизм регулировки скорости обновления ФБ (пропуск тактов, троттлинг, throttling). Ничего особенного, это обычный подход в такой ситуации. Кратко выглядит так:

TYPE T_ThrottleTimes: ARRAY[0..MAX_THROTTLE_MODE] OF TIME;
END_TYPE

throttleTimes: T_ThrottleTimes := T#0s, T#10ms, T#20ms, T#40ms, T#60ms, T#80ms, T#100ms,
                                  T#200ms, T#400ms, T#600ms, T#800ms, T#1s, T#2s;

И скрытая, только для внутреннего использования, функция-обертка над таймером FB_ThrottleTimer, которая состоит всего-лишь из одной строки с вызовом таймера:

timer(
    IN := bIn,
    PT := throttleTimes[LIMIT(0, selector, MAX_THROTTLE_MODE)],
    Q  => bOut,
    ET => tElapsed );

Здесь `selector` задает текущий режим, а его значение изменяется через вызов одного из четыре экшенов (Action):
  • MaxSpeed — selector = 0.
  • MinSpeed — selector = MAX_THROTTLE_MODE.
  • SlowDown — увеличивает задержку, уменьшает скорость опроса.
  • SpeedUp — уменьшает задержку, увеличивает скорость опроса.

Суть этого действа в автоматическом регулировании интервал ожидания: увеличивать интервал если сообщений нет, и снижать интервал ожидания, если сообщения пошли часто-часто.

No comments

Post a Comment

Note: Only a member of this blog may post a comment.