-
Notifications
You must be signed in to change notification settings - Fork 57
Advanced topics message delivery
A crucial factor when dealing with heterogeneous sensor networks is the delivery of messages from one node to another when both nodes are based on different hardware platforms. There are mainly two problems that can occur. First, the underlying systems may be big and little endian, respectively. So, when the one node writes a 32-bit integer value as big endian, it can not be read correctly on the other node when that is a little endian one. Second, not all hardware platforms may support reading all types from all addresses. So, for example, the MSP430 can not directly read a 16-bit integer that starts at an odd address in memory.
Our solution of this problem contains again heavy template usage to be able to be both flexible and efficient. First of all, we give templated basic read and write operations that work for different types and generic buffers. Each platform (Os Model) can now specialize these operations for its own purposes - and provide an optimal implementation. Then, messages should basically consists of a data buffer, whereby read and write operations are only done by our previously defined (templated) operations. We also define that each data value inside a message must be stored in network byte order (hence, systems already using this order can implement very efficient access operations).
The basic functions that can be called from the outside (e.g., when writing data to messages, or read entries from a message):
template<typename OsModel_P, typename BlockData_P, typename Type_P> inline Type_P read( BlockData_P *target ) { return Serialization<OsModel_P, BlockData_P, Type_P>::read( target ); } template<typename OsModel_P, typename BlockData_P, typename Type_P> inline typename OsModel_P::size_t write( BlockData_P *target, Type_P& value ) { return Serialization<OsModel_P, BlockData_P, Type_P>::write( target, value ); }
Note that the BlockData_P, albeit mostly just an uint8_t taken from the Radio or OsModel, can not be used as a default template parameter, because this is not allowed for function templates. Internally, they call static member functions from the class Serialization
, because function templates can not be partially specialized. Instead, one must (partially) specialize the Serialization
class. The basic implementation looks as follows:
template<typename OsModel_P, typename BlockData_P, typename Type_P> class Serialization { public: typedef OsModel_P OsModel; typedef BlockData_P BlockData; typedef Type_P Type; typedef typename OsModel::size_t size_t; static inline Type read( BlockData *target ) { return *((Type*)target); } static inline size_t write( BlockData *target, Type& value ) { *((Type*)target) = value; return sizeof(Type); } };
Example of message implementation:
#include "util/serialization/simple_types.h" concept Message { public: typedef OsModel_P OsModel; typedef Radio_P Radio; typedef typename Radio::block_data_t block_data_t; inline uint8_t msg_id() { return read<OsModel, uint8_t>(buffer); } inline void set_msg_id( uint8_t id ) { write<OsModel, block_data_t, uint8_t>(buffer, id); } // number of bytes in payload; is set automatically when calling set_data inline uint8_t payload_size() { return read<OsModel, block_data_t, uint8_t>(buffer + 1); } // access user data inline block_data_t payload() { return buffer + 2; } // set arbitrary user data inline void set_payload( uint8_t len, block_data_t *data ) { // write number of bytes for payload, followed by payload itself write<OsModel, block_data_t, uint8_t>(buffer + 1, len); memcpy( buffer + 2, data, len ); } // buffer size is the number if bytes needed when copying this message // to a message buffer (e.g., when passing to the radio) inline size_t buffer_size() { return 2 + len(); } private: // 2 is the minimum data length given to message id and data length; // since the data can be of arbitrary length, its maximum is defined // by the template parameter DATA_SIZE block_data_t buffer[2 + DATA_SIZE]; };
Data representation:
- Nice Discussion in Google Groups
- RFC1932: XDR: External Data Representation Standard
- Big/Little Endian in C++ (but unfortunately not available in all compilers/ e.g. not in msp430-g++ and avr-g++)
Other Serialization Libraries:
Floating Point: