Develop a reliable piece of software that collects pertinent data from Electric Vehicle subsystems
Allow device interfacing, configuration and programming through CAN Bus
Bootable image for Raspberry Pi with current stable release
Website with full access to accumulated data and in-browser visualization through client-side scripts
Core is simply the the code designed to allow the communications between applications and servers, and hybrid applications. Core and Engine will allow us to build applications easily.
Core Recieve - Reponsible for receiving messages from Core Socket (Unix Socket)
Core Send - Responsible for placing messages on the Core Socket
Core Error - Responsible for sending error messages on the Core Socket
Can Send - Responsible for sending messages to be fowarded onto the can bus
Can Receive - Responsible for receiving messages from the Core Socket
Since all applications will be using unix sockets it makes sense to seperate this functionality into its own module.
There are two different Cores, CoreServer and CoreApplication. Thes can be specified with the configuration file under the name 'core_type'.
It is also important that Core can also be put into standalone mode.
Core components are designed under the server concept, where core is reading and sending to listening and connected applications. Its can receive and can send funcations are enabled, allowing for
information from clients ultimately allowed to be passed onto the canbus.
Core components are designed under the application concept, where core is reading and sending to designated core server. Its can receive and can send functions are disabled,
unless standalone mode is enabled. This will allow the application to directly link to the canbus and will wrap core send and receive with the data coming from and too the bus.
CAN Engine Design
Implementation of the Engine will be a python script that begins at the main thread and spawns several
child processes to gather incoming CAN messages from the networks and relay the messages to the
core through the IPC protocol. Messages are converted to JSON format for inter process communication.
Additional meta-data, such as type, time and network is also recorded before transmission. Engine will also
receive incoming CAN messages from the Core to be sent over the CAN buses.
Current CAN Engine design
The Gateway Rev2 design is going to utilize a JSON format for the IPC messages. The JSON format will include
the time the message was recieved, the type of message, the network that the data was received from or going
to be sent to, the canid, and the data of the message.
The CAN engine will be responsible of converting incoming CAN messages into the JSON format using serialization
of the incoming data. The CAN engine will have two methods that deal with converting bytes of data to JSON and vice versa:
Takes 16 bytes from CAN interface and return a JSON object
Meta-data is encapsulated and CAN-ID is extracted
Time, Type and Network are specified as attributes
Converts a JSON representation of a CAN message to 16 bytes that can be sent over CAN network
Time
The time attribute will determine the time that the data was recieved from. This time will be given using the imported time from the
python time module. This time will give the data a time which will then be used to graph the data in the telemetry side of the project.
Network
The network attribute of the JSON format will be determine two different things depending on where the JSON data comes from.
If the data comes from the CAN OPEN/EVT then the program will determine that the network is the origin of the data (e.i. OPEN/EVT).
But if the JSON data is being sent from the Core to the CAN engine then the program will determine that the network is the destination
of the data.
Type
The type attribute will tells the program the type of message. At the moment the only implementation for type will be for CAN messages but
the idea in the future will be to implement event types which can handle shutting down threads but could also have other uses.
Canid
The canid attribute will contain the header info of the CAN message. This will include the id of the sender, length of data and the CAN method
response (e.i. PDO,SDO).
Data
The data attribute will hold all the data from the CAN message. The length of the data can be from 0-8 bytes.
Engine will have three socket connections: One to the Core Server, one to evtCAN and one to openCAN.
evtCAN and openCAN are network interfaces within the Linux environment. The Core Server address will
be a file within the Linux file system. Bind sockets routine will take addresses of these nodes and establish
a socket connection with each individually and place sock objects into a module level variable called Sockets.
returns a list: sockets
sockets[0] = core socket connection
sockets[1] = openCAN socket
sockets[2] = evtCAN socket
Functionality across the several socket connections is isolated to separate threads running in infinite loops.
Engine's main thread will call CORE recieve, before creating a thread for each
can network which then calls the CAN read method defined below.
Accepts incoming messages from network socket
Message is converted into JSON object format
Sends the JSON object to the CORE
Loops forever
This method is run after the init is done with setting up the ending. So the init never returns
Polls the core socket receive function for messages from the Core
If type of message is CAN, Converts JSON object to CAN message and sends it to the
respective can that is given from the JSON attribute, network.
If type of message is EVENT, command attribute is expected
command example: Shutdown - signals parent thread to kill all processes
Unix sockets provide the same functionality of most networking protocols. Recently brought to my attentention is that the Engine is not a server but a workhorse client. I do not
not know what a workhorse client is, therefore nick should go into more detail in his thoughts.
Several things to consider when working with Unix sockets.
The core socket(AF_UNIX) cannot simply be written too and there is no address associated with the socket(file descriptor). It acts as a listener to accept incoming client connections.
Initially it was thought that anyone can attach to the address and listen onto messages being sent by the engine. Sadly this is not the case as the engine has to esbtalish a connection with a client,
via accept().
It may be the case that core will only have one connection. Though I advise against that as other people connecting to the engine may need the data for dianostic purposes, while still maintaining a connection to core.
Currently the design is very functional, not relying on any object state. It could be argued that some of the functions which I assume will be designated on another thread, will only need a limited amount of resouces, can just pass in the resources as arguments to pythons Thread object. An example can be seen below
It would to be more effcient to seperate send/recieve all and one should be seperated into their own functions.
I've compose a list of designs that could help address many issues such . Below is a simple flow from
The Engine is responsible distrubuting the messages between clients and the bus, handling errors when they arise and maintaining state between running threads. A can_frame is first read in from a Receiver running as a Daemon. Recievers maintain state about the socket and foward any messages to the outlet. The outlet is decorator, reponsible for deconstructing the raw byte message into something meaningful. Ultimately it determines whether to foward the frame or throw it away and sends it to the Engine via CoreSEND. Errors are also forwarded by the reciever, through the outlet and handled by the engine. Since CoreSend and CoreError are called from the daemon thread but share the same object amongst all running threads, it is a good idea to add conditions to signal to the main thread if any issues or critical events occcur.
Reciever reads from can-bus socket/buffer
If it fails or a timout occurs, foward error to outlet
if successful foward bytes object to outlet
Outlet deconstructs message.
validates deconstructed message and whether it should be forwared to engine
if an error comes through deconstruct the message and foward it to COREerror
Engine receives message(Note this is called from the daemon thread)
from CORESEND it will send the message to all its clients
COREError, will send the error to a Queue object to ultimately be processed by the main thread
Handling Errors with the Main Thread
It should be noted since the main thread controls state and execution of the project.COREerror ultimately called on another thread should not contain any logic pertaining to recovery or modifying state. It simply adds the the error to a Queue and raises a flag.
There are many exceptions to account for
SocketTimeout - raised when no traffic is recieved within a certain time period
NonExistentAddress - raised when a non existent address is used to access a file descriptor
AddressInUse - when the socket is rebinded to the given address
Now the engine will atempt to recover or fail depending on how severe the exception and current state of the program
Handling Incoming connections
For ease of use, python provides a socketserver implementation. For now the engine will be responsible for receiving and establishing the following,
A UDP based unix socket, binded to an address and listening for incoming connections.
Repond to error or service requests. The engine can respond or ignore these requests.
Connect to one application, CORE ( although it is easy to expand and accept several connections)
Forward and recieve can bus related data.
The server part of the engine will not be responsible for handling any logic pertaining to the contents of the messages. The engine will be forwarded the data untouched, allowing for the CORErecieve function to
interpret that data.