ProfinetConnector/profinet_test/sample_app/sampleapp_main.c
svad05 c9fe825657 dev(UML-981): Автоматизация сборки
1. Добавил скрипт build.sh - собирает библиотеку pnet и группирует
получившиеся файлы в папку install, туда же помещает библиотеку osal.
Теперь все библиотеки и их заголовки в одном месте, а не распизаны по
папкам в папке build.
2. Добавил проект sample_app в папку profinet_test. Он отвязан от
процесса сборки pnet и использует уже собранную библиотеку из папки
install.
2022-07-12 15:59:03 +02:00

520 lines
16 KiB
C

/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
#define _GNU_SOURCE /* For asprintf() */
#include "sampleapp_common.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "app_utils.h"
#include "osal.h"
#include "osal_log.h" /* For LOG_LEVEL */
#include "pnal.h"
#include "pnal_filetools.h"
#include <pnet_api.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sampleapp_main.h"
#if PNET_MAX_PHYSICAL_PORTS == 1
#define APP_DEFAULT_ETHERNET_INTERFACE "eth0"
#else
#define APP_DEFAULT_ETHERNET_INTERFACE "br0,eth0,eth1"
#endif
#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
#define APP_ETH_THREAD_STACKSIZE 4096 /* bytes */
#define APP_BG_WORKER_THREAD_PRIORITY 5
#define APP_BG_WORKER_THREAD_STACKSIZE 4096 /* bytes */
/* Note that this sample application uses os_timer_create() for the timer
that controls the ticks. It is implemented in OSAL, and the Linux
implementation uses a thread internally. To modify the timer thread priority,
modify OSAL or use some other timer */
app_args_t app_args = {0};
/************************* Utilities ******************************************/
void show_usage()
{
printf ("\nSample application for p-net Profinet device stack.\n");
printf ("\n");
printf ("Wait for connection from IO-controller.\n");
printf ("Then read buttons (input) and send to controller.\n");
printf ("Listen for application LED output (from controller) and set "
"application LED state.\n");
printf ("It will also send a counter value (useful also without buttons and "
"LED).\n");
printf ("Button1 value is sent in the periodic data.\n");
printf ("Button2 cycles through triggering an alarm, setting diagnosis and "
"creating logbook entries.\n");
printf ("\n");
printf ("Also the mandatory Profinet signal LED is controlled by this "
"application.\n");
printf ("\n");
printf ("The LEDs are controlled by the script set_profinet_leds\n");
printf ("located in the same directory as the application binary.\n");
printf ("A version for Raspberry Pi is available, and also a version "
"writing\n");
printf ("to plain text files (useful for demo if no LEDs are available).\n");
printf ("\n");
printf ("Assumes the default gateway is found on .1 on same subnet as the "
"IP address.\n");
printf ("\n");
printf ("Optional arguments:\n");
printf (" --help Show this help text and exit\n");
printf (" -h Show this help text and exit\n");
printf (" -v Incresase verbosity. Can be repeated.\n");
printf (" -f Reset to factory settings, and store to file. "
"Exit.\n");
printf (" Remember to give the -p flag if necessary.\n");
printf (" -r Remove stored files and exit.\n");
printf (" Remember to give the -p flag if necessary.\n");
printf (" -g Show stack details and exit. Repeat for more "
"details.\n");
printf (
" -i INTERF Name of Ethernet interface to use. Defaults to %s\n",
APP_DEFAULT_ETHERNET_INTERFACE);
printf (" Comma separated list if more than one interface "
"given.\n");
printf (
" -s NAME Set station name. Defaults to \"%s\". Only used\n",
APP_GSDML_DEFAULT_STATION_NAME);
printf (" if not already available in storage file.\n");
printf (" -b FILE Path (absolute or relative) to read Button1. "
"Defaults to not read Button1.\n");
printf (" -d FILE Path (absolute or relative) to read Button2. "
"Defaults to not read Button2.\n");
printf (" -p PATH Absolute path to storage directory. Defaults to "
"use current directory.\n");
#if PNET_OPTION_DRIVER_ENABLE
printf (" -m MODE Application offload mode. Only used if P-Net is\n");
printf (" built with hw offload enabled "
" (PNET_OPTION_DRIVER_ENABLE). \n");
printf (" Supported modes: none, cpu, full\n");
printf (" Defaults to none\n");
#endif
printf ("\n");
printf ("p-net revision: " PNET_VERSION "\n");
}
/**
* Parse command line arguments
*
* @param argc In: Number of arguments
* @param argv In: Arguments
* @return Parsed arguments
*/
app_args_t parse_commandline_arguments (int argc, char * argv[])
{
app_args_t output_arguments = {0};
int option;
/* Special handling of long argument */
if (argc > 1)
{
if (strcmp (argv[1], "--help") == 0)
{
show_usage();
exit (EXIT_FAILURE);
}
}
/* Default values */
strcpy (output_arguments.path_button1, "");
strcpy (output_arguments.path_button2, "");
strcpy (output_arguments.path_storage_directory, "");
strcpy (output_arguments.station_name, APP_GSDML_DEFAULT_STATION_NAME);
strcpy (output_arguments.eth_interfaces, APP_DEFAULT_ETHERNET_INTERFACE);
output_arguments.verbosity = 0;
output_arguments.show = 0;
output_arguments.factory_reset = false;
output_arguments.remove_files = false;
output_arguments.mode = MODE_HW_OFFLOAD_NONE;
while ((option = getopt (argc, argv, "hvgfri:s:b:d:p:m:")) != -1)
{
switch (option)
{
case 'v':
output_arguments.verbosity++;
break;
case 'g':
output_arguments.show++;
break;
case 'f':
output_arguments.factory_reset = true;
break;
case 'r':
output_arguments.remove_files = true;
break;
case 'i':
if ((strlen (optarg) + 1) > sizeof (output_arguments.eth_interfaces))
{
printf ("Error: The argument to -i is too long.\n");
exit (EXIT_FAILURE);
}
strcpy (output_arguments.eth_interfaces, optarg);
break;
case 's':
strcpy (output_arguments.station_name, optarg);
break;
case 'b':
if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
{
printf ("Error: The argument to -b is too long.\n");
exit (EXIT_FAILURE);
}
strcpy (output_arguments.path_button1, optarg);
break;
case 'd':
if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
{
printf ("Error: The argument to -d is too long.\n");
exit (EXIT_FAILURE);
}
strcpy (output_arguments.path_button2, optarg);
break;
case 'p':
if (strlen (optarg) + 1 > PNET_MAX_FILE_FULLPATH_SIZE)
{
printf ("Error: The argument to -p is too long.\n");
exit (EXIT_FAILURE);
}
strcpy (output_arguments.path_storage_directory, optarg);
break;
#if PNET_OPTION_DRIVER_ENABLE
case 'm':
if (strcmp ("none", optarg) == 0)
{
output_arguments.mode = MODE_HW_OFFLOAD_NONE;
}
else if (strcmp ("cpu", optarg) == 0)
{
output_arguments.mode = MODE_HW_OFFLOAD_CPU;
}
else if (strcmp ("full", optarg) == 0)
{
output_arguments.mode = MODE_HW_OFFLOAD_FULL;
}
else
{
printf ("Error: mode (-m) not supported.\n");
exit (EXIT_FAILURE);
}
break;
#endif
case 'h':
/* fallthrough */
case '?':
/* fallthrough */
default:
show_usage();
exit (EXIT_FAILURE);
}
}
/* Use current directory for storage, if not given */
if (strlen (output_arguments.path_storage_directory) == 0)
{
if (
getcwd (
output_arguments.path_storage_directory,
sizeof (output_arguments.path_storage_directory)) == NULL)
{
printf ("Error: Could not read current working directory. Is "
"PNET_MAX_DIRECTORYPATH_SIZE too small?\n");
exit (EXIT_FAILURE);
}
}
return output_arguments;
}
/**
* Read a bool from a file
*
* @param filepath In: Path to file
* @return true if file exists and the first character is '1'
*/
bool read_bool_from_file (const char * filepath)
{
FILE * fp;
char ch;
int eof_indicator;
fp = fopen (filepath, "r");
if (fp == NULL)
{
return false;
}
ch = fgetc (fp);
eof_indicator = feof (fp);
fclose (fp);
if (eof_indicator)
{
return false;
}
return ch == '1';
}
bool app_get_button (uint16_t id)
{
if (id == 0)
{
if (app_args.path_button1[0] != '\0')
{
return read_bool_from_file (app_args.path_button1);
}
}
else if (id == 1)
{
if (app_args.path_button2[0] != '\0')
{
return read_bool_from_file (app_args.path_button2);
}
}
return false;
}
void app_set_led (uint16_t id, bool led_state)
{
/* Important:
* The Linux sample application uses a script to set the LED state,
* for easy adaption to different development boards.
*
* The script typically writes to files in the /sys directory to set LED
* state via GPIO operations. If you do not have any physical LEDs you can
* use a script that writes to regular files instead.
*
* However, file operations shall be avoided within the main task
* in a real application. File operations may affect the timing of the
* Profinet communication depending on file system implementation.
*/
char id_str[7] = {0}; /** Terminated string */
const char * argv[4];
sprintf (id_str, "%u", id);
id_str[sizeof (id_str) - 1] = '\0';
argv[0] = "set_profinet_leds";
argv[1] = (char *)&id_str;
argv[2] = (led_state == 1) ? "1" : "0";
argv[3] = NULL;
if (pnal_execute_script (argv) != 0)
{
printf ("Failed to set LED state\n");
}
}
/** Update configuration with file storage path.
* Validate this path, and Linux button file paths
*
* @param p_cfg InOut: Configuration to be updated
* @param p_args In: Command line arguments
* @return 0 on success, -1 on error.
*/
static int app_pnet_cfg_init_storage (
pnet_cfg_t * p_cfg,
const app_args_t * p_args)
{
strcpy (p_cfg->file_directory, p_args->path_storage_directory);
if (p_args->verbosity > 0)
{
printf ("Storage directory: %s\n\n", p_cfg->file_directory);
}
/* Validate paths */
if (!pnal_does_file_exist (p_cfg->file_directory))
{
printf (
"Error: The given storage directory does not exist: %s\n",
p_cfg->file_directory);
return -1;
}
if (p_args->path_button1[0] != '\0')
{
if (!pnal_does_file_exist (p_args->path_button1))
{
printf (
"Error: The given input file for Button1 does not exist: %s\n",
p_args->path_button1);
return -1;
}
}
if (p_args->path_button2[0] != '\0')
{
if (!pnal_does_file_exist (p_args->path_button2))
{
printf (
"Error: The given input file for Button2 does not exist: %s\n",
p_args->path_button2);
return -1;
}
}
return 0;
}
/****************************** Main ******************************************/
//int main (int argc, char * argv[])
int c_main (int argc, char * argv[])
{
int ret;
int32_t app_log_level = APP_LOG_LEVEL_FATAL;
pnet_cfg_t pnet_cfg = {0};
app_data_t * sample_app = NULL;
app_utils_netif_namelist_t netif_name_list;
pnet_if_cfg_t netif_cfg = {0};
uint16_t number_of_ports = 1;
/* Enable line buffering for printouts, especially when logging to
the journal (which is default when running as a systemd job) */
setvbuf (stdout, NULL, _IOLBF, 0);
/* Parse and display command line arguments */
app_args = parse_commandline_arguments (argc, argv);
app_log_level = (app_args.verbosity <= APP_LOG_LEVEL_FATAL)
? APP_LOG_LEVEL_FATAL - app_args.verbosity
: APP_LOG_LEVEL_DEBUG;
app_log_set_log_level (app_log_level);
printf ("\n** Starting P-Net sample application " PNET_VERSION " **\n");
APP_LOG_INFO (
"Number of slots: %u (incl slot for DAP module)\n",
PNET_MAX_SLOTS);
APP_LOG_INFO ("P-net log level: %u (DEBUG=0, FATAL=4)\n", LOG_LEVEL);
APP_LOG_INFO ("App log level: %u (DEBUG=0, FATAL=4)\n", app_log_level);
APP_LOG_INFO ("Max number of ports: %u\n", PNET_MAX_PHYSICAL_PORTS);
APP_LOG_INFO ("Network interfaces: %s\n", app_args.eth_interfaces);
APP_LOG_INFO ("Button1 file: %s\n", app_args.path_button1);
APP_LOG_INFO ("Button2 file: %s\n", app_args.path_button2);
APP_LOG_INFO ("Default station name: %s\n", app_args.station_name);
/* Prepare configuration */
app_pnet_cfg_init_default (&pnet_cfg);
strcpy (pnet_cfg.station_name, app_args.station_name);
ret = app_utils_pnet_cfg_init_netifs (
app_args.eth_interfaces,
&netif_name_list,
&number_of_ports,
&netif_cfg);
if (ret != 0)
{
exit (EXIT_FAILURE);
}
pnet_cfg.if_cfg = netif_cfg;
pnet_cfg.num_physical_ports = number_of_ports;
app_utils_print_network_config (&netif_cfg, number_of_ports);
/* Operating system specific settings */
pnet_cfg.pnal_cfg.snmp_thread.prio = APP_SNMP_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.snmp_thread.stack_size = APP_SNMP_THREAD_STACKSIZE;
pnet_cfg.pnal_cfg.eth_recv_thread.prio = APP_ETH_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.eth_recv_thread.stack_size = APP_ETH_THREAD_STACKSIZE;
pnet_cfg.pnal_cfg.bg_worker_thread.prio = APP_BG_WORKER_THREAD_PRIORITY;
pnet_cfg.pnal_cfg.bg_worker_thread.stack_size =
APP_BG_WORKER_THREAD_STACKSIZE;
ret = app_pnet_cfg_init_storage (&pnet_cfg, &app_args);
if (ret != 0)
{
printf ("Failed to initialize storage.\n");
printf ("Aborting application\n");
exit (EXIT_FAILURE);
}
/* Remove files and exit */
if (app_args.remove_files == true)
{
printf ("\nRemoving stored files\n");
printf ("Exit application\n");
(void)pnet_remove_data_files (pnet_cfg.file_directory);
exit (EXIT_SUCCESS);
}
/* Initialise stack and application */
sample_app = app_init (&pnet_cfg, &app_args);
if (sample_app == NULL)
{
printf ("Failed to initialize P-Net.\n");
printf ("Do you have enough Ethernet interface permission?\n");
printf ("Aborting application\n");
exit (EXIT_FAILURE);
}
/* Do factory reset and exit */
if (app_args.factory_reset == true)
{
printf ("\nPerforming factory reset\n");
printf ("Exit application\n");
(void)pnet_factory_reset (app_get_pnet_instance (sample_app));
exit (EXIT_SUCCESS);
}
/* Show stack info and exit */
if (app_args.show > 0)
{
int level = 0xFFFF;
printf ("\nShowing stack information.\n\n");
if (app_args.show == 1)
{
level = 0x2010; /* See documentation for pnet_show() */
}
pnet_show (app_get_pnet_instance (sample_app), level);
printf ("Exit application\n");
exit (EXIT_SUCCESS);
}
/* Start main loop */
if (app_start (sample_app, RUN_IN_SEPARATE_THREAD) != 0)
{
printf ("Failed to start\n");
printf ("Aborting application\n");
exit (EXIT_FAILURE);
}
for (;;)
{
os_usleep (APP_MAIN_SLEEPTIME_US);
}
return 0;
}