#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#ifdef __QNX__
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#include <atomic.h>
#elif defined (__linux__)
#include <sys/timerfd.h>
#ifdef __cplusplus
#include <atomic>
//#define _Atomic(X) std::atomic< X >
using namespace std;
#else
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif
#endif
#endif
#include "utilites.h" //Лог, конфигурация, работа со строками
#include "../sw_emka2_sil2_bur_utilites/can2udp/cfilter.h"
#include "impl/defines.h" //Типы данных
#include "impl/routines.h" //Прикладные функции программы
#include "../sw_emka2_sil1_bur_canlib/can_lib.h" //Библиотека CAN
#include "svnrevision.h"
//
volatile bool b_doExit = false; //Признак завершения программы
#ifdef __QNX__
volatile unsigned g_n_tickCnt = 0; ///Количество тиков
#elif defined (__linux__)
volatile atomic_uint g_n_tickCnt(0); ///Количество тиков
#endif
volatile unsigned g_n_tickItvMs = 1; ///Интервал между тиками, миллисекунды.
char g_sz_hbPath[] = "/tmp/sw_emka2_sil1_bur_can2udp_hb.txt";
#define MY_PULSE_CODE_CONTROLBUO _PULSE_CODE_MINAVAIL
#define MAX_HB 65536
#define FLUSH_LOG_ITV 100
#define FLUSH_LOG_TICK (FLUSH_LOG_ITV < g_n_tickItvMs ? 1 : FLUSH_LOG_ITV / g_n_tickItvMs)
#ifdef __QNX__
typedef union
{
struct _pulse pulse;
/* your other message structures would go here too */
} my_message_t;
#endif
/**
* @brief Структура, содержащая данные по параметрам командной строки
*/
typedef struct SCmdParams
{
char m_sz_cfgFile[MAX_STRLEN];///Путь к файлу конфигурации.
char m_sz_logFile[MAX_STRLEN];///Путь к файлу лога.
bool m_b_help;///Запрос на вывод справки.
} DTCmdParams;
/**
* @brief Функция вывода справки в стандартный вывод.
*
* @param sz_name -- название программы.
*/
void usage(const char* sz_name);
/**
* @brief Функция разбора параметров командной строки.
*
* @param s32_argc -- количество параметров командной строки;
* @param p_sz_argv -- указатель на массив параметров командной строки.
* @param h_cmd -- струкута с параметрами командной строки.
*
* @return
* @arg true -- параметры командной строки распознаны, ошибок нет.
* @arg false -- не заданы требуемые параметры.
*/
bool parseArgs(int32_t s32_argc,
char* p_sz_argv[],
DTCmdParams& h_cmd);
/**
* @brief Функция сохранения данных о номере версии ПО устройство разделяемой памяти.
*
* @param u32_version -- номер версии ПО.
*/
void saveVersion(uint32_t u32_version);
/**
* @brief Функция записи счетчика жизни в файл.
*
* @param u64_tickCnt -- счетчик циклов;
* @param sz_path -- полный или относительный путь к целефому файлу.
*/
void WriteHB(uint64_t u64_tickCnt,
const char* sz_path);
/**
* @brief Точка входа в программу.
*
* @param s32_argc -- количество параметров командной строки;
* @param p_sz_argv -- указатель на массив параметров командной строки.
*
* @return
* @arg EXIT_FAILURE -- в случае ошибки.
* @arg EXIT_SUCCESS -- при отсутствии ошибок.
*/
int main(int32_t s32_argc,
char* p_sz_argv[])
{
int32_t s32_res = EXIT_SUCCESS;
DTRoute v_route[MAX_ROUTES];
DTCanInfo v_canInfo[MAX_CAN];
DTGroupInfo v_grpInfo[MAX_GROUPS];
int32_t v_canFds[MAX_CAN];
int32_t v_grpFds[MAX_GROUPS];
CFilter* v_filterCan[MAX_FILTERS];
for (int i = 0; i < MAX_FILTERS; i++) {
v_filterCan[i] = NewFilter();
}
DTCmdParams h_cmd;
h_cmd.m_b_help = false;
h_cmd.m_sz_cfgFile[0] = '\0';
snprintf
(&h_cmd
.m_sz_logFile
[0], sizeof(h_cmd
.m_sz_logFile
), "/var/log/%s.log", p_sz_argv
[0]);
saveVersion(u32_svn_revision);
if (!parseArgs(s32_argc, p_sz_argv, h_cmd))
{
usage(p_sz_argv[0]);
s32_res = EXIT_FAILURE;
}
else
{
doLog(h_cmd.m_sz_logFile, "Запуск программы.");
if (h_cmd.m_b_help)
{
usage(p_sz_argv[0]);
}
else //Читаем конфигурационный файл
{
CConfig h_cfg;
int32_t s32_errCode = 0;//Код ошибки
int32_t n_canChannels = 0;//Количество каналов шины CAN
int32_t n_udpChannels = 0;//Количество групп UDP-multicast
int32_t n_filters = 0;//Количество фильтров в конфигурации
if (0 == (s32_errCode = h_cfg.Load(h_cmd.m_sz_cfgFile)))
{
//Чтение фильтров
n_filters = readIdFilter(&h_cfg, v_filterCan, MAX_ROUTES, h_cmd.m_sz_logFile);
//Заполнение данных по линиям шины CAN
n_canChannels = readCanInfo(&h_cfg, &v_canInfo[0], MAX_CAN);
//Заполнение данных по группам Multicast
n_udpChannels = readGroupInfo(&h_cfg, &v_grpInfo[0], MAX_GROUPS);
//Запуск портов CAN (получение дескрипторов CAN-портов)
int32_t s32_res = runCanChannels(&v_canInfo[0], MAX_CAN, &v_canFds[0], n_canChannels);
//Присоединение к группам UDP-multicast(получение дескрипторов Multicast-групп)
s32_res = runMulticastChannels(&v_grpInfo[0], MAX_GROUPS, &v_grpFds[0], n_udpChannels);
//Составление маршрутов
int32_t s32_nRoutes = MAX_ROUTES;
s32_errCode = readAndBuildRoutes(&h_cfg, &v_canFds[0], n_canChannels, &v_grpFds[0],
n_udpChannels, v_grpInfo, n_udpChannels, v_filterCan, n_filters, &v_route[0],
&s32_nRoutes);
if (-1 == s32_errCode)
{
doLog(h_cmd.m_sz_logFile, "Ошибка конфигурации: имеются некорректные маршруты.");
}
else
{
struct itimerspec itime;
itime.it_value.tv_sec = 0;
itime.it_value.tv_nsec = 1000000 * g_n_tickItvMs;
itime.it_interval.tv_sec = 0;
itime.it_interval.tv_nsec = 1000000 * g_n_tickItvMs;
#ifdef __QNX__
struct sigevent event;
timer_t timer_id;
int chid;
int rcvid;
my_message_t msg;
chid = ChannelCreate(0);
event.sigev_notify = SIGEV_PULSE;
event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0, chid, _NTO_SIDE_CHANNEL, 0);
event.sigev_priority = getprio(0);
event.sigev_code = MY_PULSE_CODE_CONTROLBUO;
timer_create(CLOCK_REALTIME, &event, &timer_id);
timer_settime(timer_id, 0, &itime, NULL);
#elif defined (__linux__)
// int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // создаёт новый объект таймера и возвращает файловый дескриптор, который ссылается на таймер. CLOCK_MONOTONIC/CLOCK_REALTIME -
// if (timerfd == -1)
// {
// perror("timerfd_create");
// exit(EXIT_FAILURE);
// }
// if (timerfd_settime(timerfd, 0, &itime, NULL) == -1) // запускает таймер
// {
// close(timerfd);
// perror("timerfd_settime");
// exit(EXIT_FAILURE);
// }
#endif
//Маршрутизация
while (!b_doExit)
{
#ifdef __QNX__
rcvid
= MsgReceive
(chid
, &msg
, sizeof(msg
), NULL); if (rcvid == 0)
{
if (msg.pulse.code == MY_PULSE_CODE_CONTROLBUO)
{
atomic_add(&g_n_tickCnt, 1U);
#elif defined (__linux__)
// {
// uint64_t expirationsNumber = 0; //
// ssize_t s = read(timerfd, &expirationsNumber, sizeof(uint64_t)); // wait of timer
// if (s != sizeof(uint64_t))
// {
// //perror("read");
// continue; //
// }
// else
// {
// ++g_n_tickCnt;
#endif
uint8_t u8_hiMask = 0;
//Получение маски (1-й раз через 15-16 секун после запуска, далее -- каждые 5-6 секунд)
parseProps(&h_cfg, &u8_hiMask);
//Передача данных по всем маршрутам
for (int i = 0; i < s32_nRoutes && !b_doExit; ++i)
{
if (!b_doExit)
broadcastCAN(&v_route[i], u8_hiMask, &v_canFds[0], n_canChannels, &v_grpFds[0],
n_udpChannels, &v_filterCan[0], n_filters);
if (!b_doExit)
broadcastUDP(&v_route[i], u8_hiMask, &v_canFds[0], n_canChannels, &v_grpFds[0],
n_udpChannels, &v_filterCan[0], n_filters);
}
WriteHB(g_n_tickCnt, g_sz_hbPath);
#ifdef __WITH_PKG_MAP__
if (g_n_tickCnt % FLUSH_LOG_TICK == 0)
can_flush_log();
#endif
}
}
}//while
}
}//Load
else
doLog(h_cmd.m_sz_logFile, "Конфигурационный файл не доступен или содержит ошибки.");
}
}//parse
doLog(h_cmd.m_sz_logFile, "Завершение программы.");
return s32_res;
}
/**
* @brief Функция вывода справки в стандартный вывод.
*
* @param sz_name -- название программы.
*/
void usage(const char* sz_name)
{
printf("usage: %s --config=/path/to/config [--log=/path/to/log] | --help \n", sz_name
); printf(" --config -- the absolute or relative path to the configuration file\n"); printf(" --log -- the absolute or relative path to the log-file.\n"); printf(" --help -- show this message.\n\n"); }
/**
* @brief Функция разбора параметров командной строки.
*
* @param s32_argc -- количество параметров командной строки;
* @param p_sz_argv -- указатель на массив параметров командной строки.
* @param h_cmd -- струкута с параметрами командной строки.
*
* @return
* @arg true -- параметры командной строки распознаны, ошибок нет.
* @arg false -- не заданы требуемые параметры.
*/
bool parseArgs(int32_t s32_argc,
char* p_sz_argv[],
DTCmdParams & h_cmd)
{
bool b_res = false;
if (s32_argc < 3)
{
std::list<std::pair<std::string, std::string> > v_cmd;
for (int i = 1; i < s32_argc; ++i)
{
std::string str(p_sz_argv[i]);
std
::list<std
::string> v_str
= strsub
::split(str
, "="); v_cmd.push_back(std::make_pair(v_str.front(), v_str.size() > 1 ? v_str.back() : std::string(
"")));
}
bool b_done = false;
for (std::list<std::pair<std::string, std::string> >::iterator it = v_cmd.begin(); it
!= v_cmd
.end() && !b_done
; ++it
) {
if (it->first == "--help" && s32_argc == 2)
{
b_done = true;
b_res = true;
h_cmd.m_b_help = true;
}
if (it
->first == "--config" && !it
->second.empty()) {
b_res = true;
snprintf
(&h_cmd
.m_sz_cfgFile
[0], sizeof(h_cmd
.m_sz_cfgFile
), "%s", it
->second.c_str
()); }
if (it
->first == "--log" && !it
->second.empty()) snprintf
(&h_cmd
.m_sz_logFile
[0], sizeof(h_cmd
.m_sz_logFile
), "%s", it
->second.c_str
()); }
}
return b_res;
}
/**
* @brief Функция сохранения данных о номере версии ПО устройство разделяемой памяти.
*
* @param u32_version -- номер версии ПО.
*/
void saveVersion(uint32_t u32_version)
{
const char sz_path[] = "/tmp/sw_emka2_sil1_bur_can2udp_ver.txt";
if (p_fd)
{
fprintf(p_fd
, "%d\n", u32_version
); }
}
/**
* @brief Функция записи счетчика жизни в файл.
*
* @param u64_tickCnt -- счетчик циклов;
* @param sz_path -- полный или относительный путь к целефому файлу.
*/
void WriteHB(uint64_t u64_tickCnt,
const char* sz_path)
{
if (sz_path)
{
if (fd)
{
fprintf(fd
, "%d", (int32_t
)(u64_tickCnt
% MAX_HB
)); }
}
}