Peers, Tweaks & DNA - but were afraid to ask...

This is probably long overdue, but I'll try to explain how to tweak DNA (Descriptions of Neighboring Autotoma) 
 

The head arduino is created inside the InMoovHead service.

It's this line in file InMoovHead.java :

arduino = (Arduino) createPeer("arduino");

These are "peer" methods.  Peer methods use peer "keys" - in this case the peer key is "arduino" ..
Peer keys are used as a reference from a service to another service.
In this case its a reference of some Arduino service from the InMoovHead service.

The Peer system allows composite services to reference and control the actual names of the services, while the underlying services use 'simple' key references.

Composite services are services which references services which reference more services.  At the top typically there is something which orchestrates all of the things below it.  In this case the InMoov service is the orchestrator of all the services below it.  InMoov service is allowed to know "everything", but the lower level services know nothing of the higher level services.  This is important because it allows the complexity of higher level orchestration at the same time the lower level services can operate indepenently of the higher ones.  In a short statement, "InMoov service can control everything, InMoovHead can operate by itself, so can Servo etc etc..

You can think of each service having a blueprint of itself, so it may control it's "peers" or "peers" of "peers" successfully.

Lets look at InMoovs blueprint.
I find the WebGui easiest to do this - because the json is formatted in the browser.

This the InMoov service set of peer "keys" - 
http://localhost:8888/api/service/runtime/buildDnaKeys/i01/InMoov 

["i01.ear","i01.eyesTracking","i01.eyesTracking.controller","i01.eyesTracking.opencv","i01.eyesTracking.x","i01.eyesTracking.y","i01.head","i01.head.arduino","i01.headTracking","i01.headTracking.controller","i01.headTracking.opencv","i01.headTracking.x","i01.headTracking.y","i01.integratedMovement","i01.jmonkeyEngine","i01.leftArm","i01.leftArm.arduino","i01.leftHand","i01.leftHand.arduino","i01.mouth","i01.mouthControl","i01.mouthControl.arduino","i01.mouthControl.jaw","i01.mouthControl.mouth","i01.opencv","i01.openni","i01.pid","i01.python","i01.rightArm","i01.rightArm.arduino","i01.rightHand","i01.rightHand.arduino","i01.torso","i01.torso.arduino"]

These are the "keys" (not actual names) of all the service peers of an InMoov service named i01.  These services are a list of all the things InMoov can 'possibly' control.

Notice there is a :
"i01.head.arduino" and
"i01.eyesTracking.controller" and
"i01.headTracking.controller"

Are there three 3 Arduinos in the head ?  No. 

Most people do not want to have to buy 3 Arduinos to support the InMoovHead servos, the Tracking service for the eyes and the Tracking service for the head.  But we don't want to give up the control of referencing each by its key.  So what do we do ?

Share !

We allow the InMoov service to modify the "actual names" of the service.  In MyRobotLab-Land there one and only one "actual name" of a service.  One service - one name .. that's the law.  But the keys allow for a form of aliasing.

The default way the InMoov service is expected to run is with a single Arduino controlling these 3 sub-services.  So, within the Java code of InMoov, sub-services are told to share.

You can find these 3 lines (and others)  in the getMetaData method of the InMoov service.

    meta.sharePeer("eyesTracking.controller", "left", "Arduino", "shared head Arduino");
    meta.sharePeer("headTracking.controller", "left", "Arduino", "shared head Arduino");
    meta.sharePeer("head.arduino", "left", "Arduino", "shared left arduino");

These lines are a default way to modify the original keys so that 3 of the keys point to the same arduino.
The arduino's actual name will be i01.left

You can query how the InMoov service will modify the keys with the following url (it lists all modifications)

http://localhost:8888/api/service/runtime/mergeDna/i01/InMoov 

"i01.eyesTracking.controller": {"key": "eyesTracking.controller","actualName": "i01.left","fullTypeName": "org.myrobotlab.service.Arduino","comment": "shared head Arduino","isRoot": false}

"i01.headTracking.controller": {"key": "headTracking.controller","actualName": "i01.left","fullTypeName": "org.myrobotlab.service.Arduino","comment": "shared head Arduino","isRoot": false}

"i01.head.arduino": {"key": "head.arduino","actualName": "i01.left","fullTypeName": "org.myrobotlab.service.Arduino","comment": "shared left arduino","isRoot": false}

 

In fact you'll find many entries who's alias key maps to i01.left ...  because many of the services share this resource.  Same goes for Tracking service. We have 2 Tracking services but only one OpenCV service .. same thing.

All of this was designed so that much of the structure and actual build can vary, without having to modify code.  If someone was to hack up the "code" for there specific build of InMoov most likely it would break others who had a different design.  So next question should be .. How do I modify these mappings ?

Keep in mind two important laws of MRL :
"Every service has a unique name" and
"Once a service is created with a name it can not change"

First lets go over the sequence of creation :

  1. DNA Blue-Print of "default" is created from code - This happens in getMetaData() method
  2. Composite Service (InMoov) is created and allowed to modify internal structure - (sharing in getMetaData()
  3. Composite Service creates and starts sub-services - actual names cannot  change now

We have access to modify DNA between 1. & 2. AND the Composite Service will not modify a mapping if one already exists.

The easiest way to modify the dna is the reserveRoot(key, actualName, type, comment) method.

You want coerce these services to use your new Arduino.  So it only takes 3 lines of code (in Python), before anything else is done.

# put in reserves
python.reserveRoot("i01.eyesTracking.controller","MyNewArduino", "Arduino", "want new one")
python.reserveRoot("i01.headTracking.controller","MyNewArduino", "Arduino", "want new one")
python.reserveRoot("i01.head.arduino","MyNewArduino", "Arduino", "want new one")

Now you'll see the DNA mappings change.
http://localhost:8888/api/service/runtime/mergeDna/i01/InMoov 

"i01.eyesTracking.controller": {"key": "i01.eyesTracking.controller","actualName": "MyNewArduino","fullTypeName": "org.myrobotlab.service.Arduino","comment": "want new one","isRoot": false}

"i01.head.arduino": {"key": "i01.head.arduino","actualName": "MyNewArduino","fullTypeName": "org.myrobotlab.service.Arduino","comment": "want new one","isRoot": false}

"i01.headTracking.controller": {"key": "i01.headTracking.controller","actualName": "MyNewArduino","fullTypeName": "org.myrobotlab.service.Arduino","comment": "want new one","isRoot": false}

So, now when I create the InMoov service i01 and start the Head and Arm some of the services use the original i01.left and some now use MyNewArduino.  You can create MyNewArduino before you create all the other services if you want connect it to a different port.  You'll want to do this, because otherwise there will be port contention (Arduinos can share ports).

Be aware, you could also change the "Type" to be any type of ServoController ..  e.g. Adafruit16CServoDriver instead of the Arduino.

With the following code :

# virtual arduinos no real arduinos on hand
v1 = Runtime.start('v1','VirtualArduino')
v1.connect('COM4')
v2 = Runtime.start('v2','VirtualArduino')
v2.connect('COM5')

# start i01, head & left arm
i01 = Runtime.start('i01', 'InMoov')
i01.startHead("COM4")
i01.startLeftArm("COM5")
 

Now, I get left arm stuff using the original i01.left arduino and everything in the head using MyNewArduino

Although I could have said - use these 3 lines, I thought it would be beneficial to explain all the parts. And hopefully with that knowledge you can "re-mix" services to your desire.

Cheers.

References: