profeth: Initial add

This commit is contained in:
Tobias Waldekranz
2023-03-06 10:50:58 +01:00
committed by Joachim Wiberg
parent 23a25147cc
commit 5a441252f8
20 changed files with 6001 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
*~
*.o
*.la
*.lo
*.so
.deps
.libs
/aclocal.m4
/autom4te.cache/
/aux
/compile
/config.h
/config.h.in
/config.guess
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4
/missing
GPATH
GRTAGS
GTAGS
Makefile
Makefile.in
@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<ISO15745Profile xmlns="http://www.profibus.com/GSDML/2003/11/DeviceProfile" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.profibus.com/GSDML/2003/11/DeviceProfile ..\xsd\GSDML-DeviceProfile-V2.43.xsd">
<ProfileHeader>
<ProfileIdentification>PROFINET Device Profile</ProfileIdentification>
<ProfileRevision>1.00</ProfileRevision>
<ProfileName>Device Profile for PROFINET Devices</ProfileName>
<ProfileSource>PROFIBUS Nutzerorganisation e. V. (PNO)</ProfileSource>
<ProfileClassID>Device</ProfileClassID>
<ISO15745Reference>
<ISO15745Part>4</ISO15745Part>
<ISO15745Edition>1</ISO15745Edition>
<ProfileTechnology>GSDML</ProfileTechnology>
</ISO15745Reference>
</ProfileHeader>
<ProfileBody>
<DeviceIdentity VendorID="0xdead" DeviceID="0x0001">
<InfoText TextId="IDT_DEV_INFO"/>
<VendorName Value="Kernelkit"/>
</DeviceIdentity>
<DeviceFunction>
<Family MainFamily="Network Components" ProductFamily="Infix Switch"/>
</DeviceFunction>
<ApplicationProcess>
<DeviceAccessPointList>
<DeviceAccessPointItem ID="DAP_1" PNIO_Version="V2.43" PhysicalSlots="0" ModuleIdentNumber="0x00000001" MinDeviceInterval="32" DNS_CompatibleName="infix" FixedInSlots="0" ObjectUUID_LocalIndex="1" DeviceAccessSupported="false" MultipleWriteSupported="true" CheckDeviceID_Allowed="true" NameOfStationNotTransferable="false" LLDP_NoD_Supported="true" ResetToFactoryModes="1..2">
<ModuleInfo>
<Name TextId="IDT_DAP_NAME"/>
<InfoText TextId="IDT_DAP_INFO"/>
<VendorName Value="Kernelkit"/>
<OrderNumber Value="1"/>
<HardwareRelease Value="A1"/>
<SoftwareRelease Value="1.0.0"/>
</ModuleInfo>
<CertificationInfo ConformanceClass="B" ApplicationClass="" NetloadClass="I"/>
<SubslotList>
<SubslotItem SubslotNumber="32768" TextId="IDT_SS_IFACE"/>
<SubslotItem SubslotNumber="32769" TextId="IDT_SS_P1"/>
<SubslotItem SubslotNumber="32770" TextId="IDT_SS_P2"/>
<SubslotItem SubslotNumber="32771" TextId="IDT_SS_P3"/>
<SubslotItem SubslotNumber="32772" TextId="IDT_SS_P4"/>
<SubslotItem SubslotNumber="32773" TextId="IDT_SS_P5"/>
<SubslotItem SubslotNumber="32774" TextId="IDT_SS_P6"/>
<SubslotItem SubslotNumber="32775" TextId="IDT_SS_P7"/>
<SubslotItem SubslotNumber="32776" TextId="IDT_SS_P8"/>
<SubslotItem SubslotNumber="32777" TextId="IDT_SS_P9"/>
<SubslotItem SubslotNumber="32778" TextId="IDT_SS_P10"/>
</SubslotList>
<IOConfigData MaxInputLength="244" MaxOutputLength="244"/>
<!-- <IOConfigData MaxInputLength="18" MaxOutputLength="18" MaxDataLength="36"/> -->
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDS_1" SubmoduleIdentNumber="0x00000001" FixedInSubslots="1" PROFIsafeSupported="false" Writeable_IM_Records="1 2 3" MayIssueProcessAlarm="false">
<IOData/>
<ModuleInfo>
<Name TextId="IDT_DAP_NAME"/>
<InfoText TextId="IDT_DAP_INFO"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
<SystemDefinedSubmoduleList>
<InterfaceSubmoduleItem ID="IDS_I" SubmoduleIdentNumber="0x00008000" SubslotNumber="32768" TextId="IDT_IFACE" SupportedRT_Classes="RT_CLASS_1" SupportedProtocols="SNMP;LLDP" NetworkComponentDiagnosisSupported="false" PTP_BoundarySupported="true" DCP_BoundarySupported="true">
<ApplicationRelations StartupMode="Advanced">
<TimingProperties SendClock="32" ReductionRatio="1 2 4 8 16 32 64 128 256 512"/>
</ApplicationRelations>
</InterfaceSubmoduleItem>
<PortSubmoduleItem ID="IDS_P1" SubmoduleIdentNumber="0x00008001" SubslotNumber="32769" TextId="IDT_P1" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P2" SubmoduleIdentNumber="0x00008002" SubslotNumber="32770" TextId="IDT_P2" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P3" SubmoduleIdentNumber="0x00008003" SubslotNumber="32771" TextId="IDT_P3" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P4" SubmoduleIdentNumber="0x00008004" SubslotNumber="32772" TextId="IDT_P4" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P5" SubmoduleIdentNumber="0x00008005" SubslotNumber="32773" TextId="IDT_P5" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P6" SubmoduleIdentNumber="0x00008006" SubslotNumber="32774" TextId="IDT_P6" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P7" SubmoduleIdentNumber="0x00008007" SubslotNumber="32775" TextId="IDT_P7" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P8" SubmoduleIdentNumber="0x00008008" SubslotNumber="32776" TextId="IDT_P8" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P9" SubmoduleIdentNumber="0x00008009" SubslotNumber="32777" TextId="IDT_P9" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P10" SubmoduleIdentNumber="0x0000800a" SubslotNumber="32778" TextId="IDT_P10" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
</SystemDefinedSubmoduleList>
<!-- <Graphics> -->
<!-- <GraphicItemRef Type="DeviceSymbol" GraphicItemTarget="Kernelkit-Infix-Switch"/> -->
<!-- </Graphics> -->
</DeviceAccessPointItem>
</DeviceAccessPointList>
<!-- <GraphicsList> -->
<!-- <GraphicItem ID="Kernelkit-Infix-Image" GraphicFile="GSDML-Kernelkit-Infix-Switch"/> -->
<!-- </GraphicsList> -->
<ExternalTextList>
<PrimaryLanguage>
<Text TextId="IDT_DEV_INFO" Value="Infix 10-port Switch"/>
<Text TextId="IDT_DAP_NAME" Value="Infix 10-port Switch"/>
<Text TextId="IDT_DAP_INFO" Value="Infix 10-port Switch"/>
<Text TextId="IDT_SS_IFACE" Value="X1"/>
<Text TextId="IDT_SS_P1" Value="X1 P1"/>
<Text TextId="IDT_SS_P2" Value="X1 P2"/>
<Text TextId="IDT_SS_P3" Value="X1 P3"/>
<Text TextId="IDT_SS_P4" Value="X1 P4"/>
<Text TextId="IDT_SS_P5" Value="X1 P5"/>
<Text TextId="IDT_SS_P6" Value="X1 P6"/>
<Text TextId="IDT_SS_P7" Value="X1 P7"/>
<Text TextId="IDT_SS_P8" Value="X1 P8"/>
<Text TextId="IDT_SS_P9" Value="X1 P9"/>
<Text TextId="IDT_SS_P10" Value="X1 P10"/>
<Text TextId="IDT_IFACE" Value="PN-IO"/>
<Text TextId="IDT_P1" Value="eth1"/>
<Text TextId="IDT_P2" Value="eth2"/>
<Text TextId="IDT_P3" Value="eth3"/>
<Text TextId="IDT_P4" Value="eth4"/>
<Text TextId="IDT_P5" Value="eth5"/>
<Text TextId="IDT_P6" Value="eth6"/>
<Text TextId="IDT_P7" Value="eth7"/>
<Text TextId="IDT_P8" Value="eth8"/>
<Text TextId="IDT_P9" Value="eth9"/>
<Text TextId="IDT_P10" Value="eth10"/>
</PrimaryLanguage>
</ExternalTextList>
</ApplicationProcess>
</ProfileBody>
</ISO15745Profile>
+3
View File
@@ -0,0 +1,3 @@
SUBDIRS = src
DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
+42
View File
@@ -0,0 +1,42 @@
AC_PREREQ(2.61)
AC_INIT([profeth], [1.0.0],
[https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
LT_INIT
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_PROG_CC
AC_PROG_INSTALL
AC_CHECK_LIB([profinet], [pnet_init],, AC_MSG_ERROR([Profinet not found]))
test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DATAROOTDIR=`eval echo $datarootdir`
DATAROOTDIR=`eval echo $DATAROOTDIR`
AC_SUBST(DATAROOTDIR)
LIBDIR=`eval echo $libdir`
LIBDIR=`eval echo $LIBDIR`
AC_SUBST(LIBDIR)
LOCALSTATEDIR=`eval echo $localstatedir`
LOCALSTATEDIR=`eval echo $LOCALSTATEDIR`
AC_SUBST(LOCALSTATEDIR)
RUNSTATEDIR=`eval echo $runstatedir`
RUNSTATEDIR=`eval echo $RUNSTATEDIR`
AC_SUBST(RUNSTATEDIR)
SYSCONFDIR=`eval echo $sysconfdir`
SYSCONFDIR=`eval echo $SYSCONFDIR`
AC_SUBST(SYSCONFDIR)
AC_OUTPUT
+15
View File
@@ -0,0 +1,15 @@
sbin_PROGRAMS = profeth
profeth_SOURCES = profeth.c \
app_data.c app_data.h \
app_gsdml.c app_gsdml.h \
app_log.c app_log.h \
app_utils.c app_utils.h \
pnal_filetools.c pnal_filetools.h \
pnal.h \
pnal_sys.h \
sampleapp_common.c sampleapp_common.h
profeth_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
profeth_LDADD = -losal -lprofinet
+315
View File
@@ -0,0 +1,315 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
#include "app_data.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define APP_DATA_DEFAULT_OUTPUT_DATA 0
/* Parameter data for digital submodules
* The stored value is shared between all digital submodules in this example.
*
* Todo: Data is always in pnio data format. Add conversion to uint32_t.
*/
static uint32_t app_param_1 = 0; /* Network endianness */
static uint32_t app_param_2 = 0; /* Network endianness */
/* Parameter data for echo submodules
* The stored value is shared between all echo submodules in this example.
*
* Todo: Data is always in pnio data format. Add conversion to uint32_t.
*/
static uint32_t app_param_echo_gain = 1; /* Network endianness */
/* Digital submodule process data
* The stored value is shared between all digital submodules in this example. */
static uint8_t inputdata[APP_GSDML_INPUT_DATA_DIGITAL_SIZE] = {0};
static uint8_t outputdata[APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE] = {0};
static uint8_t counter = 0;
/* Network endianness */
static uint8_t echo_inputdata[APP_GSDML_INPUT_DATA_ECHO_SIZE] = {0};
static uint8_t echo_outputdata[APP_GSDML_OUTPUT_DATA_ECHO_SIZE] = {0};
CC_PACKED_BEGIN
typedef struct CC_PACKED app_echo_data
{
/* Network endianness.
Used as a float, but we model it as a 4-byte integer to easily
do endianness conversion */
uint32_t echo_float_bytes;
/* Network endianness */
uint32_t echo_int;
} app_echo_data_t;
CC_PACKED_END
CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_INPUT_DATA_ECHO_SIZE);
CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_OUTPUT_DATA_ECHO_SIZE);
/**
* Set LED state.
*
* Compares new state with previous state, to minimize system calls.
*
* Uses the hardware specific app_set_led() function.
*
* @param led_state In: New LED state
*/
static void app_handle_data_led_state (bool led_state)
{
static bool previous_led_state = false;
if (led_state != previous_led_state)
{
app_set_led (APP_DATA_LED_ID, led_state);
}
previous_led_state = led_state;
}
uint8_t * app_data_get_input_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
bool button_pressed,
uint16_t * size,
uint8_t * iops)
{
float inputfloat;
float outputfloat;
uint32_t hostorder_inputfloat_bytes;
uint32_t hostorder_outputfloat_bytes;
app_echo_data_t * p_echo_inputdata = (app_echo_data_t *)&echo_inputdata;
app_echo_data_t * p_echo_outputdata = (app_echo_data_t *)&echo_outputdata;
if (size == NULL || iops == NULL)
{
return NULL;
}
if (
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN ||
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
{
/* Prepare digital input data
* Lowest 7 bits: Counter Most significant bit: Button
*/
inputdata[0] = counter++;
if (button_pressed)
{
inputdata[0] |= 0x80;
}
else
{
inputdata[0] &= 0x7F;
}
*size = APP_GSDML_INPUT_DATA_DIGITAL_SIZE;
*iops = PNET_IOXS_GOOD;
return inputdata;
}
if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
{
/* Calculate echodata input (to the PLC)
* by multiplying the output (from the PLC) with a gain factor
*/
/* Integer */
p_echo_inputdata->echo_int = CC_TO_BE32 (
CC_FROM_BE32 (p_echo_outputdata->echo_int) *
CC_FROM_BE32 (app_param_echo_gain));
/* Float */
/* Use memcopy to avoid strict-aliasing rule warnings */
hostorder_outputfloat_bytes =
CC_FROM_BE32 (p_echo_outputdata->echo_float_bytes);
memcpy (&outputfloat, &hostorder_outputfloat_bytes, sizeof (outputfloat));
inputfloat = outputfloat * CC_FROM_BE32 (app_param_echo_gain);
memcpy (&hostorder_inputfloat_bytes, &inputfloat, sizeof (outputfloat));
p_echo_inputdata->echo_float_bytes =
CC_TO_BE32 (hostorder_inputfloat_bytes);
*size = APP_GSDML_INPUT_DATA_ECHO_SIZE;
*iops = PNET_IOXS_GOOD;
return echo_inputdata;
}
/* Automated RT Tester scenario 2 - unsupported (sub)module */
return NULL;
}
int app_data_set_output_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint8_t * data,
uint16_t size)
{
bool led_state;
if (data == NULL)
{
return -1;
}
if (
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_OUT ||
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
{
if (size == APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE)
{
memcpy (outputdata, data, size);
/* Most significant bit: LED */
led_state = (outputdata[0] & 0x80) > 0;
app_handle_data_led_state (led_state);
return 0;
}
}
else if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
{
if (size == APP_GSDML_OUTPUT_DATA_ECHO_SIZE)
{
memcpy (echo_outputdata, data, size);
return 0;
}
}
return -1;
}
int app_data_set_default_outputs (void)
{
outputdata[0] = APP_DATA_DEFAULT_OUTPUT_DATA;
app_handle_data_led_state (false);
return 0;
}
int app_data_write_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
const uint8_t * data,
uint16_t length)
{
const app_gsdml_param_t * par_cfg;
par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
if (par_cfg == NULL)
{
APP_LOG_WARNING (
"PLC write request unsupported submodule/parameter. "
"Submodule id: %u Index: %u\n",
(unsigned)submodule_id,
(unsigned)index);
return -1;
}
if (length != par_cfg->length)
{
APP_LOG_WARNING (
"PLC write request unsupported length. "
"Index: %u Length: %u Expected length: %u\n",
(unsigned)index,
(unsigned)length,
par_cfg->length);
return -1;
}
if (index == APP_GSDML_PARAMETER_1_IDX)
{
memcpy (&app_param_1, data, length);
}
else if (index == APP_GSDML_PARAMETER_2_IDX)
{
memcpy (&app_param_2, data, length);
}
else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
{
memcpy (&app_param_echo_gain, data, length);
}
APP_LOG_DEBUG (" Writing parameter \"%s\"\n", par_cfg->name);
app_log_print_bytes (APP_LOG_LEVEL_DEBUG, data, length);
return 0;
}
int app_data_read_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
uint8_t ** data,
uint16_t * length)
{
const app_gsdml_param_t * par_cfg;
par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
if (par_cfg == NULL)
{
APP_LOG_WARNING (
"PLC read request unsupported submodule/parameter. "
"Submodule id: %u Index: %u\n",
(unsigned)submodule_id,
(unsigned)index);
return -1;
}
if (*length < par_cfg->length)
{
APP_LOG_WARNING (
"PLC read request unsupported length. "
"Index: %u Max length: %u Data length for our parameter: %u\n",
(unsigned)index,
(unsigned)*length,
par_cfg->length);
return -1;
}
APP_LOG_DEBUG (" Reading \"%s\"\n", par_cfg->name);
if (index == APP_GSDML_PARAMETER_1_IDX)
{
*data = (uint8_t *)&app_param_1;
*length = sizeof (app_param_1);
}
else if (index == APP_GSDML_PARAMETER_2_IDX)
{
*data = (uint8_t *)&app_param_2;
*length = sizeof (app_param_2);
}
else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
{
*data = (uint8_t *)&app_param_echo_gain;
*length = sizeof (app_param_echo_gain);
}
app_log_print_bytes (APP_LOG_LEVEL_DEBUG, *data, *length);
return 0;
}
+140
View File
@@ -0,0 +1,140 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 APP_DATA_H
#define APP_DATA_H
/**
* @file
* @brief Sample application data interface
*
* Functions for:
* - Getting input data (Button 1 and counter value)
* - Setting output data (LED 1)
* - Setting default output state. This should be
* part of all device implementations for setting
* defined state when device is not connected to PLC
* - Reading and writing parameters
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
/**
* Get application specific PNIO input data (for sending to PLC)
*
* The main sample application keeps track
* of button so it is a parameter to this function.
*
* This function is not called for the DAP submodules (slot_nbr==0).
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param button_state In: State of button 1
* @param size Out: Size of pnio data.
* Not modified on error.
* @param iops Out: Provider status. If for example
* a sensor is failing or a short
* circuit is detected on digital
* input this shall be set to BAD.
* Not modified on error.
* @return Reference to PNIO data, NULL on error
*/
uint8_t * app_data_get_input_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
bool button_state,
uint16_t * size,
uint8_t * iops);
/**
* Set application specific PNIO output data (received from PLC)
*
* This function is not called for the DAP submodules (slot_nbr==0).
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param data In: Reference to output data
* @param size In: Length of output data
* @return 0 on success, -1 on error
*/
int app_data_set_output_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint8_t * data,
uint16_t size);
/**
* Set default outputs for all subslots.
*
* For the sample application this means that
* LED 1 is turned off.
*
* @return 0 on success, -1 on error
*/
int app_data_set_default_outputs (void);
/**
* Write parameter index for a subslot
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param index In: Parameter index
* @param data In: New parameter value
* @param write_length In: Length of parameter data
* @return 0 on success, -1 on error
*/
int app_data_write_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
const uint8_t * data,
uint16_t write_length);
/**
* Read parameter index from a subslot
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param index In: Parameter index
* @param data In: Reference to parameter data
* @param length InOut: The maximum (in) and actual (out) length in
* bytes of the data.
* @return 0 on success, -1 on error
*/
int app_data_read_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
uint8_t ** data,
uint16_t * length);
#ifdef __cplusplus
}
#endif
#endif /* APP_DATA_H */
+260
View File
@@ -0,0 +1,260 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
#include "sampleapp_common.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/******************* Supported modules ***************************/
static const app_gsdml_module_t dap_1 = {
.id = PNET_MOD_DAP_IDENT,
.name = "DAP 1",
.submodules = {
PNET_SUBMOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
0}};
static const app_gsdml_module_t module_digital_in = {
.id = APP_GSDML_MOD_ID_8_0_DIGITAL_IN,
.name = "DI 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN, 0},
};
static const app_gsdml_module_t module_digital_out = {
.id = APP_GSDML_MOD_ID_0_8_DIGITAL_OUT,
.name = "DO 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT, 0}};
static const app_gsdml_module_t module_digital_in_out = {
.id = APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT,
.name = "DIO 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT, 0}};
static const app_gsdml_module_t module_echo = {
.id = APP_GSDML_MOD_ID_ECHO,
.name = "Echo module",
.submodules = {APP_GSDML_SUBMOD_ID_ECHO, 0}};
/******************* Supported submodules ************************/
static const app_gsdml_submodule_t dap_indentity_1 = {
.name = "DAP Identity 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_interface_1 = {
.name = "DAP Interface 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_1 = {
.name = "DAP Port 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_2 = {
.name = "DAP Port 2",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_3 = {
.name = "DAP Port 3",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_4 = {
.name = "DAP Port 4",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_in = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN,
.name = "Digital Input",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_out = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT,
.name = "Digital Output",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_inout = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT,
.name = "Digital Input/Output",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_IO,
.insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
.outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
.parameters = {APP_GSDML_PARAMETER_1_IDX, APP_GSDML_PARAMETER_2_IDX, 0}};
static const app_gsdml_submodule_t submod_echo = {
.id = APP_GSDML_SUBMOD_ID_ECHO,
.name = "Echo submodule",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_IO,
.insize = APP_GSDML_INPUT_DATA_ECHO_SIZE,
.outsize = APP_GSDML_OUTPUT_DATA_ECHO_SIZE,
.parameters = {APP_GSDML_PARAMETER_ECHO_IDX, 0}};
/** List of supported modules */
static const app_gsdml_module_t * app_gsdml_modules[] = {
&dap_1,
&module_digital_in,
&module_digital_out,
&module_digital_in_out,
&module_echo};
/** List of supported submodules */
static const app_gsdml_submodule_t * app_gsdml_submodules[] = {
&dap_indentity_1,
&dap_interface_1,
&dap_port_1,
&dap_port_2,
&dap_port_3,
&dap_port_4,
&submod_digital_in,
&submod_digital_out,
&submod_digital_inout,
&submod_echo,
};
/* List of supported parameters.
* Note that parameters are submodule attributes.
* This list contain all parameters while each
* submodule list its supported parameters using
* their indexes.
*/
static app_gsdml_param_t app_gsdml_parameters[] = {
{
.index = APP_GSDML_PARAMETER_1_IDX,
.name = "Demo 1",
.length = APP_GSDML_PARAMETER_LENGTH,
},
{
.index = APP_GSDML_PARAMETER_2_IDX,
.name = "Demo 2",
.length = APP_GSDML_PARAMETER_LENGTH,
},
{
.index = APP_GSDML_PARAMETER_ECHO_IDX,
.name = "Echo gain setting",
.length = APP_GSDML_PARAMETER_LENGTH,
}};
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_modules); i++)
{
if (app_gsdml_modules[i]->id == id)
{
return app_gsdml_modules[i];
}
}
return NULL;
}
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_submodules); i++)
{
if (app_gsdml_submodules[i]->id == id)
{
return app_gsdml_submodules[i];
}
}
return NULL;
}
const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
uint32_t submodule_id,
uint32_t index)
{
uint16_t i;
uint16_t j;
const app_gsdml_submodule_t * submodule_cfg =
app_gsdml_get_submodule_cfg (submodule_id);
if (submodule_cfg == NULL)
{
/* Unsupported submodule id */
return NULL;
}
/* Search for parameter index in submodule configuration */
for (i = 0; submodule_cfg->parameters[i] != 0; i++)
{
if (submodule_cfg->parameters[i] == index)
{
/* Find parameter configuration */
for (j = 0; j < NELEMENTS (app_gsdml_parameters); j++)
{
if (app_gsdml_parameters[j].index == index)
{
return &app_gsdml_parameters[j];
}
}
}
}
return NULL;
}
+184
View File
@@ -0,0 +1,184 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 APP_GSDML_H
#define APP_GSDML_H
/**
* @file
* @brief Device properties defined by the GSDML device definition
*
* Functions for getting module, submodule and parameter
* configurations using their ids.
*
* Important:
* Any change in this file may require an update of the GSDML file.
* Note that when the GSDML file is updated it has to be reloaded
* in your Profinet engineering tool. PLC applications may be affected.
*
* Design requires unique submodule IDs and unique parameter indexes.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <pnet_api.h>
#define APP_GSDML_API 0
#define APP_GSDML_DEFAULT_STATION_NAME "rt-labs-dev"
/* GSDML tag: VendorID */
#define APP_GSDML_VENDOR_ID 0x0493
/* GSDML tag: DeviceID */
#define APP_GSDML_DEVICE_ID 0x0002
/* Used in DCP communication */
#define APP_GSDML_OEM_VENDOR_ID 0xcafe
#define APP_GSDML_OEM_DEVICE_ID 0xee02
/* Used in I&M0 */
#define APP_GSDML_IM_HARDWARE_REVISION 3
#define APP_GSDML_IM_VERSION_MAJOR 1
#define APP_GSDML_IM_VERSION_MINOR 2
/* Allowed: 'V', 'R', 'P', 'U', 'T' */
#define APP_GSDML_SW_REV_PREFIX 'V'
#define APP_GSDML_PROFILE_ID 0x1234
#define APP_GSDML_PROFILE_SPEC_TYPE 0x5678
#define APP_GSDML_IM_REVISION_COUNTER 0 /* Typically 0 */
/* Note: You need to read out the actual hardware serial number instead */
#define APP_GSDML_EXAMPLE_SERIAL_NUMBER "007"
/* Initial values. Can be overwritten by PLC */
#define APP_GSDML_TAG_FUNCTION "my function"
#define APP_GSDML_TAG_LOCATION "my location"
#define APP_GSDML_IM_DATE "2022-03-01 10:03"
#define APP_GSDML_DESCRIPTOR "my descriptor"
#define APP_GSDML_SIGNATURE ""
/* GSDML tag: Writeable_IM_Records */
#define APP_GSDML_IM_SUPPORTED \
(PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3)
/* GSDML tag: OrderNumber */
#define APP_GSDML_ORDER_ID "12345 Abcdefghijk"
/* GSDML tag: ModuleInfo / Name */
#define APP_GSDML_PRODUCT_NAME "P-Net Sample Application"
/* GSDML tag: MinDeviceInterval */
#define APP_GSDML_MIN_DEVICE_INTERVAL 32 /* 1 ms */
#define APP_GSDML_DIAG_CUSTOM_USI 0x1234
/* See "Specification for GSDML" 8.26 LogBookEntryItem for allowed values */
#define APP_GSDML_LOGBOOK_ERROR_CODE 0x20 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_DECODE 0x82 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_CODE_1 PNET_ERROR_CODE_1_FSPM
#define APP_GSDML_LOGBOOK_ERROR_CODE_2 0x00 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ENTRY_DETAIL 0xFEE1DEAD /* Manufacturer specific */
#define APP_GSDML_PARAMETER_1_IDX 123
#define APP_GSDML_PARAMETER_2_IDX 124
#define APP_GSDML_PARAMETER_ECHO_IDX 125
/* Use same size for all parameters in example */
#define APP_GSDML_PARAMETER_LENGTH 4
#define APP_GSDML_DEFAULT_MAUTYPE 0x10 /* Copper 100 Mbit/s Full duplex */
typedef struct app_gsdml_module
{
uint32_t id;
/** Module name */
const char * name;
/** Submodule IDs. Variable length, ends with 0. */
uint32_t submodules[];
} app_gsdml_module_t;
typedef struct app_gsdml_submodule
{
uint32_t id;
/** Submodule name */
const char * name;
uint32_t api;
pnet_submodule_dir_t data_dir;
uint16_t insize;
uint16_t outsize;
/** Parameter indexes. See app_gsdml_parameters.
* Variable length, ends with 0. */
uint16_t parameters[];
} app_gsdml_submodule_t;
typedef struct
{
uint32_t index;
const char * name;
uint16_t length;
} app_gsdml_param_t;
#define APP_GSDML_MOD_ID_8_0_DIGITAL_IN 0x00000030
#define APP_GSDML_MOD_ID_0_8_DIGITAL_OUT 0x00000031
#define APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT 0x00000032
#define APP_GSDML_MOD_ID_ECHO 0x00000040
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN 0x00000130
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT 0x00000131
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT 0x00000132
#define APP_GSDML_SUBMOD_ID_ECHO 0x00000140
#define APP_GSDML_INPUT_DATA_DIGITAL_SIZE 1 /* bytes */
#define APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE 1 /* bytes */
#define APP_GSDML_INPUT_DATA_ECHO_SIZE 8 /* bytes */
#define APP_GSDML_OUTPUT_DATA_ECHO_SIZE APP_GSDML_INPUT_DATA_ECHO_SIZE
#define APP_GSDML_ALARM_PAYLOAD_SIZE 1 /* bytes */
/**
* Get module configuration from module ID
* @param module_id In: Module ID
* @return Module configuration, NULL if not found
*/
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t module_id);
/**
* Get submodule module configuration from submodule ID
* @param submodule_id In: Submodule ID
* @return Submodule configuration, NULL if not found
*/
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (
uint32_t submodule_id);
/**
* Get parameter configuration from parameter index
* @param submodule_id In: Submodule ID
* @param index In: Parameters index
* @return Parameter configuration, NULL if not found
*/
const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
uint32_t submodule_id,
uint32_t index);
#ifdef __cplusplus
}
#endif
#endif /* APP_GSDML_H */
+52
View File
@@ -0,0 +1,52 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
#include "app_log.h"
#include <stdarg.h>
#include <stdio.h>
static int32_t log_level = APP_DEFAULT_LOG_LEVEL;
void app_log_set_log_level (int32_t level)
{
log_level = level;
}
void app_log (int32_t level, const char * fmt, ...)
{
va_list list;
if (level >= log_level)
{
va_start (list, fmt);
vprintf (fmt, list);
va_end (list);
fflush (stdout);
}
}
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t len)
{
if (level >= log_level)
{
printf (" Bytes: ");
for (uint32_t i = 0; i < len; i++)
{
printf ("%02X ", bytes[i]);
}
printf ("\n");
}
}
+75
View File
@@ -0,0 +1,75 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 APP_LOG_H
#define APP_LOG_H
/**
* @file
* @brief Application debug log utility
*
* Runtime configurable debug log using printf()
* Levels matches levels used in P-Net.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define APP_LOG_LEVEL_DEBUG 0x00
#define APP_LOG_LEVEL_INFO 0x01
#define APP_LOG_LEVEL_WARNING 0x02
#define APP_LOG_LEVEL_ERROR 0x03
#define APP_LOG_LEVEL_FATAL 0x04
#define APP_DEFAULT_LOG_LEVEL APP_LOG_LEVEL_FATAL
#define APP_LOG(level, ...) app_log (level, __VA_ARGS__)
#define APP_LOG_DEBUG(...) APP_LOG (APP_LOG_LEVEL_DEBUG, __VA_ARGS__)
#define APP_LOG_INFO(...) APP_LOG (APP_LOG_LEVEL_INFO, __VA_ARGS__)
#define APP_LOG_WARNING(...) APP_LOG (APP_LOG_LEVEL_WARNING, __VA_ARGS__)
#define APP_LOG_ERROR(...) APP_LOG (APP_LOG_LEVEL_ERROR, __VA_ARGS__)
#define APP_LOG_FATAL(...) APP_LOG (APP_LOG_LEVEL_FATAL, __VA_ARGS__)
/**
* Print log message depending on level
* Use the APP_LOG_xxxxx macros instead of this function.
* @param level In: Message log level
* @param fmt In: Log message format string
*/
void app_log (int32_t level, const char * fmt, ...);
/**
* Log an array of bytes
* @param level In: Log level
* @param bytes In: Array of bytes
* @param length In: Length of array
*/
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t length);
/**
* Set log level
* @param level In: Log level
*/
void app_log_set_log_level (int32_t level);
#ifdef __cplusplus
}
#endif
#endif /* APP_LOG_H */
+865
View File
@@ -0,0 +1,865 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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)
int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar)
{
uint16_t i;
CC_ASSERT (api != NULL);
CC_ASSERT (ar != NULL);
for (i = 0; i < PNET_MAX_AR; ++i)
{
if (api->ar[i].arep == UINT32_MAX)
{
api->ar[i].arep = arep;
api->ar[i].events = 0;
*ar = api->ar + i;
return 1;
}
}
return 0;
}
uint32_t app_ar_arep (app_ar_t * ar)
{
CC_ASSERT (ar != NULL);
return ar->arep;
}
void app_ar_event_set (app_ar_t * ar, uint32_t event)
{
CC_ASSERT (ar != NULL);
ar->events |= event;
}
int app_ar_event_clr (app_ar_t * ar, uint32_t event)
{
CC_ASSERT (ar != NULL);
if (ar->events & event)
{
ar->events &= ~event;
return 1;
}
return 0;
}
void app_ar_iterator_init (
app_ar_iterator_t * iterator,
app_api_t * api)
{
CC_ASSERT (iterator != NULL);
CC_ASSERT (api != NULL);
iterator->ar = api->ar;
iterator->index = -1;
iterator->modified = false;
}
int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar)
{
#if PNET_MAX_AR > 1
uint16_t i;
uint16_t j;
#endif
CC_ASSERT (iterator != NULL);
CC_ASSERT (ar != NULL);
++iterator->index;
if (
(iterator->index < PNET_MAX_AR) &&
(iterator->ar[iterator->index].arep != UINT32_MAX))
{
*ar = iterator->ar + iterator->index;
return 1;
}
#if PNET_MAX_AR > 1
else if (iterator->modified)
{
for (i = 0; i < PNET_MAX_AR; ++i)
{
if (iterator->ar[i].arep == UINT32_MAX)
{
for (j = i + 1; j < PNET_MAX_AR; ++j)
{
if (iterator->ar[j].arep != UINT32_MAX)
{
iterator->ar[i] = iterator->ar[j];
iterator->ar[j].arep = UINT32_MAX;
break;
}
}
if (j >= PNET_MAX_AR - 1)
{
break;
}
}
}
}
#endif
return 0;
}
int app_ar_iterator_done (app_ar_iterator_t * iterator)
{
CC_ASSERT (iterator != NULL);
if (
(iterator->index < 0) ||
((iterator->index < PNET_MAX_AR) &&
(iterator->ar[iterator->index].arep != UINT32_MAX)))
{
return 0;
}
return 1;
}
void app_ar_iterator_delete_current (app_ar_iterator_t * iterator)
{
CC_ASSERT (iterator != NULL);
CC_ASSERT (iterator->index >= 0);
if (iterator->index < PNET_MAX_AR)
{
iterator->ar[iterator->index].arep = UINT32_MAX;
iterator->modified = true;
}
}
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_AR_RPC_READ_ERROR:
*err_code_str = "AR RPC-Read error";
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 ioxs_current,
uint8_t ioxs_new)
{
if (ioxs_current != ioxs_new)
{
if (ioxs_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 (ioxs_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 (ioxs_new != PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
"PLC reports %s %u for slot %u subslot %u \"%s\".\n"
" Is the PLC in STOP mode?\n",
ioxs_str,
ioxs_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);
}
}
}
}
+519
View File
@@ -0,0 +1,519 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 APP_UTILS_H
#define APP_UTILS_H
/**
* @file
* @brief Application utilities and helper functions
*
* Functions for getting string representation of
* P-Net events, error codes and more.
*
* API, slot and subslot administration.
*
* Initialization of P-Net configuration from app_gsdml.h.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
typedef struct app_utils_netif_name
{
char name[PNET_INTERFACE_NAME_MAX_SIZE];
} app_utils_netif_name_t;
typedef struct app_utils_netif_namelist
{
app_utils_netif_name_t netif[PNET_MAX_PHYSICAL_PORTS + 1];
} app_utils_netif_namelist_t;
/* Forward declaration */
typedef struct app_subslot app_subslot_t;
/**
* Callback for updated cyclic data
*
* @param subslot InOut: Subslot structure
* @param tag InOut: Typically a handle to a submodule
*/
typedef void (*app_utils_cyclic_callback) (app_subslot_t * subslot, void * tag);
/**
* Information of submodule plugged into a subslot.
*
* Note that submodule data is not stored here but must
* be handled by the submodule implementation.
*
* All parameters are initialized by the app_utils_plug_submodule()
* function.
*
* The cyclic_callback is used when app_utils_cyclic_data_poll()
* is called. Typically on the tick event in the main task.
* The \a tag parameter is passed with the cyclic_callback and
* is typically a handle to a submodule on application.
*/
typedef struct app_subslot
{
/** True when the position in the subslot array is occupied */
bool used;
/** True when the subslot is plugged */
bool plugged;
uint16_t slot_nbr;
uint16_t subslot_nbr;
uint32_t submodule_id;
const char * submodule_name;
pnet_data_cfg_t data_cfg;
/** Status indicator from PLC */
uint8_t indata_iocs;
/** Status indicator from PLC */
uint8_t outdata_iops;
/** Callback for cyclic input- or output data, or NULL if not implemented */
app_utils_cyclic_callback cyclic_callback;
void * tag;
} app_subslot_t;
/**
* Information of module plugged into a slot,
* and array of subslots for admin of submodules.
*/
typedef struct app_slot
{
bool plugged;
uint32_t module_id;
const char * name; /** Module name */
/** Subslots. Use a separate index, as the subslot number might be large.
* For example the subslot for DAP port 1 has number 0x8001 */
app_subslot_t subslots[PNET_MAX_SUBSLOTS];
} app_slot_t;
/**
* Information relating to an application relation.
*/
typedef struct app_ar
{
uint32_t arep;
uint32_t events;
} app_ar_t;
/**
* AR list iterator state.
*/
typedef struct app_ar_iterator
{
app_ar_t *ar;
int16_t index;
bool modified;
} app_ar_iterator_t;
/**
* Profinet API state for application
*
* Used to manage plugged modules into slots (and submodules into subslots).
*/
typedef struct app_api_t
{
uint32_t api_id;
/**
* Active AR:s.
* A list which is terminated by an entry with arep == UINT32_MAX.
*/
app_ar_t ar[PNET_MAX_AR];
/** Slots. Use slot number as index */
app_slot_t slots[PNET_MAX_SLOTS];
} app_api_t;
/**
* Add an arep to the AR list of an API.
* @param api InOut: The \a app_api_t instance.
* @param arep In: The arep to add.
* @param ar Out: The AR entry, if successful.
*
* @return 0 if the arep could not be added.
* 1 if the arep was added.
*/
int app_ar_add_arep (app_api_t * api, uint32_t arep, app_ar_t ** ar);
/**
* Get the arep of an AR.
* @param ar In: The AR.
* @return the arep of the AR.
*/
uint32_t app_ar_arep (app_ar_t * ar);
/**
* Clear an event from the events value of the AR.
* @param ar InOut: The AR to modify.
* @param event In: The event(s) (bitmask) that should be cleared.
* @return 0 if none of the set bits in \a event were set for the AR.
* 1 if at least one of the set bits in \a event were set for the AR.
*/
int app_ar_event_clr (app_ar_t * ar, uint32_t event);
/**
* Set an event from the events value of the AR.
* @param ar InOut: The AR to modify.
* @param event In: The event(s) (bitmask) that should be set.
*/
void app_ar_event_set (app_ar_t * ar, uint32_t event);
/**
* Initialize a list iterator for the AR list of an API.
* @param iterator InOut: The iterator to be initialized.
* @param api In: The \a app_api_t instance.
*/
void app_ar_iterator_init (
app_ar_iterator_t * iterator,
app_api_t * api);
/**
* Get the next AR from a list iterator.
* @param iterator InOut: The iterator to use.
* @param ar Out: If the return value is 1, the next AR from
* the list.
* @return 0 if there were no more items in the list.
* 1 if there was another item in the list.
*/
int app_ar_iterator_next (app_ar_iterator_t * iterator, app_ar_t ** ar);
/**
* Check whether the iterator has run to the end of entries.
* @param iterator InOut: The iterator.
* @return 0 if the iterator is finished.
* 1 if the iterator is finished.
*/
int app_ar_iterator_done (app_ar_iterator_t * iterator);
/**
* Delete the current AR entry of the list iterator.
* @param ar InOut: The AR.
*/
void app_ar_iterator_delete_current (app_ar_iterator_t * iterator);
/**
* Convert IP address to string
* @param ip In: IP address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_INET_ADDRSTR_SIZE.
*/
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring);
/**
* Get string description of data direction
* @param direction In: Submodule data direction
* @return String representation of data direction
*/
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction);
/**
* Get string description of PNIO producer or consumer status
* @param ioxs In: Producer or consumer status (IOPS/IOCS)
* @return String representation of ioxs (IOPS/IOCS)
*/
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs);
/**
* Convert MAC address to string
* @param mac In: MAC address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_ETH_ADDRSTR_SIZE.
*/
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring);
/**
* Convert error code to string format
* Only common error codes supported.
* Todo: Add rest of error codes.
*
* @param err_cls In: The error class. See PNET_ERROR_CODE_1_*
* @param err_code In: The error code. See PNET_ERROR_CODE_2_*
* @param err_cls_str Out: The error class string
* @param err_code_str Out: The error code string
*/
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);
/**
* Copy an IP address (as an integer) to a struct
* @param destination_struct Out: Destination
* @param ip In: IP address
*/
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip);
/**
* Return a string representation of
* the given dcontrol command.
* @param event In: control_command
* @return A string representing the command
*/
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command);
/**
* Return a string representation of the given event.
* @param event In: event
* @return A string representing the event
*/
const char * app_utils_event_to_string (pnet_event_values_t event);
/**
* Update network configuration from a string
* defining a list of network interfaces examples:
* "eth0" or "br0,eth0,eth1"
*
* Read IP, netmask etc from operating system.
*
* @param netif_list_str In: Comma separated string of network ifs
* @param if_list Out: Array of network ifs
* @param number_of_ports Out: Number of ports
* @param if_cfg Out: P-Net network configuration to be updated
* @return 0 on success, -1 on error
*/
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);
/**
* Parse a comma separated list of network interfaces and check
* that the number of interfaces match the PNET_MAX_PHYSICAL_PORTS
* configuration.
*
* For a single Ethernet interface, the \a arg_str should consist of
* one name. For two Ethernet interfaces, the \a arg_str should consist of
* three names, as we also need a bridge interface.
*
* Does only consider the number of comma separated names. No check of the
* names themselves are done.
*
* Examples:
* arg_str num_ports
* "eth0" 1
* "eth0,eth1" error (We need a bridge as well)
* "br0,eth0,eth1" 2
*
* @param arg_str In: Network interface list as comma separated,
* terminated string. For example "eth0" or
* "br0,eth0,eth1".
* @param max_port In: PNET_MAX_PHYSICAL_PORTS, passed as argument to
* allow test.
* @param p_if_list Out: List of network interfaces
* @param p_num_ports Out: Resulting number of physical ports
* @return 0 on success
* -1 on error
*/
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);
/**
* Print network configuration using APP_LOG_INFO().
*
* @param if_cfg In: Network configuration
* @param number_of_ports In: Number of used ports
*/
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports);
/**
* Print message if IOXS has changed.
*
* Uses APP_LOG_INFO()
*
* @param subslot In: Subslot
* @param ioxs_str In: String description Producer or Consumer
* @param ioxs_current In: Current status
* @param ioxs_new In: New status
*/
void app_utils_print_ioxs_change (
const app_subslot_t * subslot,
const char * ioxs_str,
uint8_t ioxs_current,
uint8_t ioxs_new);
/**
* Init the p-net configuration to default values.
*
* Most values are picked from app_gsdml.h
*
* Network configuration not initialized.
* This means that \a '.if_cfg' must be set by application.
*
* Use this function to init P-Net configuration before
* before passing config to app_init().
*
* @param pnet_cfg Out: Configuration for use by p-net
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int app_utils_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Plug application module
*
* This is for the application to remember which slots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param id In: Module identity
* @param name In: Module name
* @return 0 on success, -1 on error
*/
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name);
/**
* Pull any application module in given slot.
*
* This is for the application to remember which slots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @return 0 on success, -1 on error
*/
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr);
/**
* Plug application submodule.
*
* This is for the application to remember which subslots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule identity
* @param p_data_cfg In: Data configuration,
* direction, in and out sizes
* @param submodule_name In: Submodule name
* @param cyclic_callback In: Submodule data callback
* @param tag In: Tag passed in cyclic callback
* Typically application or
* submodule handle
* @return Reference to allocated subslot,
* NULL if no free subslot is available. This should
* never happen if application is aligned with p-net state.
*/
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag);
/**
* Unplug any application submodule from given subslot.
*
* This is for the application to remember which subslots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @return 0 on success, -1 on error.
*/
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Trigger data callback for all plugged submodules in all slots.
*
* The callbacks given in \a app_utils_plug_submodule() are used.
*
* @param p_api In: API
*/
void app_utils_cyclic_data_poll (app_api_t * p_api);
/**
* Get subslot application information.
*
* @param p_appdata InOut: Application state.
* @param slot_nbr In: Slot number.
* @param subslot_nbr In: Subslot number. Range 0 - 0x9FFF.
* @return Reference to application subslot,
* NULL if subslot is not found/plugged.
*/
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Return true if subslot is input.
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot);
/**
* Return true if subslot is neither input or output.
*
* This is applies for DAP submodules/slots
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot);
/**
* Return true if subslot is output.
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is output or input/output,
* false if not.
*/
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot);
#ifdef __cplusplus
}
#endif
#endif /* APP_UTILS_H */
+554
View File
@@ -0,0 +1,554 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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.
********************************************************************/
/**
* @file
* @brief Definitions for profinet abstraction layer.
*
* The Ethernet-related functions should not use \a loc_port_num, but
* the interface name instead. The lookup from \a loc_port_num to
* interface name must happen somewhere else in the stack.
*/
#ifndef PNAL_H
#define PNAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "options.h"
#include "pnet_api.h"
#include "pnal_sys.h"
#define PNAL_MAKEU32(a, b, c, d) \
(((uint32_t)((a)&0xff) << 24) | ((uint32_t)((b)&0xff) << 16) | \
((uint32_t)((c)&0xff) << 8) | (uint32_t)((d)&0xff))
#define PNAL_MAKEU16(a, b) (((uint16_t)((a)&0xff) << 8) | (uint16_t)((b)&0xff))
#define PNAL_INET_ADDRSTR_SIZE 16 /** Incl termination */
#define PNAL_ETH_ADDRSTR_SIZE 18 /** Incl termination */
#ifndef PNAL_HOSTNAME_MAX_SIZE
#define PNAL_HOSTNAME_MAX_SIZE 64 /** Incl termination. Value from Linux. */
#endif
/**
* Ethernet autonegotiation capabilities (not exhaustive)
*
* See IEEE 802.3 (Ethernet) ch. 30.6 "Management for link Auto-Negotiation".
*
* See IETF RFC 4836 "Definitions of Managed Objects for
* IEEE 802.3 Medium Attachment Units (MAUs)", object
* "IANAifMauAutoNegCapBits"
*/
#define PNAL_ETH_AUTONEG_CAP_1000BaseT_FULL_DUPLEX BIT (0)
#define PNAL_ETH_AUTONEG_CAP_1000BaseT_HALF_DUPLEX BIT (1)
#define PNAL_ETH_AUTONEG_CAP_1000BaseX_FULL_DUPLEX BIT (2)
#define PNAL_ETH_AUTONEG_CAP_1000BaseX_HALF_DUPLEX BIT (3)
#define PNAL_ETH_AUTONEG_CAP_100BaseTX_FULL_DUPLEX BIT (10)
#define PNAL_ETH_AUTONEG_CAP_100BaseTX_HALF_DUPLEX BIT (11)
#define PNAL_ETH_AUTONEG_CAP_10BaseT_FULL_DUPLEX BIT (13)
#define PNAL_ETH_AUTONEG_CAP_10BaseT_HALF_DUPLEX BIT (14)
#define PNAL_ETH_AUTONEG_CAP_UNKNOWN BIT (15)
/**
* Ethernet MAU type (not exhaustive).
*
* See IEEE 802.3 (Ethernet) ch. 30.5 "Layer management for medium
* attachment units (MAUs)".
*
* See IETF RFC 4836 "Definitions of Managed Objects for
* IEEE 802.3 Medium Attachment Units (MAUs)", objects
* "IANAifMauTypeListBits" and "dot3MauType".
*
* See Profinet "5.2.12.3.12 Coding of the field MAUType"
*/
typedef enum pnal_eth_mau
{
PNAL_ETH_MAU_UNKNOWN = 0x00, /* When link is down */
PNAL_ETH_MAU_RADIO = 0x00, /* When link is up */
PNAL_ETH_MAU_COPPER_10BaseT = 0x05,
PNAL_ETH_MAU_COPPER_100BaseTX_HALF_DUPLEX = 0x0F,
PNAL_ETH_MAU_COPPER_100BaseTX_FULL_DUPLEX = 0x10,
PNAL_ETH_MAU_COPPER_1000BaseT_HALF_DUPLEX = 0x1D,
PNAL_ETH_MAU_COPPER_1000BaseT_FULL_DUPLEX = 0x1E,
PNAL_ETH_MAU_FIBER_100BaseFX_HALF_DUPLEX = 0x11,
PNAL_ETH_MAU_FIBER_100BaseFX_FULL_DUPLEX = 0x12,
PNAL_ETH_MAU_FIBER_1000BaseX_HALF_DUPLEX = 0x15,
PNAL_ETH_MAU_FIBER_1000BaseX_FULL_DUPLEX = 0x16,
} pnal_eth_mau_t;
/* See Profinet 2.4 section 5.2.28 and annex W */
typedef struct pnal_port_stats
{
uint32_t if_in_octets;
uint32_t if_out_octets;
uint32_t if_in_discards;
uint32_t if_out_discards;
uint32_t if_in_errors;
uint32_t if_out_errors;
} pnal_port_stats_t;
/** Set an IP address given by the four byte-parts */
#define PNAL_IP4_ADDR_TO_U32(ipaddr, a, b, c, d) \
ipaddr = PNAL_MAKEU32 (a, b, c, d)
typedef enum pnal_ethertype
{
PNAL_ETHTYPE_IP = 0x0800U,
PNAL_ETHTYPE_ARP = 0x0806U,
PNAL_ETHTYPE_VLAN = 0x8100U,
PNAL_ETHTYPE_PROFINET = 0x8892U,
PNAL_ETHTYPE_ETHERCAT = 0x88A4U,
PNAL_ETHTYPE_LLDP = 0x88CCU,
PNAL_ETHTYPE_ALL = 0xFFFFU,
} pnal_ethertype_t;
/* 255.255.255.255 */
#ifndef PNAL_IPADDR_NONE
#define PNAL_IPADDR_NONE ((uint32_t)0xffffffffUL)
#endif
/* 127.0.0.1 */
#ifndef PNAL_IPADDR_LOOPBACK
#define PNAL_IPADDR_LOOPBACK ((uint32_t)0x7f000001UL)
#endif
/* 0.0.0.0 */
#ifndef PNAL_IPADDR_ANY
#define PNAL_IPADDR_ANY ((uint32_t)0x00000000UL)
#endif
#define PNAL_IPADDR_INVALID PNAL_IPADDR_ANY
/* 255.255.255.255 */
#ifndef PNAL_IPADDR_BROADCAST
#define PNAL_IPADDR_BROADCAST ((uint32_t)0xffffffffUL)
#endif
typedef uint32_t pnal_ipaddr_t;
typedef uint16_t pnal_ipport_t;
/**
* The Ethernet MAC address.
*
* TODO: Remove this and use pnet_ethaddr_t instead
*/
typedef struct pnal_ethaddr
{
uint8_t addr[6];
} pnal_ethaddr_t;
/**
* Ethernet link status.
*
* See also type pf_lldp_link_status_t.
*/
typedef struct pnal_eth_status_t
{
/* Is autonegotiation supported on this port? */
bool is_autonegotiation_supported;
/* Is autonegotiation supported on this port? */
bool is_autonegotiation_enabled;
/* Capabilities advertised to link partner during autonegotiation.
*
* See macros PNAL_ETH_AUTONEG_CAP_xxx.
*/
uint16_t autonegotiation_advertised_capabilities;
/* Operational MAU type.
*
* This specifies both the physical medium as well as
* speed and duplex setting used for the link.
*/
pnal_eth_mau_t operational_mau_type;
/* Corresponds to linux "RUNNING" flag.
* Up and cable connected
*/
bool running;
} pnal_eth_status_t;
/**
* Get system uptime from the SNMP implementation.
*
* This is the sysUpTime, as used by SNMP:
* "The time (in hundredths of a second) since the network
* management portion of the system was last re-initialized."
* - IETF RFC 3418 (SNMP MIB-2).
*
* Typically used to store the SNMP-timestamp for incoming LLDP-packets.
* This timestamp is later reported back to the SNMP implementation, and
* remote SNMP managers might use it for filtering.
*
* The returned value should be the one used by the SNMP implementation.
* Ask the SNMP implementation for the value, or make sure that this
* function and the SNMP implementation use the same mechanism to get the
* value.
*
* Starts at 0, with wrap-around after ~497 days.
*
* @return System uptime, in units of 10 milliseconds.
*/
uint32_t pnal_get_system_uptime_10ms (void);
/**
* Load a binary file.
*
* Can load the data into two buffers.
*
* @param fullpath In: Full path to the file
* @param object_1 Out: Data to load, or NULL. Mandatory if size_1 > 0
* @param size_1 In: Size of object_1.
* @param object_2 Out: Data to load, or NULL. Mandatory if size_2 > 0
* @param size_2 In: Size of object_2.
* @return 0 if the operation succeeded.
* -1 if not found or an error occurred.
*/
int pnal_load_file (
const char * fullpath,
void * object_1,
size_t size_1,
void * object_2,
size_t size_2);
/**
* Save a binary file.
*
* Can handle two output buffers.
*
* @param fullpath In: Full path to the file
* @param object_1 In: Data to save, or NULL. Mandatory if size_1 > 0
* @param size_1 In: Size of object_1.
* @param object_2 In: Data to save, or NULL. Mandatory if size_2 > 0
* @param size_2 In: Size of object_2.
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_save_file (
const char * fullpath,
const void * object_1,
size_t size_1,
const void * object_2,
size_t size_2);
/**
* Clear a binary file.
*
* @param fullpath In: Full path to the file
*/
void pnal_clear_file (const char * fullpath);
/*
*/
/**
* Allocate a buffer
*
* The resulting \a pnal_buf_t buffer is defined for
* each operating system, and should at least contain:
* void * payload;
* uint16_t len;
*
* @param length In: Length, in bytes
* @return a pnal_buf_t, or NULL at failure
*/
pnal_buf_t * pnal_buf_alloc (uint16_t length);
/**
* Free a buffer
*
* @param p In: Buffer to free
*/
void pnal_buf_free (pnal_buf_t * p);
/** Not yet used */
uint8_t pnal_buf_header (pnal_buf_t * p, int16_t header_size_increment);
/**
* Network interface handle, forward declaration.
*/
typedef struct pnal_eth_handle pnal_eth_handle_t;
/**
* The prototype of raw Ethernet reception call-back functions.
*
* @param eth_handle InOut: Network interface handle
* @param arg InOut: User-defined (may be NULL).
* @param p_buf InOut: The incoming Ethernet frame
*
* @return 0 If the frame was NOT handled by this function.
* 1 If the frame was handled and the buffer freed.
*/
typedef int (pnal_eth_callback_t) (
pnal_eth_handle_t * eth_handle,
void * arg,
pnal_buf_t * p_buf);
/**
* Get status of Ethernet link on specified port
*
* @param interface_name In: Ethernet interface name, for example eth0
* @param status Out: Returned link status (autoneg etc)
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_eth_get_status (
const char * interface_name,
pnal_eth_status_t * status);
/**
* Get network interface index
*
* The interface index (ifIndex) is the (row) index for the network interface
* in the table ifTable, which is part of the SNMP MIB-II data structure.
* See RFC 2863 "The Interfaces Group MIB".
*
* @param interface_name In: Ethernet interface name, for example eth0
*
* @return The interface index, or 0 if not available.
*/
int pnal_get_interface_index (const char * interface_name);
/**
* Get network interface (port) statistics
*
* In Profinet naming a physical Ethernet interface is a "port".
* This corresponds to for example a Linux Ethernet interface.
*
* @param interface_name In: Ethernet interface name for example eth0
* @param port_stats Out: Returned statistics
*
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_get_port_statistics (
const char * interface_name,
pnal_port_stats_t * port_stats);
/**
* Send raw Ethernet data
*
* @param handle In: Ethernet handle
* @param buf In: Buffer with data to be sent
* @return The number of bytes sent, or -1 if an error occurred.
*/
int pnal_eth_send (pnal_eth_handle_t * handle, pnal_buf_t * buf);
/**
* Initialize receiving of raw Ethernet frames on one interface (in separate
* thread)
*
* @param if_name In: Ethernet interface name
* @param receive_type In: Ethernet frame types that shall be received
* by the network interface / port.
* @param pnal_cfg In: Operating system dependent configuration
* @param callback In: Callback for received raw Ethernet frames
* @param arg InOut: User argument passed to the callback
*
* @return the Ethernet handle, or NULL if an error occurred.
*/
pnal_eth_handle_t * pnal_eth_init (
const char * if_name,
pnal_ethertype_t receive_type,
const pnal_cfg_t * pnal_cfg,
pnal_eth_callback_t * callback,
void * arg);
/**
* Open an UDP socket
*
* @param addr In: IP address to listen to. Typically used with
* PNAL_IPADDR_ANY.
* @param port In: UDP port to listen to.
* @return Socket ID, or -1 if an error occurred.
*/
int pnal_udp_open (pnal_ipaddr_t addr, pnal_ipport_t port);
/**
* Send UDP data
*
* @param id In: Socket ID
* @param dst_addr In: Destination IP address
* @param dst_port In: Destination UDP port
* @param data In: Data to be sent
* @param size In: Size of data
* @return The number of bytes sent, or -1 if an error occurred.
*/
int pnal_udp_sendto (
uint32_t id,
pnal_ipaddr_t dst_addr,
pnal_ipport_t dst_port,
const uint8_t * data,
int size);
/**
* Receive UDP data.
*
* This is a nonblocking function, and it
* returns 0 immediately if no data is available.
*
* @param id In: Socket ID
* @param src_addr Out: Source IP address
* @param src_port Out: Source UDP port
* @param data Out: Received data
* @param size In: Size of buffer for received data
* @return The number of bytes received, or -1 if an error occurred.
*/
int pnal_udp_recvfrom (
uint32_t id,
pnal_ipaddr_t * src_addr,
pnal_ipport_t * src_port,
uint8_t * data,
int size);
/**
* Close an UDP socket
*
* @param id In: Socket ID
*/
void pnal_udp_close (uint32_t id);
/**
* Configure SNMP server.
*
* This function configures a platform-specific SNMP server as to
* enable a connected SNMP client to read variables from the p-net stack,
* as well as to write some variables to it.
*
* @param net InOut: The p-net stack instance
* @param pnal_cfg In: Operating system dependent configuration
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_snmp_init (pnet_t * net, const pnal_cfg_t * pnal_cfg);
/**
* Get network parameters (IP address, netmask etc)
*
* For example:
*
* IP address Represented by
* 1.0.0.0 0x01000000 = 16777216
* 0.0.0.1 0x00000001 = 1
*
* @param interface_name In: Ethernet interface name, for example eth0
* @param p_ipaddr Out: IPv4 address
* @param p_netmask Out: Netmask
* @param p_gw Out: Default gateway
* @param hostname Out: Host name, for example my_laptop_4. Existing
* buffer should have size
* PNAL_HOSTNAME_MAX_SIZE.
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_get_ip_suite (
const char * interface_name,
pnal_ipaddr_t * p_ipaddr,
pnal_ipaddr_t * p_netmask,
pnal_ipaddr_t * p_gw,
char * hostname);
/**
* Read the IP address as an integer. For IPv4.
*
* For example:
*
* IP address Represented by
* 1.0.0.0 0x01000000 = 16777216
* 0.0.0.1 0x00000001 = 1
*
* @param interface_name In: Name of network interface
* @return IP address on success and
* 0 if an error occurred
*/
pnal_ipaddr_t pnal_get_ip_address (const char * interface_name);
/**
* Read the netmask as an integer. For IPv4.
*
* @param interface_name In: Name of network interface
* @return netmask
*/
pnal_ipaddr_t pnal_get_netmask (const char * interface_name);
/**
* Read the default gateway address as an integer. For IPv4.
*
* Assumes the default gateway is found on .1 on same subnet as the IP address.
*
* @param interface_name In: Name of network interface
* @return netmask
*/
pnal_ipaddr_t pnal_get_gateway (const char * interface_name);
/**
* Read the MAC address.
*
* @param interface_name In: Name of network interface
* @param p_mac Out: MAC address
*
* @return 0 on success and
* -1 if no such interface is available
*/
int pnal_get_macaddress (const char * interface_name, pnal_ethaddr_t * p_mac);
/**
* Read the current host name
*
* The resulting string is null terminated.
*
* @param hostname Out: Host name, for example my_laptop_4. Existing
* buffer should have size
* PNAL_HOSTNAME_MAX_SIZE.
* @return 0 on success and
* -1 if an error occurred
*/
int pnal_get_hostname (char * hostname);
/**
* Set network parameters (IP address, netmask etc)
*
* For example:
*
* IP address Represented by
* 1.0.0.0 0x01000000 = 16777216
* 0.0.0.1 0x00000001 = 1
*
* @param interface_name In: Ethernet interface name, for example eth0
* @param p_ipaddr In: IPv4 address
* @param p_netmask In: Netmask
* @param p_gw In: Default gateway
* @param hostname In: Host name, for example my_laptop_4
* @param permanent In: 1 if changes are permanent, or 0 if temporary
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int pnal_set_ip_suite (
const char * interface_name,
const pnal_ipaddr_t * p_ipaddr,
const pnal_ipaddr_t * p_netmask,
const pnal_ipaddr_t * p_gw,
const char * hostname,
bool permanent);
#ifdef __cplusplus
}
#endif
#endif /* PNAL_H */
+225
View File
@@ -0,0 +1,225 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2020 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 /* Enable execvpe () */
#include "pnal_filetools.h"
#include "pnet_options.h"
#include "options.h"
#include "osal_log.h"
#include <sys/stat.h>
#include <sys/wait.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
bool pnal_does_file_exist (const char * filepath)
{
struct stat statbuffer;
return (stat (filepath, &statbuffer) == 0);
}
/**
* @internal
* Get the path to the directory where the main binary is located.
*
* @param tempstring Temp: Temporary buffer.
* @param dirpath Out: Path to directory. Terminated string.
* @param size In: Size of the stringbuffers. Should be large
* enough for the full path to the binary.
*
* @return 0 on success.
* -1 if an error occurred.
*/
static int pnal_get_directory_of_binary (
char * tempstring,
char * dirpath,
size_t size)
{
ssize_t num_bytes = 0;
memset (tempstring, 0, size); /* Path to executable */
memset (dirpath, 0, size);
char * resulting_directory = NULL;
num_bytes = readlink ("/proc/self/exe", tempstring, size);
if (num_bytes == -1)
{
return -1;
}
/* Verify that we have the full path (size is big enough) */
if (!pnal_does_file_exist (tempstring))
{
return -1;
}
resulting_directory = dirname (tempstring);
snprintf (dirpath, size, "%s", resulting_directory);
return 0;
}
/**
* @internal
* Create a PATH string used when searching for scripts
*
* The PATH string is colon separated, and includes the directory where
* the main binary is located.
*
* @param path Out: Resulting path. Terminated string.
* Might be modified also on failure.
* @param size In: Size of the outputbuffer. Should be
* sizeof(PNAL_DEFAULT_SEARCHPATH) +
* PNET_MAX_DIRECTORYPATH_SIZE
* @return 0 on success.
* -1 if an error occurred.
*/
int pnal_create_searchpath (char * path, size_t size)
{
int written = 0;
/** Should temporarily hold full path. Resulting size is
* PNET_MAX_DIRECTORYPATH_SIZE */
char
directory_of_binary[PNET_MAX_DIRECTORYPATH_SIZE + PNET_MAX_FILENAME_SIZE] =
{0};
char tempstring[PNET_MAX_DIRECTORYPATH_SIZE + PNET_MAX_FILENAME_SIZE] = {0};
if (
pnal_get_directory_of_binary (
tempstring,
directory_of_binary,
sizeof (directory_of_binary)) != 0)
{
return -1;
}
written = snprintf (
path,
size,
"%s:%s",
PNAL_DEFAULT_SEARCHPATH,
directory_of_binary);
if (written < 0 || (unsigned)written >= size)
{
return -1;
}
return 0;
}
int pnal_execute_script (const char * argv[])
{
/** Terminated string, see pnal_create_searchpath() */
char child_searchpath
[sizeof (PNAL_DEFAULT_SEARCHPATH) + PNET_MAX_DIRECTORYPATH_SIZE] = {0};
int childstatus = 0;
pid_t childpid;
char * scriptenviron[] = {NULL};
int script_returnvalue = 0;
#if LOG_DEBUG_ENABLED(PF_PNAL_LOG)
uint16_t ix;
#endif
if (argv == NULL)
{
LOG_ERROR (
PF_PNAL_LOG,
"PNAL(%d): No argument vector given for running script.\n",
__LINE__);
return -1;
}
if (argv[0] == NULL)
{
LOG_ERROR (PF_PNAL_LOG, "PNAL(%d): No script name given.\n", __LINE__);
return -1;
}
if (pnal_create_searchpath (child_searchpath, sizeof (child_searchpath)) != 0)
{
LOG_ERROR (
PF_PNAL_LOG,
"PNAL(%d): Could not build PATH to run script.\n",
__LINE__);
return -1;
}
#if LOG_DEBUG_ENABLED(PF_PNAL_LOG)
printf ("PNAL(%d): Command for script:", __LINE__);
for (ix = 0; argv[ix] != NULL; ix++)
{
if (strlen (argv[ix]) > 0)
{
printf (" %s", argv[ix]);
}
else
{
printf (" ''");
}
}
printf ("\n");
#endif
/* Fork and exec */
childpid = fork();
if (childpid < 0)
{
LOG_ERROR (
PF_PNAL_LOG,
"PNAL(%d): Failed to fork the process to run script.\n",
__LINE__);
return -1;
}
else if (childpid == 0)
{
/* We are in the child process */
/* setenv() will malloc memory. We will not free() it, but it does not
matter since we do it in the short-lived child process */
if (setenv ("PATH", child_searchpath, 1) != 0)
{
printf (
"PNAL(%d): Failed to set PATH in child process to run script.\n",
__LINE__);
exit (EXIT_FAILURE);
}
execvpe (argv[0], (char * const *)argv, scriptenviron);
printf (
"PNAL(%d): Failed to execute in child process. Is the script file "
"missing or lacks execution permission? %s Search path %s\n",
__LINE__,
argv[0],
child_searchpath);
exit (EXIT_FAILURE);
}
else
{
/* We are in the parent (original) process */
wait (&childstatus);
script_returnvalue = WEXITSTATUS (childstatus);
}
return (script_returnvalue == 0) ? 0 : -1;
}
+57
View File
@@ -0,0 +1,57 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2020 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 PNAL_FILETOOLS_H
#define PNAL_FILETOOLS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
/* Colon separated paths to search for scripts. No colon at end. */
#define PNAL_DEFAULT_SEARCHPATH "/bin:/usr/bin"
/**
* Check if a file or directory exists
*
* @param filepath In: Path to file or directory. Null terminated.
* Trailing slash is optional for directories.
* @return true if file exists
*/
bool pnal_does_file_exist (const char * filepath);
/**
* Execute a script.
*
* The script will be searched for in:
* - Directories given in PNAL_DEFAULT_SEARCHPATH
* - Same directory as the main binary is located in.
*
* @param argv In: Arguments (null terminated strings).
* The first argument should be the name of
* the script.
* The last element should be NULL.
* @return 0 on success
* -1 if an error occurred
*/
int pnal_execute_script (const char * argv[]);
#ifdef __cplusplus
}
#endif
#endif /* PNAL_FILETOOLS_H */
+39
View File
@@ -0,0 +1,39 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 PNAL_SYS_H
#define PNAL_SYS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "osal.h"
#include <netinet/in.h>
#define PNAL_BUF_MAX_SIZE 1522
typedef struct os_buf
{
void * payload;
uint16_t len;
} pnal_buf_t;
#ifdef __cplusplus
}
#endif
#endif /* PNAL_SYS_H */
+516
View File
@@ -0,0 +1,516 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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>
#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 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;
}
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* 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 SAMPLEAPP_COMMON_H
#define SAMPLEAPP_COMMON_H
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#ifdef __cplusplus
extern "C" {
#endif
#define APP_TICK_INTERVAL_US 1000 /* 1 ms */
/* Thread configuration for targets where sample
* event loop is run in a separate thread (not main).
* This applies for linux sample app implementation.
*/
#define APP_MAIN_THREAD_PRIORITY 15
#define APP_MAIN_THREAD_STACKSIZE 4096 /* bytes */
#define APP_DATA_LED_ID 1
#define APP_PROFINET_SIGNAL_LED_ID 2
#define APP_TICKS_READ_BUTTONS 10
#define APP_TICKS_UPDATE_DATA 100
/** HW Offload configuration. */
typedef enum
{
MODE_HW_OFFLOAD_NONE = 0,
MODE_HW_OFFLOAD_CPU,
MODE_HW_OFFLOAD_FULL,
} app_mode_t;
/** Command line arguments for sample application */
typedef struct app_args
{
char path_button1[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_button2[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_storage_directory[PNET_MAX_DIRECTORYPATH_SIZE]; /** Terminated */
char station_name[PNET_STATION_NAME_MAX_SIZE]; /** Terminated string */
char eth_interfaces
[PNET_INTERFACE_NAME_MAX_SIZE * (PNET_MAX_PHYSICAL_PORTS + 1) +
PNET_MAX_PHYSICAL_PORTS]; /** Terminated string */
int verbosity;
int show;
bool factory_reset;
bool remove_files;
app_mode_t mode;
} app_args_t;
typedef enum
{
RUN_IN_SEPARATE_THREAD,
RUN_IN_MAIN_THREAD
} app_run_in_separate_task_t;
typedef struct app_data_t app_data_t;
/**
* AR specific event handler type.
*
* Handles an AR specific event.
*
* @param app InOut: Application handle
* @param arep In: Arep of the AR.
*
* @return 0 to indicate that the arep should be kept
* 1 to indicate that the arep should be forgotten
*/
typedef int (*app_ar_event_handler_t) (app_data_t * app, uint32_t arep);
/** Partially initialise config values, and use proper callbacks
*
* @param pnet_cfg Out: Configuration to be updated
*/
void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Initialize P-Net stack and application.
*
* The \a pnet_cfg argument shall have been initialized using
* \a app_pnet_cfg_init_default() before this function is
* called.
*
* @param pnet_cfg In: P-Net configuration
* @param app_args In: Application arguments
* @return Application handle, NULL on error
*/
app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args);
/**
* Start application main loop
*
* Application must have been initialized using \a app_init() before
* this function is called.
*
* If \a task_config parameters is set to RUN_IN_SEPARATE_THREAD a
* thread execution the \a app_loop_forever() function is started.
* If task_config is set to RUN_IN_MAIN_THREAD no such thread is
* started and the caller must call the \a app_loop_forever() after
* calling this function.
*
* RUN_IN_MAIN_THREAD is intended for rt-kernel targets.
* RUN_IN_SEPARATE_THREAD is intended for linux targets.
*
* @param app In: Application handle
* @param task_config In: Defines if stack and application
* is run in main or separate task.
* @return 0 on success, -1 on error
*/
int app_start (app_data_t * app, app_run_in_separate_task_t task_config);
/**
* Application task definition. Handles events in eternal loop.
*
* @param arg In: Application handle
*/
void app_loop_forever (void * arg);
/**
* Get P-Net instance from application
*
* @param app In: Application handle
* @return P-Net instance, NULL on failure
*/
pnet_t * app_get_pnet_instance (app_data_t * app);
/**
* Set LED state
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: LED number, starting from 0.
* @param led_state In: LED state. Use true for on and false for off.
*/
void app_set_led (uint16_t id, bool led_state);
/**
* Read button state
*
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: Button number, starting from 0.
* @return true if button is pressed, false if not
*/
bool app_get_button (uint16_t id);
#ifdef __cplusplus
}
#endif
#endif /* SAMPLEAPP_COMMON_H */