Implementation details ====================== This software stack is written in C, version C99. For running the unit tests C++ (version C++11 or later) is required. The primary p-net use case is to run in embedded devices, why there in general are no functions to shut down the stack. Application relations, communication relations and sessions ----------------------------------------------------------- An "Application Relation" (AR) is a connection to an IO-controller (PLC). An AR consists of two "Communication Relations" (CR) for cyclic data; one input and one output. There might be multiple ARs, as several PLCs can use the same IO-device. On the wire an AR is identified by an UUID (the ARUUID). The first AR has AREP=1, next has AREP=2 etc. Each AR has its own CMDEV state machine. Each AR has two sessions. One session is used for incoming DCE/RPC communication via UDP, and uses the main listening UDP socket. That session is created at the first incoming Connect message, and is reused for the subsequent messages. A separate session is created for the outgoing CControl request to the IO-controller, and it creates its own UDP socket. For an incoming Read Implicit request a session is created, and then closed after the response. Each session has an input and output buffer, in order to be able to handle fragmented messages (larger than a single Ethernet frame). On the wire, the session is identified by the activity UUID in the DCE/RPC header. C header files -------------- The header file ``pnet_export.h`` is generated by cmake. It ends up in ``build/include/pnet_export.h``. Incoming frame PDU types ------------------------ * A_Data Acyclic RTA_CLASS_1 * C_Data Cyclic RT_CLASS_1 or RT_CLASS_2 or RT_CLASS_3 * N_Data RT_CLASS_UDP or RTA_CLASS_UDP * P_Data TimeStamped * R_Data RSI Stack development helper functions ---------------------------------- Use ``pf_memory_contents_show()`` to show the contents of a buffer/memory area. State machines -------------- * ALPMI Alarm Protocol Machine Initiator. Initiates alarm (for sending to IO-controller) * ALPMR Alarm Protocol Machine Responder. Responds to alarm (from IO-controller) * APMR Acyclic Protocol Machine Receiver. Receives high- and low priority alarm Ethernet frames from the IO-controller (both alarms triggered by IO-Controller, and ack to alarms we trigger) * APMS Acyclic Protocol Machine Sender. Sends alarm Ethernet frames to IO-controller (both alarms we trigger, and ack to alarms triggered by IO-Controller) * CMDEV Context Management protocol machine Device, handles connection establishment for IO-Devices. * CMDMC Multicast communication between IO devices via DCP Identify messages. * CMINA Context Management Ip and Name Assignment protocol machine * CMIO Context Management Input Output protocol machine * CMPBE Context Management Parameter Begin End protocol machine. Optional. * CMRDR Context Management Read Record Responder protocol machine, responds to parameter read from the IO-controller * CMRPC Context Management RPC Protocol Machine, handles RPC communication via UDP * CMSM Context Management Surveillance protocol Machine, monitors the establishment of a connection. * CMSU Context Management Start Up machine. Starts other state machines, for example PPM and CPM * CMWRR Context Management Write Record Responder protocol machine * CPM Consumer Protocol Machine, for receiving cyclic data * FSPM Fieldbus Application Layer Service Protocol Machine. Interaction with user application; implements callbacks. * PPM Provider Protocol Machine, for sending cyclic data Alarm related state machines ---------------------------- * APMS: It resends the Alarm DATA frame until it gets a TACK (transport acknowledge) back. * APMR: Receives Alarm DATA frames, and sends TACK. * ALPMI: Issues an Alarm Notification PDU, and waits for the Alarm ACK PDU. * ALPMR: Hands over Alarm Notification PDU to the application, and waits for the application to respond to it. Waits for the sent ACK PDU to have its TACK. Overview of alarm communication: (right-click "View Image" to magnify) .. image:: illustrations/AlarmStateMachines.png Sections in 61784-2 (profiles) describing alarms: +---------------+------------------------------------------------------------------------+ | Section | Description | +===============+========================================================================+ | 7.1.3.2.1 | Communication (Class D should support one additional Device Access AR) | +---------------+------------------------------------------------------------------------+ Sections in 61158-5-10 (services) describing alarms: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 7.1.6.2 | Application process rules (for pull module alarm allowed) | +---------------+-------------------------------------------------------------+ | 7.3.1.3.6.2.5 | State table DEVSM (Multicast Communication Mismatch Alarm) | +---------------+-------------------------------------------------------------+ | 7.3.1.4.6.1.5 | Behavior on transitions (Alarms on IOCS/IOPS) | +---------------+-------------------------------------------------------------+ | 7.3.1.6 | Alarm class | +---------------+-------------------------------------------------------------+ | 7.3.4.2.6.2 | Alarm Behavior (for diagnosis class) | +---------------+-------------------------------------------------------------+ Sections in 61158-6-10 (protocol) describing alarms: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 4.8.2 | RTA transfer syntax | +---------------+-------------------------------------------------------------+ | 4.8.4.2 | APMS | +---------------+-------------------------------------------------------------+ | 4.8.4.3 | APMR | +---------------+-------------------------------------------------------------+ | 5.1.2 | APDU abstract syntax | +---------------+-------------------------------------------------------------+ | 5.2.3 | Coding section related to RTA-SDU specific fields | +---------------+-------------------------------------------------------------+ | 5.2.9 | Coding section related to Alarm and Diagnosis Data | +---------------+-------------------------------------------------------------+ | 5.2.42.2.6 | AlarmCRBlockReq Check incoming message | +---------------+-------------------------------------------------------------+ | 5.2.42.3.5 | AlarmCRBlockRes Check incoming message | +---------------+-------------------------------------------------------------+ | 5.6.1 | ALPMI | +---------------+-------------------------------------------------------------+ | 5.6.2 | ALPMR | +---------------+-------------------------------------------------------------+ | 5.6.3.9 | CMPBE (Alarms during startup) | +---------------+-------------------------------------------------------------+ | A.3 | Startup of Alarm transmitter and receiver | +---------------+-------------------------------------------------------------+ Diagnosis --------- Sections in 61784-2 (profiles) describing LLDP: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 7.1.3.2.2.1 | Diagnosis (less than 65536 octets) | +---------------+-------------------------------------------------------------+ Sections in 61158-5-10 (services) describing diagnosis: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 7.1.4.5.3.6 | Channel and channel numbers | +---------------+-------------------------------------------------------------+ | 7.3.1.5.6.3 | Behavior of the Module Diff Block regarding diagnosis | +---------------+-------------------------------------------------------------+ | 7.3.1.6.1.2 | Alarm types attached to diagnosis ASE | +---------------+-------------------------------------------------------------+ | 7.3.1.6.5.1 | Alarm Notification | +---------------+-------------------------------------------------------------+ | 7.3.2.5 | Observer class (PD Port Data Check etc) | +---------------+-------------------------------------------------------------+ | 7.3.4 | Diagnosis ASE | +---------------+-------------------------------------------------------------+ | Annex F | Precondition for Diagnosis | +---------------+-------------------------------------------------------------+ Sections in 61158-6-10 (protocol) describing diagnosis: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 5.1.2 | APDU abstract syntax | +---------------+-------------------------------------------------------------+ | 5.2.3.2 | Coding of the field AlarmSpecifier | +---------------+-------------------------------------------------------------+ | 5.2.4.4.3 | Grouping of DiagnosisData for the diagnosis records | +---------------+-------------------------------------------------------------+ | 5.2.9 | Coding section related to Alarm and Diagnosis Data | +---------------+-------------------------------------------------------------+ See also the "Diagnosis for Profinet" Guideline published by the Profinet organisation. An array of PNET_MAX_DIAG_ITEMS diagnosis items is available for use. Each subslot uses a linked list of diagnosis items, and stores the index to the head of its list. Logbook ------- For details, see: * Profinet 2.4 Services, section 7.3.6 * Profinet 2.4 Protocol, section 5.2.38 "Coding section related to logbook" * "Specification for GSDML", section 8.26 "LogBookEntryItem" (allowed error codes) FSPM - Fieldbus application layer Service Protocol Machine ---------------------------------------------------------- Stores the user-defined configuration, and calls the user-defined callbacks. Create logbook entries. Reads and writes identification & maintenance records. CMRPC - Context Management RPC device protocol machine ------------------------------------------------------ Handles the DCE/RPC UDP communication in the start up phase, especially these messages: * connect * release * DControl ("Parameter end" is sent to IO-Device) * CControl ("Application ready" is sent to IO-Controller) * parameter read (Uses CMRDR) * parameter write Incoming UDP packets are parsed by ``pf_cmrpc_dce_packet()``, which also prepares the return UDP packet. This is done by putting together incoming fragments and then calling ``pf_cmrpc_rpc_request()``. On DCE RPC connect requests the function ``pf_cmrpc_rm_connect_ind()`` is called, and it will create a DCE RPC connect response. It will also trigger these user callbacks: * ``pnet_exp_module_ind()`` * ``pnet_exp_submodule_ind()`` * ``pnet_connect_ind()`` * ``pnet_state_ind()`` with PNET_EVENT_STARTUP The function ``pf_cmrpc_rm_write_ind()`` is called for incoming (parameter) write request messages, and it will trigger the ``pnet_write_ind()`` user callback for certain parameters. Other parameters are handled by the stack itself. Incoming control (DControl) requests are handled by ``pf_cmrpc_rm_dcontrol_ind()`` which typically triggers these user callbacks: * ``pnet_dcontrol_ind()`` with PNET_CONTROL_COMMAND_PRM_END * ``pnet_state_ind()`` with PNET_EVENT_PRMEND When the IO-device is sending a request to an IO-Controller (and expects a response) a new separate session is started. Incoming CControl responses are handled by ``pf_cmrpc_rm_ccontrol_cnf()``, which will trigger these user callbacks: * ``pnet_state_ind()`` with PNET_EVENT_DATA. * ``pnet_ccontrol_cnf()`` Show current details on the CMRPC state machine:: pf_cmrpc_show(0xFFFF); DCP --- Handles these DCP messages: * Set * Get * Ident * Hello Flashes a LED on reception of the "Set request" DCP message with suboption "Signal". CMINA - Context Management Ip and Name Assignment protocol machine ------------------------------------------------------------------ This state machine is responsible for assigning station name and IP address. Does factory reset when requested by IO-controller. States: * SETUP * SET_NAME * SET_IP * W_CONNECT Helps handling DCP Set and DCP Get requests. CMRDR - Context Management Read record Responder protocol machine ----------------------------------------------------------------- Contains a single function ``pf_cmrdr_rm_read_ind()``, that handles RPC parameter read requests. Triggers the ``pnet_read_ind()`` user callback for some values. Other values, for example the Identification & Maintenance (I&M) values, are handled internally by the stack. This state machine is used by CMRPC. CMWRR - Context Management Write Record Responder protocol machine ------------------------------------------------------------------ Handles RPC parameter write requests. Triggers the ``pnet_write_ind()`` user callback for some values. CMDEV - Context Management protocol machine Device -------------------------------------------------- This handles connection establishment for IO-Devices. For example pulling and plugging modules and submodules in slots and subslots are done in this file. Also implements handling connect, release, CControl and DControl. States: * POWER_ON, Data initialization. (Must be first) * W_CIND, Wait for connect indication (in the connect UDP message) * W_CRES, Wait for connect response from app and CMSU startup. * W_SUCNF, Wait for CMSU confirmation. * W_PEIND, Wait for PrmEnd indication (in the DControl UDP message) * W_PERES, Wait for PrmEnd response from app. * W_ARDY, Wait for app ready from app. * W_ARDYCNF, Wait for app ready confirmation from controller. * WDATA, Wait for established cyclic data exchange. * DATA, Data exchange and connection monitoring. * ABORT, Abort application relation. Implements these user functions (via ``pnet_api.c``): * ``pnet_plug_module()`` * ``pnet_plug_submodule()`` * ``pnet_pull_module()`` * ``pnet_pull_submodule()`` * ``pnet_application_ready()`` Triggers the ``pnet_state_ind()`` user callback with PNET_EVENT_APPLRDY. * ``pnet_ar_abort()`` Show the plugged modules and sub-modules, and number of bytes sent and received for subslots:: pf_cmdev_device_show(); Show current state for CMDEV state machine:: pf_cmdev_ar_show(p_ar); CMSM - Context Management Surveillance protocol Machine ------------------------------------------------------- The CMSM component monitors the establishment of a connection. Once the device enters the DATA state this component is done. This is basically a timer, which has two states; IDLE and RUN. If not stopped before it times out, the stack will enter PNET_EVENT_ABORT state. The timer returns to state IDLE at timeout. Typically the timeout setting is around 20 seconds (can be adjusted by the IO-Controller). The timer is started on PNET_EVENT_STARTUP (at the connect request message), and stopped at PNET_EVENT_DATA. It also monitors these response and indication messages: * Read * Write * DControl It starts the timer at sending the "response" message, and stops the timer when the "indication" message is received. CPM - Consumer Protocol Machine ------------------------------- Receives cyclic data. Monitors that the incoming data fulfills the protocol, and that the timing of incoming frames is correct. Stores incoming data into a buffer. Several instances of CPM can be used in parallel. States: * W_START Wait for initialization * FRUN * RUN Running If there is a timeout in the RUN state, it will transition back to state W_START. Implements these user functions (via ``pnet_api.c``): * ``pnet_output_get_data_and_iops()`` * ``pnet_input_get_iocs()`` Triggers the ``pnet_new_data_status_ind()`` user callback on data status changes (not on changes in the data itself). PPM - Provider Protocol Machine ------------------------------- Sends cyclic data. States: * W_START * RUN Implements these user functions (via ``pnet_api.c``): * ``pnet_input_set_data_and_iops()`` * ``pnet_output_set_iocs()`` * ``pnet_set_primary_state()`` * ``pnet_set_redundancy_state()`` * ``pnet_set_provider_state()`` Block reader and writer ----------------------- The files ``pf_block_reader.c`` and ``pf_block_writer.c`` implement functions for parsing and writing data in buffers. ETH --- Registers and invokes frame handlers for incoming raw Ethernet frames. LLDP - Link Layer Discovery Protocol ------------------------------------ Sections in 61784-2 (profiles) describing LLDP: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 7.1.4.6 | Link layer discovery protocol (Transmission times) | +---------------+-------------------------------------------------------------+ | 7.1.11 | Conformance class behaviors (LLDP MIBs) | +---------------+-------------------------------------------------------------+ Sections in 61158-5-10 (services) describing LLDP: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 6.3.13.2 | IEEE 802.1AB class specification (LLDP) | +---------------+-------------------------------------------------------------+ | 6.3.13.3 | IEEE 802.1AB service specification (LLDP) | +---------------+-------------------------------------------------------------+ | 7.3.3.3 | Communication Interface Management class (LLDP blocking) | +---------------+-------------------------------------------------------------+ | 7.3.3.10 | MIB class (LLDP MIB) | +---------------+-------------------------------------------------------------+ Sections in 61158-6-10 (protocol) describing LLDP: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 4.11 | Link layer discovery (LLDP abstract & transfer syntax) | +---------------+-------------------------------------------------------------+ | 4.16.6 | MIB cross reference (LLDP MIB) | +---------------+-------------------------------------------------------------+ | 4.16.8 | LLDP EXT MIB (found in Annex U) | +---------------+-------------------------------------------------------------+ | Annex U | LLDP EXT MIB | +---------------+-------------------------------------------------------------+ Simple Network Management Protocol (SNMP) ----------------------------------------- Sections in 61158-5-10 (services) describing SNMP: +---------------+-------------------------------------------------------------+ | Section | Description | +===============+=============================================================+ | 6.3.5 | Simple network management ASE | +---------------+-------------------------------------------------------------+ | 6.3.13.1 | IEEE 802.1AB ASE Overview | +---------------+-------------------------------------------------------------+ | 7.3.3.1 | Communication Interface Management ASE Overview | +---------------+-------------------------------------------------------------+ | 7.3.3.3.4 | Attributes for Communication Interface Management class | +---------------+-------------------------------------------------------------+ | 7.3.3.3.5 | Services for Communication Interface Management class | +---------------+-------------------------------------------------------------+ | 7.3.3.3.6.2 | Persistency | +---------------+-------------------------------------------------------------+ | 7.3.3.3.6.5 | Station Name / IP address. DHCP requirement. | +---------------+-------------------------------------------------------------+ | 7.3.3.10 | MIB class | +---------------+-------------------------------------------------------------+ Sections in 61158-6-10 (protocol) describing SNMP: +---------------+-------------------------------------------------------------------------+ | Section | Description | +===============+=========================================================================+ | 4.11.3.18 | Coding section related to LLDP | +---------------+-------------------------------------------------------------------------+ | 4.16 | Simple network management | +---------------+-------------------------------------------------------------------------+ | 5.2.41 | Coding of the field SNMPControl, CommunityStringLength, CommunityString | +---------------+-------------------------------------------------------------------------+ | Annex S | List of supported MIBs | +---------------+-------------------------------------------------------------------------+ | Annex U | Extension to a MIB | +---------------+-------------------------------------------------------------------------+ | Annex W.4 | Statistic counters in SNMPv1 and SNMPv2 | +---------------+-------------------------------------------------------------------------+ Sections in 61784-2 (profiles) describing SNMP: +---------------+-------------------------------------------------------------------------+ | Section | Description | +===============+=========================================================================+ | 7.1.4.11 | Simple Network Management Protocol (Community strings and timeouts) | +---------------+-------------------------------------------------------------------------+ | 7.1.11 | Conformance class behaviors (Mandatory MIBs) | +---------------+-------------------------------------------------------------------------+ See also the "Topology and Asset Discovery" guideline published by the Profinet organisation, and the list of supported OIDs in the test case specification "Topology discovery check". Conformance class D ------------------- 7.1.3.2.1 Communication (Class D should support one additional Device Access AR) Start up procedure ------------------ +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | Incoming | | Outgoing | | CMDEV | Application | Other | | | message | | message | | state | | | +==================+====================+=======================+============================================+=====================================+ | Connect req | | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_exp_module_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_exp_submodule_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_connect_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_CRES | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | | PPM starts sending cyclic data | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | | PF_CPM_STATE_FRUN | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_SUCNF | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_connect_ind() with PNET_EVENT_STARTUP | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | | CMSM timer started | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_new_data_status_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | | PF_CPM_STATE_RUN | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_PEIND | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | Connect resp | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | Write req | | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_write_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | Write resp | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | DControl req | | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_PERES | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_dcontrol_ind() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_ARDY | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | (PRMEND) | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_connect_ind() with PNET_EVENT_PRMEND | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | Run pnet_input_set_data_and_iops() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | DControl resp | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | Run pnet_application_ready() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | (APPLRDY) | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_connect_ind() with PNET_EVENT_APPLRDY | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | CControl req | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | W_ARDYCNF | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | CControl resp | | | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | WDATA | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_ccontrol_cnf() | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | | pnet_connect_ind() with PNET_EVENT_DATA | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ | | | DATA | | | +------------------+--------------------+-----------------------+--------------------------------------------+-------------------------------------+ Useful functions ---------------- Show lots of details of the stack state:: pnet_show(net, 0xFFFF); Coding rules ------------ In order to be platform independent, use ``CC_ASSERT()`` instead of ``assert()``. Use ``ASSERT()`` for rt-kernel-specific code. Include headers in sorted groups in this order: * Interface header (Corresponding to the .c file) * Headers from same project * Headers from the operating system * Standard C headers New files should have the rt-labs standard header comment, with description of the license and "Copyright YYYY rt-labs AB, Sweden." Avoid "Yoda conditions":: if (3 == a) { /* ... */ } Use C-style comments in C files, C or C++ comments in C++ files:: /* C style comment */ // C++ style comment Declare (and initialize) internal variables in the beginning of a function. Function names should start with the file name, for example functions in ``src/common/pf_ppm.c`` are named ``pf_ppm_xxx``. Typically functions should return 0 on success and -1 on error. Name functions and variables using "snake_case", for example ``pf_lldp_get_chassis_id ()`` and ``min_device_interval``. Avoid to start pointer names with ``p_``. It can be useful in some special situations but we will gradually remove those names from the p-net stack. Instead of:: if (have_dhcp == true){...} if (!have_dhcp){...} use:: if (have_dhcp){...} if (have_dhcp == false){...} (Note that this not yet is fully implemented in the stack.) Run clang-format on staged files before committing:: $ git add . $ git clang-format This will format the commit using clang-format. Examine and stage modified files before finalizing the commit. Workflow -------- We have chosen to host the code on Github to ease the collaboration between users and different developers, and we take advantage of the standard Github workflow: * Open a Github issue on https://github.com/rtlabs-com/p-net/ for each separate bug found. * Fork the repository to your own account on Github, and make a local clone on your work station. * Create a branch with a descriptive name. * Commit your fix to the branch. Add the line "Closes #123" (for example) in the commit message, to indicate which Github issue it closes. * Push the branch to your Github account. * Create a pull request to https://github.com/rtlabs-com/p-net * After review the fix will be merged.