-
Notifications
You must be signed in to change notification settings - Fork 1
PiNet
PiNet is a class from the pinet.py module of the net package and is intended to facilitate and simplify the task of sending information from one computer to another within the car. As the name suggests, this component of the net library is intended to send data between to Raspberry Pis (or two desktop computers in general) while the other components like ArduinoNet and CANBusNet are meant to communicate with a different device and a computer. The primary goal of PiNet is to abstract the processes of sending and receiving data into simple methods while maintaining reliability, a standardized data format, and overall program performance. This document intends to outline everything you need to know about PiNet to make the most of it.
As mentioned above, the primary goals of this class are to promote an abstract, robust, and high-performance networking interface for the car's main computers. As such, PiNet makes use of sockets and threads in close conjunction with each network connection being handled on a separate thread to avoid blocking application code and each other. In order to facilitate this, it was necessary to split the incoming and outgoing data operations onto separate threads, leading to the response key system that will be described further below. In addition to this, as directed network requests often require a server-client relationship, the class can be initialized in a separate mode for both roles. It is intended for the car's systems computer to instantiate a server while separate computers, such as those serving the UI, will act as clients.
As the amount of data expected to be handled by PiNet is relatively low and to ease debugging efforts as the car project progresses, PiNet automatically converts information to and from JSON format for transmission. Although somewhat opaque to the users of PiNet, the format of these JSON messages is as follows: Data transmission occurs in one of three types, each consisting of a dictionary with several keys within. The arrow indicates the format of the response if any and the dictionaries are show from immediately before or after conversion to JSON:
- Messages - A unidirectional update or notice that can be sent by servers or clients to make the peer aware of something:
{"type": "msg", "msg": msg}
where 'msg' is the contents to be sent as a string. - Requests - A bidirectional query for information that can be sent by servers or clients to set or get a variable by name:
{"type": ["get"|"set"], "name": name, "value": value} -> {"responseKey": int, "response": [any|bool]}
where name refers to the desired variable and value is to be assigned to the variable if the type is "set". "response" will be any type if its being returned by a "get" request or True if a "set" request was successful. - Actions - A type of request that triggers a controller action in the systems computer. It can only be sent by a client:
{"type": "action", "name": name, "args": [any...]} -> {"responseKey": int, "response": any}
where name refers to the desired action and the tuple "args" are the calling arguments for the action itself. "response" will be the return value of the action name.
Creating an instance of PiNet is as simple as calling the constructor. Suppose the one wished to create a server with the address 127.0.0.1:4000, then the statement might be server = PiNet(True, ("127.0.0.1", 4000))
. The corresponding client would look very similar: client= PiNet(False, ("127.0.0.1", 4000))'. A call to
server.start()' open up the appropriate sockets for the server to listen on the specified address. Until the server is running in this manner, the client instance will be unable to connect via client.start()
and will raise a PiNetError
.
Now that both server and client are running and connected, calls to send_smg()
, send_request()
, and send_action()
can be made. For servers to send anything, the target
parameter of sending methods must be supplied with one of the strings returned by get_connected()
. Both the server and client must frequently check incoming transmissions with get_msg()
and get_request()
and act accordingly. get_response()
must also be called and if 256 responses are allowed to accumulate a PiNetError
will be raised and the program must be altered to correct it. There is no action required when receiving messages via get_msg()
, but an appropriate response must be sent for requests with send_response()
. All responses must return the original "responseKey"
that was sent in the request dictionary along with a return value appropriate to what was asked. If calling send_response()
from a server instance, the target
parameter must be supplied with the contents of the "target"
key that belongs to the request dictionary.
Note that while server.is_running() == True
or if the instance is a client, calls to server.set_address()
will raise a PiNetError
. When the program has completed its execution and wishes to end, a call to stop()
is required (which sets the running state to FALSE
).