1. Выделение разделяемой памяти учитывает количество требуемой памяти 2. Выложил собранные библиотеки pnet и osal 3. Добавил общее описание в readme.md
179 lines
13 KiB
Markdown
179 lines
13 KiB
Markdown
# profinet_io_dev
|
||
|
||
Драйвер profinet io device. Предназначен для работы в фоне и взаимодействием с Profinet контроллером с использованием ethernet.
|
||
|
||
Реализует следующий функционал:
|
||
|
||
1. Читает из json файла структуру данных Profinet,
|
||
2. Конфигурирует и запускает циклический обмен по Profinet,
|
||
3. Принимает циклические данные от контроллера Profinet и записывает их в разделяемую память.
|
||
4. Берет циклические данные из разделяемой памяти и передает их контроллеру Profinet
|
||
|
||
Использует следующие библиотеки:
|
||
1. pnet
|
||
2. boost (1.74)
|
||
|
||
## Структура проекта:
|
||
* ***debug_support*** - содержит скрипт для запуска отладки с правами суперпользователя и обновления gdb (нужно для Ubuntu 22.04, там есть баг с gdb - он падает при переключении на другой поток в отлажеваемом ПО),
|
||
* ***libs*** - собранные библиотеки ***pnet*** и используемая ей ***osal***,
|
||
* ***profinet_stack*** - исходники библиотеки pnet,
|
||
* ***profinet_test*** - тестовые проекты. Наиболее интересен проект в папке ***sample_app_echo*** реализует функционал тестового модуля ECHO через взаимодействие с profinet_io_dev и подключение к разделяемой памяти.
|
||
* ***src*** - исходный код приложения profinet_io_dev.
|
||
|
||
## Общее описание данных Profinet
|
||
Терминалогия принятая в Profinet:
|
||
* Выходные данные - данные от контроллера к устройству,
|
||
* Входные данные - данные от устройства к контроллеру.
|
||
|
||
Профинет предоставляет абстракцию типичной системы управления на базе ПЛК, который имеет универсальные слоты в которые подключаются специализированные модули, например CAN, RS-422, аналоговых входов и т.д. В терминах Profinet все тоже самое: есть контроллер у которого есть слоты в которые он при установлении связи подключает модули устройства.
|
||
|
||
На стороне контроллера имеется один или несколько слотов(slots), каждый из которых имеет один или несколько подслотов(subslots).
|
||
|
||
На стороне устройства имеется один или несколько модулей(modules), каждый из которых имеет один или несколько подмодулей(submodules).
|
||
|
||
К каждому слоту на стороне контроллера может быть поключен модуль со стороны устройства.
|
||
|
||
К каждому подслоту на стороне контроллера может быть подключен подмодуль со стороны устройства.
|
||
|
||
Каждый подмодуль может включать:
|
||
* Выходные циклические данные (от контроллера к устройству)
|
||
* Входные циклические данные (от устройства к контроллеру)
|
||
* один или больше параметров.
|
||
|
||
Могут быть подмодули не содержащие данных вообще.
|
||
|
||
Параметры подмодуля отличаются от циклических данных тем, что записываются контроллером при установлении соединения, перед началом циклического обмена и больше не меняются. Их может изменить только пользователь, предварительно разорвав связь (по крайней мере в CodeSys так).
|
||
|
||
Параметры могут использоваться устройством для расчета входных данных для контроллера.
|
||
|
||
Описание конфигурации данных Profinet определяется специальным ***gsdml*** файлом, который на самом деле является ***xml*** файлом содержимое которого описано в спецификации Profinet. Этот файл нужен для всяких СКАДА систем при конфигурировании контроллера Profinet, загружаешь в СКАДА этот файл и в нем появляются Profinet устройство описанное в этом файле, а так-же все его модули, подмодули и параметры.
|
||
|
||
Идентификация модулей и подмодулей осуществляется с помощью 32-х битного идентификатора.
|
||
Идентификация параметра осуществляется с помощью 32-х битного индекса.
|
||
|
||
Установление связи выглядит следующим образом:
|
||
1. Мастер запрашивает соединение,
|
||
2. Дает команду устройству: модуль с идентификатором A подключить к слоту с номером B.
|
||
3. Дает команду устройству: подмодуль с идентификатором A входящий в состав модуля с идентификатором B подключить к подслоту с номером C слота с номером D.
|
||
4. Записывает параметры подключенного подмодуля.
|
||
5. Начинается обмен циклическими данными.
|
||
|
||
**Все данные в Profinet пишутся в порядке big endian.**
|
||
|
||
|
||
## Циклический обмен
|
||
|
||
Циклический обмен инициируется устройством, т.е. устройство должно с каким-либо периодом отправлять данные контроллеру.
|
||
|
||
## Файл конфигурации *program_configure.json*
|
||
|
||
Описывает общие настройки ПО.
|
||
|
||
***ProfinetSettings:***
|
||
* *ticks_us* - период таймера в мкс который вызывает pnet и реализует логику обмена.
|
||
* *cyclic_ms* - период обмена циклическими данными.
|
||
* *eth_dev_name* - наименование устройства ethernet в Linux через которое будет идти обмен по Profinet.
|
||
* *profinet_device_config* - путь к json файлу конфигурации устройства, который описывает структуру модулей и подмодулей.
|
||
|
||
## Файл конфигурации устройства Profinet
|
||
|
||
Описывает структуру модулей и подмодулей. В качетсве примера можно рассмотреть описание тестового модуля ECHO: profinet_device_configure.json.
|
||
|
||
***ProfinetDeviceSettings***
|
||
|
||
Содержить часть параметров структуры ***pnet_cfg*** из ***pnet_api.h***. Это стандартные параметры Profinet которые указаны и описаны в спецификации. Например имя станции или имя устройства.
|
||
|
||
***ProfinetDeviceModulesSettings***
|
||
|
||
Описание структуры данных устройства.
|
||
* ***modules***
|
||
* *id* - идентификатор модуля в шеснадцатиричном или десятичном виде. Шеснадцатиричный вид обозначется как в С через 0х.
|
||
* *name* - имя модуля, в обмене не используется, нужно для удобства.
|
||
* ***submodules*** - описание подмодулей:
|
||
* *id* - идентификатор подмодуля,
|
||
* *name* - имя подмодуля. В обмене не используется, нужно для удобства.
|
||
* *cyc_data_dir* - направление циклических данных. Может принимать значения:
|
||
* PNET_DIR_NO_IO - нет циклических данных,
|
||
* PNET_DIR_IO - входные и выходные циклические данные,
|
||
* PNET_DIR_INPUT - только входные циклические данные,
|
||
* PNET_DIR_OUTPUT - только выходные циклические данные.
|
||
* *cyc_inp_data_size* - размер входных циклических данных в байтах,
|
||
* *cyc_out_data_size* - размер выходных циклических данных в байтах,
|
||
* ***parameters*** - описание параметров:
|
||
* *index* - индекс параметра,
|
||
* *name* - наименование параметра. В обмене не используется, нужно для удобства.
|
||
* *data_size* - размер данных параметра в байтах.
|
||
|
||
## Межпроцессное взаимодействие
|
||
|
||
При запуске ПО выделяет блок разделяемой памяти с наименованием ***"profinet_shared_data"***.
|
||
|
||
Структура описана в ***profinet_data_map.hpp***.
|
||
|
||
API для работы с памятью приведено в файле: ***profinet_shared_data_client.hpp***
|
||
|
||
### Пример подключения к памяти:
|
||
|
||
```C++
|
||
ProfinetSharedDataClient shared_data;
|
||
|
||
ProfinetData_Map * p_profinet_data = shared_data.Connect("profinet_shared_data");
|
||
|
||
/// Берем указатель на данные подмодуля в разделяемой памяти
|
||
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
|
||
|
||
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
|
||
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
|
||
|
||
```
|
||
### Пример ожидания наступления события
|
||
В структуре `ProfinetData_Map` есть разделяемые флаги `Events` типа `SharedFlags` которые могут быть использованы для ожидания следующих событий:
|
||
* `EVENT_CONNECTION_ESTABLISHED` - соединение установлено
|
||
* `EVENT_NEW_CYCLIC_DATA` - получены новые циклические данные. Не указывает на то, что данные изменились, просто по профинету была получена новая порция цикличесиких данных от контроллера.
|
||
* `EVENT_NEW_PARAM_DATA` - контроллером была осуществлена запись данных параметров какого-либо подмодуля.
|
||
* `EVENT_CONNECTION_ABORT` - соединение с контроллером разорвано.
|
||
|
||
Пример ожидания установления соединения или приема новых циклических данных:
|
||
```C++
|
||
uint32_t events = p_profinet_data->Events.wait_flags(EVENT_CONNECTION_ESTABLISHED | EVENT_NEW_CYCLIC_DATA);
|
||
|
||
```
|
||
`wait_flags` - блокирует поток до момента установления флага.
|
||
|
||
Когда флаг установлен, его нужно сбросить:
|
||
```C++
|
||
p_profinet_data->Events.clear_flags(EVENT_NEW_CYCLIC_DATA);
|
||
```
|
||
|
||
### Пример чтения данных параметров подмодуля
|
||
|
||
```C++
|
||
uint32_t Echo_Gain = 0;
|
||
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
|
||
/// Конвертируем в литл эндиан
|
||
endian_convert_32((uint8_t*)&Echo_Gain);
|
||
```
|
||
|
||
### Пример чтения и записи циклических данных подмодуля
|
||
```C++
|
||
///Читаем данные от ПЛК
|
||
echo_submod_ptr->out_data.Read(0, Echo_outCycData.mem, echo_submod_ptr->cyc_outdata_len);
|
||
|
||
/// Конвертируем в литл эндиан
|
||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
|
||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
||
|
||
///Подготавливаем данные для ПЛК
|
||
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
|
||
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
|
||
///Конвертируем в биг эндиан
|
||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
|
||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
||
///Отправляем данные для ПЛК
|
||
echo_submod_ptr->inp_data.Write(0, Echo_inpCycData.mem, echo_submod_ptr->cyc_indata_len);
|
||
```
|
||
|
||
|
||
|
||
|