/********************************************************************* * _ _ _ * _ __ | |_ _ | | __ _ | |__ ___ * | '__|| __|(_)| | / _` || '_ \ / __| * | | | |_ _ | || (_| || |_) |\__ \ * |_| \__|(_)|_| \__,_||_.__/ |___/ * * 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 #include #include #include /* 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"); } } }