March 31, 2016

Каталоги TwinCAT 2

С активным приходом новой версии номер три, пора бы уже разобраться — что и где лежит в каталогах TwinCAT: программы, библиотеки, утилиты — все это с немецкой точностью и аккуратностью разложено по папкам. На первый взгляд, трудно выцепить — где лежит PLC control, а где System Manager, но они там есть.



Две версии одновременно


TwinCAT'ы разных версий могут спокойно сосуществовать бок о бок, но сначала лучше установить TwinCAT 2 и только затем TwinCAT 3. При этом станет доступна утилита TcSwitchRuntime, лежащая в C:\TwinCAT\TcSwitchRuntime\TcSwitchRuntime.exe. Она позволяет переключать версии рантайма "туда-и-обратно".

В то же время системные утилиты TwinCAT 2 могут спокойно работать через роутер TwinCAT  3. Совместимость обеспечивается тем, что все утилиты TwinCAT работают через AMS-роутер и протокол общения зафиксирован в стандарте.

Еще раз: для работы с рантаймами разных версии нужно использовать утилиту TcSwitchRuntime; для конфигурирования и программирования просто используйте утилиты соответствующих версий. Где их искать — позднее и ниже.


Корень TwinCAT 2


Предполагается, что TwinCAT установлен в каталог по умолчанию C:\TwinCAT
  • 3.1 — TwinCAT 3.
  • AdsApi, ADS Api — библиотеки ADS API. В каталоге лежит файл ReadMe.txt в котором подробно расписано какие языки и платформы поддерживаются.
  • IO Api — библиотеки TwinCAT R3IO для прямого доступа к подсистеме ввод/вывода TwinCAT без использования протокола ADS.
  • Boot — загрузочные проекты ПЛК-задач; текущая, активная конфигурация; retain- и persistent-данные.
  • CE — расширения и библиотеки для контроллеров на базе компактных операционных систем Windows Compact|CE.
  • Common32, Common64 — коммуникационные драйверы TwinCAT и системные библиотеки утилит TwinCAT 2.
  • CNC — программы в G-коде для ЧПУ системы.
  • Driver — драйверы TwinCAT 2 уровня ядра операционной системы. Это и есть рантайм TwinCAT 2.
  • EventLogger — все что связано с ведением логов в TwinCAT.
  • Functions — будет в TwinCAT 3.
  • InfoSysInfosystem — локальная копия информационной системы Бекхофф.
  • Io — все, что связано с системной шиной и настройкой ПЛК.
  • Mc — все, что связано с цифровым управлением движения NC, NCI, и т. п.
  • Plc — разработка программ и прикладные библиотеки.
  • Resource — файлы с настройками системы логирования, NCI и пр. дополнительные настройки. В компактных контроллерах CX в этом месте лежат драйверы подсистем ядра TwinCAT.
  • Samples — примеры программ и подобное.
  • Scope — цифровой осциллограф первой версии. Входит в стандартную установку.
  • Scope 2 — цифровой осциллограф второй версии. Покупается и устанавливается отдельно.
  • TcSwitchRuntime — наличие этого каталога указывает на установленный TwinCAT 3. Позволяет переключать текущие, рабочие версии рантайма.
  • Versions — утилита для переключения версий сборок TwinCAT. Например, можно переключиться с версии 2.10 на версию 2.11, и обратно.

Утилиты в корне каталога C:\TwinCAT:
  • TCATSysCtrl.exe — окно главных настроек TwinCAT. Появляется при выборе пункта Properties.
  • TCATSysSrv.exe — AMS-роутер как системный сервис Windows.
  • TcSysUI.exe — утилита которая сидит в системной лотке (traybar, рядом с часами). Позволяет управлять рантаймом и запускать другие утилиты TwinCAT.
  • TcStgEditor.exe — TwinCAT Storage Editor, позволяет просмотреть установленные сервера сервисов TwinCAT (Modbus, EventLogger, и т. п.). Открывает лежащий рядом файл default.tps который по сути zip-архив и считывает оттуда настройки.
  • DefaultConfig.xml — чистая конфигурация "по умолчанию". Дает возможность стартовать ПЛК с "чистого листа".


