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

1623 lines
44 KiB
C

/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "sampleapp_common.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_data.h"
#include "app_log.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Events handled by main task */
#define APP_EVENT_READY_FOR_DATA BIT (0)
#define APP_EVENT_TIMER BIT (1)
#define APP_EVENT_ALARM BIT (2)
#define APP_EVENT_ABORT BIT (15)
/* Defines used for alarm demo functionality */
#define CHANNEL_ERRORTYPE_SHORT_CIRCUIT 0x0001
#define CHANNEL_ERRORTYPE_LINE_BREAK 0x0006
#define CHANNEL_ERRORTYPE_DATA_TRANSMISSION_IMPOSSIBLE 0x8000
#define CHANNEL_ERRORTYPE_NETWORK_COMPONENT_FUNCTION_MISMATCH 0x8008
#define EXTENDED_CHANNEL_ERRORTYPE_FRAME_DROPPED 0x8000
#define EXTENDED_CHANNEL_ERRORTYPE_MAUTYPE_MISMATCH 0x8001
#define EXTENDED_CHANNEL_ERRORTYPE_LINE_DELAY_MISMATCH 0x8002
#define APP_ALARM_USI 0x0010
#define APP_DIAG_CHANNEL_NUMBER 4
#define APP_DIAG_CHANNEL_DIRECTION PNET_DIAG_CH_PROP_DIR_INPUT
#define APP_DIAG_CHANNEL_NUMBER_OF_BITS PNET_DIAG_CH_PROP_TYPE_1_BIT
#define APP_DIAG_CHANNEL_SEVERITY PNET_DIAG_CH_PROP_MAINT_FAULT
#define APP_DIAG_CHANNEL_ERRORTYPE CHANNEL_ERRORTYPE_SHORT_CIRCUIT
#define APP_DIAG_CHANNEL_ADDVALUE_A 0
#define APP_DIAG_CHANNEL_ADDVALUE_B 1234
#define APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE 0
#define APP_DIAG_CHANNEL_QUAL_SEVERITY 0 /* Not used (Max one bit set) */
typedef enum app_demo_state
{
APP_DEMO_STATE_ALARM_SEND = 0,
APP_DEMO_STATE_LOGBOOK_ENTRY,
APP_DEMO_STATE_ABORT_AR,
APP_DEMO_STATE_CYCLIC_REDUNDANT,
APP_DEMO_STATE_CYCLIC_NORMAL,
APP_DEMO_STATE_DIAG_STD_ADD,
APP_DEMO_STATE_DIAG_STD_UPDATE,
APP_DEMO_STATE_DIAG_STD_REMOVE,
APP_DEMO_STATE_DIAG_USI_ADD,
APP_DEMO_STATE_DIAG_USI_UPDATE,
APP_DEMO_STATE_DIAG_USI_REMOVE,
} app_demo_state_t;
typedef struct app_data_t
{
pnet_t * net;
/* P-Net configuration passed in app_init(). */
const pnet_cfg_t * pnet_cfg;
/* Application API for administration of plugged
* (sub)modules and connection state. */
app_api_t main_api;
os_timer_t * main_timer;
os_event_t * main_events;
bool alarm_allowed;
pnet_alarm_argument_t alarm_arg;
app_demo_state_t alarm_demo_state;
uint8_t alarm_payload[APP_GSDML_ALARM_PAYLOAD_SIZE];
uint32_t arep_for_appl_ready;
bool button1_pressed;
bool button2_pressed;
bool button2_pressed_previous;
/* Counters used to control when buttons are checked
* and process data is updated */
uint32_t buttons_tick_counter;
uint32_t process_data_tick_counter;
} app_data_t;
/* Forward declarations */
static void app_plug_dap (app_data_t * app, uint16_t number_of_ports);
static int app_set_initial_data_and_ioxs (app_data_t * app);
static void app_cyclic_data_callback (app_subslot_t * subslot, void * tag);
/** Static app data */
static app_data_t app_state;
pnet_t * app_get_pnet_instance (app_data_t * app)
{
if (app == NULL)
{
return NULL;
}
return app->net;
}
/** Check if we are connected to the controller
*
* @param app InOut: Application handle
* @return true if we are connected to the IO-controller
*/
static bool app_is_connected_to_controller (app_data_t * app)
{
return app->main_api.arep != UINT32_MAX;
}
app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args)
{
APP_LOG_INFO ("Init P-Net stack and sample application\n");
app_data_t * app = &app_state;
app->alarm_allowed = true;
app->main_api.arep = UINT32_MAX;
app->pnet_cfg = pnet_cfg;
app->net = pnet_init (app->pnet_cfg);
if (app->net == NULL)
{
return NULL;
}
return app;
}
/**
* Callback for timer tick.
*
* This is a callback for the timer defined in OSAL.
* See \a os_timer_create() for details.
*
* @param timer InOut: Timer instance
* @param arg InOut: User defined argument, app_data_t pointer
*/
static void main_timer_tick (os_timer_t * timer, void * arg)
{
app_data_t * app = (app_data_t *)arg;
os_event_set (app->main_events, APP_EVENT_TIMER);
}
int app_start (app_data_t * app, app_run_in_separate_task_t task_config)
{
APP_LOG_INFO ("Start sample application main loop\n");
if (app == NULL)
{
return -1;
}
app->main_events = os_event_create();
if (app->main_events == NULL)
{
return -1;
}
app->main_timer = os_timer_create (
APP_TICK_INTERVAL_US,
main_timer_tick,
(void *)app,
false);
if (app->main_timer == NULL)
{
os_event_destroy (app->main_events);
return -1;
}
if (task_config == RUN_IN_SEPARATE_THREAD)
{
os_thread_create (
"app_loop_foreve\0", //Максимальное количество символов 16, включая 0 в конце
APP_MAIN_THREAD_PRIORITY,
APP_MAIN_THREAD_STACKSIZE,
app_loop_forever,
(void *)app);
}
os_timer_start (app->main_timer);
return 0;
}
/**
* Set outputs to default value
*/
static void app_set_outputs_default_value (void)
{
APP_LOG_DEBUG ("Setting outputs to default values.\n");
app_data_set_default_outputs();
}
/*********************************** Callbacks ********************************/
static int app_connect_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
APP_LOG_DEBUG ("PLC connect indication. AREP: %u\n", arep);
/*
* Handle the request on an application level.
* This is a very simple application which does not need to handle anything.
* All the needed information is in the AR data structure.
*/
return 0;
}
static int app_release_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
APP_LOG_DEBUG ("PLC release (disconnect) indication. AREP: %u\n", arep);
app_set_outputs_default_value();
return 0;
}
static int app_dcontrol_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_control_command_t control_command,
pnet_result_t * p_result)
{
APP_LOG_DEBUG (
"PLC dcontrol message (The PLC is done with parameter writing). "
"AREP: %u Command: %s\n",
arep,
app_utils_dcontrol_cmd_to_string (control_command));
return 0;
}
static int app_ccontrol_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_result_t * p_result)
{
APP_LOG_DEBUG (
"PLC ccontrol message confirmation (The PLC has received our Application "
"Ready message). AREP: %u Status codes: %d %d %d %d\n",
arep,
p_result->pnio_status.error_code,
p_result->pnio_status.error_decode,
p_result->pnio_status.error_code_1,
p_result->pnio_status.error_code_2);
return 0;
}
static int app_write_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint16_t write_length,
const uint8_t * p_write_data,
pnet_result_t * p_result)
{
int result = 0;
app_data_t * app = (app_data_t *)arg;
app_subslot_t * subslot;
APP_LOG_DEBUG (
"PLC write record indication.\n"
" AREP: %u API: %u Slot: %2u Subslot: %u Index: %u Sequence: %2u "
"Length: %u\n",
arep,
api,
slot_nbr,
subslot_nbr,
(unsigned)idx,
sequence_number,
write_length);
subslot = app_utils_subslot_get (&app->main_api, slot_nbr, subslot_nbr);
if (subslot == NULL)
{
APP_LOG_WARNING (
"No submodule plugged in AREP: %u API: %u Slot: %2u Subslot: %u "
"Index will not be written.\n",
arep,
api,
slot_nbr,
subslot_nbr);
p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
return -1;
}
result = app_data_write_parameter (
slot_nbr,
subslot_nbr,
subslot->submodule_id,
idx,
p_write_data,
write_length);
if (result != 0)
{
APP_LOG_WARNING (
"Failed to write index for AREP: %u API: %u Slot: %2u Subslot: %u "
"index %u.\n",
arep,
api,
slot_nbr,
subslot_nbr,
idx);
p_result->pnio_status.error_code = PNET_ERROR_CODE_WRITE;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_WRITE_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
}
return result;
}
static int app_read_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint16_t idx,
uint16_t sequence_number,
uint8_t ** pp_read_data,
uint16_t * p_read_length,
pnet_result_t * p_result)
{
int result = 0;
app_data_t * app = (app_data_t *)arg;
app_subslot_t * subslot;
APP_LOG_DEBUG (
"PLC read record indication.\n"
" AREP: %u API: %u Slot: %2u Subslot: %u Index: %u Sequence: %2u Max "
"length: %u\n",
arep,
api,
slot_nbr,
subslot_nbr,
(unsigned)idx,
sequence_number,
(unsigned)*p_read_length);
subslot = app_utils_subslot_get (&app->main_api, slot_nbr, subslot_nbr);
if (subslot == NULL)
{
APP_LOG_WARNING (
"No submodule plugged in AREP: %u API: %u Slot: %2u Subslot: %u "
"Index will not be read.\n",
arep,
api,
slot_nbr,
subslot_nbr);
p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
return -1;
}
result = app_data_read_parameter (
slot_nbr,
subslot_nbr,
subslot->submodule_id,
idx,
pp_read_data,
p_read_length);
if (result != 0)
{
APP_LOG_WARNING (
"Failed to read index for AREP: %u API: %u Slot: %2u Subslot: %u "
"index %u.\n",
arep,
api,
slot_nbr,
subslot_nbr,
idx);
p_result->pnio_status.error_code = PNET_ERROR_CODE_READ;
p_result->pnio_status.error_decode = PNET_ERROR_DECODE_PNIORW;
p_result->pnio_status.error_code_1 = PNET_ERROR_CODE_1_APP_READ_ERROR;
p_result->pnio_status.error_code_2 = 0; /* User specific */
}
return result;
}
static int app_state_ind (
pnet_t * net,
void * arg,
uint32_t arep,
pnet_event_values_t event)
{
uint16_t err_cls = 0; /* Error code 1 */
uint16_t err_code = 0; /* Error code 2 */
const char * error_class_description = "";
const char * error_code_description = "";
app_data_t * app = (app_data_t *)arg;
APP_LOG_DEBUG (
"Event indication %s AREP: %u\n",
app_utils_event_to_string (event),
arep);
if (event == PNET_EVENT_ABORT)
{
if (pnet_get_ar_error_codes (net, arep, &err_cls, &err_code) == 0)
{
app_utils_get_error_code_strings (
err_cls,
err_code,
&error_class_description,
&error_code_description);
APP_LOG_DEBUG (
" Error class: 0x%02x %s \n"
" Error code: 0x%02x %s \n",
(unsigned)err_cls,
error_class_description,
(unsigned)err_code,
error_code_description);
}
else
{
APP_LOG_DEBUG (" No error status available\n");
}
/* Set output values */
app_set_outputs_default_value();
/* Only abort AR with correct session key */
os_event_set (app->main_events, APP_EVENT_ABORT);
}
else if (event == PNET_EVENT_PRMEND)
{
if (app_is_connected_to_controller (app))
{
APP_LOG_WARNING ("Warning - AREP out of sync\n");
}
app->main_api.arep = arep;
app_set_initial_data_and_ioxs (app);
(void)pnet_set_provider_state (net, true);
/* Send application ready at next tick
Do not call pnet_application_ready() here as it will affect
the internal stack states */
app->arep_for_appl_ready = arep;
os_event_set (app->main_events, APP_EVENT_READY_FOR_DATA);
}
else if (event == PNET_EVENT_DATA)
{
APP_LOG_DEBUG ("Cyclic data transmission started\n\n");
}
return 0;
}
static int app_reset_ind (
pnet_t * net,
void * arg,
bool should_reset_application,
uint16_t reset_mode)
{
APP_LOG_DEBUG (
"PLC reset indication. Application reset mandatory: %u Reset mode: %d\n",
should_reset_application,
reset_mode);
return 0;
}
static int app_signal_led_ind (pnet_t * net, void * arg, bool led_state)
{
APP_LOG_INFO ("Profinet signal LED indication. New state: %u\n", led_state);
app_set_led (APP_PROFINET_SIGNAL_LED_ID, led_state);
return 0;
}
static int app_exp_module_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint32_t module_ident)
{
int ret = -1;
int result = 0;
app_data_t * app = (app_data_t *)arg;
const char * module_name = "unknown";
const app_gsdml_module_t * module_config;
APP_LOG_DEBUG ("Module plug indication\n");
if (slot >= PNET_MAX_SLOTS)
{
APP_LOG_ERROR (
"Wrong slot number received: %u It should be less than %u\n",
slot,
PNET_MAX_SLOTS);
return -1;
}
module_config = app_gsdml_get_module_cfg (module_ident);
if (module_config == NULL)
{
APP_LOG_ERROR (" Module ID %08x not found.\n", (unsigned)module_ident);
/*
* Needed to pass Behavior scenario 2
*/
APP_LOG_DEBUG (" Plug expected module anyway\n");
}
else
{
module_name = module_config->name;
}
APP_LOG_DEBUG (" Pull old module. API: %u Slot: %2u\n", api, slot);
result = pnet_pull_module (net, api, slot);
if (result == 0)
{
(void)app_utils_pull_module (&app->main_api, slot);
}
APP_LOG_DEBUG (
" Plug module. API: %u Slot: %2u Module ID: 0x%x \"%s\"\n",
api,
slot,
(unsigned)module_ident,
module_name);
ret = pnet_plug_module (net, api, slot, module_ident);
if (ret == 0)
{
(void)app_utils_plug_module (
&app->main_api,
slot,
module_ident,
module_name);
}
else
{
APP_LOG_ERROR (
"Plug module failed. Ret: %u API: %u Slot: %2u Module ID: 0x%x\n",
ret,
api,
slot,
(unsigned)module_ident);
}
return ret;
}
static int app_exp_submodule_ind (
pnet_t * net,
void * arg,
uint32_t api,
uint16_t slot,
uint16_t subslot,
uint32_t module_id,
uint32_t submodule_id,
const pnet_data_cfg_t * p_exp_data)
{
int ret = -1;
int result = 0;
pnet_data_cfg_t data_cfg = {0};
app_data_t * app = (app_data_t *)arg;
const app_gsdml_submodule_t * submod_cfg;
const char * name = "Unsupported";
app_utils_cyclic_callback cyclic_data_callback = NULL;
APP_LOG_DEBUG ("Submodule plug indication.\n");
submod_cfg = app_gsdml_get_submodule_cfg (submodule_id);
if (submod_cfg != NULL)
{
data_cfg.data_dir = submod_cfg->data_dir;
data_cfg.insize = submod_cfg->insize;
data_cfg.outsize = submod_cfg->outsize;
name = submod_cfg->name;
if (data_cfg.insize > 0 || data_cfg.outsize > 0)
{
cyclic_data_callback = app_cyclic_data_callback;
}
}
else
{
APP_LOG_WARNING (
" Submodule ID 0x%x in module ID 0x%x not found. API: %u Slot: %2u "
"Subslot %u \n",
(unsigned)submodule_id,
(unsigned)module_id,
api,
slot,
subslot);
/*
* Needed for behavior scenario 2 to pass.
* Iops will be set to bad for this subslot
*/
APP_LOG_WARNING (" Plug expected submodule anyway \n");
data_cfg.data_dir = p_exp_data->data_dir;
data_cfg.insize = p_exp_data->insize;
data_cfg.outsize = p_exp_data->outsize;
}
APP_LOG_DEBUG (
" Pull old submodule. API: %u Slot: %2u Subslot: %u\n",
api,
slot,
subslot);
result = pnet_pull_submodule (net, api, slot, subslot);
if (result == 0)
{
(void)app_utils_pull_submodule (&app->main_api, slot, subslot);
}
APP_LOG_DEBUG (
" Plug submodule. API: %u Slot: %2u Module ID: 0x%-4x\n"
" Subslot: %u Submodule ID: 0x%x \"%s\"\n",
api,
slot,
(unsigned)module_id,
subslot,
(unsigned)submodule_id,
name);
APP_LOG_DEBUG (
" Data Dir: %s In: %u bytes Out: %u bytes\n",
app_utils_submod_dir_to_string (data_cfg.data_dir),
data_cfg.insize,
data_cfg.outsize);
if (
data_cfg.data_dir != p_exp_data->data_dir ||
data_cfg.insize != p_exp_data->insize ||
data_cfg.outsize != p_exp_data->outsize)
{
APP_LOG_WARNING (
" Warning expected Data Dir: %s In: %u bytes Out: %u bytes\n",
app_utils_submod_dir_to_string (p_exp_data->data_dir),
p_exp_data->insize,
p_exp_data->outsize);
}
ret = pnet_plug_submodule (
net,
api,
slot,
subslot,
module_id,
submodule_id,
data_cfg.data_dir,
data_cfg.insize,
data_cfg.outsize);
if (ret == 0)
{
(void)app_utils_plug_submodule (
&app->main_api,
slot,
subslot,
submodule_id,
&data_cfg,
name,
cyclic_data_callback,
app);
}
else
{
APP_LOG_ERROR (
" Plug submodule failed. Ret: %u API: %u Slot: %2u Subslot %u "
"Module ID: 0x%x Submodule ID: 0x%x \n",
ret,
api,
slot,
subslot,
(unsigned)module_id,
(unsigned)submodule_id);
}
return ret;
}
static int app_new_data_status_ind (
pnet_t * net,
void * arg,
uint32_t arep,
uint32_t crep,
uint8_t changes,
uint8_t data_status)
{
bool is_running = data_status & BIT (PNET_DATA_STATUS_BIT_PROVIDER_STATE);
bool is_valid = data_status & BIT (PNET_DATA_STATUS_BIT_DATA_VALID);
APP_LOG_DEBUG (
"Data status indication. AREP: %u Data status changes: 0x%02x "
"Data status: 0x%02x\n",
arep,
changes,
data_status);
APP_LOG_DEBUG (
" %s, %s, %s, %s, %s\n",
is_running ? "Run" : "Stop",
is_valid ? "Valid" : "Invalid",
(data_status & BIT (PNET_DATA_STATUS_BIT_STATE)) ? "Primary" : "Backup",
(data_status & BIT (PNET_DATA_STATUS_BIT_STATION_PROBLEM_INDICATOR))
? "Normal operation"
: "Problem",
(data_status & BIT (PNET_DATA_STATUS_BIT_IGNORE))
? "Ignore data status"
: "Evaluate data status");
if (is_running == false || is_valid == false)
{
app_set_outputs_default_value();
}
return 0;
}
static int app_alarm_ind (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg,
uint16_t data_len,
uint16_t data_usi,
const uint8_t * p_data)
{
app_data_t * app = (app_data_t *)arg;
APP_LOG_DEBUG (
"Alarm indication. AREP: %u API: %d Slot: %d Subslot: %d "
"Type: %d Seq: %d Length: %d USI: %d\n",
arep,
p_alarm_arg->api_id,
p_alarm_arg->slot_nbr,
p_alarm_arg->subslot_nbr,
p_alarm_arg->alarm_type,
p_alarm_arg->sequence_number,
data_len,
data_usi);
app->alarm_arg = *p_alarm_arg;
os_event_set (app->main_events, APP_EVENT_ALARM);
return 0;
}
static int app_alarm_cnf (
pnet_t * net,
void * arg,
uint32_t arep,
const pnet_pnio_status_t * p_pnio_status)
{
app_data_t * app = (app_data_t *)arg;
APP_LOG_DEBUG (
"PLC alarm confirmation. AREP: %u Status code %u, "
"%u, %u, %u\n",
arep,
p_pnio_status->error_code,
p_pnio_status->error_decode,
p_pnio_status->error_code_1,
p_pnio_status->error_code_2);
app->alarm_allowed = true;
return 0;
}
static int app_alarm_ack_cnf (pnet_t * net, void * arg, uint32_t arep, int res)
{
APP_LOG_DEBUG (
"PLC alarm ACK confirmation. AREP: %u Result: "
"%d\n",
arep,
res);
return 0;
}
/******************************************************************************/
/**
* Plug all DAP (sub)modules
* Use existing callback functions to plug the (sub-)modules
* @param app InOut: Application handle
* @param number_of_ports In: Number of active ports
*/
static void app_plug_dap (app_data_t * app, uint16_t number_of_ports)
{
const pnet_data_cfg_t cfg_dap_data = {
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
};
APP_LOG_DEBUG ("\nPlug DAP module and its submodules\n");
app_exp_module_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_MOD_DAP_IDENT);
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_IDENT,
&cfg_dap_data);
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
&cfg_dap_data);
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
&cfg_dap_data);
if (number_of_ports >= 2)
{
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_2_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
&cfg_dap_data);
}
if (number_of_ports >= 3)
{
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_3_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
&cfg_dap_data);
}
if (number_of_ports >= 4)
{
app_exp_submodule_ind (
app->net,
app,
APP_GSDML_API,
PNET_SLOT_DAP_IDENT,
PNET_SUBSLOT_DAP_INTERFACE_1_PORT_4_IDENT,
PNET_MOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
&cfg_dap_data);
}
APP_LOG_DEBUG ("Done plugging DAP\n\n");
}
/**
* Send application ready to the PLC
* @param net InOut: p-net stack instance
* @param arep In: Arep
*/
static void app_handle_send_application_ready (pnet_t * net, uint32_t arep)
{
int ret = -1;
APP_LOG_DEBUG (
"Application will signal that it is ready for data, for "
"AREP %u.\n",
arep);
ret = pnet_application_ready (net, arep);
if (ret != 0)
{
APP_LOG_ERROR (
"Error returned when application telling that it is ready for "
"data. Have you set IOCS or IOPS for all subslots?\n");
}
/* When the PLC sends a confirmation to this message, the
pnet_ccontrol_cnf() callback will be triggered. */
}
/**
* Send alarm ACK to the PLC
*
* @param net InOut: p-net stack instance
* @param arep In: Arep
* @param p_alarm_arg In: Alarm argument (slot, subslot etc)
*/
static void app_handle_send_alarm_ack (
pnet_t * net,
uint32_t arep,
const pnet_alarm_argument_t * p_alarm_arg)
{
pnet_pnio_status_t pnio_status = {0, 0, 0, 0};
int ret;
ret = pnet_alarm_send_ack (net, arep, p_alarm_arg, &pnio_status);
if (ret != 0)
{
APP_LOG_DEBUG ("Error when sending alarm ACK. Error: %d\n", ret);
}
else
{
APP_LOG_DEBUG ("Alarm ACK sent\n");
}
}
/**
* Handle cyclic input- and output data for a subslot.
*
* Data is read and written using functions in the .c file,
* which handles the data and update the physical input and outputs.
*
* @param subslot InOut: Subslot reference
* @param tag In: Application handle, here \a app_data_t pointer
*/
static void app_cyclic_data_callback (app_subslot_t * subslot, void * tag)
{
app_data_t * app = (app_data_t *)tag;
uint8_t indata_iops = PNET_IOXS_BAD;
uint8_t indata_iocs = PNET_IOXS_BAD;
uint8_t * indata;
uint16_t indata_size = 0;
bool outdata_updated;
uint16_t outdata_length;
uint8_t outdata_iops;
uint8_t outdata_buf[20]; /* Todo: Remove temporary buffer */
if (app == NULL)
{
APP_LOG_ERROR ("Application tag not set in subslot?\n");
return;
}
if (subslot->slot_nbr != PNET_SLOT_DAP_IDENT && subslot->data_cfg.outsize > 0)
{
outdata_length = subslot->data_cfg.outsize;
CC_ASSERT (outdata_length < sizeof (outdata_buf));
/* Get output data from the PLC */
(void)pnet_output_get_data_and_iops (
app->net,
APP_GSDML_API,
subslot->slot_nbr,
subslot->subslot_nbr,
&outdata_updated,
outdata_buf,
&outdata_length,
&outdata_iops);
app_utils_print_ioxs_change (
subslot,
"Provider Status (IOPS)",
subslot->outdata_iops,
outdata_iops);
subslot->outdata_iops = outdata_iops;
if (outdata_length != subslot->data_cfg.outsize)
{
APP_LOG_ERROR ("Wrong outputdata length: %u\n", outdata_length);
app_set_outputs_default_value();
}
else if (outdata_iops == PNET_IOXS_GOOD)
{
/* Application specific handling of the output data to a submodule.
For the sample application, the data sets a LED. */
(void)app_data_set_output_data (
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_id,
outdata_buf,
outdata_length);
}
else
{
app_set_outputs_default_value();
}
}
if (subslot->slot_nbr != PNET_SLOT_DAP_IDENT && subslot->data_cfg.insize > 0)
{
/* Get application specific input data from a submodule (not DAP)
*
* For the sample application, the data includes a button
* state and a counter value. */
indata = app_data_get_input_data (
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_id,
app->button1_pressed,
&indata_size,
&indata_iops);
/* Send input data to the PLC */
(void)pnet_input_set_data_and_iops (
app->net,
APP_GSDML_API,
subslot->slot_nbr,
subslot->subslot_nbr,
indata,
indata_size,
indata_iops);
(void)pnet_input_get_iocs (
app->net,
APP_GSDML_API,
subslot->slot_nbr,
subslot->subslot_nbr,
&indata_iocs);
app_utils_print_ioxs_change (
subslot,
"Consumer Status (IOCS)",
subslot->indata_iocs,
indata_iocs);
subslot->indata_iocs = indata_iocs;
}
}
/**
* Set initial input data, provider and consumer status for a subslot.
*
* @param app In: Application handle
*/
static int app_set_initial_data_and_ioxs (app_data_t * app)
{
int ret;
uint16_t slot;
uint16_t subslot_index;
const app_subslot_t * p_subslot;
uint8_t * indata;
uint16_t indata_size;
uint8_t indata_iops;
for (slot = 0; slot < PNET_MAX_SLOTS; slot++)
{
for (subslot_index = 0; subslot_index < PNET_MAX_SUBSLOTS;
subslot_index++)
{
p_subslot = &app->main_api.slots[slot].subslots[subslot_index];
if (p_subslot->plugged)
{
indata = NULL;
indata_size = 0;
indata_iops = PNET_IOXS_BAD;
if (
p_subslot->data_cfg.insize > 0 ||
p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO)
{
/* Get input data for submodule
*
* For the sample application data includes
* includes button state and a counter value
*/
if (
p_subslot->slot_nbr != PNET_SLOT_DAP_IDENT &&
p_subslot->data_cfg.insize > 0)
{
indata = app_data_get_input_data (
p_subslot->slot_nbr,
p_subslot->subslot_nbr,
p_subslot->submodule_id,
app->button1_pressed,
&indata_size,
&indata_iops);
}
else if (p_subslot->slot_nbr == PNET_SLOT_DAP_IDENT)
{
indata_iops = PNET_IOXS_GOOD;
}
ret = pnet_input_set_data_and_iops (
app->net,
app->main_api.api_id,
p_subslot->slot_nbr,
p_subslot->subslot_nbr,
indata,
indata_size,
indata_iops);
/*
* If a submodule is still plugged but not used in current AR,
* setting the data and IOPS will fail.
* This is not a problem.
* Log message below will only be printed for active submodules.
*/
if (ret == 0)
{
APP_LOG_DEBUG (
" Set initial input data and IOPS for slot %2u subslot "
"%5u %9s size %3d \"%s\" \n",
p_subslot->slot_nbr,
p_subslot->subslot_nbr,
app_utils_ioxs_to_string (indata_iops),
p_subslot->data_cfg.insize,
p_subslot->submodule_name);
}
}
if (p_subslot->data_cfg.outsize > 0)
{
ret = pnet_output_set_iocs (
app->net,
app->main_api.api_id,
p_subslot->slot_nbr,
p_subslot->subslot_nbr,
PNET_IOXS_GOOD);
if (ret == 0)
{
APP_LOG_DEBUG (
" Set initial output IOCS for slot %2u subslot "
"%5u %9s \"%s\"\n",
p_subslot->slot_nbr,
p_subslot->subslot_nbr,
app_utils_ioxs_to_string (PNET_IOXS_GOOD),
p_subslot->submodule_name);
}
}
}
}
}
return 0;
}
/**
* Send and receive cyclic/process data for all subslots.
*
* Updates the data only on every APP_TICKS_UPDATE_DATA invocation
*
* @param app In: Application handle
*/
static void app_handle_cyclic_data (app_data_t * app)
{
/* For the sample application cyclic data is updated
* with a period defined by APP_TICKS_UPDATE_DATA
*/
app->process_data_tick_counter++;
if (app->process_data_tick_counter < APP_TICKS_UPDATE_DATA)
{
return;
}
app->process_data_tick_counter = 0;
app_utils_cyclic_data_poll (&app->main_api);
}
/**
* Set alarm, diagnostics and logbook entries.
*
* Alternates between these functions each time the button2 is pressed:
* - pnet_alarm_send_process_alarm()
* - pnet_diag_std_add()
* - pnet_set_redundancy_state()
* - pnet_set_state()
* - pnet_diag_std_update()
* - pnet_diag_usi_add()
* - pnet_diag_usi_update()
* - pnet_diag_usi_remove()
* - pnet_diag_std_remove()
* - pnet_create_log_book_entry()
* - pnet_ar_abort()
*
* Uses first 8-bit digital input module, if available.
*
* @param app InOut: Application handle
*/
static void app_handle_demo_pnet_api (app_data_t * app)
{
uint16_t slot = 0;
bool found_inputsubslot = false;
uint16_t subslot_ix = 0;
const app_subslot_t * p_subslot = NULL;
pnet_pnio_status_t pnio_status = {0};
pnet_diag_source_t diag_source = {
.api = APP_GSDML_API,
.slot = 0,
.subslot = 0,
.ch = APP_DIAG_CHANNEL_NUMBER,
.ch_grouping = PNET_DIAG_CH_INDIVIDUAL_CHANNEL,
.ch_direction = APP_DIAG_CHANNEL_DIRECTION};
/* Loop though all subslots to find first digital 8-bit input subslot */
while (!found_inputsubslot && (slot < PNET_MAX_SLOTS))
{
for (subslot_ix = 0;
!found_inputsubslot && (subslot_ix < PNET_MAX_SUBSLOTS);
subslot_ix++)
{
p_subslot = &app->main_api.slots[slot].subslots[subslot_ix];
if (
app_utils_subslot_is_input (p_subslot) &&
(p_subslot->submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN ||
p_subslot->submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT))
{
found_inputsubslot = true;
break;
}
}
if (!found_inputsubslot)
{
slot++;
}
}
if (!found_inputsubslot)
{
APP_LOG_DEBUG ("Did not find any input module in the slots. Skipping.\n");
return;
}
diag_source.slot = slot;
diag_source.subslot = p_subslot->subslot_nbr;
switch (app->alarm_demo_state)
{
case APP_DEMO_STATE_ALARM_SEND:
if (app->alarm_allowed == true && app_is_connected_to_controller (app))
{
app->alarm_payload[0]++;
APP_LOG_INFO (
"Sending process alarm from slot %u subslot %u USI %u to "
"PLC. Payload: 0x%x\n",
slot,
p_subslot->subslot_nbr,
APP_ALARM_USI,
app->alarm_payload[0]);
pnet_alarm_send_process_alarm (
app->net,
app->main_api.arep,
APP_GSDML_API,
slot,
p_subslot->subslot_nbr,
APP_ALARM_USI,
APP_GSDML_ALARM_PAYLOAD_SIZE,
app->alarm_payload);
app->alarm_allowed = false; /* Not allowed until ACK received */
/* todo handle return code on pnet_alarm_send_process_alarm */
}
else
{
APP_LOG_WARNING (
"Could not send process alarm, as alarm_allowed == false or "
"no connection available\n");
}
break;
case APP_DEMO_STATE_CYCLIC_REDUNDANT:
APP_LOG_INFO (
"Setting cyclic data to backup and to redundant. See Wireshark.\n");
if (pnet_set_primary_state (app->net, false) != 0)
{
APP_LOG_WARNING (" Could not set cyclic data state to backup.\n");
}
if (pnet_set_redundancy_state (app->net, true) != 0)
{
APP_LOG_WARNING (" Could not set cyclic data state to reundant.\n");
}
break;
case APP_DEMO_STATE_CYCLIC_NORMAL:
APP_LOG_INFO (
"Setting cyclic data back to primary and non-redundant. See "
"Wireshark.\n");
if (pnet_set_primary_state (app->net, true) != 0)
{
APP_LOG_ERROR (" Could not set cyclic data state to primary.\n");
}
if (pnet_set_redundancy_state (app->net, false) != 0)
{
APP_LOG_ERROR (
" Could not set cyclic data state to non-reundant.\n");
}
break;
case APP_DEMO_STATE_DIAG_STD_ADD:
APP_LOG_INFO (
"Adding standard diagnosis. Slot %u subslot %u channel %u Errortype "
"%u\n",
diag_source.slot,
diag_source.subslot,
diag_source.ch,
APP_DIAG_CHANNEL_ERRORTYPE);
(void)pnet_diag_std_add (
app->net,
&diag_source,
APP_DIAG_CHANNEL_NUMBER_OF_BITS,
APP_DIAG_CHANNEL_SEVERITY,
APP_DIAG_CHANNEL_ERRORTYPE,
APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE,
APP_DIAG_CHANNEL_ADDVALUE_A,
APP_DIAG_CHANNEL_QUAL_SEVERITY);
break;
case APP_DEMO_STATE_DIAG_STD_UPDATE:
APP_LOG_INFO (
"Updating standard diagnosis. Slot %u subslot %u channel %u\n",
diag_source.slot,
diag_source.subslot,
diag_source.ch);
pnet_diag_std_update (
app->net,
&diag_source,
APP_DIAG_CHANNEL_ERRORTYPE,
APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE,
APP_DIAG_CHANNEL_ADDVALUE_B);
break;
case APP_DEMO_STATE_DIAG_STD_REMOVE:
APP_LOG_INFO (
"Removing standard diagnosis. Slot %u subslot %u channel %u\n",
diag_source.slot,
diag_source.subslot,
diag_source.ch);
pnet_diag_std_remove (
app->net,
&diag_source,
APP_DIAG_CHANNEL_ERRORTYPE,
APP_DIAG_CHANNEL_EXTENDED_ERRORTYPE);
break;
case APP_DEMO_STATE_DIAG_USI_ADD:
APP_LOG_INFO (
"Adding USI diagnosis. Slot %u subslot %u\n",
slot,
p_subslot->subslot_nbr);
pnet_diag_usi_add (
app->net,
APP_GSDML_API,
slot,
p_subslot->subslot_nbr,
APP_GSDML_DIAG_CUSTOM_USI,
11,
(uint8_t *)"diagdata_1");
break;
case APP_DEMO_STATE_DIAG_USI_UPDATE:
APP_LOG_INFO (
"Updating USI diagnosis. Slot %u subslot %u\n",
slot,
p_subslot->subslot_nbr);
pnet_diag_usi_update (
app->net,
APP_GSDML_API,
slot,
p_subslot->subslot_nbr,
APP_GSDML_DIAG_CUSTOM_USI,
13,
(uint8_t *)"diagdata_123");
break;
case APP_DEMO_STATE_DIAG_USI_REMOVE:
APP_LOG_INFO (
"Removing USI diagnosis. Slot %u subslot %u\n",
slot,
p_subslot->subslot_nbr);
pnet_diag_usi_remove (
app->net,
APP_GSDML_API,
slot,
p_subslot->subslot_nbr,
APP_GSDML_DIAG_CUSTOM_USI);
break;
case APP_DEMO_STATE_LOGBOOK_ENTRY:
if (app_is_connected_to_controller (app))
{
APP_LOG_INFO (
"Writing to logbook. Error_code1: %02X Error_code2: %02X Entry "
"detail: 0x%08X\n",
APP_GSDML_LOGBOOK_ERROR_CODE_1,
APP_GSDML_LOGBOOK_ERROR_CODE_2,
APP_GSDML_LOGBOOK_ENTRY_DETAIL);
pnio_status.error_code = APP_GSDML_LOGBOOK_ERROR_CODE;
pnio_status.error_decode = APP_GSDML_LOGBOOK_ERROR_DECODE;
pnio_status.error_code_1 = APP_GSDML_LOGBOOK_ERROR_CODE_1;
pnio_status.error_code_2 = APP_GSDML_LOGBOOK_ERROR_CODE_2;
pnet_create_log_book_entry (
app->net,
app->main_api.arep,
&pnio_status,
APP_GSDML_LOGBOOK_ENTRY_DETAIL);
}
else
{
APP_LOG_WARNING (
"Could not add logbook entry as no connection is available\n");
}
break;
case APP_DEMO_STATE_ABORT_AR:
if (app_is_connected_to_controller (app))
{
APP_LOG_INFO (
"Sample app will disconnect and reconnect. Executing "
"pnet_ar_abort() AREP: %u\n",
app->main_api.arep);
(void)pnet_ar_abort (app->net, app->main_api.arep);
}
else
{
APP_LOG_WARNING (
"Could not execute pnet_ar_abort(), as no connection is "
"available\n");
}
break;
}
switch (app->alarm_demo_state)
{
case APP_DEMO_STATE_ALARM_SEND:
app->alarm_demo_state = APP_DEMO_STATE_CYCLIC_REDUNDANT;
break;
case APP_DEMO_STATE_CYCLIC_REDUNDANT:
app->alarm_demo_state = APP_DEMO_STATE_CYCLIC_NORMAL;
break;
case APP_DEMO_STATE_CYCLIC_NORMAL:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_ADD;
break;
case APP_DEMO_STATE_DIAG_STD_ADD:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_UPDATE;
break;
case APP_DEMO_STATE_DIAG_STD_UPDATE:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_ADD;
break;
case APP_DEMO_STATE_DIAG_USI_ADD:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_UPDATE;
break;
case APP_DEMO_STATE_DIAG_USI_UPDATE:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_USI_REMOVE;
break;
case APP_DEMO_STATE_DIAG_USI_REMOVE:
app->alarm_demo_state = APP_DEMO_STATE_DIAG_STD_REMOVE;
break;
case APP_DEMO_STATE_DIAG_STD_REMOVE:
app->alarm_demo_state = APP_DEMO_STATE_LOGBOOK_ENTRY;
break;
case APP_DEMO_STATE_LOGBOOK_ENTRY:
app->alarm_demo_state = APP_DEMO_STATE_ABORT_AR;
break;
default:
case APP_DEMO_STATE_ABORT_AR:
app->alarm_demo_state = APP_DEMO_STATE_ALARM_SEND;
break;
}
}
void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg)
{
app_utils_pnet_cfg_init_default (pnet_cfg);
pnet_cfg->state_cb = app_state_ind;
pnet_cfg->connect_cb = app_connect_ind;
pnet_cfg->release_cb = app_release_ind;
pnet_cfg->dcontrol_cb = app_dcontrol_ind;
pnet_cfg->ccontrol_cb = app_ccontrol_cnf;
pnet_cfg->read_cb = app_read_ind;
pnet_cfg->write_cb = app_write_ind;
pnet_cfg->exp_module_cb = app_exp_module_ind;
pnet_cfg->exp_submodule_cb = app_exp_submodule_ind;
pnet_cfg->new_data_status_cb = app_new_data_status_ind;
pnet_cfg->alarm_ind_cb = app_alarm_ind;
pnet_cfg->alarm_cnf_cb = app_alarm_cnf;
pnet_cfg->alarm_ack_cnf_cb = app_alarm_ack_cnf;
pnet_cfg->reset_cb = app_reset_ind;
pnet_cfg->signal_led_cb = app_signal_led_ind;
pnet_cfg->cb_arg = (void *)&app_state;
}
/**
* Read button states from operating system
*
* Actual reading is done every APP_TICKS_READ_BUTTONS invocation
*
* @param app InOut: Application handle
*/
static void update_button_states (app_data_t * app)
{
app->buttons_tick_counter++;
if (app->buttons_tick_counter > APP_TICKS_READ_BUTTONS)
{
app->button1_pressed = app_get_button (0);
app->button2_pressed = app_get_button (1);
app->buttons_tick_counter = 0;
}
}
void app_loop_forever (void * arg)
{
app_data_t * app = (app_data_t *)arg;
uint32_t mask = APP_EVENT_READY_FOR_DATA | APP_EVENT_TIMER |
APP_EVENT_ALARM | APP_EVENT_ABORT;
uint32_t flags = 0;
app->main_api.arep = UINT32_MAX;
app_set_led (APP_DATA_LED_ID, false);
app_plug_dap (app, app->pnet_cfg->num_physical_ports);
APP_LOG_INFO ("Waiting for PLC connect request\n\n");
/* Main event loop */
for (;;)
{
os_event_wait (app->main_events, mask, &flags, OS_WAIT_FOREVER);
if (flags & APP_EVENT_READY_FOR_DATA)
{
os_event_clr (app->main_events, APP_EVENT_READY_FOR_DATA);
app_handle_send_application_ready (app->net, app->arep_for_appl_ready);
}
else if (flags & APP_EVENT_ALARM)
{
os_event_clr (app->main_events, APP_EVENT_ALARM);
app_handle_send_alarm_ack (
app->net,
app->main_api.arep,
&app->alarm_arg);
}
else if (flags & APP_EVENT_TIMER)
{
os_event_clr (app->main_events, APP_EVENT_TIMER);
update_button_states (app);
if (app_is_connected_to_controller (app))
{
app_handle_cyclic_data (app);
}
/* Run alarm demo function if button2 is pressed */
if (
(app->button2_pressed == true) &&
(app->button2_pressed_previous == false))
{
app_handle_demo_pnet_api (app);
}
app->button2_pressed_previous = app->button2_pressed;
/* Run p-net stack */
pnet_handle_periodic (app->net);
}
else if (flags & APP_EVENT_ABORT)
{
os_event_clr (app->main_events, APP_EVENT_ABORT);
app->main_api.arep = UINT32_MAX;
app->alarm_allowed = true;
APP_LOG_DEBUG ("Connection closed\n");
APP_LOG_DEBUG ("Waiting for PLC connect request\n\n");
}
}
}