src/platform/net/msg.h File Reference

Handles MCF packed messages from the higher protocol layer. More...

#include "errorcode.h"
#include "cfg_msg.h"
#include <stdint.h>
#include <stdbool.h>
#include "util/databuffer.h"
#include "net/net.h"
#include "net/mcf.h"

Go to the source code of this file.

Data Structures

struct  MsgId
 A MsgId is a struct which contains all information required to generate a valid reply. More...

Defines

#define E_MSG_TYPE_UNKNOWN   ( E_MSG + 0x01 )
 Message unknown.
#define E_MSG_TYPE_UNKNOWN_DESCR   "Unknown message"
#define E_MSG_FUNCTION   ( E_MSG + 0x02 )
 Message function returned false.
#define E_MSG_FUNCTION_DESCR   "Function error"
#define E_MSG_CLASS_UNSUPPORT   ( E_MSG + 0x03 )
 Message class received not supported.
#define E_MSG_CLASS_UNSUPPORT_DESCR   "Class not supported"
#define MSG_CMD(CMD_TYPE, CMD_STUB)   bool _msg ## CMD_STUB (MsgId * id, DataBuffer * buf);
 Generate all message callbacks.

Functions

void msgRx (SockAddr *addr, uint8_t *data, int len)
 Receive a packet with data as a command.
void _msgRxCmd (MsgId id, DataBuffer *buf)
 Invoked when a command is received.
bool msgTxReply (MsgId *id, DataBuffer *buf)
 Invoke to send a reply.
bool msgTxError (MsgId *id, int errCode, const char *errMsg)
 Invoke to send an error response.
void _msgTx (SockAddr *addr, uint8_t *data, int len)
 Stub function, invoked by RMT when sending a MCF packet.

Detailed Description

Handles MCF packed messages from the higher protocol layer.

Commands & Replies ================== From the lower-level protocol layer (in this case SRP, but it might just as well be TCP/IP), the _msgRx() command is invoked. The received packets are unpacked, and its contents decoded.

For each command a function prototype is generated, which should be implemented by the higher level functions. These commands get a message identifier and a DataBuffer containing the content of the message.

For example, the ReadAddr message is defined as follows: MSG_CMD(GROUP_DEBUG, 1, ReadAddr)

The message module will then generate a stub-signature as follows:

 bool _msgReadAddr(MsgId * msgId, DataBuffer * content)

However, the implementation is left to the higher-level layers.

There are 3 ways in which you can generate a reply: 1. Synchronous (returns < 100ms) 2. ASynchronous (returns < 1 second) 3. Synchronous + Event (anything > second)

Which to take depends on the situation, however, take care with option 1, since it will stall the network stack.

Synchronous ----------- Synchronous replies are replies which yield a result immediately. They will be used most common situations.

For example, the following code returns a number of memory addresses read.

 bool _msgReadAddr(MsgId * msgId, DataBuffer * cmd)
 {
  uint32_t * addr;
    uint16_t count;
    // parse command arguments
    dbReadUInt(cmd, &addr);                             // read first argument, the offset address
    dbReadUShort(cmd, &count);                          // read second argument the count
    if (count > MAX_READS) {                            // can't read that many addresses
        msgTxError(msgId, E_INVALID_ARGUMENT);          // return an error
        return true;                                    // true only means 'I've handled the error'
    }

    // create reply arguments
    DataBuffer rply = DB_BUF_INIT(replyBuf, MAX_SIZE);  // initialize reply buffer
    dbWriteUShort(&reply, count);                       // return no of elements in return array
    for (i = 0; i < count; ++i, ++addr)                 // for each address
        dbWriteUInt(&reply, *addr);                     // load the data into the reply buffer

    msgTxReply(msgId, &reply);                          // send the reply to the message processing
    return true;
 }

ASynchronous ------------ However, it is not required to generate a reply inside the processing function. Functions which require some asynchronous processing, but don't take more than a second, can use asynchronous processing.

    static volatile MsgId       _asyncMsgId;
    static volatile bool        _asyncBusy;

    bool _msgCalibrateTDCs(MsgId * msgId, DataBuffer * cmd)
    {
        if (_eventBusy) return false;   // its better to generate a nice reply, but this will do

        _asyncMsgId = *msgId;           // must copy it: pointer no longer valid after function!
        _asyncBusy = true;

        tdcCalibrateAll();

        return true;                    // don't generate a reply
    }

    void replyTDCCalibratResult()
    {
        int i;
        DataBuffer rply = DB_BUF_INIT(replyBuf, MAX_SIZE);  // initialize reply buffer
        dbWriteUShort(&reply, TDC_CHANNELS);                // return no of elements in return array
        for (i = 0; i < TDC_CHANNELS; ++i)                  // for each channel
            dbWriteUInt(&reply, tdcGetCalibValue(i));       // load the TDC calibration value

        msgTxReypl(&_asyncMsgId, &reply);                   // create the event

        _asyncBusy = false;                                 // allow new async replies

        return true;

    }

    // TDC IRQ
    void IRQ_HANDLER(TDC_IRQ) {
        // take it outside of the interrupt handler, message processing is not thread safe.
        if (tdcGetIrq() & TDC_IRQ_CALIBRATION_DONE) {
            schdCallOnce(replyTDCCalibratResult);   // command, not yet implemented.
            tdcClearIrq(TDC_IRQ_CALIBRATION_DONE);
        }
    }

