ahoy all. First off, im making a generic I2C device manager, allowing you to connect any i2c device with any open i2CController service and allow you to read from/write to the I2cDevice without having to use the controller services directly. My purpose for this project was not my own, rather Ray wanted an easier way to read/write to i2cDevices regardless of the controller service they use. Yes, im aware that i2c is just a few basic operations, but clearly i dont need to write those operations from scratch given the "msg" system built into mrl and used by the controller services. This service just allows the user to have one service with easy methods for all necessary i2c operations  without having to reference each  controller service for each i2c method call.   Again, im making this for ray, i know what he wants it to have, but what i2c devices/hardware he uses I am unaware of and don't need to know since this service generalizes the controllers as an I2CController (the built in interface that all i2c controllers implement), and my generic I2cDevice class.  
 
 
I have a couple questions about I2CControl interface methods and the attach/detach logic. Looking at the pcf8574 service as my guide for an I2CControl class (which is my I2cDevice class), im assuming that the only methods that are required for an I2CControl are the methods found in the I2CControl interface? Assuming that is so, I also see that the pcf service has a "public void attach(String service)" that appears to allow you to only attach the controller and not set the device bus or device address. While that certainly can be useful, what confuses me is the way in which that method accomplishes its task. It calls the " public void attach(Attachable service) " method which checks that the controller service you provided is actually an i2CController. Why have this extra method when you can just check that within the same "public void attach(String service)" method? My only guess is that, for some reason, you can add the controller to the pcf as an "attachable". I ask this because im uncertain whether this method as well as "attachables" are used behind the scenes by mrl. If so, then i guess i should add support for attachables in my generic I2cDevice class im making.
 

GroG

3 years 10 months ago

Hi Alex.

To Recap:

I2CController is just a "something" to write packets onto an I2C bus which potentially will do something useful when it gets to an I2CControl driver thingy.

I2CControl is typically referred to as a "driver".  Because its a thingy that has nice simple methods on one side. For Example :

mpr121.enablePin(3)   <- Nice right? I'll recieve data back from pin 3 on this device https://www.adafruit.com/product/1982 
and my robot will recieve a stream of data from