Boot


  • CurrentConfig.xml — текущая, активная конфигурация TwinCAT.
  • CurrentConfig.tsm — текущая, активная конфигурация TwinCAT в виде бинарного файла. По сути, это zip-архив, внутри которого лежит общая информация SummaryInformation, плюс еще раз конфигурация, но в виде однофайловой базы данных с полями фиксированной длины: TcSysManager\WSM.
  • SystemConfig.xml — в контроллерах на Windows Compact|CE — это текущая конфигурация для дополнительных подсистем TwinCAT, Например, для TcEventLogger.

Ряд файлов с расширением wbp:
  • TCPLC_P_1.wbp — загрузочный образ ПЛК-программы (boot project) для рантайма #1.
  • TCPLC_T_1.wbp — PERSISTENT-данные.
  • TCPLC_R_1.wbp — RETAIN-данные.
  • TCNC.wbp — образ NC-задачи.
Цифра в названии указывает на принадлежность к номеру рантайма. Тильда в расширении файла .wb~ означает либо сохраненную копию предыдущей версии (backup), либо временную версию файла, существующего до первой перезагрузки.


Io


В корне каталога находится TCatSysManager.exe — исполняемый файл TwinCAT System Manager. Остальная часть заполнена составными частями System Manager'а в виде .dll библиотек.

Подкаталоги содержат файлы XML со словарями-описанием различного оборудования. Подкаталог с именем EtherCAT выделяется из общей массы — он хранит описания всех устройств которые могут присутствовать на шине EtherCAT.

Если появляется новое устройство, его описание необходимо поместить именно в каталог EtherCAT. При следующем старте System Manager перекеширует словари (он будет загружаться продолжительное время) и новое устройство начнет автоматически распознаваться.
Заметьте, что для контроллера этот XML-файл со словарем не нужен, а необходим только для System Manager: он должен знать с чем работает и как правильно конфигурировать шину.


Io \ TcDriveManager


Подсистема конфигурирования и управления сервоприводами отделена от остальной системы. В каталоге TcDriveManager расположена надстройка System Manager в виде библиотеки TcDriveManager.dll. Она добавляется автоматически при установке Drive Manager.
  • FirmwarePool — хранит прошивки сервоприводов.
  • MotorPool — описания сервомотором. Сюда подкладывать новые и неизвестные системе моторы.
  • MotorPool → CustomerGenerated — описания моторов созданные вручную.
Если вы часто работаете с сервоприводами Бекхофф, то необходимо следить за обновлениями DriveManager: новые версии все еще выходят.


Plc


Здесь расположен TCatPlcCtrl.exe — среда разработки PLC Control. Каталог Lib содержит прикладные библиотеки разработчика.

Файл TwinCAT PLC Control.ini содержит настройки среды разработки. Отсутствующая по умолчанию, но полезная настройка — это размер экрана визуализации по умолчанию:

[TwinCAT PLC Control]
VisuWidth=800
VisuHeight=600

March 29, 2016

Как найти контроллер и добавить запись в роутер

Чего не хватает в стандартных библиотеках TwinCAT.Ads API, так это поиска родственных душ контроллеров в Ethernet-подсети и удаленной работы с удаленными же таблицами роутеров. В System Manager и XAE эта функция реализована через "секретный" протокол и следовательно закрыт от пытливого ума разработчика. Почему именно так решил сумрачный немецкий гений — не известно, но видимо по причине дополнительной безопасности.

Тем не менее разработчики по всему миру засучили рукава и взялись оголенными руками за акулу-провод (WireShark). В результате, в той или иной степени подробности, они выпотрошили этот тайный протокол, но так и не поделилось друг с другом результатами. А зря — сэкономили бы пару другую часов рабочего времени.

