Bertrand's Design Study
Last year, I was involved in a project which consisted in building an autonomous robot to compete in the French Robotic Cup. My main task was to handle all the data transmissions in the robot. This mean I had to work a lot with networks, especially the CAN network (widely used in cars).
This one is not like TCP/IP, there is no sources nor destinations. All frames have IDs (11 or 29 bits), and each nodes connected to the network needs to determine if the frame is relevant or not by filtering IDs.
The main problem is for managing the whole network, which mean handling IDs:
- nodes are mostly µc like PIC, ARM, ... and are running home-made firmwares, without OS (written in C)
- the data part of a frame can be up to 8 bytes long, the format is determined by the developer
Managing the network "by hand" would be a really painful task: determining IDs, format, writing them in the code, ... can lead to an infinite amount of errors and wasted time. In order to do that, I developed a tool called CanXml in C++ which takes an XML-based configuration file describing the network (nodes, frames, format), and exporting it as a C header with all the networks IDs defined in here using #define with convenient names.
This tool was really helpful but when designing it, I was thinking in building a library that feature tools to manage the network, but also that can be used to debug the network.
This goal was not reached and the design I realized then is far from perfect, that's why I think it can be a great subject for my design study.
Requirements
The library must feature:
- network management abilities (IDs generation, XML import and C-header export)
- a way to process existing frames
- be memory efficient (in order to be used on Linux embedded devices)
And it also need to be clean, since I want to release it under an open source license.
First implementation
In my first attempt, I was mainly trying to make things work and I did not much think about what the design should be to be a good OO design.
Here is the first UML (I didn't represent most getter and setters, they are everywhere though):
My naming convention appeared as a bad choice when I started implementing a way to process existing frames, there is a conflict between "abstract frames" (the "family") and "concrete frames" (transfered frames), which gets really confusing when processing the code.
The way it determines IDs is too static, the policy is static while it should be more ... well, dynamic. Network/Nodes/Frames is some kind of tree, and each frame generate his ID using a simple algorithm based on the node it belongs to, the priority, ...
There is another problem for XML import and C export which is closely related. The XML file is processed recursively by objects in order to build the tree. A similar pattern is used for C export. The whole library heavily depend on one XML library (ticpp).
Another thing about the way it export things is that even if both exportViz and exportC seems similar, they are not implemented in the same way.
The pattern I used for storing the format is too complex and inefficient. Since I was looking for a memory-efficient way to handle formats of different type, bit sizes, endianness while being able to process existing frames, I produced something that is heavy and not clean. It is a merge between the factory and flyweight pattern, mixing elements that are initiated when the factory is first used with more complex elements like Enum. Another funny thing is that the Format is a factory buildings elements of the same type ... This is done in a dirty way using lots of static variables (static is the new global :o), rather than having a factory singleton class which would have been a lot cleaner. I broke the law in so many ways here I can't believe I'm not in Guantanamo Bay.
Another error I made was to start writing the concept using french words for attributes, class names, etc ... When I realized I would probably like to release it, I switched to english, but it was too late for that ...
Actual implementation
I released my work in OpenSource, the library is called CanArch (CanOE at first but this name was already taken).
UML diagram for CanArch:
I started from scratch in order to build something clean from the beginning.