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 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 @@
|
||||
#********************************************************************
|
||||
# _ _ _
|
||||
# _ __ | |_ _ | | __ _ | |__ ___
|
||||
# | '__|| __|(_)| | / _` || '_ \ / __|
|
||||
# | | | |_ _ | || (_| || |_) |\__ \
|
||||
# |_| \__|(_)|_| \__,_||_.__/ |___/
|
||||
#
|
||||
# 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)
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
project (PN_DEV_TEST VERSION 0.0.1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(LIBS_INSTALL_PATH ../libs)
|
||||
set(PNET_PATH ../profinet_stack/p-net)
|
||||
set(TARGET_NAME profinet_io_dev)
|
||||
|
||||
include(./utils/utils.cmake)
|
||||
include(./profinet/profinet.cmake)
|
||||
include(./nlohmann_json/nlohmann_json.cmake)
|
||||
include(./file_api/file_api.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}
|
||||
${LIBS_INSTALL_PATH}/include
|
||||
@ -35,6 +44,6 @@ target_include_directories(pn_dev_test PRIVATE
|
||||
${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 "../profinet/profinet_data_map.hpp"
|
||||
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
|
||||
using namespace boost::interprocess;
|
||||
using json = nlohmann::json;
|
||||
using string = std::string;
|
||||
|
||||
@ -131,93 +137,228 @@ static bool programconf_getPnetDeviceSettings(json& j, ProfinetDeviceSettings& p
|
||||
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")
|
||||
{
|
||||
out = PNET_DIR_IO;
|
||||
out = PROFINET_SUBMOD_DIR_IO;
|
||||
}
|
||||
else if (dir_str == "PNET_DIR_INPUT")
|
||||
{
|
||||
out = PNET_DIR_INPUT;
|
||||
out = PROFINET_SUBMOD_DIR_INPUT;
|
||||
}
|
||||
else if (dir_str == "PNET_DIR_OUTPUT")
|
||||
{
|
||||
out = PNET_DIR_OUTPUT;
|
||||
out = PROFINET_SUBMOD_DIR_OUTPUT;
|
||||
}
|
||||
|
||||
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"))
|
||||
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"];
|
||||
///Узнаем количество модулей
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (module_ptr == nullptr)
|
||||
++module_index;
|
||||
|
||||
/// Копируем параметры модуля
|
||||
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;
|
||||
}
|
||||
|
||||
auto& submodules_sett = mod_set["submodules"];
|
||||
mod.submods_nbr = submudules_number;
|
||||
|
||||
uint32_t submodules_index = 0;
|
||||
|
||||
for (auto& submod_sett : submodules_sett)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_indata_len);
|
||||
if (p_data == nullptr)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -231,7 +372,7 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!programconf_getProfinetModulesSettings(j, modules_vect))
|
||||
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -239,3 +380,4 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,9 +5,10 @@
|
||||
#include "json.hpp"
|
||||
#include "profinet_settings.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_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);
|
||||
|
@ -2,33 +2,67 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
|
||||
class Flags {
|
||||
#include <iostream>
|
||||
|
||||
#include "flags_iface.hpp"
|
||||
|
||||
class Flags : public FlagsIface {
|
||||
public:
|
||||
Flags() : flags_{0} {}
|
||||
|
||||
void set_flag(uint32_t mask) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
flags_ |= mask;
|
||||
Flags() : flags_{0} {
|
||||
}
|
||||
|
||||
uint32_t get_flag(uint32_t mask) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return flags_ & mask;
|
||||
virtual void set_flag(uint32_t mask) override
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
flags_ |= mask;
|
||||
}
|
||||
flag_cond_.notify_all();
|
||||
}
|
||||
|
||||
uint32_t get_flags() {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return flags_;
|
||||
virtual uint32_t get_flag(uint32_t mask) override {
|
||||
uint32_t out;
|
||||
{
|
||||
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_);
|
||||
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:
|
||||
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 "profinet.hpp"
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
#include "program_config.hpp"
|
||||
#include "app.hpp"
|
||||
|
||||
#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
|
||||
#include "./interprocess/profinet_shared_data.hpp"
|
||||
|
||||
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;
|
||||
}
|
||||
App app;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
|
||||
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.Init("program_configure.json");
|
||||
|
||||
/**
|
||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||
*/
|
||||
/// Запуск потока 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;
|
||||
}
|
||||
app.Run();
|
||||
|
||||
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}
|
||||
./profinet/profinet.cpp
|
||||
|
@ -33,7 +33,6 @@ using namespace std;
|
||||
#define GET_HIGH_BYTE(id) ((id >> 8) & 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_STACKSIZE 256 * 1024 /* bytes */
|
||||
#define APP_ETH_THREAD_PRIORITY 10
|
||||
@ -43,9 +42,7 @@ using namespace std;
|
||||
|
||||
#define FLAGS_AREP_APP_READY 0x00000001
|
||||
|
||||
//#define RELEASE
|
||||
|
||||
#ifndef RELEASE
|
||||
#ifdef LOG_ENABLE
|
||||
#define LOG(STR) log_.put(STR)
|
||||
#else
|
||||
#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,
|
||||
ProfinetDeviceSettings& DevSettings,
|
||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
|
||||
ProfinetData_Map * const p_data_map)
|
||||
{
|
||||
bool ret = true;
|
||||
pnet_cfg_t pnet_cfg = {0};
|
||||
@ -206,6 +232,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
||||
/// Инициализация библиотеки pnet
|
||||
m_pnet_data.pnet_ptr = pnet_init (&pnet_cfg);
|
||||
|
||||
p_events_ = &p_data_map->Events;
|
||||
|
||||
if (m_pnet_data.pnet_ptr == nullptr)
|
||||
return false;
|
||||
|
||||
@ -214,15 +242,17 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
||||
/// Создаем обязательный модуль DAP
|
||||
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 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
|
||||
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
|
||||
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_iface_1_indent_1);
|
||||
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
||||
/// Добавляем модуль в конфигурацию
|
||||
addModule(dap_module);
|
||||
|
||||
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
||||
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
||||
@ -234,21 +264,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
||||
/// 3. Подмодуль "DAP Port 1"
|
||||
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)
|
||||
{
|
||||
if (module_ptr == nullptr)
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!addModule(module_ptr))
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
///Создаем внутреннию структуру модулей
|
||||
createInternalStucture(p_data_map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -500,6 +517,7 @@ void Profinet::initDataAndIoxs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
submodule_ptr->inputSetData( m_pnet_data.pnet_ptr,
|
||||
m_pnet_data.api,
|
||||
slot_ptr->m_slot_nbr,
|
||||
@ -543,13 +561,6 @@ void Profinet::PeriodicOperations()
|
||||
/// Ждем время в мкс: ticks_period_us, по умолчанию 1мс
|
||||
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
|
||||
if (++m_cyclic_io_cnt >= m_cyclic_io_period_ms)
|
||||
{
|
||||
@ -559,11 +570,25 @@ void Profinet::PeriodicOperations()
|
||||
|
||||
/// запускаем pnet
|
||||
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()
|
||||
{
|
||||
///Циклический обмен только когда установлено соединение
|
||||
if (!isConnectedToController())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto slot : m_slots)
|
||||
{
|
||||
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
||||
@ -586,22 +611,23 @@ void Profinet::cyclicIoData()
|
||||
|
||||
if (submodule_ptr->m_data_cfg.outsize > 0)
|
||||
{
|
||||
bool outdata_updated;
|
||||
uint16_t outdata_length = submodule_ptr->m_data_cfg.outsize;
|
||||
uint8_t outdata_iops;
|
||||
|
||||
bool data_updated;
|
||||
/// Копируем данные от контроллера в m_out_data_ptr
|
||||
submodule_ptr->outputGetData( m_pnet_data.pnet_ptr,
|
||||
m_pnet_data.api,
|
||||
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)
|
||||
{
|
||||
uint8_t indata_iocs;
|
||||
uint8_t indata_iops = PNET_IOXS_GOOD;
|
||||
|
||||
/**
|
||||
* @brief Отправляем данные подмодуля в ПЛК
|
||||
*
|
||||
@ -661,25 +687,32 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
|
||||
|
||||
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)
|
||||
{
|
||||
LOG("err_cls= " + std::to_string(err_cls));
|
||||
LOG("err_code= " + std::to_string(err_code));
|
||||
/**
|
||||
* @brief Коды ошибок в err_cls и err_code
|
||||
* TODO: Нужно передавать в управляющее приложение
|
||||
*/
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Ошибок нет
|
||||
}
|
||||
|
||||
p_events_->set_flag(EVENT_CONNECTION_ABORT);
|
||||
|
||||
m_pnet_data.arep = UINT32_MAX;
|
||||
|
||||
/* Only abort AR with correct session key */
|
||||
///os_event_set (app->main_events, APP_EVENT_ABORT);
|
||||
}
|
||||
else if (event == PNET_EVENT_PRMEND)
|
||||
{
|
||||
LOG("callbackStateInd:PNET_EVENT_PRMEND");
|
||||
LOG("callbackStateInd: PNET_EVENT_PRMEND: Controller has sent all write records.");
|
||||
if (isConnectedToController())
|
||||
{
|
||||
/// Если уже были подключены и пришел запрос на еще одно подключение
|
||||
@ -706,14 +739,21 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
|
||||
the internal stack states */
|
||||
m_pnet_data.arep_for_appl_ready = arep;
|
||||
flags_.set_flag(FLAGS_AREP_APP_READY);
|
||||
|
||||
///os_event_set (app->main_events, APP_EVENT_READY_FOR_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
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -759,10 +799,7 @@ int Profinet::callbackDcontrolInd ( uint32_t arep,
|
||||
int Profinet::callbackCcontrolInd ( uint32_t arep, pnet_result_t * p_result)
|
||||
{
|
||||
LOG("callbackCcontrolInd");
|
||||
/**
|
||||
* @brief
|
||||
* Можно оставить пустым
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -800,11 +837,11 @@ int Profinet::callbackReadInd ( uint32_t arep,
|
||||
|
||||
///3. Передаем данные параметра
|
||||
/**
|
||||
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
|
||||
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||
* Нужно поисследовать.
|
||||
* В функции getReadBufPtr данные копируются во временный буфер и уже затем передаются в стек pnet.
|
||||
* Это нужно из-за отсутствия события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||
*
|
||||
*/
|
||||
*pp_read_data = param_ptr->getDataPointer();
|
||||
*pp_read_data = param_ptr->getReadBufPtr();
|
||||
*p_read_length = param_ptr->length;
|
||||
|
||||
return 0;
|
||||
@ -844,7 +881,13 @@ int Profinet::callbackWriteInd ( uint32_t arep,
|
||||
}
|
||||
|
||||
///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;
|
||||
}
|
||||
|
@ -15,14 +15,27 @@
|
||||
#include "../log/log.hpp"
|
||||
#include "../flags/flags.hpp"
|
||||
|
||||
#include "../flags/flags_iface.hpp"
|
||||
|
||||
#include "profinet_data_map.hpp"
|
||||
|
||||
class Profinet {
|
||||
public:
|
||||
enum Events
|
||||
{
|
||||
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||
EVENT_CONNECTION_ABORT = 0x00000008
|
||||
};
|
||||
|
||||
Profinet();
|
||||
|
||||
~Profinet();
|
||||
|
||||
bool Config(ProfinetSettings& Settings,
|
||||
ProfinetDeviceSettings& DevSettings,
|
||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
|
||||
ProfinetData_Map * const p_data_map);
|
||||
|
||||
/**
|
||||
* @brief Добавляет ранее созданный слот slot_ptr
|
||||
@ -143,8 +156,10 @@ public:
|
||||
*/
|
||||
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:
|
||||
ProfinetServiceData m_pnet_data;
|
||||
|
||||
@ -168,28 +183,43 @@ private:
|
||||
std::map<uint32_t, std::shared_ptr<ProfinetModule>> m_modules;
|
||||
|
||||
Log log_;
|
||||
|
||||
Flags flags_;
|
||||
|
||||
|
||||
Flags flags_; /// Внутренние флаги: взаимодействиe между потоком Profinet и потоком стека Pnet
|
||||
|
||||
FlagsIface * p_events_; /// Флаги для внешних событий. Это более общие флаги, например флаг получения данных от ПЛК, а в каком подмодуле, уже не уточняется.
|
||||
private:
|
||||
/**
|
||||
* @brief Проверяет наличие слота slot_nbr в конфигурации
|
||||
* @brief Проверяет наличие слота slot_nbr в конфигурации и возвращает указатель на него
|
||||
*
|
||||
* @param slot_nbr номер слота
|
||||
* @return ProfinetSlot* - указатель на слот, при успехе или nullptr есои слот в конфигурации отсутсвует
|
||||
* @return std::shared_ptr<ProfinetSlot> указатель на слот, при успехе или nullptr если слот в конфигурации отсутсвует
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Флаг подключение к контроллеру(ПЛК).
|
||||
* Реализация взята с примера стека pnet. На самом деле не понятна роль AREP.
|
||||
* Открытой информации недостаточно, нужно покупать и смотреть спецификацию. *
|
||||
*
|
||||
* @return true подключение есть
|
||||
* @return false
|
||||
*/
|
||||
bool isConnectedToController() { return m_pnet_data.arep != UINT32_MAX; };
|
||||
|
||||
void initDataAndIoxs();
|
||||
|
||||
void cyclicIoData();
|
||||
|
||||
void createInternalStucture(ProfinetData_Map * const p_data_map);
|
||||
|
||||
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 <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),
|
||||
name(Name),
|
||||
length(Length),
|
||||
data_ptr_(new uint8_t[Length])
|
||||
length(p_data->Size()),
|
||||
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)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
||||
|
||||
if (data_ptr == nullptr)
|
||||
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data_ptr, data_ptr_.get(), length);
|
||||
p_data_->Read(0, (void*)data_ptr, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
||||
|
||||
if (data_ptr == nullptr)
|
||||
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data_ptr_.get(), data_ptr, length);
|
||||
p_data_->Write(0, (void*)data_ptr, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t * ProfinetParameter::getDataPointer()
|
||||
uint8_t * ProfinetParameter::getReadBufPtr()
|
||||
{
|
||||
/**
|
||||
* @brief Слабое утешение от ситуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||
* @brief Cитуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||
* прочитать и вызвался коллбэк 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
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "../shared_data/shared_data_iface.hpp"
|
||||
|
||||
|
||||
class ProfinetParameter
|
||||
{
|
||||
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 Защищенная запись данных параметра
|
||||
@ -39,17 +45,18 @@ public:
|
||||
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
||||
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
||||
* см. profinet_cb_read.cpp
|
||||
* Не использовать эту функцию в приложении!!!
|
||||
* @return uint8_t*
|
||||
*/
|
||||
uint8_t * getDataPointer();
|
||||
uint8_t * getReadBufPtr();
|
||||
private:
|
||||
|
||||
|
||||
|
||||
public:
|
||||
const std::string name; /// Имя параметра
|
||||
const uint32_t index; /// Идентфикатор параметра
|
||||
const uint16_t length; /// длина параметра в байтах
|
||||
|
||||
private:
|
||||
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
|
||||
mutable std::mutex data_mutex_; /// Доступ к данным
|
||||
};
|
||||
std::shared_ptr<uint8_t[]> read_buf_; ///Буфер данных для передачи его в стек pnet для операции чтения
|
||||
SharedDataIface * const p_data_; ///Указатель на данные параметра(в разделяемой области)
|
||||
};
|
@ -3,24 +3,34 @@
|
||||
|
||||
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
||||
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_name(submodule_name),
|
||||
m_data_cfg(submodule_data_cfg),
|
||||
inp_data_ptr_(nullptr),
|
||||
out_data_ptr_(nullptr),
|
||||
inp_data_ptr_(inp_data_ptr),
|
||||
out_data_ptr_(out_data_ptr),
|
||||
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.insize > 0)
|
||||
if (m_data_cfg.outsize && (out_data_ptr_!=nullptr))
|
||||
{
|
||||
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,
|
||||
uint8_t iops)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
||||
|
||||
if ((pnet_ptr == nullptr) || (inp_data_ptr_ == nullptr))
|
||||
if ((pnet_ptr == nullptr) ||
|
||||
((inp_data_buf_ == nullptr) && (m_data_cfg.insize > 0)))
|
||||
{
|
||||
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,
|
||||
api,
|
||||
slot_nbr,
|
||||
subslot_nbr,
|
||||
inp_data_ptr_.get(),
|
||||
inp_data_buf_.get(),
|
||||
m_data_cfg.insize,
|
||||
iops);
|
||||
|
||||
@ -68,69 +90,56 @@ bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
|
||||
bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
||||
uint32_t api,
|
||||
uint16_t slot_nbr,
|
||||
uint16_t subslot_nbr )
|
||||
uint16_t subslot_nbr, bool& data_update )
|
||||
{
|
||||
bool out = true;
|
||||
|
||||
/// Существуют подмодули без данных, например DAP
|
||||
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
||||
if ((pnet_ptr == nullptr) || ((out_data_ptr_ == nullptr) && (m_data_cfg.outsize > 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
||||
|
||||
uint16_t outdata_length = m_data_cfg.outsize;
|
||||
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,
|
||||
api,
|
||||
slot_nbr,
|
||||
subslot_nbr,
|
||||
&updated,
|
||||
out_data_ptr_.get(),
|
||||
&data_updated_,
|
||||
out_data_buf_.get(),
|
||||
&outdata_length,
|
||||
&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;
|
||||
data_updated_ = updated;
|
||||
|
||||
if (m_data_cfg.outsize != outdata_length)
|
||||
{
|
||||
/// Неправильная длина данных
|
||||
out = false;
|
||||
}
|
||||
else if (outdata_iops == PNET_IOXS_BAD)
|
||||
{
|
||||
/// Что-то не так с данными от контроллера
|
||||
out = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return out;
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "profinet_parameter.hpp"
|
||||
#include "../shared_data/shared_data_iface.hpp"
|
||||
|
||||
#include "../../libs/include/pnet_api.h"
|
||||
|
||||
@ -15,21 +16,27 @@ class ProfinetSubmodule {
|
||||
public:
|
||||
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
||||
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,
|
||||
submodule_name,
|
||||
submodule_data_cfg));
|
||||
submodule_data_cfg,
|
||||
inp_data_ptr,
|
||||
out_data_ptr));
|
||||
}
|
||||
|
||||
ProfinetSubmodule(uint32_t submodule_id,
|
||||
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);
|
||||
|
||||
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
|
||||
|
||||
|
||||
bool inputSetData( pnet_t * pnet_ptr,
|
||||
uint32_t api,
|
||||
uint16_t slot_nbr,
|
||||
@ -39,13 +46,11 @@ public:
|
||||
bool outputGetData(pnet_t * pnet_ptr,
|
||||
uint32_t api,
|
||||
uint16_t slot_nbr,
|
||||
uint16_t subslot_nbr);
|
||||
uint16_t subslot_nbr, bool& data_update);
|
||||
|
||||
bool getDataFromPlc(uint8_t * data_ptr);
|
||||
|
||||
bool putDataToPlc (uint8_t * data_ptr);
|
||||
|
||||
bool isDataUpdated() { return data_updated_; };
|
||||
bool isDataUpdated() {
|
||||
return data_updated_;
|
||||
};
|
||||
|
||||
public:
|
||||
const uint32_t m_id; /// Идентификатор подмодуля
|
||||
@ -62,12 +67,13 @@ public:
|
||||
private:
|
||||
/// Набор параметров подмодуля
|
||||
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)
|
||||
std::mutex inp_data_mutex_;
|
||||
|
||||
std::shared_ptr<uint8_t[]> out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||
std::mutex out_data_mutex_;
|
||||
SharedDataIface * const inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
||||
SharedDataIface * const out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||
|
||||
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