Usage¶
To use microparcel_tools from command line:
microparcel_tools path/to/schema.json --py path/to/generate/py/dir -cxx path/to/generate/c++/dir
or
python -m microparcel_tools path/to/schema.json --py path/to/generate/py/dir -cxx path/to/generate/c++/dir
The schemas is the source file, written in JSON. See the examples in examples. It will generates: * A Message File: defines all the necessary fields * A Router File per endpoint; they provides static “make” methods and pure virtual “process” methods.
basically, it describes nodes and fields.
A node can have children node, it allows to organize messages by subject. And a node without children (called a Leaf) can have Fields.
The endpoints are the termination of the serial line (eg: Master or Slave). A message is send by one or more endpoint (defined by sender) This defines which Endpoint has a “make” method and a “process” method
The microparcel_tool generates, in python or C++, source code to generate microparcel messages, or to process them, in order to send them via a serial line.
This piece of Schema will generate
"name":"Farm",
"version":{"major":0, "minor":1},
"endpoints":["Master", "Slave"],
"nodes":{
"name":"MsgType",
"children":[
{
"name":"Measure",
"children":[
{
"name":"Windspeed",
"senders":["Slave"],
"fields":[
{"name":"ID", "short_name":"ID", "bitsize":4},
{"name":"Unit", "short_name":"Un", "enum_name":"SpeedUnit"},
{"name":"Value", "short_name":"Va", "bitsize":12},
]
},
A Master Router
class FarmMasterRouter {
virtual void processWindspeed(uint8_t in_windspeedid, FarmMsg::SpeedUnit in_windspeedunit, uint16_t in_windspeedvalue) = 0;
};
A Slave Router
class FarmSlaveRouter {
static FarmMsg makeWindspeed(uint8_t in_windspeedid, FarmMsg::SpeedUnit in_windspeedunit, uint16_t in_windspeedvalue){
FarmMsg msg = FarmMsg();
msg.setAddress(in_address);
msg.setProtocolVersion(in_protocolversion);
msg.setMsgType(FarmMsg::MsgType_Measure);
msg.setMeasure(FarmMsg::Measure_Windspeed);
msg.setWindspeedID(in_windspeedid);
msg.setWindspeedUnit(in_windspeedunit);
msg.setWindspeedValue(in_windspeedvalue);
return msg;
}
};
Sending Message¶
Creating and sending a message is easy (from Slave side):
#include <microparcel/microparcel.h>
#include "FarmMsg.h"
#include "FarmSlaveRouter.h"
class FarmSlaveRouterImplementation: public FarmSlaveRouter {
// need to implement all pure virtual process methods for others messages.
// virtual void process...{
//}
};
// prototype to send data
void send(uint8_t *data, uint8_t datasize);
int main(){
// a Parser for FarmMessage.
using TParser = microparcel::Parser<FarmMsg::kSize>;
FarmMsg msg = FarmSlaveRouterImplementation::makeWindspeed(5, FarmMsg::SpeedUnit_Knot, 100);
// builds the frame, with SOF and checksum
TParser::Frame_T frame = TParser.encode(msg);
// send over physical layer of choice
send((uint8_t*)&inFrame, TFrame::FrameSize);
}
Receiving message¶
The master side and the slave implements the virtual process methods; where their parameters are the relevant one (windspeed and ID of the measure)
A Router has a “process” methods:
void process(FarmMsg &in_msg){
// big automatic generated switch-case
Calling “process” with a VALID message received from microparcel ‘s Parser.parse will call the right virtual processes method.
#include <microparcel/microparcel.h>
#include "FarmMsg.h"
#include "FarmMasterRouter.h"
class FarmMasterRouterImplementation: public FarmMasterRouter {
virtual void processWindspeed(uint8_t in_windspeedid, FarmMsg::SpeedUnit in_windspeedunit, uint16_t in_windspeedvalue){
// DO SOMETHING
notifyViaWifi(in_windspeedid, in_windspeedvalue);
}
};
// a way to get data from a Serial Line (UART ?)
uint8_t getByteFromDataLine();
bool isDataLineEmpty();
int main(){
// a Parser for FarmMessage.
using TParser = microparcel::Parser<FarmMsg::kSize>;
FarmMasterRouterImplementation fmri;
FarmMsg msg;
TParser parser;
TParser::Status status;
// main loop of embedded application
while(true){
// continue till the fifo is empty
while(!isDataLineEmpty()){
uint8_t byte = getByteFromDataLine();
status = parser.parse(byte, &msg);
switch(status){
// not complete and error could be treated differently...
// error means mainly that the checksum is not valid; transmission failed.
case TParser::eNotComplete:
case TParser::eError:
break;
case TParser::eComplete:
// msg is complete, handle it
fmri.process(msg);
break;
}
}
}
}