ProfinetConnector/profinet_test/sample_app/app_utils.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

742 lines
18 KiB
C

/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
#define _GNU_SOURCE /* For asprintf() */
#include "app_utils.h"
#include "app_log.h"
#include "app_gsdml.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "osal_log.h" /* For LOG_LEVEL */
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
#define GET_LOW_BYTE(id) (id & 0xFF)
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring)
{
snprintf (
outputstring,
PNAL_INET_ADDRSTR_SIZE,
"%u.%u.%u.%u",
(uint8_t)((ip >> 24) & 0xFF),
(uint8_t)((ip >> 16) & 0xFF),
(uint8_t)((ip >> 8) & 0xFF),
(uint8_t)(ip & 0xFF));
}
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring)
{
snprintf (
outputstring,
PNAL_ETH_ADDRSTR_SIZE,
"%02X:%02X:%02X:%02X:%02X:%02X",
mac.addr[0],
mac.addr[1],
mac.addr[2],
mac.addr[3],
mac.addr[4],
mac.addr[5]);
}
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction)
{
const char * s = "<error>";
switch (direction)
{
case PNET_DIR_NO_IO:
s = "NO_IO";
break;
case PNET_DIR_INPUT:
s = "INPUT";
break;
case PNET_DIR_OUTPUT:
s = "OUTPUT";
break;
case PNET_DIR_IO:
s = "INPUT_OUTPUT";
break;
}
return s;
}
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs)
{
const char * s = "<error>";
switch (ioxs)
{
case PNET_IOXS_BAD:
s = "IOXS_BAD";
break;
case PNET_IOXS_GOOD:
s = "IOXS_GOOD";
break;
}
return s;
}
void app_utils_get_error_code_strings (
uint16_t err_cls,
uint16_t err_code,
const char ** err_cls_str,
const char ** err_code_str)
{
if (err_cls_str == NULL || err_cls_str == NULL)
{
return;
}
*err_cls_str = "Not decoded";
*err_code_str = "Not decoded";
switch (err_cls)
{
case PNET_ERROR_CODE_1_RTA_ERR_CLS_PROTOCOL:
*err_cls_str = "Real-Time Acyclic Protocol";
switch (err_code)
{
case PNET_ERROR_CODE_2_ABORT_AR_CONSUMER_DHT_EXPIRED:
*err_code_str = "Device missed cyclic data "
"deadline, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_CMI_TIMEOUT:
*err_code_str = "Communication initialization "
"timeout, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_RELEASE_IND_RECEIVED:
*err_code_str = "AR release indication received";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_STATION_NAME_CHANGED:
*err_code_str = "DCP station name changed, "
"device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_RESET_TO_FACTORY:
*err_code_str = "DCP reset to factory or factory "
"reset, device terminated AR";
break;
}
break;
case PNET_ERROR_CODE_1_CTLDINA:
*err_cls_str = "CTLDINA = Name and IP assignment from controller";
switch (err_code)
{
case PNET_ERROR_CODE_2_CTLDINA_ARP_MULTIPLE_IP_ADDRESSES:
*err_code_str = "Multiple users of same IP address";
break;
}
break;
}
}
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip)
{
destination_struct->a = ((ip >> 24) & 0xFF);
destination_struct->b = ((ip >> 16) & 0xFF);
destination_struct->c = ((ip >> 8) & 0xFF);
destination_struct->d = (ip & 0xFF);
}
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command)
{
const char * s = NULL;
switch (control_command)
{
case PNET_CONTROL_COMMAND_PRM_BEGIN:
s = "PRM_BEGIN";
break;
case PNET_CONTROL_COMMAND_PRM_END:
s = "PRM_END";
break;
case PNET_CONTROL_COMMAND_APP_RDY:
s = "APP_RDY";
break;
case PNET_CONTROL_COMMAND_RELEASE:
s = "RELEASE";
break;
default:
s = "<error>";
break;
}
return s;
}
const char * app_utils_event_to_string (pnet_event_values_t event)
{
const char * s = "<error>";
switch (event)
{
case PNET_EVENT_ABORT:
s = "PNET_EVENT_ABORT";
break;
case PNET_EVENT_STARTUP:
s = "PNET_EVENT_STARTUP";
break;
case PNET_EVENT_PRMEND:
s = "PNET_EVENT_PRMEND";
break;
case PNET_EVENT_APPLRDY:
s = "PNET_EVENT_APPLRDY";
break;
case PNET_EVENT_DATA:
s = "PNET_EVENT_DATA";
break;
}
return s;
}
int app_utils_pnet_cfg_init_default (pnet_cfg_t * cfg)
{
memset (cfg, 0, sizeof (pnet_cfg_t));
cfg->tick_us = APP_TICK_INTERVAL_US;
/* Identification & Maintenance */
cfg->im_0_data.im_vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_hardware_revision = APP_GSDML_IM_HARDWARE_REVISION;
cfg->im_0_data.im_sw_revision_prefix = APP_GSDML_SW_REV_PREFIX;
cfg->im_0_data.im_sw_revision_functional_enhancement = PNET_VERSION_MAJOR;
cfg->im_0_data.im_sw_revision_bug_fix = PNET_VERSION_MINOR;
cfg->im_0_data.im_sw_revision_internal_change = PNET_VERSION_PATCH;
cfg->im_0_data.im_revision_counter = APP_GSDML_IM_REVISION_COUNTER;
cfg->im_0_data.im_profile_id = APP_GSDML_PROFILE_ID;
cfg->im_0_data.im_profile_specific_type = APP_GSDML_PROFILE_SPEC_TYPE;
cfg->im_0_data.im_version_major = 1; /** Always 1 */
cfg->im_0_data.im_version_minor = 1; /** Always 1 */
cfg->im_0_data.im_supported = APP_GSDML_IM_SUPPORTED;
snprintf (
cfg->im_0_data.im_order_id,
sizeof (cfg->im_0_data.im_order_id),
"%s",
APP_GSDML_ORDER_ID);
snprintf (
cfg->im_0_data.im_serial_number,
sizeof (cfg->im_0_data.im_serial_number),
"%s",
APP_GSDML_EXAMPLE_SERIAL_NUMBER);
snprintf (
cfg->im_1_data.im_tag_function,
sizeof (cfg->im_1_data.im_tag_function),
"%s",
APP_GSDML_TAG_FUNCTION);
snprintf (
cfg->im_1_data.im_tag_location,
sizeof (cfg->im_1_data.im_tag_location),
"%s",
APP_GSDML_TAG_LOCATION);
snprintf (
cfg->im_2_data.im_date,
sizeof (cfg->im_2_data.im_date),
"%s",
APP_GSDML_IM_DATE);
snprintf (
cfg->im_3_data.im_descriptor,
sizeof (cfg->im_3_data.im_descriptor),
"%s",
APP_GSDML_DESCRIPTOR);
snprintf (
cfg->im_4_data.im_signature,
sizeof (cfg->im_4_data.im_signature),
"%s",
APP_GSDML_SIGNATURE);
/* Device configuration */
cfg->device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_DEVICE_ID);
cfg->device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_DEVICE_ID);
cfg->oem_device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_VENDOR_ID);
cfg->oem_device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_VENDOR_ID);
cfg->oem_device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_DEVICE_ID);
cfg->oem_device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_DEVICE_ID);
snprintf (
cfg->product_name,
sizeof (cfg->product_name),
"%s",
APP_GSDML_PRODUCT_NAME);
cfg->send_hello = true;
/* Timing */
cfg->min_device_interval = APP_GSDML_MIN_DEVICE_INTERVAL;
/* Should be set by application as part of network configuration. */
cfg->num_physical_ports = 1;
snprintf (
cfg->station_name,
sizeof (cfg->station_name),
"%s",
APP_GSDML_DEFAULT_STATION_NAME);
/* Diagnosis mechanism */
/* We prefer using "Extended channel diagnosis" instead of
* "Qualified channel diagnosis" format on the wire,
* as this is better supported by Wireshark.
*/
cfg->use_qualified_diagnosis = false;
return 0;
}
int app_utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
app_utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports)
{
int ret = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t if_index = 0;
uint16_t number_of_given_names = 1;
uint16_t if_list_size = max_port + 1;
char c;
if (max_port == 0)
{
printf ("Error: max_port is 0.\n");
return -1;
}
memset (p_if_list, 0, sizeof (*p_if_list));
c = arg_str[i++];
while (c != '\0')
{
if (c != ',')
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = c;
}
}
else
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = '\0';
j = 0;
if_index++;
}
number_of_given_names++;
}
c = arg_str[i++];
}
if (max_port == 1 && number_of_given_names > 1)
{
printf ("Error: Only 1 network interface expected as max_port is 1.\n");
return -1;
}
if (number_of_given_names == 2)
{
printf ("Error: It is illegal to give 2 interface names. Use 1, or one "
"more than the number of physical interfaces.\n");
return -1;
}
if (number_of_given_names > max_port + 1)
{
printf (
"Error: You have given %u interface names, but max is %u as "
"PNET_MAX_PHYSICAL_PORTS is %u.\n",
number_of_given_names,
max_port + 1,
max_port);
return -1;
}
if (number_of_given_names == 1)
{
if (strlen (p_if_list->netif[0].name) == 0)
{
printf ("Error: Zero length network interface name.\n");
return -1;
}
else
{
p_if_list->netif[1] = p_if_list->netif[0];
*p_num_ports = 1;
}
}
else
{
for (i = 0; i < number_of_given_names; i++)
{
if (strlen (p_if_list->netif[i].name) == 0)
{
printf ("Error: Zero length network interface name (%d).\n", i);
return -1;
}
}
*p_num_ports = number_of_given_names - 1;
}
return ret;
}
int app_utils_pnet_cfg_init_netifs (
const char * netif_list_str,
app_utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg)
{
int ret = 0;
int i = 0;
pnal_ipaddr_t ip;
pnal_ipaddr_t netmask;
pnal_ipaddr_t gateway;
ret = app_utils_get_netif_namelist (
netif_list_str,
PNET_MAX_PHYSICAL_PORTS,
if_list,
number_of_ports);
if (ret != 0)
{
return ret;
}
if_cfg->main_netif_name = if_list->netif[0].name;
for (i = 1; i <= *number_of_ports; i++)
{
if_cfg->physical_ports[i - 1].netif_name = if_list->netif[i].name;
if_cfg->physical_ports[i - 1].default_mau_type =
APP_GSDML_DEFAULT_MAUTYPE;
}
/* Read IP, netmask, gateway from operating system */
ip = pnal_get_ip_address (if_cfg->main_netif_name);
netmask = pnal_get_netmask (if_cfg->main_netif_name);
gateway = pnal_get_gateway (if_cfg->main_netif_name);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_addr, ip);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_gateway, gateway);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_mask, netmask);
return ret;
}
static void app_utils_print_mac_address (const char * netif_name)
{
pnal_ethaddr_t pnal_mac_addr;
if (pnal_get_macaddress (netif_name, &pnal_mac_addr) == 0)
{
APP_LOG_INFO (
"%02X:%02X:%02X:%02X:%02X:%02X\n",
pnal_mac_addr.addr[0],
pnal_mac_addr.addr[1],
pnal_mac_addr.addr[2],
pnal_mac_addr.addr[3],
pnal_mac_addr.addr[4],
pnal_mac_addr.addr[5]);
}
else
{
APP_LOG_ERROR ("Failed read mac address\n");
}
}
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports)
{
uint16_t i;
char hostname_string[PNAL_HOSTNAME_MAX_SIZE]; /* Terminated string */
APP_LOG_INFO ("Management port: %s ", if_cfg->main_netif_name);
app_utils_print_mac_address (if_cfg->main_netif_name);
for (i = 1; i <= number_of_ports; i++)
{
APP_LOG_INFO (
"Physical port [%u]: %s ",
i,
if_cfg->physical_ports[i - 1].netif_name);
app_utils_print_mac_address (if_cfg->physical_ports[i - 1].netif_name);
}
if (pnal_get_hostname (hostname_string) != 0)
{
hostname_string[0] = '\0';
}
APP_LOG_INFO ("Hostname: %s\n", hostname_string);
APP_LOG_INFO (
"IP address: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_addr.a,
if_cfg->ip_cfg.ip_addr.b,
if_cfg->ip_cfg.ip_addr.c,
if_cfg->ip_cfg.ip_addr.d);
APP_LOG_INFO (
"Netmask: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_mask.a,
if_cfg->ip_cfg.ip_mask.b,
if_cfg->ip_cfg.ip_mask.c,
if_cfg->ip_cfg.ip_mask.d);
APP_LOG_INFO (
"Gateway: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_gateway.a,
if_cfg->ip_cfg.ip_gateway.b,
if_cfg->ip_cfg.ip_gateway.c,
if_cfg->ip_cfg.ip_gateway.d);
}
void app_utils_print_ioxs_change (
const app_subslot_t * subslot,
const char * ioxs_str,
uint8_t iocs_current,
uint8_t iocs_new)
{
if (iocs_current != iocs_new)
{
if (iocs_new == PNET_IOXS_BAD)
{
APP_LOG_DEBUG (
"PLC reports %s BAD for slot %u subslot %u \"%s\"\n",
ioxs_str,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
else if (iocs_new == PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
"PLC reports %s GOOD for slot %u subslot %u \"%s\".\n",
ioxs_str,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
else if (iocs_new != PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
"PLC reports %s %u for input slot %u subslot %u \"%s\".\n"
" Is the PLC in STOP mode?\n",
ioxs_str,
iocs_new,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
}
}
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].module_id = id;
p_api->slots[slot_nbr].plugged = true;
p_api->slots[slot_nbr].name = name;
return 0;
}
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].plugged = false;
return 0;
}
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_ident,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL || p_data_cfg == NULL)
{
return NULL;
}
/** Find a free subslot */
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].used == false)
{
app_subslot_t * p_subslot =
&p_api->slots[slot_nbr].subslots[subslot_ix];
p_subslot->used = true;
p_subslot->plugged = true;
p_subslot->slot_nbr = slot_nbr;
p_subslot->subslot_nbr = subslot_nbr;
p_subslot->submodule_name = submodule_name;
p_subslot->submodule_id = submodule_ident;
p_subslot->data_cfg = *p_data_cfg;
p_subslot->cyclic_callback = cyclic_callback;
p_subslot->tag = tag;
p_subslot->indata_iocs = PNET_IOXS_BAD;
p_subslot->outdata_iops = PNET_IOXS_BAD;
return p_subslot;
}
}
return NULL;
}
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
app_subslot_t * p_subslot = NULL;
if (slot_nbr >= PNET_MAX_SUBSLOTS || p_api == NULL)
{
return -1;
}
p_subslot = app_utils_subslot_get (p_api, slot_nbr, subslot_nbr);
if (p_subslot == NULL)
{
return -1;
}
memset (p_subslot, 0, sizeof (app_subslot_t));
p_subslot->used = false;
return 0;
}
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL)
{
return NULL;
}
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].subslot_nbr == subslot_nbr)
{
return &p_api->slots[slot_nbr].subslots[subslot_ix];
}
}
return NULL;
}
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
if (
p_subslot->data_cfg.data_dir == PNET_DIR_INPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO)
{
return true;
}
return false;
}
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
return p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO;
}
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
if (
p_subslot->data_cfg.data_dir == PNET_DIR_OUTPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO)
{
return true;
}
return false;
}
void app_utils_cyclic_data_poll (app_api_t * p_api)
{
uint16_t slot_nbr;
uint16_t subslot_index;
app_subslot_t * p_subslot;
for (slot_nbr = 0; slot_nbr < PNET_MAX_SLOTS; slot_nbr++)
{
for (subslot_index = 0; subslot_index < PNET_MAX_SUBSLOTS;
subslot_index++)
{
p_subslot = &p_api->slots[slot_nbr].subslots[subslot_index];
if (p_subslot->plugged && p_subslot->cyclic_callback != NULL)
{
p_subslot->cyclic_callback (p_subslot, p_subslot->tag);
}
}
}
}