Если вы с трудом понимаете о чем были предыдущие абзацы, а их становится все больше и больше, то вспомните про кнопки Choose Target, Search (Ethernet) и Broadcast Search. Именно про их программную реализацию будут следующие абзацы текста.
Отдельные разрозненные куски протокола поиска контроллеров можно найти в LinkedIn и на GitHub, но окончательная работоспособная реализация появилась только в библиотеках AdsRemote и AdsClient.


Broadcast Search


Широковещательный поиск контроллеров реализован через UDP-протокол. В справочной системе этот факт отражен в виде рекомендации по настройке файервола Windows: для нормальной работы TwinCAT рекомендуется сразу открыть два порта — TCP:48898 и UDP:48899. Второй — как раз отвечает за широковещательную рассылку поисковых пакетов. Соответственно, работает только в локальной подсети.

Все устройства получившие пакет должны откликнуться в соответствующем формате. В библиотеке AdsRemote эта функция реализована в методе BroadcastSearchAsync статического класса Ads.Remote.Router.AmsRouter. Метод к тому же асинхронный (await/async), что удобно для разработки пользовательских интерфейсов.

На входе метод получает два параметра: широковещательный адрес подсети и таймаут времени поиска. Если со вторым параметром ничего сложного нет — для быстрой локальной сети задаем 100 миллисекунд, для более медленной подсети интервал можно увеличивать до десятков секунд; первый же параметр выглядит совершенно непонятным для людей не знакомых с функционированием сетей Ethernet. Поэтому существует очередной костыль — вспомогательный статический класс IPHelper.

IPHelper позволяет получить список IP-адресов локального компьютера, за это отвечает свойство Localhosts, которое возвращает список локальных адресов только для Ethernet и WiFi. Если этого мало — можно воспользоваться методом FilteredLocalhosts который на вход принимает список сетевых интерфейсов.

После выбора требуемого локального адреса можно опять-таки с помощью IPHelper'а вытащить широковещательный адрес. За это чудо отвечает метод GetBroadcastAddress.

Посмотреть как эта машинерия работает в совокупности можно в подпроекте CxFinder.



В итоге, мы все равно получим список контроллеров List<RemotePlcInfo> из которого можем подчерпнуть: имя контроллера, IP-адрес, Ams NetId, версию TwinCAT и версию операционной системы с комментариями. Этого вполне достаточно для добавления записи в локальный и удаленный роутеры.

Если IP-адрес ПЛК известен заранее — мы можем запросить у него аналогичное описание. В этом нам помогает статический, асинхронный метод GetRemotePlcInfoAsync.


Локальная таблица AMS-роутера


Домашнее задание для зависших в начале 2000-х годов — исследовать ветку реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Beckhoff\TwinCAT\Remote\

Для ребят помоложе и уже успевших соскочить с 32-разрядных операционок, то же самое, но с приставкой Wow: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Beckhoff\TwinCAT\Remote

Также не помешает ознакомиться со статьей про WOW6432Node и API-функции RegOpenKeyEx / RegEnumKeyEx. На десерт можно освежить память про роутеры AMS и роутеры ADS.


Add Route


Чтобы добавить запись в таблицу AMS-роутера ПЛК необходимо создать экземпляр класса PLC, затем через его поле Router получить непосредственно доступ к таблице AMS-роутера. Добавление записи происходит с помощью метода AddRecordAsync:

public async Task<bool> AddRecordAsync(
    IPAddress localhost,
    IPAddress remoteHost,
    AmsNetId localAmsNetId,
    string localIpName = null,
    string name = null,
    string login = "Administrator",
    string password = "1",
    bool isTemporaryRoute = false,
    int timeout = 10000,
    int adsUdpPort = Request.DEFAULT_UDP_PORT)
  • localIpName может быть как именем локального компьютера, так и IP-адресом.
  • name — это название записи в таблице роутера. По умолчанию, оно берется из Environment.MachineName.
  • login и password — это учетная запись пользователя на контроллере. У пользователя должно быть достаточно прав на работу с записями роутера. Обычно, это администратор.
  • isTemporaryRoute — позволяет создать временный, одноразовый роутинг который чаще всего никому не нужен.