public void onPin(PinData pindata) {
boolean sense = (pindata.value != 0);
if (!isActive && sense) {
...

Or maybe I want to recieve orientation data from an MPU6050 (https://playground.arduino.cc/Main/MPU-6050/)
I can use the lovely handy dandy Mpu6050 I2CControl driver like this:

mpu6050.startOrientationTracking()

My robot will have a callback method called onOrientation and get a lovely stream of data

public Orientation onOrientation(Orientation orientation) {
computeAngles(orientation.roll, orientation.pitch, orientation.yaw);

oculus.yaw = Double.valueOf(rothead);
oculus.pitch = Double.valueOf(head);
oculus.roll = Double.valueOf(bicep);

...

Very Nice !

The I2CControl driver is something useful and specific to the sensor or motor, actuator etc.
The I2CController - is some that the driver needs to send and recieve its bytes from ... just a gateway.

RasPi, Esp8226_01 and Arduino are I2CControllers.  Which means they all know how to write bytes to an I2C device on a I2C Bus connected to them.  I'm not sure I understand your desire to write a "general" I2C device manager.  It's already "generalized".  RasPi Esp8226_01 and Arduino are already "generalized" I2C device managers, they are only specific to the I2CBus hardware to write and read bytes too.  You need some form of hardware to read and write bytes too, simple as that.  If you generalize beyon that, you must be talking about virtual hardware, and we already have virtual hardware.

"Attach" is a MRL activity.  It's like attaching lego blocks.  You can attach services together.  And the service "might" know how to correctly attach to each other. 
(some references : http://myrobotlab.org/content/attach-pattern http://myrobotlab.org/content/service-attaching-diving-deeper)

In general you attach services by reference like this:

service01.attach(service02)

or by name:

service01.attach("service02") <- this one just auto-magically gets turned into the previous example

The details of the attach are specific to the I2CControl Driver ...
but potentially is would hook up the correct publications and subscriptions to make everything worky the way you want.

The Attachable interface of Services allows services to attach to one another.

Hope this makes sense.

 

Alexinator40

3 years 9 months ago

In reply to by GroG

Hi Grog. Thanks for the great detailed explanation. I am "generalizing" past the arduino, thus allowing you to to virtually manage i2c device data and i2c device connections to any running  i2c Controller services (like Arduino, Raspi, etc.) The main goal Ray was looking for was to be able to have a basic i2c device service that can be used for new i2c devices that currently dont have a service, like the pcf does, in mrl. Instead of writing a class for each and every i2c device, I can generalize the devices into an "I2cDevice" class that stores the  device address, controller, and device bus it is attached to. I put that I2cDevice into a concurrent hashmap in my I2cDeviceManager.   The manager has methods that allow the user to get/set the device address and device bus, as well as methods that, given that information and the controller service the device should be connected to, will call on that device's controller to attach it.  This should both dramatically expand the possible i2c devices that can be used, but also simplifies the coding for writing a script to read/write to an i2c device.  Now obviously the user still has to start the controller services, but once those are running, the user either chooses an existing but detached i2cDevice in the manager, or they add a new i2cDevice object to the manager for which they can attach to an existing controller service.   

 

My only question about attachable is with the following lines of code found in the pcf8574 class (which is an i2cControl). You'll notice that the second method only checks if the "attachable" service is an I2CController before it calls the attachI2CController class.  Why have the second method at all?  Why not just do a check in the first method to see if the service is an I2CController  and if so call attachI2CController.   As I said before, my only guess is that something else, like some kind of backend mrl code or another service, passes the supposed I2CController service as an attachable. I cant see any other reason why its necessary to have a separate method to do that check, unless perhaps its just split up for cleaner/more understandable code?   If im modeling my generic "I2cDevice" class after the pcf class (which is an i2cControl), i want to be certain that all the methods I incorporate are actually necessary. Thanks for the help so far :)

public void attach(String service) throws Exception {
    attach((Attachable) Runtime.getService(service));
}

@Override
public void attach(Attachable service) throws Exception {

    if (I2CController.class.isAssignableFrom(service.getClass())) {
        attachI2CController((I2CController) service);
        return;
    }
}

public void attachI2CController(I2CController controller) {

    if (isAttached(controller))
        return;

    if (this.controllerName != controller.getName()) {
        log.error("Trying to attached to {}, but already attached to ({})", controller.getName(), this.controllerName);
        return;
    }

    this.controller = controller;
    isAttached = true;
    controller.attachI2CControl(this);
    log.info("Attached {} device on bus: {} address {}", controllerName, deviceBus, deviceAddress);
    broadcastState();
}

In this case the top method should be removed.  

The abstract class Service does exactly this, and this overrides it but does exactly the same thing. 
There is no point in overriding methods if there is no difference in the behavior.

The top method allows attaching by a name string.

s1.attach("s2")

vs the middle which attaches by reference to an interface allowing

s1.attach(s2)

The multi-parameter overloads are not preferred either.

these :  s1.attach(s2, busAddress, deviceAddress, .. etc)

what should happen is:

s2.setBusAddress(32)
s2.setDeviceAddress(17)
# then
s1.attach(s2)

the parameters are always configuration - and it should be done outside of the attach.

Hope that makes sense.

Hi grog. Yea i totally understand. Thanks for clearing that up for me. I suppose the Pcf class might need to be refactored at some point then, lol  :)    

I was going over my code for my I2cDevice class, and i realized why I have to keep the multi-parameter attach methods. The I2CControl interface requires them. Perhaps the I2CControl interface needs to be updated?

 
public interface I2CControl extends NameProvider, Attachable {
 
  public void setDeviceBus(String deviceBus);
 
  public void setDeviceAddress(String deviceAddress);
 
  public String getDeviceBus();
 
  public String getDeviceAddress();
 
  public void attach(String controllerName, String deviceBus, String deviceAddress);
 
  public void attach(I2CController controller, String deviceBus, String deviceAddress);
 
  public void attachI2CController(I2CController controller);
 
  public void detachI2CController(I2CController controller);
}
 
 
Also of note, the the Service class' attach methods lead to printing an "info" message "Service.attach does not know how to attach" to the other service.   Whats happening here? It appears to me that these attach methods dont do anything besides provide that message. Just want to make sure im not unnecessarily rewriting methods that may exist in super classes. 
 
 
public void attach(String serviceName) throws Exception {
  attach(Runtime.getService(serviceName));
}@Override
public void attach(Attachable service) throws Exception {
  info(String.format("Service.attach does not know how to attach %s to a %s", service.getClass().getSimpleName(), this.getClass().getSimpleName()));
}
 

Is a "pr" a public release?  Anyways, I suppose the issues I was mentioning above boil down to  two questions . Should my service override the attach methods of the parent "Service" class?  From what ive seen in other services, it does appear I have to override the attach methods.   Also,  should the I2CControl interface already defined in MRL be updated to remove those "multi-parameter" attach methods that you don't like?   Since its an interface, any class that implements I2CControl, like my "I2cDevice" class   and the "PCF8574" class,  must implement those methods you don't like.

Is a "pr" a public release?

PR == Pull Request 
https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork

The purpose is to submit changes, and the way its done, it gives people a view of proposed changes.  Works very nice if there is someone to review and respond.

  Anyways, I suppose the issues I was mentioning above boil down to  two questions . Should my service override the attach methods of the parent "Service" class?  From what ive seen in other services, it does appear I have to override the attach methods.   Also,  should the I2CControl interface already defined in MRL be updated to remove those "multi-parameter" attach methods that you don't like?   Since its an interface, any class that implements I2CControl, like my "I2cDevice" class   and the "PCF8574" class,  must implement those methods you don't like.

The only one you should need to override is the

public void attach(Attachable service) throws Exception {

This one internal to your service is used as a "router" to route to a more specific 

public void attachI2CController(I2CController controller);

 

The following should be removed from the interface

  public void attach(String controllerName, String deviceBus, String deviceAddress);
 
  public void attach(I2CController controller, String deviceBus, String deviceAddress);

Is that clear ?   The history of it is we wanted a generalized attach method, so the user would just need to :
 
s1.attach(s2)
 
but there are different types of services and it can potentially be a many to many relation ship (ie a service attached to many others).   The desire was to make it appear simple to the user, so their has to be an entry point for the attachment - the entry point is :
 
public void attach(Attachable service) throws Exception {