ArduinoMsgGenerator

arduino3 branch (along with ArduinoMsgGenerator) and MRLCOMM version 46 has been merged into develop branch ... WHOOHOO !

publishPinArray and enablePin appear to be working with new ArduinoMsgGenerator framework now...
(on arduino3 branch)

I've pushed all the stuff I have been working on into a new branch called arduino3.

All the important stuff is in src/resource/Arduino/generate

They include all template files & the arduinoMsgs.schema.

A huge amount of power is given to the one who changes the schema.
You can change method signatures then run

ArduinoMsgGenerator

And all the RPC methods will be created for you.
I've tested all the documented data types - pick what is appropriate for your method calls.

Once the generator runs, you can upload the new code in an Arduino and call it from the Arduino service, or call any Arduino service method from MrlComm.

The compiler will do type checking and verify any methods expected are implemented in some form (very nice!)

At the time of writing I had Servo working - none of the other devices I have checked, but done a first pass, best effort on trying to re-implement them.

I need your help !   I'd like anyone interested to try to understand the pattern and make use of it.  I suspect Arduino service will go through further refactoring.   Its becoming more and more a transparent "pass through" to the C++ code into MrlComm.

Besides the message generator and the (I think) solid & easy to maintain RPC code, there are a few other really big changes..

1. Arduino service owns the deviceId.  
2. There are no (or few - which need to be removed) while(x != null) sleep loops.   Initial connect is done with thread locking - and it works without any unecessary wait time.  MrlComm start talking first, Arduino service is listening.
3. publishBoardStatus comes back with an array of deviceIds & deviceTypes (Yay)
4. Rebuild of devices should be possible and fairly easy - ie complete disconnect & power outage of MrlComm/Arduino Hardware -> reconnect -> send all device info -> back to were you were.
I think this is a new level of robustness.

5.  Attaching devices is explicit - previously all config data on deviceAttach was sent into an object array. This is still done in the DeviceMapping, but only to preserve the data for future disconnect/rebuild of the deviceList. 

Patterns : - the concept of 'attach' we want to provide as a high level interface to the user or builder of a script - where it's easy for them to attach services with other services.  To attach a MrlComm device to the deviceList with initialization data -> it needs to be explicit

I've created servoAttach as an example for other devices.

 

In general there should be "NO PARSING, OR BIT OPERATIONS" needed in human created code. 

I hope we can go through the rest of the devices, clean Arduino code, and quickly get this branch merged back onto develop

YAY !


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Mats's picture

i2c

Hi GroG

I pulled the arduino3 branch and started to look at the code. 

To expain how I have been thinking in the current i2c implementation:

An i2cBus represent the physical 2-wires. It is very similar to a serial line. 

An i2cDevice represents any type of i2c enabled device like the Adafruit16CServoDriver, OledSsd1306, Mpu6050, Bno055 and so on. 

In MRLComm a device represents an object with a set of methods. In the case of i2c only one set of methods  is needed even of you connect multiple i2cDevices to the same i2cBus. So to preserve memory in MRLComm only one device/object is created. It represent one single i2cBus.

So I will change i2cAttach in the schema to i2cBusAttach to make the difference clear.

i2cBusAttach will be an internal Arduino method that corresponds to the i2cBus object creation in MRLComm.

i2cAttach will be the method used in the i2CController interface used by all the i2c devices

I hope this makes sense. 

 /Mats

GroG's picture

It does It does make sense !

It does It does make sense ! :D

Great, 

excellent plan, and I think the MrlSerial device should behave the same way...

calamity's picture

Hi Grog Yesterday I have take

Hi Grog

Yesterday I have take a small look on what to do to make the neopixel service worky again. With a small change all seem to go errorless, but I have not test yet with a physical device.

It look very great, but i'm still a little bit confuse about how to implement new things and generate all the code need to have it work.

Let said I want to implement the following method -> newServiceMethod(byte deviceId, long data1, byte[] data2)

Can you descripbe the step need to implement it and have everything generated?

Another thing I have notice, at least for neopixel service, is that you use byte deviceID, I think we should use a bigger holder than byte because if the deviceID increase everytimes a device get attach/detach, the maximum value will be quickly reached.

GroG's picture

Sure Calamity, I was thinking

Sure Calamity,

I was thinking perhaps I should begin with MrlSerial device, which I would propose to relay serial data (since MSG_ROUTE) was removed.