March 24, 2016

Визуализации в TwinCAT 3

Третьего марта прошел вебинар на тему новых систем визуализации в третьем Твинкате. Для начала были рассмотрены три типовых сценария работы:
  1. Локальная программа-клиент (Local PLC HMI Client) работает и отображает визуализацию на том же ПЛК, где работает ПЛК-задача. Поток данных ложится в цепочку:
    ПЛК → ADS-сервер → HMI-клиент.
  2. Удаленный клиент (Remote PLC HMI Client) — программа клиент работает и рисует "картинку" на другом компьютере:
    ПЛК → ADS-сервер ~→~ Локальная и сеть или VPN ~→~ ADS-сервер → HMI-клиент.
  3. Удаленный мобильный клиент (Remote PLC HMI Web) с возможностью отображать визуализацию через веб-браузеры (акцент на использование обычных интернет-браузеров):
    ПЛК → ADS-сервер → MS IIS веб-сервер ~→~ Интернет (http/https) ~→~ Веб-браузер.
Обработчик данных ПЛК-задачи VISU_TASK работает совместно с TwinCAT-runtime в режиме ядра операционной системы (kernel mode). HMI-клиент работает в пользовательском кольце (user mode) и через ADS-cервер переправляет в VISU-TASK события мыши, клавиатуры, сенсорного экрана и пр. "телодвижения" пользователя, а от VISU-TASK получает команды на отрисовку/обновление/перерисовку интерфейса визуализации.


Начало работы


Редактор визуализаций встроен в среду разработки и доступен в дереве проекта в разделе ПЛК-задачи. Состоит из трех частей:
  1. Поле рисования (Interface Editor) состоит из трех вкладок:
    - Переменные визуализации. Это самостоятельные переменные принадлежащие задаче визуализации VISU-TASK.
    - Настройки горячих клавиш.
    - Список графических элементов на визуализации.
  2. Стандартные элементы интерфейса визуализации: кнопки, графические элементы, тренды, индикаторы и т. п.
  3. Параметры элементов очень похожие на параметры WinForms.

Visualization Manager — главный компонент проекта, отвечающий за визуализации. Он содержит различные настройки, такие как:
  • Тема или стиль отображения.
  • Язык по умолчанию (интернационализация интерфейса).
  • Управление пользователями для разграничения доступа.

Для активации локального PLC HMI или PLC Web HMI необходимо добавить соответствующие объекты в ветку Vizualization Manager. После этого автоматически создается и добавляется задача визуализации VISU_TASK.


Клиентская часть HMI


Заявлены следующие преимущества HMI-клиента:
  1. Отдельная программа в виде независимого .exe файла.
  2. Клиент работает как независимый процесс операционной системы.
  3. Клиент запускается автоматически (только при ручном добавлении в папку автостарта TwinCAT).
  4. Обмен данными только через ADS. Никакой блокировки отладочного debug-интерфейса.

Преимущества Web-клиента:
  1. Работает на веб-сервере Microsoft IIS, который встроен во все контроллеры Бекхофф.
  2. Построен на HTML5 и JavaScript.
  3. Клиент платформонезависим т. к. работает через браузер.
  4. Идеально подходит для обслуживания объектов и диагностики, в том числе удаленно (со смартфона, например).

Можно запустить несколько PLC HMI-клиентов, с синхронизацией вида на разных клиентах с разных ПК. Удаленный PLC HMI могут просматривать множество пользователей: максимальное количество зависит только от настроек размера буфера связи.


TwinCAT _PLC_ HMI — это не TwinCAT HMI


Различия между TwinCAT__PLC__HMIa и TwinCAT__HMIb:
  • (a) старая-добрая таргет-визуализация, (b) почти-скада.
  • (a) встраивается в ПЛК-проект, (b) выступает как независимый проект.
  • (a) подключен только к одному ПЛК-проекту. (b) может подключаться к множеству различных рантаймов одновременно.
  • (a) рабоатет состандартным набором элементов, в (b) можно легко добавлять новые элементы.
  • Цифровой осциллограф будет только в (b), для (a) будут доступны простые тренды.


