ProtoPlexer

description.markdown at tip
Login

File description.markdown from the latest check-in


ProtoPlexer protocol

Description

ProtoPlexer protocol is lightweight hardware protocol designed to be used in small satellite onboard network. It was designed to with CAN satellite network in mind, yet it should be suitable for other onboard hardware networks and for other applications.

Features

Underlaying protocol requirement.

To be implemented underlaying protocol should provide several features such as:

Deployment overview

To deploy and use ProtoPlexer user should:

Protocol key parts and description

First lets describe some key definitions of the protocol parts.

Nodes

Every logical instance within network have 12bit address and called node. Nodes could be hardware devices, software applications, or logical parts of software applications. Every time you need to separate network flow to or from one part of your system - you could create a node for it and assign address.

Messages

Protocol transfer data between nodes using messages, messages have following structure:

Hardware header contains data used to transmit data over underlaying protocol.

Addresses from an to - are addresses of transmitting and receiving nodes accordingly.

Inverted priority - is priority used to transmit message over underlaying protocol. Because ProtoPlexer designed with CAN network in mind and main application is satellite protocol mostly for CAN network, which uses inverted priority for messages, we also use inverted one. So the lowest parameter value means biggest priority. It's advised to use lowest priority (biggest value) unless you have to do otherwise.

Message header contains data used to transmit data within ProtoPlexer protocol

Message ID is unique ID for specific message type. Protocol does not yet specify Message ID's or their usage, but range 0xFFF0-0xFFFF should be considered reserved for protocol purposes. Later it may contain messages like "ping" "version" and other, which should be implemented on every node. It's possible to use the same Message ID for different purposes within differed devices, yet it's strongly not recommended, unless you have to.

Length and CRC used within protocol and shouldn't be used by user. They, as you could expect are the whole length of the message and it's CRC16 value. 16bit length means messages longer then 64kbytes are not possible.

Data is any user data associated with Message ID it's advised to use strong correlation between the Message ID and data interpritation. Also you should consider using messages without data. Sometimes Message ID is enough to transmit command for the node.

Chunks

Chunks - are minimal frames used to transmit data over underlaying protocol. ProtoPlexer separate every message to the chunks according to hardware settings, and transmit data over underlaying protocol in chunks. In CAN network they are equal to CAN frames. During receive ProtoPlexer receive separate chunks from network, and keep them till the whole message is ready.

Every chunk have the following fields

Different approaches could be used to transmit chunks on different hardware networks, example for some of them would be provided in further articles. Underlaying protocol and it's hardware driver should receive data in chunks, transmit them and provide received chunks to the protocol.

Plexer

Plexer is actual ProtoPlexer implementation class. It has following interface

plexer_result poll_rx();
plexer_result poll_tx();
plexer_result poll_new();
plexer_result send_msg (plexer_msg * msg);
plexer_result add_chunk(const plexer_chunk * chunk);

virtual plexer_event_result rx_event (plexer_msg *, plexer_eventtype);
virtual shared_ptr<plexer_chunk> receive_chunk();
virtual plexer_hw_result send_chunk(plexer_chunk * chunk);

uint16_t own_address() const;
uint_fast16_t max_send_attempts() const;
void setMax_send_attempts(const uint_fast16_t &max_send_attempts);
uint_fast16_t max_chunk_length() const;

static std::string result2string(plexer_result value);

poll_rx function should be called from user software. This function check (once) plexer_hw for new messages, and in case last chunk of valid message received it also calls receive event with polling type of event. This function will not keep asking plexer_hw till it's empty. In case you want such behavior you should poll plexer itself till it respond that hardware is empty.

poll_tx function is used to transmit data to network. When message sent protocol will not block thread till it actually transmitted over network. ProtoPlexer will cut the message to chunks according to settings and put them into outgoing buffer. To send message you should call poll_tx till this buffer is empty. ProtoPlexer will try to send every message up to 5 times as reaction to poll_tx and will return "ok" if hardware is busy. Only afterwards it'll respond with error code.