Event Reply ----------- An event reply is used when the function takes even more time. For simplicity sake we will use the same code as before, and modify it to be an event.

First of all the code immediately generates an OK reply. Second the function msgTxReplyEvent is used instead of msgTxReply. Those are the only differences at this side.

In practice what will happen on the shore-station side is that the function is invoked, and returns immediately, later on the reply will be passed to the shore-station API and returned through an asynchronous listener.

    static volatile MsgId       _eventMsgId;
    static volatile bool        _eventBusy;

    bool _msgCalibrateTDCs(MsgId * msgId, DataBuffer * cmd)
    {
        if (_eventBusy) return false;   // its better to generate a nice reply, but this will do

        _eventMsgId = *msgId;   // must copy it, pointer no longer valid after this function!
        _eventBusy = true;

        tdcCalibrateAll();

        msgTxReply(msgId, NULL);    // generate simple Ok.

        return true;
    }

    void replyTDCCalibratResult()
    {
        int i;
        DataBuffer reply = DB_BUF_INIT(replyBuf, MAX_SIZE); // initialize reply buffer
        dbWriteUShort(&reply, TDC_CHANNELS);                // return no of elements in return array
        for (i = 0; i < TDC_CHANNELS; ++i)                  // for each channel
            dbWriteUInt(&reply, tdcGetCalibValue(i));       // load the TDC calibration value

        msgTxReplyEvent(&_eventMsgId, &reply);              // create the reply event

        _eventBusy = false;                                 // allow new events

        return true;

    }

    // TDC IRQ
    void IRQ_HANDLER(TDC_IRQ) {
        // take it outside of the interrupt handler, message processing is not thread safe.
        if (tdcGetIrq() & TDC_IRQ_CALIBRATION_DONE) {
            schdCallOnce(replyTDCCalibratResult);   // command, not yet implemented.
            tdcClearIrq(TDC_IRQ_CALIBRATION_DONE);
        }
    }

Stand-Alone Events ================== The Msg module can also be used to generate periodic stand-alone events. These can be used to communicate status updates, or maybe unexpected failures.

However, the address to send the event to should be known before hand. To do this we first need to configure the receiver. For this we have defined the 'ConfStatusReceiver' command which takes the sender of the command to be the receiver for asynchronous events. This command should be invoked by the shore station before events can be send.

  static SockAddr _statusRecv;
  static bool     _statusRecvValid;

  // configures the address to send status events to.
    bool _msgConfStatusReceiver(MsgId * msgId, DataBuffer * cmd)
    {
        _statusRecv = msgId->addr;  // copy socket address from message ID;
        _statusRecvValid = true;

        msgTxReply(msgId, NULL);    // generate simple Ok.

        return true;
    }

Once this is done, we can schedule a periodic function to create status messages.

  #define MSG_EVT_STATUS        MSG_MK_TYPE(GROUP_CLB, 1)

  static int statusTask;

  static void statusFull(DataBuffer * eventData)
  {
    // outside of scope of example, fills the event with the required fields.
  }

  // called every second
    void statusPeridoic()
    {
        if (!_statusRecValid) return;       // no valid sender address, cancel.
        uint8_t eventBuf[MAX_SIZE];
        DataBuffer event = DB_BUF_INIT(eventBuf, MAX_SIZE);
        statusFill(&eventBuf);
        msgTxEvent(MSG_EVT_STATUS, &statusRecv, &event);
    }


    bool statusInit()
    {
        if (!schdRegister(statusPeriodic, false, &statusTask)) return false;
        if (!schdPeriodic(statusTask, 1000)) return false;
    }

Other remarks ============= Message Container Format is just what it is, a format. It does not imply any kind of protocol. The Msg module, however, uses it to parse and format messages. Still on the remote side it is expected that a command always yields a reply within a specified time. Care must be taken by the higher-level layer, that is, a command implementation, to always generate a reply. In its simplest form this is done by returning false in the stub function. Failure to do so will cause time-out's on the shore-station, and may hold-up processes.


Function Documentation

void _msgRxCmd ( MsgId  id,
DataBuffer buf 
)

Invoked when a command is received.

Parameters:
id 
buf 
void _msgTx ( SockAddr addr,
uint8_t *  message,
int  len 
)

Stub function, invoked by RMT when sending a MCF packet.

Parameters:
addr Target address
data Pointer to the data
len Length of the message.
void msgRx ( SockAddr addr,
uint8_t *  data,
int  len 
)

Receive a packet with data as a command.

Parameters:
addr Source address
data Pointer to the message data
len Length of the message
bool msgTxError ( MsgId id,
int  errCode,
const char *  errMsg 
)

Invoke to send an error response.

Parameters:
id The message ID
errCode The error code to reply.
errMsg The error message to response, may be empty or NULL.
bool msgTxReply ( MsgId id,
DataBuffer buf 
)

Invoke to send a reply.

Parameters:
id The message ID
buf The content to send.

Generated on 19 Sep 2013 for KM3NeT CLB by  doxygen 1.6.1