dev(UML-981): Решение некоторых проблем и документирование кода
1. Выделение разделяемой памяти учитывает количество требуемой памяти 2. Выложил собранные библиотеки pnet и osal 3. Добавил общее описание в readme.md
This commit is contained in:
parent
b992c1c3a6
commit
3c9774a173
177
README.md
177
README.md
@ -1,3 +1,178 @@
|
|||||||
# profinet_io_dev
|
# profinet_io_dev
|
||||||
|
|
||||||
Драйвер profinet io device.
|
Драйвер 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
sudo /usr/bin/gdb "$@"
|
sudo /usr/local/bin/gdb "$@"
|
||||||
|
|||||||
131
libs/include/osal.h
Normal file
131
libs/include/osal.h
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2017 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the BSD 3-clause
|
||||||
|
* license. See the file LICENSE distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef OSAL_H
|
||||||
|
#define OSAL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "sys/osal_sys.h"
|
||||||
|
#include "sys/osal_cc.h"
|
||||||
|
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BIT
|
||||||
|
#define BIT(n) (1U << (n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NELEMENTS
|
||||||
|
#define NELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_WAIT_FOREVER
|
||||||
|
#define OS_WAIT_FOREVER 0xFFFFFFFF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_MAIN
|
||||||
|
#define OS_MAIN int main
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_MUTEX
|
||||||
|
typedef void os_mutex_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_SEM
|
||||||
|
typedef void os_sem_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_THREAD
|
||||||
|
typedef void os_thread_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_EVENT
|
||||||
|
typedef void os_event_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_MBOX
|
||||||
|
typedef void os_mbox_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OS_TIMER
|
||||||
|
typedef void os_timer_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void * os_malloc (size_t size);
|
||||||
|
void os_free (void * ptr);
|
||||||
|
|
||||||
|
void os_usleep (uint32_t us);
|
||||||
|
uint32_t os_get_current_time_us (void);
|
||||||
|
|
||||||
|
os_thread_t * os_thread_create (
|
||||||
|
const char * name,
|
||||||
|
uint32_t priority,
|
||||||
|
size_t stacksize,
|
||||||
|
void (*entry) (void * arg),
|
||||||
|
void * arg);
|
||||||
|
|
||||||
|
os_mutex_t * os_mutex_create (void);
|
||||||
|
void os_mutex_lock (os_mutex_t * mutex);
|
||||||
|
void os_mutex_unlock (os_mutex_t * mutex);
|
||||||
|
void os_mutex_destroy (os_mutex_t * mutex);
|
||||||
|
|
||||||
|
os_sem_t * os_sem_create (size_t count);
|
||||||
|
bool os_sem_wait (os_sem_t * sem, uint32_t time);
|
||||||
|
void os_sem_signal (os_sem_t * sem);
|
||||||
|
void os_sem_destroy (os_sem_t * sem);
|
||||||
|
|
||||||
|
os_event_t * os_event_create (void);
|
||||||
|
bool os_event_wait (
|
||||||
|
os_event_t * event,
|
||||||
|
uint32_t mask,
|
||||||
|
uint32_t * value,
|
||||||
|
uint32_t time);
|
||||||
|
void os_event_set (os_event_t * event, uint32_t value);
|
||||||
|
void os_event_clr (os_event_t * event, uint32_t value);
|
||||||
|
void os_event_destroy (os_event_t * event);
|
||||||
|
|
||||||
|
os_mbox_t * os_mbox_create (size_t size);
|
||||||
|
bool os_mbox_fetch (os_mbox_t * mbox, void ** msg, uint32_t time);
|
||||||
|
bool os_mbox_post (os_mbox_t * mbox, void * msg, uint32_t time);
|
||||||
|
void os_mbox_destroy (os_mbox_t * mbox);
|
||||||
|
|
||||||
|
os_timer_t * os_timer_create (
|
||||||
|
uint32_t us,
|
||||||
|
void (*fn) (os_timer_t * timer, void * arg),
|
||||||
|
void * arg,
|
||||||
|
bool oneshot);
|
||||||
|
void os_timer_set (os_timer_t * timer, uint32_t us);
|
||||||
|
void os_timer_start (os_timer_t * timer);
|
||||||
|
void os_timer_stop (os_timer_t * timer);
|
||||||
|
void os_timer_destroy (os_timer_t * timer);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OSAL_H */
|
||||||
78
libs/include/osal_log.h
Normal file
78
libs/include/osal_log.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2017 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the BSD 3-clause
|
||||||
|
* license. See the file LICENSE distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef OSAL_LOG_H
|
||||||
|
#define OSAL_LOG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "osal.h"
|
||||||
|
|
||||||
|
/* Log levels */
|
||||||
|
#define LOG_LEVEL_DEBUG 0x00
|
||||||
|
#define LOG_LEVEL_INFO 0x01
|
||||||
|
#define LOG_LEVEL_WARNING 0x02
|
||||||
|
#define LOG_LEVEL_ERROR 0x03
|
||||||
|
#define LOG_LEVEL_FATAL 0x04
|
||||||
|
#define LOG_LEVEL_MASK 0x07
|
||||||
|
#define LOG_LEVEL_GET(t) (t & LOG_LEVEL_MASK)
|
||||||
|
|
||||||
|
/* Log states */
|
||||||
|
#define LOG_STATE_ON 0x80
|
||||||
|
#define LOG_STATE_OFF 0x00
|
||||||
|
|
||||||
|
#define LOG_ENABLED(type) \
|
||||||
|
((LOG_LEVEL_GET (type) >= LOG_LEVEL) && (type & LOG_STATE_ON))
|
||||||
|
|
||||||
|
/** Log a message if it is enabled */
|
||||||
|
#define LOG(type, ...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (LOG_ENABLED (type)) \
|
||||||
|
{ \
|
||||||
|
os_log (type, __VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/** Log debug messages */
|
||||||
|
#define LOG_DEBUG(type, ...) LOG ((LOG_LEVEL_DEBUG | type), __VA_ARGS__)
|
||||||
|
|
||||||
|
/** Log informational messages */
|
||||||
|
#define LOG_INFO(type, ...) LOG ((LOG_LEVEL_INFO | type), __VA_ARGS__)
|
||||||
|
|
||||||
|
/** Log warning messages */
|
||||||
|
#define LOG_WARNING(type, ...) LOG ((LOG_LEVEL_WARNING | type), __VA_ARGS__)
|
||||||
|
|
||||||
|
/** Log error messages */
|
||||||
|
#define LOG_ERROR(type, ...) LOG ((LOG_LEVEL_ERROR | type), __VA_ARGS__)
|
||||||
|
|
||||||
|
/** Log fatal messages */
|
||||||
|
#define LOG_FATAL(type, ...) LOG ((LOG_LEVEL_FATAL | type), __VA_ARGS__)
|
||||||
|
|
||||||
|
#define LOG_DEBUG_ENABLED(type) LOG_ENABLED (LOG_LEVEL_DEBUG | type)
|
||||||
|
#define LOG_INFO_ENABLED(type) LOG_ENABLED (LOG_LEVEL_INFO | type)
|
||||||
|
#define LOG_WARNING_ENABLED(type) LOG_ENABLED (LOG_LEVEL_WARNING | type)
|
||||||
|
#define LOG_ERROR_ENABLED(type) LOG_ENABLED (LOG_LEVEL_ERROR | type)
|
||||||
|
#define LOG_FATAL_ENABLED(type) LOG_ENABLED (LOG_LEVEL_FATAL | type)
|
||||||
|
|
||||||
|
void os_log (uint8_t type, const char * fmt, ...) CC_FORMAT (2, 3);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OSAL_LOG_H */
|
||||||
54
libs/include/pnal_config.h
Normal file
54
libs/include/pnal_config.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2021 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is dual-licensed under GPLv3 and a commercial
|
||||||
|
* license. See the file LICENSE.md distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief PNAL-specific configuration
|
||||||
|
*
|
||||||
|
* This file contains definitions of configuration settings for the
|
||||||
|
* PNAL layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PNAL_CONFIG_H
|
||||||
|
#define PNAL_CONFIG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread priority and stack size
|
||||||
|
*/
|
||||||
|
typedef struct pnal_thread_cfg
|
||||||
|
{
|
||||||
|
uint32_t prio;
|
||||||
|
size_t stack_size;
|
||||||
|
} pnal_thread_cfg_t;
|
||||||
|
|
||||||
|
typedef struct pnal_cfg
|
||||||
|
{
|
||||||
|
pnal_thread_cfg_t snmp_thread;
|
||||||
|
pnal_thread_cfg_t eth_recv_thread;
|
||||||
|
pnal_thread_cfg_t bg_worker_thread;
|
||||||
|
} pnal_cfg_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PNAL_CONFIG_H */
|
||||||
2257
libs/include/pnet_api.h
Normal file
2257
libs/include/pnet_api.h
Normal file
File diff suppressed because it is too large
Load Diff
42
libs/include/pnet_export.h
Normal file
42
libs/include/pnet_export.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#ifndef PNET_EXPORT_H
|
||||||
|
#define PNET_EXPORT_H
|
||||||
|
|
||||||
|
#ifdef PNET_STATIC_DEFINE
|
||||||
|
# define PNET_EXPORT
|
||||||
|
# define PNET_NO_EXPORT
|
||||||
|
#else
|
||||||
|
# ifndef PNET_EXPORT
|
||||||
|
# ifdef profinet_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define PNET_EXPORT
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define PNET_EXPORT
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef PNET_NO_EXPORT
|
||||||
|
# define PNET_NO_EXPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNET_DEPRECATED
|
||||||
|
# define PNET_DEPRECATED __attribute__ ((__deprecated__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNET_DEPRECATED_EXPORT
|
||||||
|
# define PNET_DEPRECATED_EXPORT PNET_EXPORT PNET_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PNET_DEPRECATED_NO_EXPORT
|
||||||
|
# define PNET_DEPRECATED_NO_EXPORT PNET_NO_EXPORT PNET_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 /* DEFINE_NO_DEPRECATED */
|
||||||
|
# ifndef PNET_NO_DEPRECATED
|
||||||
|
# define PNET_NO_DEPRECATED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PNET_EXPORT_H */
|
||||||
33
libs/include/pnet_options.h
Normal file
33
libs/include/pnet_options.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2018 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is dual-licensed under GPLv3 and a commercial
|
||||||
|
* license. See the file LICENSE.md distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef PNET_OPTIONS_H
|
||||||
|
#define PNET_OPTIONS_H
|
||||||
|
|
||||||
|
#if !defined (PNET_MAX_PHYSICAL_PORTS)
|
||||||
|
/** Max number of physical ports */
|
||||||
|
#define PNET_MAX_PHYSICAL_PORTS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined (PNET_MAX_DIRECTORYPATH_SIZE)
|
||||||
|
/** Max directory path size, including termination */
|
||||||
|
#define PNET_MAX_DIRECTORYPATH_SIZE 240
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined (PNET_OPTION_DRIVER_ENABLE)
|
||||||
|
#define PNET_OPTION_DRIVER_ENABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PNET_OPTIONS_H */
|
||||||
41
libs/include/pnet_version.h
Normal file
41
libs/include/pnet_version.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2018 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is dual-licensed under GPLv3 and a commercial
|
||||||
|
* license. See the file LICENSE.md distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef PNET_VERSION_H
|
||||||
|
#define PNET_VERSION_H
|
||||||
|
|
||||||
|
/* #undef PROFINET_GIT_REVISION */
|
||||||
|
|
||||||
|
#if !defined(PNET_VERSION_BUILD) && defined(PROFINET_GIT_REVISION)
|
||||||
|
#define PNET_VERSION_BUILD PROFINET_GIT_REVISION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* clang-format-off */
|
||||||
|
|
||||||
|
#define PNET_VERSION_MAJOR 0
|
||||||
|
#define PNET_VERSION_MINOR 2
|
||||||
|
#define PNET_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#if defined(PNET_VERSION_BUILD)
|
||||||
|
#define PNET_VERSION \
|
||||||
|
"0.2.0+" PNET_VERSION_BUILD
|
||||||
|
#else
|
||||||
|
#define PNET_VERSION \
|
||||||
|
"0.2.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* clang-format-on */
|
||||||
|
|
||||||
|
#endif /* PNET_VERSION_H */
|
||||||
98
libs/include/sys/osal_cc.h
Normal file
98
libs/include/sys/osal_cc.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2017 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the BSD 3-clause
|
||||||
|
* license. See the file LICENSE distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef CC_H
|
||||||
|
#define CC_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#if !defined(CLANG_ANALYZER_NORETURN)
|
||||||
|
#if __has_feature(attribute_analyzer_noreturn)
|
||||||
|
#define CLANG_ANALYZER_NORETURN __attribute__ ((analyzer_noreturn))
|
||||||
|
#else
|
||||||
|
#define CLANG_ANALYZER_NORETURN
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define CLANG_ANALYZER_NORETURN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void cc_assert (int exp) CLANG_ANALYZER_NORETURN
|
||||||
|
{
|
||||||
|
assert (exp); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CC_PACKED_BEGIN
|
||||||
|
#define CC_PACKED_END
|
||||||
|
#define CC_PACKED __attribute__ ((packed))
|
||||||
|
|
||||||
|
#define CC_FORMAT(str, arg) __attribute__ ((format (printf, str, arg)))
|
||||||
|
|
||||||
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||||
|
#define CC_TO_LE16(x) ((uint16_t)(x))
|
||||||
|
#define CC_TO_LE32(x) ((uint32_t)(x))
|
||||||
|
#define CC_TO_LE64(x) ((uint64_t)(x))
|
||||||
|
#define CC_FROM_LE16(x) ((uint16_t)(x))
|
||||||
|
#define CC_FROM_LE32(x) ((uint32_t)(x))
|
||||||
|
#define CC_FROM_LE64(x) ((uint64_t)(x))
|
||||||
|
#define CC_TO_BE16(x) ((uint16_t)__builtin_bswap16 (x))
|
||||||
|
#define CC_TO_BE32(x) ((uint32_t)__builtin_bswap32 (x))
|
||||||
|
#define CC_TO_BE64(x) ((uint64_t)__builtin_bswap64 (x))
|
||||||
|
#define CC_FROM_BE16(x) ((uint16_t)__builtin_bswap16 (x))
|
||||||
|
#define CC_FROM_BE32(x) ((uint32_t)__builtin_bswap32 (x))
|
||||||
|
#define CC_FROM_BE64(x) ((uint64_t)__builtin_bswap64 (x))
|
||||||
|
#else
|
||||||
|
#define CC_TO_LE16(x) ((uint16_t)__builtin_bswap16 (x))
|
||||||
|
#define CC_TO_LE32(x) ((uint32_t)__builtin_bswap32 (x))
|
||||||
|
#define CC_TO_LE64(x) ((uint64_t)__builtin_bswap64 (x))
|
||||||
|
#define CC_FROM_LE16(x) ((uint16_t)__builtin_bswap16 (x))
|
||||||
|
#define CC_FROM_LE32(x) ((uint32_t)__builtin_bswap32 (x))
|
||||||
|
#define CC_FROM_LE64(x) ((uint64_t)__builtin_bswap64 (x))
|
||||||
|
#define CC_TO_BE16(x) ((uint16_t)(x))
|
||||||
|
#define CC_TO_BE32(x) ((uint32_t)(x))
|
||||||
|
#define CC_TO_BE64(x) ((uint64_t)(x))
|
||||||
|
#define CC_FROM_BE16(x) ((uint16_t)(x))
|
||||||
|
#define CC_FROM_BE32(x) ((uint32_t)(x))
|
||||||
|
#define CC_FROM_BE64(x) ((uint64_t)(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CC_ATOMIC_GET8(p) __atomic_load_n ((p), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_GET16(p) __atomic_load_n ((p), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_GET32(p) __atomic_load_n ((p), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_GET64(p) __atomic_load_n ((p), __ATOMIC_SEQ_CST)
|
||||||
|
|
||||||
|
#define CC_ATOMIC_SET8(p, v) __atomic_store_n ((p), (v), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_SET16(p, v) __atomic_store_n ((p), (v), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_SET32(p, v) __atomic_store_n ((p), (v), __ATOMIC_SEQ_CST)
|
||||||
|
#define CC_ATOMIC_SET64(p, v) __atomic_store_n ((p), (v), __ATOMIC_SEQ_CST)
|
||||||
|
|
||||||
|
#define CC_ASSERT(exp) cc_assert (exp)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define CC_STATIC_ASSERT(exp) static_assert (exp, "")
|
||||||
|
#else
|
||||||
|
#define CC_STATIC_ASSERT(exp) _Static_assert(exp, "")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CC_H */
|
||||||
77
libs/include/sys/osal_sys.h
Normal file
77
libs/include/sys/osal_sys.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*********************************************************************
|
||||||
|
* _ _ _
|
||||||
|
* _ __ | |_ _ | | __ _ | |__ ___
|
||||||
|
* | '__|| __|(_)| | / _` || '_ \ / __|
|
||||||
|
* | | | |_ _ | || (_| || |_) |\__ \
|
||||||
|
* |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||||
|
*
|
||||||
|
* www.rt-labs.com
|
||||||
|
* Copyright 2017 rt-labs AB, Sweden.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the BSD 3-clause
|
||||||
|
* license. See the file LICENSE distributed with this software for
|
||||||
|
* full license information.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef OSAL_SYS_H
|
||||||
|
#define OSAL_SYS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define OS_THREAD
|
||||||
|
#define OS_MUTEX
|
||||||
|
#define OS_SEM
|
||||||
|
#define OS_EVENT
|
||||||
|
#define OS_MBOX
|
||||||
|
#define OS_TIMER
|
||||||
|
|
||||||
|
typedef pthread_t os_thread_t;
|
||||||
|
typedef pthread_mutex_t os_mutex_t;
|
||||||
|
|
||||||
|
typedef struct os_sem
|
||||||
|
{
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
size_t count;
|
||||||
|
} os_sem_t;
|
||||||
|
|
||||||
|
typedef struct os_event
|
||||||
|
{
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
uint32_t flags;
|
||||||
|
} os_event_t;
|
||||||
|
|
||||||
|
typedef struct os_mbox
|
||||||
|
{
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
size_t r;
|
||||||
|
size_t w;
|
||||||
|
size_t count;
|
||||||
|
size_t size;
|
||||||
|
void * msg[];
|
||||||
|
} os_mbox_t;
|
||||||
|
|
||||||
|
typedef struct os_timer
|
||||||
|
{
|
||||||
|
timer_t timerid;
|
||||||
|
os_thread_t * thread;
|
||||||
|
pid_t thread_id;
|
||||||
|
bool exit;
|
||||||
|
void (*fn) (struct os_timer *, void * arg);
|
||||||
|
void * arg;
|
||||||
|
uint32_t us;
|
||||||
|
bool oneshot;
|
||||||
|
} os_timer_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OSAL_SYS_H */
|
||||||
BIN
libs/lib/x86_64/libosal.a
Normal file
BIN
libs/lib/x86_64/libosal.a
Normal file
Binary file not shown.
BIN
libs/lib/x86_64/libprofinet.a
Normal file
BIN
libs/lib/x86_64/libprofinet.a
Normal file
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
#include "profinet_shared_data_client.hpp"
|
#include "profinet_shared_data_client.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
||||||
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
||||||
@ -45,32 +46,34 @@ uint32_t event_mask = ( ProfinetEvent::EVENT_CONNECTION_ESTABLISHED |
|
|||||||
ProfinetEvent::EVENT_NEW_PARAM_DATA |
|
ProfinetEvent::EVENT_NEW_PARAM_DATA |
|
||||||
ProfinetEvent::EVENT_CONNECTION_ABORT );
|
ProfinetEvent::EVENT_CONNECTION_ABORT );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
p_profinet_data = shared_data.Connect("profinet_shared_data");
|
p_profinet_data = shared_data.Connect("profinet_shared_data");
|
||||||
auto& echo_module = p_profinet_data->p_mods[0];
|
|
||||||
if (echo_module.id != ECHO_MODULE_ID)
|
if (p_profinet_data == nullptr)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& echo_submod = echo_module.p_submods[0];
|
/// Берем указатель на данные подмодуля в разделяемой памяти
|
||||||
|
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
|
||||||
|
|
||||||
if (echo_submod.id != ECHO_SUBMOD_ID)
|
if (echo_submod_ptr->id != ECHO_SUBMOD_ID)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
|
||||||
|
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
|
||||||
|
|
||||||
|
if (echo_param_gain_ptr->index != ECHO_PARAMETER_GAIN_IDX)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& echo_param_gain = echo_submod.p_params[0];
|
|
||||||
|
|
||||||
if (echo_param_gain.index != ECHO_PARAMETER_GAIN_IDX)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::cout << "Strarting..." << std::endl;
|
std::cout << "Strarting..." << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
uint32_t events = p_profinet_data->Events.wait_flags(event_mask);
|
uint32_t events = p_profinet_data->Events.wait_flags(event_mask);
|
||||||
@ -84,15 +87,31 @@ int main(int argc, char * argv[])
|
|||||||
|
|
||||||
if (events & ProfinetEvent::EVENT_NEW_CYCLIC_DATA)
|
if (events & ProfinetEvent::EVENT_NEW_CYCLIC_DATA)
|
||||||
{
|
{
|
||||||
std::cout << "New cyc data" << std::endl;
|
static uint32_t value_i = 0;
|
||||||
|
static float value_f = 0.0;
|
||||||
|
|
||||||
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
|
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
|
||||||
|
|
||||||
///Читаем данные от ПЛК
|
///Читаем данные от ПЛК
|
||||||
echo_submod.out_data.Read(0, Echo_outCycData.mem, echo_submod.cyc_outdata_len);
|
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_i);
|
||||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
||||||
|
|
||||||
|
if (value_i != Echo_outCycData.data.data_i)
|
||||||
|
{
|
||||||
|
value_i = Echo_outCycData.data.data_i;
|
||||||
|
|
||||||
|
std::cout << "New data_i from PLC: " << value_i << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value_f != Echo_outCycData.data.data_f)
|
||||||
|
{
|
||||||
|
value_f = Echo_outCycData.data.data_f;
|
||||||
|
|
||||||
|
std::cout << "New data_f from PLC: " << value_f << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
///Подготавливаем данные для ПЛК
|
///Подготавливаем данные для ПЛК
|
||||||
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
|
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
|
||||||
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
|
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
|
||||||
@ -100,8 +119,7 @@ int main(int argc, char * argv[])
|
|||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
|
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
|
||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
||||||
///Отправляем данные для ПЛК
|
///Отправляем данные для ПЛК
|
||||||
echo_submod.inp_data.Write(0, Echo_inpCycData.mem, echo_submod.cyc_indata_len);
|
echo_submod_ptr->inp_data.Write(0, Echo_inpCycData.mem, echo_submod_ptr->cyc_indata_len);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events & ProfinetEvent::EVENT_NEW_PARAM_DATA)
|
if (events & ProfinetEvent::EVENT_NEW_PARAM_DATA)
|
||||||
@ -110,10 +128,16 @@ int main(int argc, char * argv[])
|
|||||||
|
|
||||||
std::cout << "Event: New parameter data: " << std::endl;
|
std::cout << "Event: New parameter data: " << std::endl;
|
||||||
//Читаем параметр записанный ПЛК при установлении связи
|
//Читаем параметр записанный ПЛК при установлении связи
|
||||||
echo_param_gain.data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain.length);
|
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
|
||||||
/// Конвертируем в литл эндиан
|
/// Конвертируем в литл эндиан
|
||||||
endian_convert_32((uint8_t*)&Echo_Gain);
|
endian_convert_32((uint8_t*)&Echo_Gain);
|
||||||
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
|
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (events & ProfinetEvent::EVENT_CONNECTION_ABORT)
|
||||||
|
{
|
||||||
|
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ABORT);
|
||||||
|
std::cout << "Connection Aborted" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,14 +1,3 @@
|
|||||||
#********************************************************************
|
|
||||||
# _ _ _
|
|
||||||
# _ __ | |_ _ | | __ _ | |__ ___
|
|
||||||
# | '__|| __|(_)| | / _` || '_ \ / __|
|
|
||||||
# | | | |_ _ | || (_| || |_) |\__ \
|
|
||||||
# |_| \__|(_)|_| \__,_||_.__/ |___/
|
|
||||||
#
|
|
||||||
# http://www.rt-labs.com
|
|
||||||
# Copyright 2017 rt-labs AB, Sweden.
|
|
||||||
# See LICENSE file in the project root for full license information.
|
|
||||||
#*******************************************************************/
|
|
||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.0)
|
||||||
|
|
||||||
project (PN_DEV_TEST VERSION 0.0.1)
|
project (PN_DEV_TEST VERSION 0.0.1)
|
||||||
@ -16,6 +5,7 @@ project (PN_DEV_TEST VERSION 0.0.1)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(LIBS_INSTALL_PATH ../libs)
|
set(LIBS_INSTALL_PATH ../libs)
|
||||||
set(PNET_PATH ../profinet_stack/p-net)
|
set(PNET_PATH ../profinet_stack/p-net)
|
||||||
|
set(TARGET_NAME profinet_io_dev)
|
||||||
|
|
||||||
include(./utils/utils.cmake)
|
include(./utils/utils.cmake)
|
||||||
include(./profinet/profinet.cmake)
|
include(./profinet/profinet.cmake)
|
||||||
@ -27,10 +17,24 @@ include (./shared_data/shared_data.cmake)
|
|||||||
|
|
||||||
set(SRC_FILES ${SRC_FILES} ./app.cpp)
|
set(SRC_FILES ${SRC_FILES} ./app.cpp)
|
||||||
|
|
||||||
|
# Копирование заглушки скрипта установки параметров сети.
|
||||||
|
# Если не копировать, то в недрах pnet после fork вызовется exit и
|
||||||
|
# вызовутся деструкторы объектов, что может привести к непредвиденным последствиям.
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/set_network_parameters
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
add_executable(pn_dev_test ./main.cpp ${SRC_FILES})
|
#Копирование файла конфигурации
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/program_configure.json
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
target_include_directories(pn_dev_test PRIVATE
|
#Копирование тестового файла конфигурации устройства Profinet
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/profinet_device_configure.json
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(${TARGET_NAME} ./main.cpp ${SRC_FILES})
|
||||||
|
|
||||||
|
target_include_directories(${TARGET_NAME} PRIVATE
|
||||||
./
|
./
|
||||||
${INC_DIRS}
|
${INC_DIRS}
|
||||||
${LIBS_INSTALL_PATH}/include
|
${LIBS_INSTALL_PATH}/include
|
||||||
@ -40,5 +44,6 @@ target_include_directories(pn_dev_test PRIVATE
|
|||||||
${PNET_PATH}/src/ports/linux
|
${PNET_PATH}/src/ports/linux
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_directories(pn_dev_test PUBLIC ${LIBS_INSTALL_PATH}/lib)
|
target_link_directories(${TARGET_NAME} PUBLIC ${LIBS_INSTALL_PATH}/lib/x86_64)
|
||||||
target_link_libraries (pn_dev_test PUBLIC profinet osal ${Boost_LIBRARIES})
|
|
||||||
|
target_link_libraries (${TARGET_NAME} PUBLIC profinet osal ${Boost_LIBRARIES})
|
||||||
|
|||||||
@ -157,12 +157,70 @@ static uint8_t programconf_dataDirConvert(string dir_str)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Считаем общий размер данных требуемый всеми подмодулями: их циклическими данными и параметрами
|
||||||
|
* Пробегает по файлу конфигурации и считает размер требуемой памяти.
|
||||||
|
* @param j
|
||||||
|
* @param memory_size
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
static bool programconf_calc_memory_size(json& j, uint32_t& memory_size)
|
||||||
|
{
|
||||||
|
if (!j.contains("ProfinetDeviceModulesSettings"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
|
||||||
|
|
||||||
|
memory_size = sizeof(ProfinetData_Map) + modules_sett.size() * sizeof(ProfinetData_Module);
|
||||||
|
|
||||||
|
for (auto& mod_set : modules_sett)
|
||||||
|
{
|
||||||
|
auto& submodules_sett = mod_set["submodules"];
|
||||||
|
|
||||||
|
memory_size += submodules_sett.size() * sizeof(ProfinetData_Submodule);
|
||||||
|
|
||||||
|
for (auto& submod_sett : submodules_sett)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint16_t in_data_len = static_cast<uint16_t>(submod_sett["cyc_inp_data_size"].get<int>());
|
||||||
|
uint16_t out_data_len = static_cast<uint16_t>(submod_sett["cyc_out_data_size"].get<int>());
|
||||||
|
|
||||||
|
memory_size+= in_data_len + out_data_len;
|
||||||
|
|
||||||
|
///Параметры подмодуля
|
||||||
|
auto& params_sett = submod_sett["parameters"];
|
||||||
|
|
||||||
|
memory_size += params_sett.size() * sizeof(ProfinetData_Parameter);
|
||||||
|
|
||||||
|
/// Записываем параметры в подмодуль
|
||||||
|
for (auto& parm_sett : params_sett)
|
||||||
|
{
|
||||||
|
uint16_t data_size = static_cast<uint16_t>(parm_sett["data_size"].get<int>());
|
||||||
|
memory_size += data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
||||||
{
|
{
|
||||||
if (!j.contains("ProfinetDeviceModulesSettings"))
|
if (!j.contains("ProfinetDeviceModulesSettings"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
uint32_t required_mem_size;
|
||||||
|
|
||||||
|
if (!programconf_calc_memory_size(j, required_mem_size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/// Проверяем достаточно ли у нас выделено памяти под текущую конфигурацию
|
||||||
|
/// Если недостаточно, то выделяем недостающее.
|
||||||
|
shared_data.checkMemorySize(required_mem_size);
|
||||||
|
|
||||||
|
/// Далее идет выделение
|
||||||
|
|
||||||
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
|
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
|
||||||
///Узнаем количество модулей
|
///Узнаем количество модулей
|
||||||
uint32_t mudules_number = modules_sett.size();
|
uint32_t mudules_number = modules_sett.size();
|
||||||
@ -297,6 +355,8 @@ static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||||
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
||||||
{
|
{
|
||||||
@ -320,3 +380,4 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ ProfinetData_Map * ProfinetSharedData::Create(std::string mem_name)
|
|||||||
{
|
{
|
||||||
/// Если память уже выделена, удаляем
|
/// Если память уже выделена, удаляем
|
||||||
shared_memory_object::remove(mem_name.c_str());
|
shared_memory_object::remove(mem_name.c_str());
|
||||||
|
/// Для начала выделяем 65к байт. Потом вызывается функция checkMemorySize и размер моджет быть увеличен
|
||||||
shmem_ = managed_shared_memory{create_only, mem_name.c_str(), 65536};
|
shmem_ = managed_shared_memory{create_only, mem_name.c_str(), 65536};
|
||||||
mem_name_ = mem_name;
|
mem_name_ = mem_name;
|
||||||
///Создаем данные в разделяемой памяти
|
///Создаем данные в разделяемой памяти
|
||||||
@ -46,3 +46,20 @@ ProfinetData_Map * ProfinetSharedData::Create(std::string mem_name)
|
|||||||
return p_profinet_data_;
|
return p_profinet_data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfinetSharedData::checkMemorySize(uint32_t required_mem_size)
|
||||||
|
{
|
||||||
|
uint32_t free_size = shmem_.get_size();
|
||||||
|
|
||||||
|
if (required_mem_size < free_size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/// Увеличиваем память
|
||||||
|
managed_shared_memory::grow(mem_name_.c_str(), required_mem_size - free_size);
|
||||||
|
|
||||||
|
/// Заново открываем память(может измениться указатель)
|
||||||
|
shmem_ = managed_shared_memory{open_only, mem_name_.c_str()};
|
||||||
|
|
||||||
|
/// Заново находим или создаем структуру ProfinetData_Map
|
||||||
|
p_profinet_data_ = shmem_.find_or_construct<ProfinetData_Map>("ProfinetData_Map")();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,8 @@ class ProfinetSharedData {
|
|||||||
public:
|
public:
|
||||||
~ProfinetSharedData() {
|
~ProfinetSharedData() {
|
||||||
///Удаляем память
|
///Удаляем память
|
||||||
//boost::interprocess::shared_memory_object::remove(mem_name_.c_str());
|
boost::interprocess::shared_memory_object::remove(mem_name_.c_str());
|
||||||
|
//std::cout << "Destruct: ProfinetSharedData: Shared data deleted" << std::endl;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Выделяыет раздеяемую память под структуру ProfinetData_Map
|
* @brief Выделяыет раздеяемую память под структуру ProfinetData_Map
|
||||||
@ -29,6 +30,15 @@ public:
|
|||||||
return shmem_.construct<T>(boost::interprocess::anonymous_instance)[nbr]();
|
return shmem_.construct<T>(boost::interprocess::anonymous_instance)[nbr]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Проверяет, что размер свободной памяти не меньше чем значение required_mem_size
|
||||||
|
* Если меньше, то увеличиват память.
|
||||||
|
*
|
||||||
|
* @param required_mem_size
|
||||||
|
* @return uint32_t
|
||||||
|
*/
|
||||||
|
void checkMemorySize(uint32_t required_mem_size);
|
||||||
|
|
||||||
ProfinetData_Map * p_profinet_data_;
|
ProfinetData_Map * p_profinet_data_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class ProfinetSharedDataClient {
|
class ProfinetSharedDataClient {
|
||||||
public:
|
public:
|
||||||
@ -22,6 +24,8 @@ public:
|
|||||||
{
|
{
|
||||||
auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
|
auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
|
||||||
out = mem.first;
|
out = mem.first;
|
||||||
|
///Упорядочиваем память для удобства обращения по индентификатору Profinet
|
||||||
|
collected_data = collect(out);
|
||||||
}
|
}
|
||||||
catch(const std::exception& e)
|
catch(const std::exception& e)
|
||||||
{
|
{
|
||||||
@ -34,10 +38,76 @@ public:
|
|||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @brief Возвращает указатель на данные подмодуля в разделяемой памяти
|
||||||
|
*
|
||||||
|
* @param module_id идентификатор модуля
|
||||||
|
* @param submodule_id идентификатор подмодуля
|
||||||
|
* @return ProfinetData_Submodule*
|
||||||
|
*/
|
||||||
|
ProfinetData_Submodule * getSubmodule(uint32_t module_id, uint32_t submodule_id)
|
||||||
|
{
|
||||||
|
return (*collected_data)[module_id].submods[submodule_id].p_submod;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Возвращает указатель на данные параметра подмодуля в разделяемой памяти
|
||||||
|
*
|
||||||
|
* @param module_id идентификатор модуля
|
||||||
|
* @param submodule_id идентификатор подмодуля
|
||||||
|
* @param param_index индекс параметра
|
||||||
|
* @return ProfinetData_Parameter*
|
||||||
|
*/
|
||||||
|
ProfinetData_Parameter * getSubmoduleParameter(uint32_t module_id, uint32_t submodule_id, uint32_t param_index)
|
||||||
|
{
|
||||||
|
return (*collected_data)[module_id].submods[submodule_id].params[param_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct ProfinetSharedDataSubmodule {
|
||||||
|
ProfinetData_Submodule* p_submod;
|
||||||
|
std::map<uint32_t, ProfinetData_Parameter*> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfinetSharedDataModule {
|
||||||
|
ProfinetData_Module* p_mod;
|
||||||
|
std::map<uint32_t, ProfinetSharedDataSubmodule> submods;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Собирает данные в словарь, можно использовать для удобства обращения к данным по идентификатору
|
||||||
|
*
|
||||||
|
* @param p_data_map
|
||||||
|
* @return std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>>
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collect(ProfinetData_Map * p_data_map) {
|
||||||
|
std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> p_map(new std::map<uint32_t, ProfinetSharedDataModule>);
|
||||||
|
|
||||||
|
for (int i = 0; i < p_data_map->mods_nbr; ++i)
|
||||||
|
{
|
||||||
|
auto& mod = (*p_map)[p_data_map->p_mods[i].id];
|
||||||
|
|
||||||
|
mod.p_mod = &p_data_map->p_mods[i];
|
||||||
|
|
||||||
|
for (int j = 0; j < p_data_map->p_mods[i].submods_nbr; ++j)
|
||||||
|
{
|
||||||
|
auto& submod = mod.submods[p_data_map->p_mods[i].p_submods[j].id];
|
||||||
|
|
||||||
|
submod.p_submod = &p_data_map->p_mods[i].p_submods[j];
|
||||||
|
|
||||||
|
for (int k = 0; k < p_data_map->p_mods[i].p_submods[j].params_nbr; ++k)
|
||||||
|
{
|
||||||
|
submod.params[p_data_map->p_mods[i].p_submods[j].p_params[k].index] = &p_data_map->p_mods[i].p_submods[j].p_params[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_map;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::interprocess::managed_shared_memory shmem_;
|
boost::interprocess::managed_shared_memory shmem_;
|
||||||
|
std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collected_data; /// Упорядоченная структура указателей на разделяемую память
|
||||||
};
|
};
|
||||||
16
src/set_network_parameters
Executable file
16
src/set_network_parameters
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# Скрипт для установки параметров сети #
|
||||||
|
############################################
|
||||||
|
#
|
||||||
|
# Данный скрипт запускается стеком pnet при инициализации.
|
||||||
|
# Если файл с именем set_network_parameters будет отсутсвовать,
|
||||||
|
# то в недрах pnet после вызова fork будет вызван exit, что может привести
|
||||||
|
# к нежелательным последствиям, например вызовам деструкторов копий объектов
|
||||||
|
# родительского процесса.
|
||||||
|
#
|
||||||
|
|
||||||
|
echo "set_network_parameters: This is dummy script wich called by pnet stack. As example see pnet/src/ports/linux/set_network_parameters."
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
Reference in New Issue
Block a user