Merge branch 'master' of http://server_gorbunov:3000/SmartForce4.0/profinet_io_dev
This commit is contained in:
commit
b2a879978e
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.
34
profinet_test/sample_app_echo/CMakeLists.txt
Normal file
34
profinet_test/sample_app_echo/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
cmake_minimum_required (VERSION 3.0)
|
||||||
|
|
||||||
|
project (PN_ECHO_TEST VERSION 0.0.1)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
set(INC_DIRS ${INC_DIRS} ../../src/profinet)
|
||||||
|
set(INC_DIRS ${INC_DIRS} ../../src/interprocess)
|
||||||
|
set(INC_DIRS ${INC_DIRS} ../../src/shared_data)
|
||||||
|
|
||||||
|
set(SRC_FILES ${SRC_FILES} ../../src/shared_data/shared_data.cpp)
|
||||||
|
set(SRC_FILES ${SRC_FILES} ../../src/interprocess/profinet_shared_data.cpp)
|
||||||
|
set(SRC_FILES ${SRC_FILES} ./main.cpp)
|
||||||
|
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
|
|
||||||
|
find_package(Boost 1.74.0)
|
||||||
|
if(Boost_FOUND)
|
||||||
|
set(INC_DIRS ${INC_DIRS} ${Boost_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
message(BOOST NOT FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||||
|
message("Boost libs:" ${Boost_LIBRARIES})
|
||||||
|
|
||||||
|
add_executable(pn_echo_test ./main.cpp ${SRC_FILES})
|
||||||
|
|
||||||
|
target_include_directories(pn_echo_test PRIVATE
|
||||||
|
./
|
||||||
|
${INC_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
143
profinet_test/sample_app_echo/main.cpp
Normal file
143
profinet_test/sample_app_echo/main.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "profinet_shared_data_client.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
||||||
|
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
||||||
|
#define ECHO_INPUT_DATA_SIZE 8
|
||||||
|
#define ECHO_OUTPUT_DATA_SIZE 8
|
||||||
|
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
uint32_t Echo_Gain = 0;
|
||||||
|
|
||||||
|
struct EchoData {
|
||||||
|
float data_f;
|
||||||
|
uint32_t data_i;
|
||||||
|
};
|
||||||
|
|
||||||
|
union EchoDataMem
|
||||||
|
{
|
||||||
|
EchoData data;
|
||||||
|
uint8_t mem[sizeof(EchoData)];
|
||||||
|
};
|
||||||
|
|
||||||
|
EchoDataMem Echo_inpCycData;
|
||||||
|
EchoDataMem Echo_outCycData;
|
||||||
|
|
||||||
|
uint8_t be_data_inp[sizeof(EchoData)];
|
||||||
|
|
||||||
|
void endian_convert_32(uint8_t * p_data)
|
||||||
|
{
|
||||||
|
uint8_t tmp = p_data[1];
|
||||||
|
p_data[1] = p_data[2];
|
||||||
|
p_data[2] = tmp;
|
||||||
|
tmp = p_data[0];
|
||||||
|
p_data[0] = p_data[3];
|
||||||
|
p_data[3] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfinetSharedDataClient shared_data;
|
||||||
|
ProfinetData_Map * p_profinet_data;
|
||||||
|
|
||||||
|
uint32_t event_mask = ( ProfinetEvent::EVENT_CONNECTION_ESTABLISHED |
|
||||||
|
ProfinetEvent::EVENT_NEW_CYCLIC_DATA |
|
||||||
|
ProfinetEvent::EVENT_NEW_PARAM_DATA |
|
||||||
|
ProfinetEvent::EVENT_CONNECTION_ABORT );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
p_profinet_data = shared_data.Connect("profinet_shared_data");
|
||||||
|
|
||||||
|
if (p_profinet_data == nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Берем указатель на данные подмодуля в разделяемой памяти
|
||||||
|
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Strarting..." << std::endl;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
uint32_t events = p_profinet_data->Events.wait_flags(event_mask);
|
||||||
|
|
||||||
|
if (events & ProfinetEvent::EVENT_CONNECTION_ESTABLISHED)
|
||||||
|
{
|
||||||
|
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ESTABLISHED);
|
||||||
|
|
||||||
|
std::cout << "Event: PLC connection established" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events & ProfinetEvent::EVENT_NEW_CYCLIC_DATA)
|
||||||
|
{
|
||||||
|
static uint32_t value_i = 0;
|
||||||
|
static float value_f = 0.0;
|
||||||
|
|
||||||
|
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
|
||||||
|
|
||||||
|
///Читаем данные от ПЛК
|
||||||
|
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);
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events & ProfinetEvent::EVENT_NEW_PARAM_DATA)
|
||||||
|
{
|
||||||
|
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_PARAM_DATA);
|
||||||
|
|
||||||
|
std::cout << "Event: New parameter data: " << std::endl;
|
||||||
|
//Читаем параметр записанный ПЛК при установлении связи
|
||||||
|
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
|
||||||
|
/// Конвертируем в литл эндиан
|
||||||
|
endian_convert_32((uint8_t*)&Echo_Gain);
|
||||||
|
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,31 +1,40 @@
|
|||||||
#********************************************************************
|
cmake_minimum_required (VERSION 3.0)
|
||||||
# _ _ _
|
|
||||||
# _ __ | |_ _ | | __ _ | |__ ___
|
|
||||||
# | '__|| __|(_)| | / _` || '_ \ / __|
|
|
||||||
# | | | |_ _ | || (_| || |_) |\__ \
|
|
||||||
# |_| \__|(_)|_| \__,_||_.__/ |___/
|
|
||||||
#
|
|
||||||
# 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.14)
|
|
||||||
|
|
||||||
project (PN_DEV_TEST VERSION 0.0.1)
|
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)
|
||||||
include(./nlohmann_json/nlohmann_json.cmake)
|
include(./nlohmann_json/nlohmann_json.cmake)
|
||||||
include(./file_api/file_api.cmake)
|
include(./file_api/file_api.cmake)
|
||||||
include(./configuration/configuration.cmake)
|
include(./configuration/configuration.cmake)
|
||||||
|
include (./interprocess/interprocess.cmake)
|
||||||
|
include (./shared_data/shared_data.cmake)
|
||||||
|
|
||||||
add_executable(pn_dev_test ./main.cpp ${SRC_FILES})
|
set(SRC_FILES ${SRC_FILES} ./app.cpp)
|
||||||
|
|
||||||
target_include_directories(pn_dev_test PRIVATE
|
# Копирование заглушки скрипта установки параметров сети.
|
||||||
|
# Если не копировать, то в недрах pnet после fork вызовется exit и
|
||||||
|
# вызовутся деструкторы объектов, что может привести к непредвиденным последствиям.
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/set_network_parameters
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
#Копирование файла конфигурации
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/program_configure.json
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
#Копирование тестового файла конфигурации устройства 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
|
||||||
@ -35,6 +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)
|
target_link_libraries (${TARGET_NAME} PUBLIC profinet osal ${Boost_LIBRARIES})
|
||||||
|
|||||||
45
src/app.cpp
Normal file
45
src/app.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "app.hpp"
|
||||||
|
#include "program_config.hpp"
|
||||||
|
|
||||||
|
bool App::Init(std::string profinet_config_file)
|
||||||
|
{
|
||||||
|
ProfinetSettings profinet_settings;
|
||||||
|
ProfinetDeviceSettings profinet_dev_settings;
|
||||||
|
|
||||||
|
/// Читаем настройки из файла
|
||||||
|
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Создаем структуру в разделяемой памяти
|
||||||
|
ProfinetData_Map * p_profinetMap = shared_data_.Create("profinet_shared_data");
|
||||||
|
|
||||||
|
/// Читаем настройки из файла, выделяем память под структуру модулей и подмодулей
|
||||||
|
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, p_profinetMap, shared_data_))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
||||||
|
if (!profinet_.Config(profinet_settings, profinet_dev_settings, p_profinetMap))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||||
|
*/
|
||||||
|
/// Запуск потока Profinet
|
||||||
|
profinet_.Start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::Run()
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/app.hpp
Normal file
14
src/app.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./profinet/profinet.hpp"
|
||||||
|
#include "./interprocess/profinet_shared_data.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class App {
|
||||||
|
public:
|
||||||
|
bool Init(std::string profinet_config_file);
|
||||||
|
void Run();
|
||||||
|
private:
|
||||||
|
ProfinetSharedData shared_data_;
|
||||||
|
Profinet profinet_;
|
||||||
|
};
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct AppSettings
|
|
||||||
{
|
|
||||||
std::string EthDevName; /// Имя адаптера
|
|
||||||
};
|
|
||||||
@ -7,6 +7,12 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../profinet/profinet_data_map.hpp"
|
||||||
|
|
||||||
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace boost::interprocess;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using string = std::string;
|
using string = std::string;
|
||||||
|
|
||||||
@ -131,93 +137,228 @@ static bool programconf_getPnetDeviceSettings(json& j, ProfinetDeviceSettings& p
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pnet_submodule_dir_t programconf_dataDirConvert(string dir_str)
|
static uint8_t programconf_dataDirConvert(string dir_str)
|
||||||
{
|
{
|
||||||
pnet_submodule_dir_t out = PNET_DIR_NO_IO;
|
ProfinetSubmoduleDir out = PROFINET_SUBMOD_DIR_NO_IO;
|
||||||
|
|
||||||
if (dir_str == "PNET_DIR_IO")
|
if (dir_str == "PNET_DIR_IO")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_IO;
|
out = PROFINET_SUBMOD_DIR_IO;
|
||||||
}
|
}
|
||||||
else if (dir_str == "PNET_DIR_INPUT")
|
else if (dir_str == "PNET_DIR_INPUT")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_INPUT;
|
out = PROFINET_SUBMOD_DIR_INPUT;
|
||||||
}
|
}
|
||||||
else if (dir_str == "PNET_DIR_OUTPUT")
|
else if (dir_str == "PNET_DIR_OUTPUT")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_OUTPUT;
|
out = PROFINET_SUBMOD_DIR_OUTPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool programconf_getProfinetModulesSettings(json& j, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
|
/**
|
||||||
{
|
* @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)
|
||||||
|
{
|
||||||
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();
|
||||||
|
|
||||||
|
if (mudules_number == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Выделяем разделяемую память
|
||||||
|
p_data_map->p_mods = shared_data.allocateData<ProfinetData_Module>(mudules_number);
|
||||||
|
|
||||||
|
if (p_data_map->p_mods == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_data_map->mods_nbr = mudules_number;
|
||||||
|
|
||||||
|
uint32_t module_index = 0;
|
||||||
|
|
||||||
for (auto& mod_set : modules_sett)
|
for (auto& mod_set : modules_sett)
|
||||||
{
|
{
|
||||||
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
|
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
|
||||||
string mod_name = mod_set["name"].get<string>();
|
|
||||||
|
///Модуль
|
||||||
|
auto& mod = p_data_map->p_mods[module_index];
|
||||||
|
|
||||||
auto module_ptr = ProfinetModule::Create(mod_id, mod_name);
|
++module_index;
|
||||||
if (module_ptr == nullptr)
|
|
||||||
|
/// Копируем параметры модуля
|
||||||
|
mod.id = mod_id;
|
||||||
|
mod.name = mod_set["name"].get<string>();
|
||||||
|
|
||||||
|
auto& submodules_sett = mod_set["submodules"];
|
||||||
|
|
||||||
|
uint32_t submudules_number = submodules_sett.size();
|
||||||
|
|
||||||
|
if (submudules_number == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
///Выделяем разделяемую память
|
||||||
|
mod.p_submods = shared_data.allocateData<ProfinetData_Submodule>(submudules_number);
|
||||||
|
|
||||||
|
if (mod.p_submods == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& submodules_sett = mod_set["submodules"];
|
mod.submods_nbr = submudules_number;
|
||||||
|
|
||||||
|
uint32_t submodules_index = 0;
|
||||||
|
|
||||||
for (auto& submod_sett : submodules_sett)
|
for (auto& submod_sett : submodules_sett)
|
||||||
{
|
{
|
||||||
uint32_t submod_id = hexstring_to_int(submod_sett["id"].get<string>());
|
uint32_t submod_id = hexstring_to_int(submod_sett["id"].get<string>());
|
||||||
string submod_name = submod_sett["name"].get<string>();
|
|
||||||
|
|
||||||
pnet_data_cfg_t data_cfg;
|
|
||||||
|
|
||||||
data_cfg.data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
|
|
||||||
data_cfg.insize = submod_sett["cyc_inp_data_size"].get<int>();
|
|
||||||
data_cfg.outsize = submod_sett["cyc_out_data_size"].get<int>();
|
|
||||||
|
|
||||||
auto submodule_ptr = ProfinetSubmodule::Create(submod_id, submod_name, data_cfg);
|
|
||||||
|
|
||||||
if (submodule_ptr == nullptr)
|
auto& submod = mod.p_submods[submodules_index];
|
||||||
|
|
||||||
|
++submodules_index;
|
||||||
|
|
||||||
|
/// Копируем данные подмодуля
|
||||||
|
submod.id = submod_id;
|
||||||
|
submod.name = submod_sett["name"].get<string>();
|
||||||
|
submod.cyc_data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
|
||||||
|
submod.cyc_indata_len = static_cast<uint16_t>(submod_sett["cyc_inp_data_size"].get<int>());
|
||||||
|
submod.cyc_outdata_len = static_cast<uint16_t>(submod_sett["cyc_out_data_size"].get<int>());
|
||||||
|
|
||||||
|
/// Выделяем память под циклические данные
|
||||||
|
if (submod.cyc_indata_len)
|
||||||
{
|
{
|
||||||
return false;
|
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_indata_len);
|
||||||
}
|
if (p_data == nullptr)
|
||||||
|
|
||||||
auto& params_sett = submod_sett["parameters"];
|
|
||||||
|
|
||||||
/// Записываем параметры в подмодуль
|
|
||||||
for (auto& parm_sett : params_sett)
|
|
||||||
{
|
|
||||||
uint32_t index = parm_sett["index"].get<int>();
|
|
||||||
string parm_name = parm_sett["name"].get<string>();
|
|
||||||
uint16_t length = parm_sett["data_size"].get<int>();
|
|
||||||
|
|
||||||
auto submod_param_ptr = ProfinetParameter::Create(index, parm_name, length);
|
|
||||||
|
|
||||||
if (submod_param_ptr == nullptr)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
submodule_ptr->addParameter(submod_param_ptr);
|
submod.inp_data.Init(p_data, submod.cyc_indata_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_ptr->addSubmodule(submodule_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
modules_vect.push_back(module_ptr);
|
if (submod.cyc_outdata_len)
|
||||||
|
{
|
||||||
|
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_outdata_len);
|
||||||
|
if (p_data == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
submod.out_data.Init(p_data, submod.cyc_outdata_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Параметры подмодуля
|
||||||
|
auto& params_sett = submod_sett["parameters"];
|
||||||
|
uint32_t params_nbr = params_sett.size();
|
||||||
|
|
||||||
|
///Выделяем разделяемую память
|
||||||
|
submod.p_params = shared_data.allocateData<ProfinetData_Parameter>(params_nbr);
|
||||||
|
|
||||||
|
if (submod.p_params == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
submod.params_nbr = params_nbr;
|
||||||
|
|
||||||
|
uint32_t params_index = 0;
|
||||||
|
|
||||||
|
/// Записываем параметры в подмодуль
|
||||||
|
for (auto& parm_sett : params_sett)
|
||||||
|
{
|
||||||
|
auto& param = submod.p_params[params_index];
|
||||||
|
++params_index;
|
||||||
|
|
||||||
|
param.index = static_cast<uint32_t>(parm_sett["index"].get<int>());
|
||||||
|
param.name = parm_sett["name"].get<string>();
|
||||||
|
param.length = static_cast<uint16_t>(parm_sett["data_size"].get<int>());
|
||||||
|
|
||||||
|
if (param.length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// Выделяем память для параметров
|
||||||
|
uint8_t * p_data = shared_data.allocateData<uint8_t>(param.length);
|
||||||
|
if (p_data == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
param.data.Init(p_data, param.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
|
|
||||||
|
|
||||||
|
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||||
|
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
||||||
{
|
{
|
||||||
json j;
|
json j;
|
||||||
|
|
||||||
@ -231,7 +372,7 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!programconf_getProfinetModulesSettings(j, modules_vect))
|
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -239,3 +380,4 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,10 @@
|
|||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "profinet_settings.hpp"
|
#include "profinet_settings.hpp"
|
||||||
#include "profinet_module.hpp"
|
#include "profinet_module.hpp"
|
||||||
|
#include "profinet_data_map.hpp"
|
||||||
|
#include "profinet_shared_data.hpp"
|
||||||
|
|
||||||
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
|
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
|
||||||
|
|
||||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& modules_vect);
|
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data);
|
||||||
|
|
||||||
|
|||||||
@ -2,33 +2,67 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
class Flags {
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "flags_iface.hpp"
|
||||||
|
|
||||||
|
class Flags : public FlagsIface {
|
||||||
public:
|
public:
|
||||||
Flags() : flags_{0} {}
|
Flags() : flags_{0} {
|
||||||
|
|
||||||
void set_flag(uint32_t mask) {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
flags_ |= mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_flag(uint32_t mask) {
|
virtual void set_flag(uint32_t mask) override
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
{
|
||||||
return flags_ & mask;
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
flags_ |= mask;
|
||||||
|
}
|
||||||
|
flag_cond_.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_flags() {
|
virtual uint32_t get_flag(uint32_t mask) override {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
uint32_t out;
|
||||||
return flags_;
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
out = flags_ & mask;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_flags(uint32_t mask)
|
virtual uint32_t get_flags() override {
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
out = flags_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void clear_flags(uint32_t mask) override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
flags_ &= ~mask;
|
flags_ &= ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Данная реализация блочит потоки pnet.
|
||||||
|
* Нужно разбираться.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual uint32_t wait_flags(uint32_t mask) override
|
||||||
|
{
|
||||||
|
uint32_t out;
|
||||||
|
std::unique_lock<std::mutex> lk(mutex_);
|
||||||
|
flag_cond_.wait(lk, [this, mask]{return static_cast<bool>(flags_ & mask);});
|
||||||
|
out = flags_ & mask;
|
||||||
|
lk.unlock();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
uint32_t flags_;
|
uint32_t flags_;
|
||||||
mutable std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
std::condition_variable flag_cond_;
|
||||||
};
|
};
|
||||||
19
src/flags/flags_iface.hpp
Normal file
19
src/flags/flags_iface.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class FlagsIface {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void set_flag(uint32_t mask) = 0;
|
||||||
|
|
||||||
|
virtual uint32_t get_flag(uint32_t mask) = 0;
|
||||||
|
|
||||||
|
virtual uint32_t get_flags() = 0;
|
||||||
|
|
||||||
|
virtual void clear_flags(uint32_t mask) = 0;
|
||||||
|
|
||||||
|
virtual uint32_t wait_flags(uint32_t mask) = 0;
|
||||||
|
|
||||||
|
virtual ~FlagsIface() {}
|
||||||
|
};
|
||||||
67
src/flags/shared_flags.hpp
Normal file
67
src/flags/shared_flags.hpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
#include <boost/interprocess/sync/interprocess_mutex.hpp>
|
||||||
|
#include <boost/interprocess/sync/interprocess_condition.hpp>
|
||||||
|
#include <boost/interprocess/sync/scoped_lock.hpp>
|
||||||
|
|
||||||
|
#include "flags_iface.hpp"
|
||||||
|
|
||||||
|
class SharedFlags : public FlagsIface {
|
||||||
|
public:
|
||||||
|
SharedFlags() : flags_{0} {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_flag(uint32_t mask) override {
|
||||||
|
{
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
flags_ |= mask;
|
||||||
|
}
|
||||||
|
/// Уведомляем об изменении флага
|
||||||
|
flag_cond_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual uint32_t get_flag(uint32_t mask) override {
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
out = flags_ & mask;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual uint32_t get_flags() override{
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
out = flags_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void clear_flags(uint32_t mask) override
|
||||||
|
{
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
flags_ &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual uint32_t wait_flags(uint32_t mask) override
|
||||||
|
{
|
||||||
|
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(mutex_wait_);
|
||||||
|
flag_cond_.wait(lock); //Ожидаем изменения данных
|
||||||
|
/// Данные изменились
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
out = flags_ & mask;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uint32_t flags_;
|
||||||
|
boost::interprocess::interprocess_mutex mutex_;
|
||||||
|
boost::interprocess::interprocess_mutex mutex_wait_;
|
||||||
|
boost::interprocess::interprocess_condition flag_cond_;
|
||||||
|
};
|
||||||
17
src/interprocess/interprocess.cmake
Normal file
17
src/interprocess/interprocess.cmake
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
set(INC_DIRS ${INC_DIRS} ./interprocess)
|
||||||
|
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
|
|
||||||
|
find_package(Boost 1.74.0)
|
||||||
|
if(Boost_FOUND)
|
||||||
|
set(INC_DIRS ${INC_DIRS} ${Boost_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
message(BOOST NOT FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||||
|
message("Boost libs:" ${Boost_LIBRARIES})
|
||||||
|
|
||||||
|
set(SRC_FILES ${SRC_FILES} ./interprocess/profinet_shared_data.cpp)
|
||||||
65
src/interprocess/profinet_shared_data.cpp
Normal file
65
src/interprocess/profinet_shared_data.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "../interprocess/profinet_shared_data.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace boost::interprocess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Расчет размера данных для выделения памяти
|
||||||
|
*
|
||||||
|
* @param data_settings
|
||||||
|
* @return uint32_t
|
||||||
|
*/
|
||||||
|
static uint32_t calc_mem_size(ProfinetData_Map& data_settings)
|
||||||
|
{
|
||||||
|
uint32_t size = 0;
|
||||||
|
for (int i = 0; i < data_settings.mods_nbr; ++i)
|
||||||
|
{
|
||||||
|
auto& submods = data_settings.p_mods[i].p_submods;
|
||||||
|
|
||||||
|
for (int j = 0; j < data_settings.p_mods[i].submods_nbr; ++j)
|
||||||
|
{
|
||||||
|
auto& submod = submods[j];
|
||||||
|
|
||||||
|
size+= submod.cyc_indata_len + submod.cyc_outdata_len;
|
||||||
|
|
||||||
|
auto& params = submod.p_params;
|
||||||
|
|
||||||
|
for (int k = 0; k < submod.params_nbr; ++k)
|
||||||
|
{
|
||||||
|
size+= params[i].length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size + sizeof(ProfinetData_Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfinetData_Map * ProfinetSharedData::Create(std::string mem_name)
|
||||||
|
{
|
||||||
|
/// Если память уже выделена, удаляем
|
||||||
|
shared_memory_object::remove(mem_name.c_str());
|
||||||
|
/// Для начала выделяем 65к байт. Потом вызывается функция checkMemorySize и размер моджет быть увеличен
|
||||||
|
shmem_ = managed_shared_memory{create_only, mem_name.c_str(), 65536};
|
||||||
|
mem_name_ = mem_name;
|
||||||
|
///Создаем данные в разделяемой памяти
|
||||||
|
p_profinet_data_ = shmem_.construct<ProfinetData_Map>("ProfinetData_Map")();
|
||||||
|
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")();
|
||||||
|
}
|
||||||
|
|
||||||
47
src/interprocess/profinet_shared_data.hpp
Normal file
47
src/interprocess/profinet_shared_data.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "../profinet/profinet_data_map.hpp"
|
||||||
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class ProfinetSharedData {
|
||||||
|
public:
|
||||||
|
~ProfinetSharedData() {
|
||||||
|
///Удаляем память
|
||||||
|
boost::interprocess::shared_memory_object::remove(mem_name_.c_str());
|
||||||
|
//std::cout << "Destruct: ProfinetSharedData: Shared data deleted" << std::endl;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Выделяыет раздеяемую память под структуру ProfinetData_Map
|
||||||
|
*
|
||||||
|
* @param mem_name
|
||||||
|
* @return ProfinetData_Map*
|
||||||
|
*/
|
||||||
|
|
||||||
|
ProfinetData_Map * Create(std::string mem_name);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T * allocateData(uint32_t 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_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::interprocess::managed_shared_memory shmem_;
|
||||||
|
std::string mem_name_;
|
||||||
|
};
|
||||||
113
src/interprocess/profinet_shared_data_client.hpp
Normal file
113
src/interprocess/profinet_shared_data_client.hpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../profinet/profinet_data_map.hpp"
|
||||||
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class ProfinetSharedDataClient {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Подключается к выделенной разделяемой памяти
|
||||||
|
*
|
||||||
|
* @param mem_name
|
||||||
|
*/
|
||||||
|
ProfinetData_Map * Connect(std::string mem_name) {
|
||||||
|
ProfinetData_Map * out{nullptr};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shmem_ = boost::interprocess::managed_shared_memory{boost::interprocess::open_only, mem_name.c_str()};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
|
||||||
|
out = mem.first;
|
||||||
|
///Упорядочиваем память для удобства обращения по индентификатору Profinet
|
||||||
|
collected_data = collect(out);
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
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:
|
||||||
|
boost::interprocess::managed_shared_memory shmem_;
|
||||||
|
std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collected_data; /// Упорядоченная структура указателей на разделяемую память
|
||||||
|
};
|
||||||
89
src/main.cpp
89
src/main.cpp
@ -1,99 +1,20 @@
|
|||||||
#include "app_settings.hpp"
|
|
||||||
#include "../libs/include/pnet_api.h"
|
#include "../libs/include/pnet_api.h"
|
||||||
#include "profinet.hpp"
|
#include "profinet.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "program_config.hpp"
|
#include "program_config.hpp"
|
||||||
|
#include "app.hpp"
|
||||||
|
|
||||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
#include "./interprocess/profinet_shared_data.hpp"
|
||||||
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
|
||||||
#define ECHO_INPUT_DATA_SIZE 8
|
|
||||||
#define ECHO_OUTPUT_DATA_SIZE 8
|
|
||||||
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
|
|
||||||
|
|
||||||
using namespace std;
|
App app;
|
||||||
|
|
||||||
uint32_t Echo_Gain = 0;
|
|
||||||
|
|
||||||
struct EchoData {
|
|
||||||
float data_f;
|
|
||||||
uint32_t data_i;
|
|
||||||
};
|
|
||||||
|
|
||||||
union EchoDataMem
|
|
||||||
{
|
|
||||||
EchoData data;
|
|
||||||
uint8_t mem[sizeof(EchoData)];
|
|
||||||
};
|
|
||||||
|
|
||||||
EchoDataMem Echo_inpCycData;
|
|
||||||
EchoDataMem Echo_outCycData;
|
|
||||||
|
|
||||||
uint8_t be_data_inp[sizeof(EchoData)];
|
|
||||||
|
|
||||||
void endian_convert_32(uint8_t * p_data)
|
|
||||||
{
|
|
||||||
uint8_t tmp = p_data[1];
|
|
||||||
p_data[1] = p_data[2];
|
|
||||||
p_data[2] = tmp;
|
|
||||||
tmp = p_data[0];
|
|
||||||
p_data[0] = p_data[3];
|
|
||||||
p_data[3] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
|
app.Init("program_configure.json");
|
||||||
Echo_inpCycData.data.data_i = 876; /// Преобразовать в big endian
|
|
||||||
|
|
||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
|
|
||||||
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
|
|
||||||
|
|
||||||
Echo_outCycData.data.data_f = 0.0f;
|
|
||||||
Echo_outCycData.data.data_i = 0;
|
|
||||||
|
|
||||||
ProfinetSettings profinet_settings;
|
|
||||||
ProfinetDeviceSettings profinet_dev_settings;
|
|
||||||
|
|
||||||
/// Читаем настройки из файла
|
|
||||||
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ProfinetModule>> Modules;
|
|
||||||
|
|
||||||
/// Читаем настройки из файла
|
|
||||||
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, Modules))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Profinet profinet;
|
|
||||||
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
|
||||||
if (!profinet.Config(profinet_settings, profinet_dev_settings, Modules))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
app.Run();
|
||||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
|
||||||
*/
|
|
||||||
/// Запуск потока Profinet
|
|
||||||
profinet.Start();
|
|
||||||
|
|
||||||
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
|
|
||||||
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
|
|
||||||
echo_submod->putDataToPlc(Echo_inpCycData.mem);
|
|
||||||
|
|
||||||
uint32_t ix = 0;
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
||||||
++ix;
|
|
||||||
std::cout << "tick: " << ix << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
message("log enable")
|
||||||
|
add_definitions(-DLOG_ENABLE)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(SRC_FILES ${SRC_FILES}
|
set(SRC_FILES ${SRC_FILES}
|
||||||
./profinet/profinet.cpp
|
./profinet/profinet.cpp
|
||||||
|
|||||||
@ -33,7 +33,6 @@ using namespace std;
|
|||||||
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
|
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
|
||||||
#define GET_LOW_BYTE(id) (id & 0xFF)
|
#define GET_LOW_BYTE(id) (id & 0xFF)
|
||||||
|
|
||||||
#define APP_MAIN_SLEEPTIME_US 5000 * 1000
|
|
||||||
#define APP_SNMP_THREAD_PRIORITY 1
|
#define APP_SNMP_THREAD_PRIORITY 1
|
||||||
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
|
#define APP_SNMP_THREAD_STACKSIZE 256 * 1024 /* bytes */
|
||||||
#define APP_ETH_THREAD_PRIORITY 10
|
#define APP_ETH_THREAD_PRIORITY 10
|
||||||
@ -43,9 +42,7 @@ using namespace std;
|
|||||||
|
|
||||||
#define FLAGS_AREP_APP_READY 0x00000001
|
#define FLAGS_AREP_APP_READY 0x00000001
|
||||||
|
|
||||||
//#define RELEASE
|
#ifdef LOG_ENABLE
|
||||||
|
|
||||||
#ifndef RELEASE
|
|
||||||
#define LOG(STR) log_.put(STR)
|
#define LOG(STR) log_.put(STR)
|
||||||
#else
|
#else
|
||||||
#define LOG(STR)
|
#define LOG(STR)
|
||||||
@ -166,9 +163,38 @@ Profinet::Profinet() : m_pnet_data({nullptr, 0, UINT32_MAX, 0}),
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Profinet::createInternalStucture(ProfinetData_Map * const p_data_map)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < p_data_map->mods_nbr; ++i)
|
||||||
|
{
|
||||||
|
auto& mod = p_data_map->p_mods[i];
|
||||||
|
|
||||||
|
auto module_ptr = ProfinetModule::Create(mod.id, mod.name);
|
||||||
|
|
||||||
|
for (int j = 0; j < mod.submods_nbr; ++j)
|
||||||
|
{
|
||||||
|
auto& submod = mod.p_submods[j];
|
||||||
|
pnet_data_cfg_t dap_data_cfg = { static_cast<pnet_submodule_dir_t>(submod.cyc_data_dir),
|
||||||
|
submod.cyc_indata_len, submod.cyc_outdata_len };
|
||||||
|
auto submodule_ptr = ProfinetSubmodule::Create(submod.id, submod.name, dap_data_cfg, &submod.inp_data, &submod.out_data);
|
||||||
|
|
||||||
|
for (int k = 0; k < submod.params_nbr; ++k)
|
||||||
|
{
|
||||||
|
auto& param = submod.p_params[k];
|
||||||
|
auto Parameter = ProfinetParameter::Create(param.index, param.name, ¶m.data);
|
||||||
|
submodule_ptr->addParameter(Parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_ptr->addSubmodule(submodule_ptr);
|
||||||
|
}
|
||||||
|
addModule(module_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Profinet::Config(ProfinetSettings& Settings,
|
bool Profinet::Config(ProfinetSettings& Settings,
|
||||||
ProfinetDeviceSettings& DevSettings,
|
ProfinetDeviceSettings& DevSettings,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
|
ProfinetData_Map * const p_data_map)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
pnet_cfg_t pnet_cfg = {0};
|
pnet_cfg_t pnet_cfg = {0};
|
||||||
@ -206,6 +232,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
|||||||
/// Инициализация библиотеки pnet
|
/// Инициализация библиотеки pnet
|
||||||
m_pnet_data.pnet_ptr = pnet_init (&pnet_cfg);
|
m_pnet_data.pnet_ptr = pnet_init (&pnet_cfg);
|
||||||
|
|
||||||
|
p_events_ = &p_data_map->Events;
|
||||||
|
|
||||||
if (m_pnet_data.pnet_ptr == nullptr)
|
if (m_pnet_data.pnet_ptr == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -214,15 +242,17 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
|||||||
/// Создаем обязательный модуль DAP
|
/// Создаем обязательный модуль DAP
|
||||||
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
|
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
|
||||||
/// Создаем обязательный подмодуль DAP Identity 1
|
/// Создаем обязательный подмодуль DAP Identity 1
|
||||||
auto dap_submodule_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg);
|
auto dap_submodule_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg, nullptr, nullptr);
|
||||||
/// Создаем обязательный подмодуль DAP IFACE1 IDENT
|
/// Создаем обязательный подмодуль DAP IFACE1 IDENT
|
||||||
auto dap_submodule_iface_1_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg);
|
auto dap_submodule_iface_1_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg, nullptr, nullptr);
|
||||||
/// Создаем обязательный подмодуль DAP Port 1
|
/// Создаем обязательный подмодуль DAP Port 1
|
||||||
auto dap_subslot_iface_1_port1_ident = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg);
|
auto dap_subslot_iface_1_port1_ident = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg, nullptr, nullptr);
|
||||||
/// Добавляем подмодули к модулю
|
/// Добавляем подмодули к модулю
|
||||||
ret&= dap_module->addSubmodule(dap_submodule_indent_1);
|
ret&= dap_module->addSubmodule(dap_submodule_indent_1);
|
||||||
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
||||||
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
||||||
|
/// Добавляем модуль в конфигурацию
|
||||||
|
addModule(dap_module);
|
||||||
|
|
||||||
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
||||||
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
||||||
@ -234,21 +264,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
|||||||
/// 3. Подмодуль "DAP Port 1"
|
/// 3. Подмодуль "DAP Port 1"
|
||||||
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT, dap_subslot_iface_1_port1_ident);
|
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT, dap_subslot_iface_1_port1_ident);
|
||||||
|
|
||||||
///Копируем конфигурацию модулей
|
///Создаем внутреннию структуру модулей
|
||||||
for (auto module_ptr : Modules)
|
createInternalStucture(p_data_map);
|
||||||
{
|
|
||||||
if (module_ptr == nullptr)
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addModule(module_ptr))
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -500,6 +517,7 @@ void Profinet::initDataAndIoxs()
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submodule_ptr->inputSetData( m_pnet_data.pnet_ptr,
|
submodule_ptr->inputSetData( m_pnet_data.pnet_ptr,
|
||||||
m_pnet_data.api,
|
m_pnet_data.api,
|
||||||
slot_ptr->m_slot_nbr,
|
slot_ptr->m_slot_nbr,
|
||||||
@ -543,13 +561,6 @@ void Profinet::PeriodicOperations()
|
|||||||
/// Ждем время в мкс: ticks_period_us, по умолчанию 1мс
|
/// Ждем время в мкс: ticks_period_us, по умолчанию 1мс
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(m_run_period_us));
|
std::this_thread::sleep_for(std::chrono::microseconds(m_run_period_us));
|
||||||
|
|
||||||
if (flags_.get_flag(FLAGS_AREP_APP_READY))
|
|
||||||
{
|
|
||||||
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
|
|
||||||
flags_.clear_flags(FLAGS_AREP_APP_READY);
|
|
||||||
LOG("FLAGS_AREP_APP_READY");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Вызов cyclic io
|
/// Вызов cyclic io
|
||||||
if (++m_cyclic_io_cnt >= m_cyclic_io_period_ms)
|
if (++m_cyclic_io_cnt >= m_cyclic_io_period_ms)
|
||||||
{
|
{
|
||||||
@ -559,11 +570,25 @@ void Profinet::PeriodicOperations()
|
|||||||
|
|
||||||
/// запускаем pnet
|
/// запускаем pnet
|
||||||
pnet_handle_periodic (m_pnet_data.pnet_ptr);
|
pnet_handle_periodic (m_pnet_data.pnet_ptr);
|
||||||
|
|
||||||
|
/// Нужно делать после pnet_handle_periodic(следующий тик).
|
||||||
|
if (flags_.get_flag(FLAGS_AREP_APP_READY))
|
||||||
|
{
|
||||||
|
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
|
||||||
|
flags_.clear_flags(FLAGS_AREP_APP_READY);
|
||||||
|
LOG("PeriodicOperations: FLAGS_AREP_APP_READY");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profinet::cyclicIoData()
|
void Profinet::cyclicIoData()
|
||||||
{
|
{
|
||||||
|
///Циклический обмен только когда установлено соединение
|
||||||
|
if (!isConnectedToController())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto slot : m_slots)
|
for (auto slot : m_slots)
|
||||||
{
|
{
|
||||||
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
||||||
@ -586,22 +611,23 @@ void Profinet::cyclicIoData()
|
|||||||
|
|
||||||
if (submodule_ptr->m_data_cfg.outsize > 0)
|
if (submodule_ptr->m_data_cfg.outsize > 0)
|
||||||
{
|
{
|
||||||
bool outdata_updated;
|
bool data_updated;
|
||||||
uint16_t outdata_length = submodule_ptr->m_data_cfg.outsize;
|
|
||||||
uint8_t outdata_iops;
|
|
||||||
|
|
||||||
/// Копируем данные от контроллера в m_out_data_ptr
|
/// Копируем данные от контроллера в m_out_data_ptr
|
||||||
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
||||||
m_pnet_data.api,
|
m_pnet_data.api,
|
||||||
slot_ptr->m_slot_nbr,
|
slot_ptr->m_slot_nbr,
|
||||||
subslot_ptr->m_subslot_nbr);
|
subslot_ptr->m_subslot_nbr, data_updated);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (data_updated)
|
||||||
|
{
|
||||||
|
p_events_->set_flag(EVENT_NEW_CYCLIC_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (submodule_ptr->m_data_cfg.insize > 0)
|
if (submodule_ptr->m_data_cfg.insize > 0)
|
||||||
{
|
{
|
||||||
uint8_t indata_iocs;
|
uint8_t indata_iocs;
|
||||||
uint8_t indata_iops = PNET_IOXS_GOOD;
|
uint8_t indata_iops = PNET_IOXS_GOOD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Отправляем данные подмодуля в ПЛК
|
* @brief Отправляем данные подмодуля в ПЛК
|
||||||
*
|
*
|
||||||
@ -661,25 +687,32 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
|
|||||||
|
|
||||||
if (event == PNET_EVENT_ABORT)
|
if (event == PNET_EVENT_ABORT)
|
||||||
{
|
{
|
||||||
LOG("callbackStateInd:PNET_EVENT_ABORT");
|
LOG("callbackStateInd: PNET_EVENT_ABORT: The AR has been closed.");
|
||||||
if (pnet_get_ar_error_codes (m_pnet_data.pnet_ptr, arep, &err_cls, &err_code) == 0)
|
if (pnet_get_ar_error_codes (m_pnet_data.pnet_ptr, arep, &err_cls, &err_code) == 0)
|
||||||
{
|
{
|
||||||
|
LOG("err_cls= " + std::to_string(err_cls));
|
||||||
|
LOG("err_code= " + std::to_string(err_code));
|
||||||
/**
|
/**
|
||||||
* @brief Коды ошибок в err_cls и err_code
|
* @brief Коды ошибок в err_cls и err_code
|
||||||
* TODO: Нужно передавать в управляющее приложение
|
* TODO: Нужно передавать в управляющее приложение
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/// Ошибок нет
|
/// Ошибок нет
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p_events_->set_flag(EVENT_CONNECTION_ABORT);
|
||||||
|
|
||||||
|
m_pnet_data.arep = UINT32_MAX;
|
||||||
|
|
||||||
/* Only abort AR with correct session key */
|
/* Only abort AR with correct session key */
|
||||||
///os_event_set (app->main_events, APP_EVENT_ABORT);
|
///os_event_set (app->main_events, APP_EVENT_ABORT);
|
||||||
}
|
}
|
||||||
else if (event == PNET_EVENT_PRMEND)
|
else if (event == PNET_EVENT_PRMEND)
|
||||||
{
|
{
|
||||||
LOG("callbackStateInd:PNET_EVENT_PRMEND");
|
LOG("callbackStateInd: PNET_EVENT_PRMEND: Controller has sent all write records.");
|
||||||
if (isConnectedToController())
|
if (isConnectedToController())
|
||||||
{
|
{
|
||||||
/// Если уже были подключены и пришел запрос на еще одно подключение
|
/// Если уже были подключены и пришел запрос на еще одно подключение
|
||||||
@ -706,14 +739,21 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
|
|||||||
the internal stack states */
|
the internal stack states */
|
||||||
m_pnet_data.arep_for_appl_ready = arep;
|
m_pnet_data.arep_for_appl_ready = arep;
|
||||||
flags_.set_flag(FLAGS_AREP_APP_READY);
|
flags_.set_flag(FLAGS_AREP_APP_READY);
|
||||||
|
|
||||||
///os_event_set (app->main_events, APP_EVENT_READY_FOR_DATA); ///
|
|
||||||
}
|
}
|
||||||
else if (event == PNET_EVENT_DATA)
|
else if (event == PNET_EVENT_DATA)
|
||||||
{
|
{
|
||||||
LOG("callbackStateInd:PNET_EVENT_DATA");
|
LOG("callbackStateInd: PNET_EVENT_DATA: The cyclic data transmission is now properly set up");
|
||||||
/// Стартовал обмен cyclic io
|
/// Стартовал обмен cyclic io
|
||||||
}
|
}
|
||||||
|
else if (event == PNET_EVENT_STARTUP)
|
||||||
|
{
|
||||||
|
LOG("callbackStateInd: PNET_EVENT_STARTUP: A connection has been initiated.");
|
||||||
|
p_events_->set_flag(EVENT_CONNECTION_ESTABLISHED);
|
||||||
|
}
|
||||||
|
else if (event == PNET_EVENT_APPLRDY)
|
||||||
|
{
|
||||||
|
LOG("callbackStateInd: PNET_EVENT_APPLRDY: Controller has acknowledged the APPL_RDY message.");
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,10 +799,7 @@ int Profinet::callbackDcontrolInd ( uint32_t arep,
|
|||||||
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
|
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
|
||||||
{
|
{
|
||||||
LOG("callbackCcontrolInd");
|
LOG("callbackCcontrolInd");
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Можно оставить пустым
|
|
||||||
*/
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,11 +837,11 @@ int Profinet::callbackReadInd ( uint32_t arep,
|
|||||||
|
|
||||||
///3. Передаем данные параметра
|
///3. Передаем данные параметра
|
||||||
/**
|
/**
|
||||||
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
|
* В функции getReadBufPtr данные копируются во временный буфер и уже затем передаются в стек pnet.
|
||||||
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
* Это нужно из-за отсутствия события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||||
* Нужно поисследовать.
|
*
|
||||||
*/
|
*/
|
||||||
*pp_read_data = param_ptr->getDataPointer();
|
*pp_read_data = param_ptr->getReadBufPtr();
|
||||||
*p_read_length = param_ptr->length;
|
*p_read_length = param_ptr->length;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -844,7 +881,13 @@ int Profinet::callbackWriteInd ( uint32_t arep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
///3. Копируем данные
|
///3. Копируем данные
|
||||||
param_ptr->writeParameter(p_write_data);
|
if (!param_ptr->writeParameter(p_write_data))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Устанавливаем событие
|
||||||
|
p_events_->set_flag(EVENT_NEW_PARAM_DATA);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,14 +15,27 @@
|
|||||||
#include "../log/log.hpp"
|
#include "../log/log.hpp"
|
||||||
#include "../flags/flags.hpp"
|
#include "../flags/flags.hpp"
|
||||||
|
|
||||||
|
#include "../flags/flags_iface.hpp"
|
||||||
|
|
||||||
|
#include "profinet_data_map.hpp"
|
||||||
|
|
||||||
class Profinet {
|
class Profinet {
|
||||||
public:
|
public:
|
||||||
|
enum Events
|
||||||
|
{
|
||||||
|
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||||
|
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||||
|
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||||
|
EVENT_CONNECTION_ABORT = 0x00000008
|
||||||
|
};
|
||||||
|
|
||||||
Profinet();
|
Profinet();
|
||||||
|
|
||||||
|
~Profinet();
|
||||||
|
|
||||||
bool Config(ProfinetSettings& Settings,
|
bool Config(ProfinetSettings& Settings,
|
||||||
ProfinetDeviceSettings& DevSettings,
|
ProfinetDeviceSettings& DevSettings,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
|
ProfinetData_Map * const p_data_map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Добавляет ранее созданный слот slot_ptr
|
* @brief Добавляет ранее созданный слот slot_ptr
|
||||||
@ -143,8 +156,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
~Profinet();
|
uint32_t EventWait(uint32_t mask) { return p_events_->wait_flags(mask); }
|
||||||
|
|
||||||
|
void EventClear(uint32_t mask) { p_events_->clear_flags(mask); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ProfinetServiceData m_pnet_data;
|
ProfinetServiceData m_pnet_data;
|
||||||
|
|
||||||
@ -168,28 +183,43 @@ private:
|
|||||||
std::map<uint32_t, std::shared_ptr<ProfinetModule>> m_modules;
|
std::map<uint32_t, std::shared_ptr<ProfinetModule>> m_modules;
|
||||||
|
|
||||||
Log log_;
|
Log log_;
|
||||||
|
|
||||||
Flags flags_;
|
|
||||||
|
|
||||||
|
|
||||||
|
Flags flags_; /// Внутренние флаги: взаимодействиe между потоком Profinet и потоком стека Pnet
|
||||||
|
|
||||||
|
FlagsIface * p_events_; /// Флаги для внешних событий. Это более общие флаги, например флаг получения данных от ПЛК, а в каком подмодуле, уже не уточняется.
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Проверяет наличие слота slot_nbr в конфигурации
|
* @brief Проверяет наличие слота slot_nbr в конфигурации и возвращает указатель на него
|
||||||
*
|
*
|
||||||
* @param slot_nbr номер слота
|
* @param slot_nbr номер слота
|
||||||
* @return ProfinetSlot* - указатель на слот, при успехе или nullptr есои слот в конфигурации отсутсвует
|
* @return std::shared_ptr<ProfinetSlot> указатель на слот, при успехе или nullptr если слот в конфигурации отсутсвует
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<ProfinetSlot> getSlotPtr(uint16_t slot_nbr);
|
std::shared_ptr<ProfinetSlot> getSlotPtr(uint16_t slot_nbr);
|
||||||
|
/**
|
||||||
|
* @brief Проверяет наличие подслота в слоте и возвращает указатель на него
|
||||||
|
*
|
||||||
|
* @param slot_nbr номер слота
|
||||||
|
* @param subslot_nbr номер подслота
|
||||||
|
* @return std::shared_ptr<ProfinetSubslot> указатель на подслот, при успехе или nullptr если подслот в конфигурации отсутсвует
|
||||||
|
*/
|
||||||
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr);
|
std::shared_ptr<ProfinetSubslot> getSubslotPtr(uint16_t slot_nbr, uint16_t subslot_nbr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Флаг подключение к контроллеру(ПЛК).
|
||||||
|
* Реализация взята с примера стека pnet. На самом деле не понятна роль AREP.
|
||||||
|
* Открытой информации недостаточно, нужно покупать и смотреть спецификацию. *
|
||||||
|
*
|
||||||
|
* @return true подключение есть
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
|
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
|
||||||
|
|
||||||
void initDataAndIoxs();
|
void initDataAndIoxs();
|
||||||
|
|
||||||
void cyclicIoData();
|
void cyclicIoData();
|
||||||
|
|
||||||
|
void createInternalStucture(ProfinetData_Map * const p_data_map);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
58
src/profinet/profinet_data_map.hpp
Normal file
58
src/profinet/profinet_data_map.hpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../flags/shared_flags.hpp"
|
||||||
|
#include "../shared_data/shared_data.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
enum ProfinetSubmoduleDir
|
||||||
|
{
|
||||||
|
PROFINET_SUBMOD_DIR_NO_IO = 0x00,
|
||||||
|
PROFINET_SUBMOD_DIR_INPUT = 0x01,
|
||||||
|
PROFINET_SUBMOD_DIR_OUTPUT = 0x02,
|
||||||
|
PROFINET_SUBMOD_DIR_IO = 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ProfinetEvent
|
||||||
|
{
|
||||||
|
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||||
|
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||||
|
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||||
|
EVENT_CONNECTION_ABORT = 0x00000008 /// Разрыв соединения
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfinetData_Parameter {
|
||||||
|
uint32_t index; /// Идентфикатор параметра
|
||||||
|
std::string name; /// Наименование
|
||||||
|
uint16_t length; /// длина параметра в байтах
|
||||||
|
SharedData data; /// Указатель на данные параметра
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfinetData_Submodule{
|
||||||
|
uint32_t id; /// Идентификатор подмодуля
|
||||||
|
std::string name; /// Наименование
|
||||||
|
uint8_t cyc_data_dir; /// Направление данных
|
||||||
|
uint16_t cyc_indata_len; /// Длина входных для ПЛК данных
|
||||||
|
uint16_t cyc_outdata_len; /// Длина выходных для ПЛК данных
|
||||||
|
SharedData inp_data; /// Входные данные(данные от контроллера к устройству)
|
||||||
|
SharedData out_data; /// Выходные данные(данные от устройства к контроллеру)
|
||||||
|
|
||||||
|
ProfinetData_Parameter * p_params; /// Параметры подмодуля
|
||||||
|
uint32_t params_nbr; /// Количество параметров
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfinetData_Module {
|
||||||
|
uint32_t id;
|
||||||
|
std::string name;
|
||||||
|
ProfinetData_Submodule * p_submods; /// Подмодули
|
||||||
|
uint32_t submods_nbr; /// Количество подмодулей
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfinetData_Map {
|
||||||
|
SharedFlags Events; /// События profinet
|
||||||
|
ProfinetData_Module * p_mods; /// Модули
|
||||||
|
uint32_t mods_nbr; /// Количество модулей
|
||||||
|
};
|
||||||
|
|
||||||
@ -1,50 +1,58 @@
|
|||||||
#include "profinet_parameter.hpp"
|
#include "profinet_parameter.hpp"
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
ProfinetParameter::ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length) :
|
ProfinetParameter::ProfinetParameter(uint32_t Index, std::string Name, SharedDataIface * p_data) :
|
||||||
index(Index),
|
index(Index),
|
||||||
name(Name),
|
name(Name),
|
||||||
length(Length),
|
length(p_data->Size()),
|
||||||
data_ptr_(new uint8_t[Length])
|
p_data_(p_data),
|
||||||
|
read_buf_(nullptr)
|
||||||
{
|
{
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
read_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[length]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ProfinetParameter::readParameter(uint8_t * const data_ptr)
|
bool ProfinetParameter::readParameter(uint8_t * const data_ptr)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||||
|
|
||||||
if (data_ptr == nullptr)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data_ptr, data_ptr_.get(), length);
|
p_data_->Read(0, (void*)data_ptr, length);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||||
|
|
||||||
if (data_ptr == nullptr)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data_ptr_.get(), data_ptr, length);
|
p_data_->Write(0, (void*)data_ptr, length);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t * ProfinetParameter::getDataPointer()
|
uint8_t * ProfinetParameter::getReadBufPtr()
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief Слабое утешение от ситуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
* @brief Cитуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||||
* прочитать и вызвался коллбэк profinet_cb_read. В этом случае будем тут висеть пока приложение не запишет параметр.
|
* прочитать и вызвался коллбэк profinet_cb_read. В этом случае будем тут висеть пока приложение не запишет параметр.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
std::lock_guard<std::mutex> guard(data_mutex_);
|
|
||||||
|
|
||||||
return data_ptr_.get();
|
if (p_data_ == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint8_t * p_read_buf = read_buf_.get();
|
||||||
|
|
||||||
|
/// Копируем данные в буфер
|
||||||
|
p_data_->Read(0, p_read_buf, length);
|
||||||
|
|
||||||
|
|
||||||
|
return p_read_buf;
|
||||||
}
|
}
|
||||||
@ -1,20 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
#include "../shared_data/shared_data_iface.hpp"
|
||||||
|
|
||||||
|
|
||||||
class ProfinetParameter
|
class ProfinetParameter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, uint16_t Length)
|
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, SharedDataIface * p_data)
|
||||||
{
|
{
|
||||||
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, Length));
|
if (p_data == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, p_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length);
|
ProfinetParameter(uint32_t Index, std::string Name, SharedDataIface * p_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Защищенная запись данных параметра
|
* @brief Защищенная запись данных параметра
|
||||||
@ -39,17 +45,18 @@ public:
|
|||||||
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
||||||
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
||||||
* см. profinet_cb_read.cpp
|
* см. profinet_cb_read.cpp
|
||||||
* Не использовать эту функцию в приложении!!!
|
|
||||||
* @return uint8_t*
|
* @return uint8_t*
|
||||||
*/
|
*/
|
||||||
uint8_t * getDataPointer();
|
uint8_t * getReadBufPtr();
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
const std::string name; /// Имя параметра
|
const std::string name; /// Имя параметра
|
||||||
const uint32_t index; /// Идентфикатор параметра
|
const uint32_t index; /// Идентфикатор параметра
|
||||||
const uint16_t length; /// длина параметра в байтах
|
const uint16_t length; /// длина параметра в байтах
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
|
std::shared_ptr<uint8_t[]> read_buf_; ///Буфер данных для передачи его в стек pnet для операции чтения
|
||||||
mutable std::mutex data_mutex_; /// Доступ к данным
|
SharedDataIface * const p_data_; ///Указатель на данные параметра(в разделяемой области)
|
||||||
};
|
};
|
||||||
@ -3,24 +3,34 @@
|
|||||||
|
|
||||||
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
std::string submodule_name,
|
||||||
pnet_data_cfg_t& submodule_data_cfg) :
|
pnet_data_cfg_t& submodule_data_cfg,
|
||||||
|
SharedDataIface * inp_data_ptr,
|
||||||
|
SharedDataIface * out_data_ptr) :
|
||||||
m_id(submodule_id),
|
m_id(submodule_id),
|
||||||
m_name(submodule_name),
|
m_name(submodule_name),
|
||||||
m_data_cfg(submodule_data_cfg),
|
m_data_cfg(submodule_data_cfg),
|
||||||
inp_data_ptr_(nullptr),
|
inp_data_ptr_(inp_data_ptr),
|
||||||
out_data_ptr_(nullptr),
|
out_data_ptr_(out_data_ptr),
|
||||||
m_indata_iocs(0),
|
m_indata_iocs(0),
|
||||||
m_outdata_iops(0)
|
m_outdata_iops(0),
|
||||||
|
data_updated_(false),
|
||||||
|
out_data_buf_(nullptr),
|
||||||
|
inp_data_buf_(nullptr)
|
||||||
{
|
{
|
||||||
/// Могут быть подмодули без данных, например DAP
|
if (m_data_cfg.outsize && (out_data_ptr_!=nullptr))
|
||||||
if (m_data_cfg.insize > 0)
|
|
||||||
{
|
{
|
||||||
inp_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
|
if (m_data_cfg.outsize == out_data_ptr_->Size())
|
||||||
|
{
|
||||||
|
out_data_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.outsize]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_data_cfg.outsize > 0)
|
if (m_data_cfg.insize && (inp_data_ptr_ != nullptr))
|
||||||
{
|
{
|
||||||
out_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.outsize]);
|
if (m_data_cfg.insize == inp_data_ptr_->Size())
|
||||||
|
{
|
||||||
|
inp_data_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,18 +57,30 @@ bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
|
|||||||
uint16_t subslot_nbr,
|
uint16_t subslot_nbr,
|
||||||
uint8_t iops)
|
uint8_t iops)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
if ((pnet_ptr == nullptr) ||
|
||||||
|
((inp_data_buf_ == nullptr) && (m_data_cfg.insize > 0)))
|
||||||
if ((pnet_ptr == nullptr) || (inp_data_ptr_ == nullptr))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Вызов когда m_data_cfg.insize==0 и inp_data_ptr_== nullptr
|
||||||
|
* нужен для корректного установления соединения
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
///Копируем данные в буфер
|
||||||
|
if (inp_data_ptr_ != nullptr)
|
||||||
|
{
|
||||||
|
inp_data_ptr_->Read(0, inp_data_buf_.get(), m_data_cfg.insize);
|
||||||
|
}
|
||||||
|
|
||||||
/// Копируем данные для передачи в ПЛК
|
/// Копируем данные для передачи в ПЛК
|
||||||
pnet_input_set_data_and_iops( pnet_ptr,
|
pnet_input_set_data_and_iops( pnet_ptr,
|
||||||
api,
|
api,
|
||||||
slot_nbr,
|
slot_nbr,
|
||||||
subslot_nbr,
|
subslot_nbr,
|
||||||
inp_data_ptr_.get(),
|
inp_data_buf_.get(),
|
||||||
m_data_cfg.insize,
|
m_data_cfg.insize,
|
||||||
iops);
|
iops);
|
||||||
|
|
||||||
@ -68,69 +90,56 @@ bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
|
|||||||
bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
||||||
uint32_t api,
|
uint32_t api,
|
||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
uint16_t subslot_nbr )
|
uint16_t subslot_nbr, bool& data_update )
|
||||||
{
|
{
|
||||||
|
bool out = true;
|
||||||
|
|
||||||
/// Существуют подмодули без данных, например DAP
|
/// Существуют подмодули без данных, например DAP
|
||||||
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
if ((pnet_ptr == nullptr) || ((out_data_ptr_ == nullptr) && (m_data_cfg.outsize > 0)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
|
||||||
|
|
||||||
uint16_t outdata_length = m_data_cfg.outsize;
|
uint16_t outdata_length = m_data_cfg.outsize;
|
||||||
uint8_t outdata_iops;
|
uint8_t outdata_iops;
|
||||||
bool updated;
|
/**
|
||||||
|
* @brief Флаг data_updated_ устанавливается внутри pnet_output_get_data_and_iops почему-то с задержкой 1 сек.
|
||||||
/// Копируем данные от полученные от контроллера
|
* Поэтому условием новых данных является не равность нулю outdata_length.
|
||||||
|
*/
|
||||||
|
/// Копируем данные полученные от контроллера
|
||||||
pnet_output_get_data_and_iops (pnet_ptr,
|
pnet_output_get_data_and_iops (pnet_ptr,
|
||||||
api,
|
api,
|
||||||
slot_nbr,
|
slot_nbr,
|
||||||
subslot_nbr,
|
subslot_nbr,
|
||||||
&updated,
|
&data_updated_,
|
||||||
out_data_ptr_.get(),
|
out_data_buf_.get(),
|
||||||
&outdata_length,
|
&outdata_length,
|
||||||
&outdata_iops);
|
&outdata_iops);
|
||||||
|
|
||||||
|
///out_data_ptr_ данные из буфера
|
||||||
|
///Когда новых данных нет, то outdata_length == 0
|
||||||
|
if ((out_data_ptr_ != nullptr) && (outdata_length == m_data_cfg.outsize))
|
||||||
|
{
|
||||||
|
out_data_ptr_->Write(0, out_data_buf_.get(), m_data_cfg.outsize);
|
||||||
|
data_update = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
m_outdata_iops = outdata_iops;
|
m_outdata_iops = outdata_iops;
|
||||||
data_updated_ = updated;
|
|
||||||
|
|
||||||
if (m_data_cfg.outsize != outdata_length)
|
if (m_data_cfg.outsize != outdata_length)
|
||||||
{
|
{
|
||||||
/// Неправильная длина данных
|
/// Неправильная длина данных
|
||||||
|
out = false;
|
||||||
}
|
}
|
||||||
else if (outdata_iops == PNET_IOXS_BAD)
|
else if (outdata_iops == PNET_IOXS_BAD)
|
||||||
{
|
{
|
||||||
/// Что-то не так с данными от контроллера
|
/// Что-то не так с данными от контроллера
|
||||||
|
out = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
|
||||||
|
|
||||||
if ((data_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(data_ptr, out_data_ptr_.get(), m_data_cfg.outsize);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProfinetSubmodule::putDataToPlc(uint8_t * data_ptr)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
|
||||||
|
|
||||||
if ((data_ptr == nullptr) || (inp_data_ptr_ == nullptr))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(inp_data_ptr_.get(), data_ptr, m_data_cfg.insize);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@ -7,6 +7,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "profinet_parameter.hpp"
|
#include "profinet_parameter.hpp"
|
||||||
|
#include "../shared_data/shared_data_iface.hpp"
|
||||||
|
|
||||||
#include "../../libs/include/pnet_api.h"
|
#include "../../libs/include/pnet_api.h"
|
||||||
|
|
||||||
@ -15,21 +16,27 @@ class ProfinetSubmodule {
|
|||||||
public:
|
public:
|
||||||
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
std::string submodule_name,
|
||||||
pnet_data_cfg_t& submodule_data_cfg)
|
pnet_data_cfg_t& submodule_data_cfg,
|
||||||
|
SharedDataIface * inp_data_ptr,
|
||||||
|
SharedDataIface * out_data_ptr)
|
||||||
{
|
{
|
||||||
return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
|
return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
|
||||||
submodule_name,
|
submodule_name,
|
||||||
submodule_data_cfg));
|
submodule_data_cfg,
|
||||||
|
inp_data_ptr,
|
||||||
|
out_data_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfinetSubmodule(uint32_t submodule_id,
|
ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
std::string submodule_name,
|
||||||
pnet_data_cfg_t& submodule_data_cfg);
|
pnet_data_cfg_t& submodule_data_cfg,
|
||||||
|
SharedDataIface * inp_data_ptr,
|
||||||
|
SharedDataIface * out_data_ptr);
|
||||||
|
|
||||||
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
|
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
|
||||||
|
|
||||||
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
|
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
|
||||||
|
|
||||||
bool inputSetData( pnet_t * pnet_ptr,
|
bool inputSetData( pnet_t * pnet_ptr,
|
||||||
uint32_t api,
|
uint32_t api,
|
||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
@ -39,13 +46,11 @@ public:
|
|||||||
bool outputGetData(pnet_t * pnet_ptr,
|
bool outputGetData(pnet_t * pnet_ptr,
|
||||||
uint32_t api,
|
uint32_t api,
|
||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
uint16_t subslot_nbr);
|
uint16_t subslot_nbr, bool& data_update);
|
||||||
|
|
||||||
bool getDataFromPlc(uint8_t * data_ptr);
|
bool isDataUpdated() {
|
||||||
|
return data_updated_;
|
||||||
bool putDataToPlc (uint8_t * data_ptr);
|
};
|
||||||
|
|
||||||
bool isDataUpdated() { return data_updated_; };
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const uint32_t m_id; /// Идентификатор подмодуля
|
const uint32_t m_id; /// Идентификатор подмодуля
|
||||||
@ -62,12 +67,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Набор параметров подмодуля
|
/// Набор параметров подмодуля
|
||||||
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
||||||
|
|
||||||
|
/// Буферы для взаимодействия с pnet и решения вопросов синхронизации
|
||||||
|
std::shared_ptr<uint8_t[]> out_data_buf_;
|
||||||
|
std::shared_ptr<uint8_t[]> inp_data_buf_;
|
||||||
|
|
||||||
std::shared_ptr<uint8_t[]> inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
SharedDataIface * const inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
||||||
std::mutex inp_data_mutex_;
|
SharedDataIface * const out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||||
|
|
||||||
std::shared_ptr<uint8_t[]> out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
|
||||||
std::mutex out_data_mutex_;
|
|
||||||
|
|
||||||
bool data_updated_;
|
bool data_updated_;
|
||||||
};
|
};
|
||||||
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
|
||||||
2
src/shared_data/shared_data.cmake
Normal file
2
src/shared_data/shared_data.cmake
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
set(SRC_FILES ${SRC_FILES} ./shared_data/shared_data.cpp)
|
||||||
|
set(INC_DIRS ${INC_DIRS} ./shared_data)
|
||||||
46
src/shared_data/shared_data.cpp
Normal file
46
src/shared_data/shared_data.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "shared_data.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
bool SharedData::Init(uint8_t * p_data, uint32_t size)
|
||||||
|
{
|
||||||
|
if ((p_data == nullptr) || (size == 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_data_ = p_data;
|
||||||
|
size_ = size;
|
||||||
|
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
std::memset(p_data_, 0, size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SharedData::Write(uint32_t offset_bytes, void * p_data, uint32_t length)
|
||||||
|
{
|
||||||
|
if ((p_data == nullptr) || ((offset_bytes + length) > size_))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
std::memcpy(p_data_ + offset_bytes, p_data, length);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SharedData::Read(uint32_t offset_bytes, void * p_data, uint32_t length)
|
||||||
|
{
|
||||||
|
if ((p_data == nullptr) || ((offset_bytes + length) > size_))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard guard(mutex_);
|
||||||
|
std::memcpy(p_data, p_data_ + offset_bytes, length);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
23
src/shared_data/shared_data.hpp
Normal file
23
src/shared_data/shared_data.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <boost/interprocess/sync/interprocess_mutex.hpp>
|
||||||
|
#include "shared_data_iface.hpp"
|
||||||
|
|
||||||
|
class SharedData : public SharedDataIface {
|
||||||
|
public:
|
||||||
|
SharedData() : p_data_{nullptr}, size_{0} {};
|
||||||
|
|
||||||
|
virtual bool Init(uint8_t * p_data, uint32_t size) override;
|
||||||
|
|
||||||
|
virtual bool Write(uint32_t offset_bytes, void * p_data, uint32_t length) override;
|
||||||
|
|
||||||
|
virtual bool Read(uint32_t offset_bytes, void * p_data, uint32_t length) override;
|
||||||
|
|
||||||
|
virtual uint32_t Size() const override { return size_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::interprocess::interprocess_mutex mutex_;
|
||||||
|
uint8_t * p_data_;
|
||||||
|
uint32_t size_;
|
||||||
|
};
|
||||||
16
src/shared_data/shared_data_iface.hpp
Normal file
16
src/shared_data/shared_data_iface.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class SharedDataIface {
|
||||||
|
public:
|
||||||
|
virtual bool Init(uint8_t * p_data, uint32_t size) = 0;
|
||||||
|
|
||||||
|
virtual bool Write(uint32_t offset_bytes, void * p_data, uint32_t length) = 0;
|
||||||
|
|
||||||
|
virtual bool Read(uint32_t offset_bytes, void * p_data, uint32_t length) = 0;
|
||||||
|
|
||||||
|
virtual uint32_t Size() const = 0;
|
||||||
|
|
||||||
|
virtual ~SharedDataIface() {}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user