Note: this is only required when using plexer_queued. plexer_blocking will send outgoing chunks immediately.

poll_new function used to check received messages buffer. We need one because messages not always cleared after event. In case some messages exist in ProtoPlexer internal buffer it'll also call receive event with polling event type.

Note: this is only required when using plexer_queud. plexer_blocking will react to new messages immediately.

add_chunk function used internally by poll_rx but you could also use it in your hardware driver. Your driver may call this function right after receiving chunk. This way plexer will not need to wait till application poll it for the new messages, and will generate event (if needed) right away

It's possible to use both ways to add chunks to the protocol. But you should guarantee sequence order. Easiest way to do so is limiting receive to one of two ways only calling add_chunk from hardware driver, or only responding to poll_rx

rx_event is virtual method used to provide reaction to received messages. Your event should react to the message according to your needs.

receive_chunk is a virtual methods providing access to underlaying protocol driver. It's used by plexer_queued to poll the hardware driver for new chunks in poll_rx

send_chunk is one of virtual methods providing access to underlaying protocol driver. It's used by plexer internally to send chunks over underlaying protocol during message transmission.

own_address is address of this node in the ProtoPlexer network

max_send_attempts / setMax_send_attempts is getter and setter for send attempts limit in plexer_queued. Default value is 5. Every time user poll poll_tx he will receive result of such polling. In case plexer will be unable to send chunk within max_send_attempts count user will still receive ok error code. If plexer will fail to transmit one chunks more than max_send_attempts count user will receive hw_send_too_many_attempts result. Plexer will reset counter to zero, but will not remove such chunk, and will continue with it later.

Note: plexer_queued assumes that the driver will take care of handling retries.

max_chunk_length will be used by protocol to cut long messages to chunks during send implementation. Yet your driver could return longer chunks from receive_chunk() it's set once by plexer constructor

result2string will just convert error codes to string for debug/logging purposes

Deployment

First you need to create class derived from plexer_queued for example

class some_example_class : public plexer_queued
{
public:
    // where some_hardware_const is the maximum number transmission unit of the network
    // (i.e. the number of bytes that can be transmitted in one network packet)
    // E.g. 8 for CAN, 64 for CAN-FD, 1500 for Ethernet and so on
    some_example_class(uint16_t own_address) :plexer(own_address, some_hardware_const) { }

    shared_ptr<plexer_chunk> receive_chunk() override;
    plexer_hw_result send_chunk(plexer_chunk *chunk) override;
    plexer_event_result rx_event(plexer_msg * msg) override;
};

Also you need some underlaying protocol driver connected to plexers methods. It could be inside or outside new class, example not provided.

You also need some event to process received messages. Example of such event could look like this:

void some_example_class::rx_event(plexer_msg * msg)
{
    switch (static_cast<some_mission_specific_enum>(msg->message_ID()))
    {
        case some_mission_specific_enum::ID_reboot:
        {
            reboot();
        } break;
        case some_mission_specific_enum::ID_hello:
        {
            cout << "Hello";
        } break;
        default:
        {
            cout << "Unknown message";
        } break;
    }
}

All you need now is an instance of your class to communicate with ProtoPlexer network.

auto plexer_inst = make_shared<plexer>(some_own_address_constant);

Hardware specific implementation

Since ProtoPlexer use some features of underlaying protocol for addressing and header transmission those rules should be fixed for every givend underlaying protocol. If some is not covered in this document you could design your own approach, and use it within your system. You could also send it to be integrated in this document.

CAN implementation

In CAN implementation maximum chunk length is fixed to be 8bytes exactly and chunks are actually CAN frames. CAN bus should use 29bit CAN_ID ProtoPlexer hardware header is transmitted inside CAN_ID as following:

CAN_ID bits 25-28 24 12-23 0-11
fields priority inverted starting bit address from address to

UART implementation

In UART implementation maximum chunk length is mission specific, yet no more than 250bytes and chunks contain 4 bytes header with all chunk specific information. Chunks are separated using COBS protocol

UART bits 25-32 24 12-23 0-11
fields priority inverted starting bit address from address to