Анонсы и слухи


Ближайший крупный билд TwinCAT 3 —  3.1.4020. В нем обещают:
  • Обновление User Management — работа с пользователями и правами доступа к страницам, контролам и т. п. Пользователи TwinCAT не связаны с пользователями операционной системы. Это отдельная подсистема прав безопасности TwinCAT.
  • Event Table — элемент интерфейса который может показывать сообщения системного журнала TcEventLog.
  • Recipe Management.
  • Trend — появится только в третьем квартале 2016 г.


Следующий вебинар пройдет 30 марта 2016 года по теме TwinCAT Интернет вещей (IoT) — быстрый и стандартизированный доступ в "облако".
Полный вебинар на английском языке TwinCAT 3 | Visualisation – from engineering to target and web

March 22, 2016

Автоматизация TwinCAT.Ads API

Удобные библиотеки Бекхофф для работы с протоколом ADS, к сожалению, работают на достаточно низком уровне. С каждым новым обновлением уровень немного повышается, но по прежнему приходится работать с функциями обмена данными, а хочется работать непосредственно с данными. Поэтому был разработан очередной бесплатный костыль на C# .NET 4.5 — библиотека AdsRemote. С открытым исходным кодом.

Внезапно, текущая версия библиотеки 0.5, т. е. родилась она не вчера и уже работает в некоторых проектах. Основные фишки AdsRemote — автоматический контроль канала связи на обрыв соединения и переподключения, а также простой способ работы с переменными ПЛК-программы и шины данных вообще. Кроме этого, библиотека умеет находить контроллеры в Ethernet-подсети и удаленно добавлять запись в AMS-роутер контроллера.

Библиотека пока не умеет работать со структурами и дает чуть большую нагрузку на контроллер, так как активно использует событийный механизм роутера, но универсальные механизмы всегда чуть менее производительны, чем узко заточенные. Удобство и скорость разработки просто так не даются.

Для работы понадобится установленный TwinCAT 2.11 или 3.1, иначе говоря — необходим работающий AMS-роутер. Еще проще говоря — Твинкат должен быть установлен и работать. Инженерной версии вполне достаточно. Не забудьте добавить записи в таблицы роутеров.


Подключение и отключение


Работа с библиотекой начинается с экземпляра контроллера. Для этого передаем AmsNetId контроллера в конструктор класса PLC. Напоминаю, связь не начнет работать до тех пор, пока вы не добавите нужные записи в таблицы роутеров.

PLC plc = new PLC("5.2.100.109.1.1");


С момента инициализации экземпляра объекта, в фоне начинает работать поток  автоматически контролирующий состояние связи с контроллером. Чтобы оперативно узнавать о проблемах со связью, необходимо подписаться на уведомления:

plc.DeviceReady += Plc_DeviceReady; // когда связь установлена или восстановлена
plc.DeviceLost += Plc_DeviceLost;   // когда связь потеряна

[...]

private void Plc_DeviceReady(object sender, AdsDevice e)
{
    Log("READY [" + e.Address.Port.ToString() + "]");
}

AdsDevice в данном случае — это ADS-устройство в составе контроллера. Именно они идентифицируются на шине по номеру порта. Если связь с контроллером прервется, мы получим столько уведомлений, сколько устройств зарегистрировано в объекте ПЛК. Соответственно, при отключении только одного устройства — придет только одно уведомление.


Работа с переменными 


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

Var<short> main_count = plc.Var<short>("MAIN.count");
Var<ushort> main_state = plc.Var<ushort>("MAIN.state");
Var<ushort> frm0 = plc.Var<ushort>("Inputs.Frm0InputToggle", 27907);
Var<ushort> devState = plc.Var<ushort>(0xF030, 0x5FE, 27907);
Var<short> g_Version = plc.Var<ushort>(".VERSION");


