2

recently I was tasked with implementing a way of adding support for versioning of hardware packet specifications to one of our libraries. First a bit of information about the project.

We have a hardware library which has classes for each of the various commands we support sending to our hardware. These hardware modules are essentially just lights with a few buttons, and a 2 or 4 digit display. The packets typically follow the format {SOH}AADD{ETX}, where AA is our sentinel action code, and DD is the device ID. These packet specs are different from one command to the next obviously, and the different firmware versions we have support different specifications. For example, on version 1 an action code of 14 may have a spec of {SOH}AADDTEXT{ETX} which would be AA = 14 literal, DD = device ID, TEXT = literal text to display on the device. Then we come out with a revision with adds an extended byte(s) onto the end of the packet like this {SOH}AADDTEXTE{ETX}. Assume the TEXT field is fixed width for this example. We have now added a new field onto the end which could be used to say specify the color or flash rate of the text/buttons. Currently this java library only supports one version of the commands, the latest.

In our hardware library we would have a class for this command, say a DisplayTextArgs.java. That class would have fields for the device ID, the text, and the extended byte. The command class would expose a method which generates the string ("{SOH}AADDTEXTE{ETX}") using the value from the class. In practice we would create the Args class as needed, populate the fields, call the method to get our packet string, then ship that down across the CAN.

Some of our other commands specification can vary for the same command, on the same version, depending on some runtime state. For example, another command for version 1 may be {SOH}AA{ETX}, where this action code clears all of the modules behind a specific controller device of their text. We may overload this packet to have option fields with multiple meanings like {SOH}AAOC{ETX} where OC is literal text, which tells the controller to only clear text on a specific module type, and to leave the others alone, or the spec could also have an option format of {SOH}AADD{ETX} to clear the text off a a specific device. Currently, in the method which generates the packet string, we would evaluate fields on the args class to determine which spec we will be using when formatting the packet. For this example, it would be along the lines of:

if m_DeviceID != null then use {SOH}AADD{ETX}
else if m_ClearOCs == true then use {SOH}AAOC{EXT}
else  use {SOH}AA{ETX}

I had considered using XML, or a database to store String.format format strings, which were linked to firmware version numbers in some table. We would load them up at startup, and pass in the version number of the hardwares firmware we are currently using (I can query the devices for their firmware version, but the version is not included in all packets as part of the spec). This breaks down pretty quickly because of the dynamic nature of how we select which version of the command to use.

I then considered using a rule engine to possibly build out expressions which could be interpreted at runtume, to evaluate the args class's state, and from that select the appropriate format string to use, but my brief look at rule engines for java scared me away with its complexity. While it seems like it might be a viable solution, it seems overly complex.

So this is why I am here. I wouldn't say design is my strongest skill, and im having trouble figuring out the best way to approach this problem. I probably wont be able to radically change the args classes, but if the trade off was good enough, I may be able to convince my boss that the change is appropriate.

What I would like from the community is some feedback on some best practices / design methodologies / API or other resources which I could use to accomplish:

Logic to determine which set of commands to use for a given firmware version Of those command, which version of each command to use (based on the args classes state)
Keep the rules logic decoupled from the application so as to avoid needing releases for every firmware version
Be simple enough so I don't need weeks of study and trial and error to implement effectively.

Thomas Owens
  • 79,623
  • 18
  • 192
  • 283
Mark W
  • 129
  • 2
  • 1
    This seems highly functional in nature (e.g. a rule is simply a function that takes some value and returns a boolean). You might be able to pull it off in traditional OOP style, but I suspect you'll just end up poorly implementing your own rules engine with all the complexity you wanted to dodge. On the other hand if you're not already comfortable with functional programming it might take you longer to do this in functional style than to learn a rules engine. – Doval Jun 05 '14 at 19:30
  • I agree Doval, I dont have any real functional programming experience. I had considered writing my own simplified rules engine, but came to the same conclusion you did. I would end up implementing it poorly, or spending just as much time on it as I would have spent learning a rules engine. – Mark W Jun 05 '14 at 20:19

1 Answers1

0

The JavaComm specification abstracts driver support through the use of advisory methods (methods that provide a hint from the program to the JVM):

  • enableReceiveThreshold

Enables receive threshold, if this feature is supported by the driver. When the receive threshold condition becomes true, a read from the input stream for this port will return immediately. enableReceiveThreshold is an advisory method which the driver may not implement. By default, receive threshold is not enabled. An application can determine whether the driver supports this feature by first calling the enableReceiveThreshold method and then calling the isReceiveThresholdEnabled method. If isReceiveThresholdEnabled still returns false, then receive threshold is not supported by the driver. If the driver does not implement this feature, it will return from blocking reads at an appropriate time.

  • enableReceiveFraming

Enables receive framing, if this feature is supported by the driver. When the receive framing condition becomes true, a read from the input stream for this port will return immediately. enableReceiveFraming is an advisory method which the driver may not implement. By default, receive framing is not enabled. An application can determine whether the driver supports this feature by first calling the enableReceiveFraming method and then calling the isReceiveFramingEnabled method. If isReceiveFramingEnabled still returns false, then receive framing is not supported by the driver. Note: As implemented in this method, framing is not related to bit-level framing at the hardware level, and is not associated with data errors.

  • enableReceiveTimeout

Enables receive timeout, if this feature is supported by the driver. When the receive timeout condition becomes true, a read from the input stream for this port will return immediately. enableReceiveTimeout is an advisory method which the driver may not implement. By default, receive timeout is not enabled. An application can determine whether the driver supports this feature by first calling the enableReceiveTimeout method and then calling the isReceiveTimeout method. If isReceiveTimeout still returns false, then receive timeout is not supported by the driver.

References

Paul Sweatte
  • 382
  • 2
  • 15