The first thing is to start with arduinoMsgs.schema.  This file controls both Java & C++ communication.
I add the following lines to arduinoMsgs.schema

# Serial - for relaying serial data to a different pin
> serialAttach/deviceId/relayPin
> serialRelay/[] data
< publishSerialData/[] data

I'm guessing we might need a target pin to relay the serial data to, then a method to send a byte array, and another to recieve reads from the pin.

Previously, there was only a single attach and we had to parse the config data to satisfy the needs of our device.  Now we explicitly have a {deviceType}Attach method which all its initialization config is specified. Once the arduinoMsgs.schema is saved. Run ArduinoMsgGenerator. 

Running this class generates the following code for our 3 new lines.
In MrlComm.h it ads the following definitions

The very nice part of this - is the compiler will complain that you did not implement these in MrlComm.cpp :)
The publishSerialData is not here because its designed to go from MrlComm to Java.  Our two above functions are "control" so they should be defined here.

publishSerialData got defined in Msg.h

Now lets look at Msg.cpp


Pretty simple for this method, just write a byte array to the return serial stream.  One of the challenges of getting the autogenerate code to work was correctly calculating the size or (going the opposite way) calculating the positioning and parsing.  But both of these mundane tasks are auto-magically done for you when you use ArduinoMsgGenerator.

Ahahaha : so I forgot the deviceId in the schema :D
This would be a pain if I had hand coded all this muck .. but it takes just a couple seconds to update the schema to this :

# Serial - for relaying serial data to a different pin
> serialAttach/deviceId/relayPin
> serialRelay/deviceId/[] data
< publishSerialData/deviceId/[] data
 
And re-run the generator.
 
All the changes are instantly interleaved correctly in all the serialization and parsing ... goodtimes :)
 
Now from our new MrlSerial device we should be able to call :
 
msg->publishSerialData(id, buffer, sizeof(buffer)) ;
 
and it will magically appear in a new method in Arduino.java here :
This is Msg.java - and its completely controlled by ArduinoMsgGenerator - 
It created the following method. It will correctly parse and invoke the appropriate method in Arduino.java... you just need to implement it ;)
 
The (invoke) boolean could potentially be an optimization, in that it could do a direct callback vs using the pub/sub queues.  But the original reason I put it in is the Java compiler couldn't show me what was needed in Arduino with the invoke.  If there is a mis-match of parameters, you won't notice it unitil it blows up in runtime with invoke.  Now the compiler tells me exactly what kind of method I need in Arduino, and being the ultimate in lazy I just press the "Create method".. 
 
 
Viola ! .. new fresh callback function in Arduino.java ready for implementation !
The first thing I'd do is change the return type from void to int [ ]  ..  since that is what I assume the thing wanting to use the MrlSerial relay device is interested in.  Perhaps it should return a new SerialData object which has the deviceId in it .. or perhaps the pin ?   That way the consumer could know which pin was read back on, if we want to have MrlSerial handle multiple pins.  Or perhaps there should be only one pin per MrlSerial device ...  all these things I have not thought about, but I believe we could make it work well after a little consideration.
 
So just to finish up inside Arduino.java you have the ability now to call the following methods

Java can now
msg.serialAttach(deviceId, relayPin);
msg.serialRelay(deviceId, buffer);
 
and MrlComm.cpp methods will get called
 
 
I always copy method signatures from MrlComm.h because they are "correct" and always in sync with the schema.  Please copy the commented schema line too so that we know the method is under schema control.
 
e.g.
 
The generation of all this code should save us a considerable amount of time otherwise spent on debugging, or maintaining parsing and serialization code. 
 
Hope this helps !
 
Cheers,
GroG
 
 
calamity's picture

That is indeed very simple

That is indeed very simple and very less prone to error, great job and thank you for the explanation

 

 

GroG's picture

Mats .. please remind me -

Mats .. please remind me - why is it

Control.attach(Controller, params...)

vs

Controller.attach(Control, params...)  ?

I'm looking at the old scripts for UltrasonicSensor and they are reversed..
http://myrobotlab.org/service/UltrasonicSensorĀ 

Ambiguity generates bugs and confusion ..   I suppose both could be supported but the details of connecting, and driver loading will always be in the Controller not the Control..
So Control.attach(Controller) will/should always just call Controller.attach(Control) ..

Sorry, my failing memory will be the death of me ....