С момента создания переменной, ее значение будет автоматически обновляться в соответствии с переменной на стороне контроллера. Переменные типа Var<T> могут спокойно участвовать в обычных арифметических операциях. За редким исключением их тип будет автоматически приведен к значимому или строковому:

long countTotal += main_count;
MessageBox.Show(main_count);


Можно подписаться на уведомления об изменении значения переменной:

main_count.ValueChanged +=
    delegate
    {
        counterStatusLabel.Text = main_count;
    };


Можно использовать единый обработчик для нескольких переменных:

main_count.ValueChanged += OnValueChanged;
main_state.ValueChanged += OnValueChanged;

protected void OnValueChanged (object src, Var v)
{
    ushort val = (ushort)v.GetValue();
    framesTotal += val / 2;
    label1.Text = val.ToString();
};


Для изменения значения переменной на стороне контроллера, необходимо обратиться к внутреннему свойству переменной — RemoteValue:

main_count.RemoteValue = 123;


Связывание группы переменных


Неудобно связывать переменные по одной за раз. Проще дать описание всех сразу, а затем активировать одной строкой:

public class PRG_Main
{
    [LinkedTo("MAIN.count")]
    public Var<short> count;

    [LinkedTo("MAIN.state")]
    public Var<ushort> state;
}


Более подробный вариант:

public class PRG_Main
{
    [LinkedTo("MAIN.count", As: typeof(short), Port: (int)AmsPort3.PlcRuntime1)]
    public Var count;

    [LinkedTo("MAIN.state", Port: (int)AmsPort3.PlcRuntime1)]
    public Var<ushort> state;

    [LinkedTo("Inputs.Frm0InputToggle", Port: 27907)]
    public Var<ushort> frm0_1;

    [LinkedTo(IGrp: 0xF030, IOffs: 0x5F4, Port: 27907)]
    public Var<ushort> frm0;
}

В любом случае, необходимо одним из двух способов указать тип переменной. В противном случае получится NULL, а не экземпляр переменной.

Для дальнейшего использования переменных в связке с WPF-контролами — необходимо объявить их как параметры:

public class PRG_Main
{
    [LinkedTo("MAIN.count")]
    public Var<short> count { get; set; }

    [LinkedTo("MAIN.state")]
    public Var<ushort> state { get; set; }
}


После задания описания, объект с данными создается и активируется одной строкой:

PRG_Main Main = plc.Class<PRG_Main>();


Если требуется вызов сложного конструктора — можно воспользоваться следующим вариантом:

Main = new PRG_Main(param1, param2, ...);
plc.Class(Main);

Все переменные, снабженные соответствующим атрибутом, будут связаны с переменными ПЛК, их значения будут обновляться автоматически.

March 10, 2016

Асинхронные функции TwinCAT.Ads API

Библиотеки ADS.API поддерживают событийно-ориентированное программирование — вместо постоянного чтения значения переменной (поллинг, polling) можно подписаться на рассылку уведомлений и получать уведомление только при изменении значения переменной. Впрочем, можно составить и постоянный опрос: уведомления будут приходить через интервалы времени, заданные разработчиком.
Можно одновременно использовать механизм уведомлений и обычные синхронные операции.
Инициатива по созданию событий-уведомлений (Device Notification) принадлежит программе созданной на языке высокого уровня (в нашем случае — C#, ПК), но сам механизм уведомлений находится в роутере контроллера. Не рекомендуется создавать более 550 уведомлений для одного устройства, т. к. каждая новая подписка дополнительно нагружает контроллер. Для увеличения нагрузочной способности лучше упаковать или организовать данные в структуры и обмениваться пачками данных. Для снижения нагрузки на клиентское приложение, можно ввести ограничение на частоту возникновения уведомлений, а точнее, на промежуток между отправкой уведомлений.
Всегда приходит первое уведомление. После подписки приемник считает, что значение переменной не определено, поэтому сразу же приходит первое уведомление с актуальным значением переменной.

Использование дополнительных задач


Начнем сразу со сложного: создаем дополнительную задачу (Additional Task), оставляем порт равным 301 по умолчанию, и затем связываем переменные задачи с модулями расширения физических входов/выходов на шине EtherCAT:


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


Подписка


Переходим к программе на C#. Для подписки на события используется метод AddDeviceNotification класса TcAdsClient:

TcAdsClient client301;
int h_client301;

AdsStream adsStream;

private void MainForm_Load(object sender, EventArgs e)
{
    client301 = new TcAdsClient();
    client301.Connect("5.2.100.109.1.1", 301);

    adsStream = new AdsStream(32);
 
    try
    {
        h_client301 = clientPlc.AddDeviceNotification(
            "Task 2.Inputs.EL1018",
            adsStream, 0, sizeof(byte),
            AdsTransMode.OnChange, 100, 10000,
            null);
    }
    catch (AdsErrorException adsExc)
    {
        // LOG: adsExс.Message;
        client301.Dispose();
        client301 = null;
    }

    if (client301 != null)
    {
        client301.AdsNotification += Client301_AdsNotification;
    }
    
    [...]

В метод передается имя переменной на изменения которой мы подписываемся. Затем указывается поток входных данных AdsStream. Необходимо создать поток с буфером, размер которого достаточен для размещения значений переменной. Кроме этого, задается смещение в потоке и размер данных переменной в байтах (подробнее — ниже).

AdsTransMode определяет тип опроса переменной. На выбор предоставляется несколько вариантов, но в данный момент реализовано только два. Cyclic — задает периодический опрос значения переменной с интервалом заданным параметром cycleTime (в примере выше, это 100 миллисекунд). OnChange — уведомления приходят только при изменении значения переменной.

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

В последний параметр userData разработчик может передать какие-либо свои данные. Они будут доступны при получении уведомления.


Освобождение ресурсов


Уведомления создаются на стороне контроллера, поэтому по отношению к программе клиенту они являются внешними и соответственно неуправляемыми ресурсами (umanaged resources). По окончании работы лучше освободить их.

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (client301 != null)
    {
        try
        {
            client301.DeleteDeviceNotification(h_client301);
        }
        catch (AdsErrorException adsExс)
        {
            // LOG: adsExс.Message;
        }
        finally
        {
            client301.Dispose();
            client301 = null;
        }
    }
    
    [...]
 
В принципе, финализаторы библиотеки самостоятельно справятся с освобождением занятого, но во избежании внезапных протечек памяти лучше сделать это самостоятельно.


Уведомления


Когда-нибудь наступит тот самый момент и придет уведомление. Первое, что необходимо сделать при получении, это сдвинуть курсор чтения на N-байт. На сколько его необходимо сдвинуть мы задаем при подписке на уведомления.

private void Client301_AdsNotification(object sender, AdsNotificationEventArgs e)
{
    e.DataStream.Position = e.Offset;
    byte el1008 = (byte)e.DataStream.ReadByte();

В общем случае, можно создать единственный поток и писать все данные в него. Тогда при подписке нужно задать смещение для переменной т. е. сериализовать ее значение в поток с заданным смещением. По приходу уведомления, задаем соответствующее для данной переменной значение смещения (десериализуем из потока). Если вам это не нужно — просто создайте для каждой переменной отдельный поток и задайте смещение равное нулю.


Чтение данных EtherCAT-шины


Включаем ADS-сервер, который будет предоставлять данные с шины.


Включенный сервер не является потребителем данных. Без задач, читающих данные с шины, вы получите спящую шину и по прежнему неработающий ADS-сервер: Device 'Device 3 (EtherCAT)' needs sync master (at least one variable linked to a task variable). Создайте хотя бы одну дополнительную задачу и слинкуйте хотя бы одну переменную.

Подписываемся аналогично, за исключением номера порта — 27907:

clientECat = new TcAdsClient();
clientECat.Connect("5.2.100.109.1.1", 27907);

h_clientECat = clientECat.AddDeviceNotification(
    "Inputs.Frm0InputToggle",
    adsStream, 1, sizeof(ushort),
    AdsTransMode.OnChange, 10, 0,
    null);


Работа со структурами


TYPE ST_DataPack :
STRUCT
    Index    : UINT;
    Name     : STRING(10);
    Speed    : REAL;
END_STRUCT
END_TYPE

PROGRAM MAIN
VAR
    Pack     : ST_DataPack := (Index := 0, Name := '', Speed := 0);
    state    : UINT;
END_VAR

CASE state OF
0: (* Инициализация *)
    Pack.Index    := 1;
    Pack.Name     := 'LightSpeed';
    Pack.Speed    := 0;

    state := 100;

100: (* Ускорение *)
    Pack.Speed := Pack.Speed + 0.1;
    Pack.Index := REAL_TO_UINT(Pack.Speed / 100);

END_CASE


На стороне клиента объявляется аналогичная структура.
В примере ниже предлагается почленное заполнение полей структуры, поэтому нет необходимости в строгой последовательности полей и применении атрибутов типа StructLayout и т. п.
Следует помнить о несоответствии размеров типов данных TwinCAT и C#, а также правильно вычислить размер буфера для потока. Для строковых переменных необходимо указывать максимальное количество символов (по умолчанию TwinCAT задает его равным 80 символам).

public struct DataPack
{
    public ushort Index;
    public string Name;
    public float Speed;
}

DataPack backpack = new DataPack();

TcAdsClient clientPlc;
int h_clientPlc;

AdsStream adsStream;

private void MainForm_Load(object sender, EventArgs e)
{
    clientPlc = new TcAdsClient();
    clientPlc.Connect("5.2.100.109.1.1", (int)AmsPort.PlcRuntime1);
    adsStream = new AdsStream(32);
 
    try
    {
        h_clientPlc = clientPlc.AddDeviceNotification(
            "MAIN.Pack",
            adsStream, 3, sizeof(ushort) + sizeof(float) + 10,
            AdsTransMode.OnChange, 100, 0,
            null);
    }
    catch (AdsErrorException adsExc)
    {
        // LOG: adsExс.Message;
        clientPlc.Dispose();
        clientPlc = null;
    }

    if (clientPlc != null)
    {
        clientPlc.AdsNotification += ClientPlc_AdsNotification;
    }

    [...]


Для удобства чтения полей структуры создается AdsBinaryStream, который умеет десериализовать в самые распространенные типы данных.

private void ClientPlc_AdsNotification(object sender, AdsNotificationEventArgs e)
{
    e.DataStream.Position = e.Offset;

    var reader = new AdsBinaryReader(e.DataStream);

    backpack.Index = reader.ReadUInt16();
    backpack.Name  = new string(reader.ReadChars(10));
    backpack.Speed = reader.ReadSingle();
}


Запись структуры производится точно так же как и запись обычной переменной.

backpack.Index = 0;
backpack.Name  = "PC written";
backpack.Speed = 300000;

clientPlc.WriteSymbol("MAIN.Pack", backpack, false);

March 2, 2016

Конфигурирование AX2000-B900

Устаревшее оборудование выходит из строя, разработчик давно сгинул в лесах амазонки, а ЗИП внезапно требует первоначальной настройки. Источник адреса задается dip-переключателями #7-8.


1 2 3 4 5 6 7 8
------------------
0 0 0 0 0 0 0 1 – адрес выдается сервером (или роутером) через DHCP. Дипы #1..6 обязательно в OFF.
0 0 0 0 0 0 1 0 – адрес выдается BootP-сервером.
0 0 0 0 0 0 0 0 – адрес выдается RT-Ethernet мастером, т. е. контроллером.

Если адреса выдает RT-Ethernet-мастер и есть несколько подчиненных, то адреса выдаются последовательно, начиная с мастера, которому достается адрес 172.16.17.1, затем 172.16.17.2... 3... 4...

Также существует легенда, что дипами 1..6 можно непосредственно задать IP-адрес, но подтвердить это, пока не удалось.