Merge branch 'master' of git.martlubbers.net:msc-thesis1617
[msc-thesis1617.git] / results.arch.tex
1 \section{Overview}
2 The goal of the architecture is to facilitate an ecosystem in which an
3 \gls{iTasks}-system can add, change and remove devices at runtime. Moreover,
4 the \gls{iTasks}-system can send \glspl{mTask}, compiled at runtime to
5 bytecode, to the device. The device runs an interpreter which can execute the
6 \gls{Task}'s bytecode. Devices are persistent during reboots of the
7 \gls{iTasks}-system. The methods of interacting with \glspl{mTask} is analogous
8 to interacting with \gls{iTasks}-\glspl{Task}.
9
10 An overview of the architecture is visible in Figure~\ref{fig:system}.
11
12 \begin{figure}[H]
13 \centering
14 \includegraphics[width=\linewidth]{system}
15 \caption{Overview of the architecture}\label{fig:system}
16 \end{figure}
17
18
19 \section{Devices}
20 The client code for the devices is compiled from one codebase. For a device to
21 be eligible for \glspl{mTask}, it must be able to compile the shared codebase
22 and implement (part of) the device specific interface. The shared codebase only
23 uses standard \gls{C} and no special libraries or tricks are used. Therefore
24 the code is compilable for almost any device or system. Note that it is not
25 needed to implement a full interface. The full interface --- excluding the
26 device specific settings --- is listed in Appendix~\ref{app:device-interface}.
27 The interface works in a similar fashion as the \gls{EDSL}. Devices do not have
28 to implement all functionality, this is analogous to the fact that views do not
29 have to implement all type classes in the \gls{EDSL}. When the device connects
30 for the first time with a server the specifications of what is implemented is
31 communicated.
32
33 At the time of writing the following device families are supported and can run
34 the device software.
35 \begin{itemize}
36 \item \texttt{POSIX} compatible systems
37
38 This includes systems running \emph{Linux} and \emph{MacOS}.
39 \item \texttt{STM32} family microcontrollers supported by \texttt{ChibiOS}.
40
41 This is tested in particular on the \texttt{STM32f7x} series \gls{ARM}
42 development board.
43 \item Microcontrollers programmable by the \gls{Arduino} \gls{IDE}.\\
44
45 This does not only include \gls{Arduino} compatible boards but also
46 other boards capable of running \gls{Arduino} code. The code
47 has been found working on the \texttt{ESP8266} powered \emph{NodeMCU}.
48 It is tested on devices as small as the regular \gls{Arduino}
49 \emph{UNO} board that only boasts a meager \emph{2K} of \emph{RAM}.
50 \end{itemize}
51
52 \section{Specification}
53 Devices are stored in a record type and all devices in the system are stored in
54 a \gls{SDS} containing all devices. From the macro settings in the interface
55 file, a profile is created for the device that describes the specification. When
56 a connection between the server and a client is established the server will
57 send a request for specification. The client will serialize his specification
58 and send it to the server so that the server knows what the client is capable
59 of. The exact specification is listed in Listing~\ref{lst:devicespec}
60
61 \begin{lstlisting}[label={lst:devicespec},
62 caption={Device specification for \glspl{mTask}}]
63 :: MTaskDeviceSpec =
64 { haveLed :: Bool
65 , haveAio :: Bool
66 , haveDio :: Bool
67 , bytesMemory :: Int
68 }
69 \end{lstlisting}
70
71 \section{Device Storage}
72 All devices available in the system are stored in a big \gls{SDS} that contains
73 a list of \CI{MTaskDevice}s. The exact specification is listed in
74 Listing~\ref{lst:mtaskdevice} with the accompanying classes and types.
75
76 The \CI{deviceResource} component of the record must implement the
77 \CI{MTaskDuplex} interface that provides a function that launches a task used
78 for synchronizing the channels. The \CI{deviceTask} stores the \gls{Task}-id
79 for this \gls{Task} when active so that it can be checked upon. This top-level
80 task has the duty to report set the \CI{deviceError} field whenever an error
81 occurs. All communication goes via these channels. If the system wants to send
82 a message to the device it just puts it in the channels. Messages sent from the
83 client to the server are also placed in there. In the case of the \gls{TCP}
84 device type the \gls{Task} is just a simple wrapper around the existing
85 \CI{tcpconnect} function in \gls{iTasks}. In case of the serial device type it
86 uses the newly developed serial port library of \gls{Clean}\footnote{\url{%
87 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
88
89 Besides all the communication information, the record also keeps track of the
90 \glspl{Task} currently on the device, the compiler state (see
91 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally it stores
92 the specification of the device that is received when connecting. All of this
93 is listed in Listing~\ref{lst:mtaskdevice}. The definitions of the message
94 format are explained in the following section.
95
96 \begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
97 deviceStore :: Shared [MTaskDevice]
98
99 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
100 :: BCState = ... // Compiler state, explained in later sections
101 :: MTaskResource
102 = TCPDevice TCPSettings
103 | SerialDevice TTYSettings
104 :: MTaskDevice =
105 { deviceTask :: Maybe TaskId
106 , deviceError :: Maybe String
107 , deviceChannels :: String
108 , deviceName :: String
109 , deviceState :: BCState
110 , deviceTasks :: [MTaskTask]
111 , deviceData :: MTaskResource
112 , deviceSpec :: Maybe MTaskDeviceSpec
113 , deviceShares :: [MTaskShare]
114 }
115
116 channels :: MTaskDevice -> Shared Channels
117
118 class MTaskDuplex a where
119 synFun :: a (Shared Channels) -> Task ()
120 \end{lstlisting}
121
122 \section{Communication}
123 All \gls{mTask} messages are encoded following the specification given in
124 Appendix~\ref{app:communication-protocol}. Available messages are:
125 \begin{lstlisting}[caption={Available messages}]
126 :: MTaskMSGRecv
127 = MTTaskAck Int Int | MTTaskDelAck Int
128 | MTSDSAck Int | MTSDSDelAck Int
129 | MTPub Int BCValue | MTMessage String
130 | MTDevSpec MTaskDeviceSpec | MTEmpty
131
132 :: MTaskMSGSend
133 = MTTask MTaskInterval String | MTTaskDel Int
134 | MTShutdown | MTSds Int BCValue
135 | MTUpd Int BCValue | MTSpec
136
137 :: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int
138 \end{lstlisting}
139
140 \subsection{Add a device}
141 A device can be added by filling in the \CI{MTaskDevice} record as much as
142 possible and running the \CI{connectDevice} function. This function grabs the
143 channels, starts the synchronization \gls{Task}, makes sure the errors are
144 handled when needed and runs a processing function in parallel to react on the
145 incoming messages. Moreover, it sends a specification request to the device in
146 question to determine the details of the device and updates the record to
147 contain the top-level \gls{Task}-id. All the device functionality heavily
148 depends on the \CI{withDevices} function that applies a function a device in
149 the \gls{SDS} when they are equal. Device equality is defined as equality on
150 their channels. This allows you to give an old device record to the function
151 and still update the latest instance. Listing~\ref{lst:connectDevice} shows the
152 connection function.
153
154 \begin{lstlisting}[label={lst:connectDevice},%
155 caption={Connect a device}]
156 withDevices :: MTaskDevice (MTaskDevice -> MTaskDevice) -> Task [MTaskDevice]
157
158 connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels
159 connectDevice procFun device = let ch = channels device
160 in appendTopLevelTask 'DM'.newMap True
161 (procFun device ch -||- catchAll (getSynFun d.deviceData ch) errHdl)
162 >>= \tid->withDevices device (\d->{d&deviceTask=Just tid,deviceError=Nothing})
163 >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch
164 where
165 errHdl e = withDevices device (\d->{d & deviceTask=Nothing, deviceError=Just e}) @! ()
166 \end{lstlisting}
167
168 Figure~\ref{fig:handshake} shows the connection diagram. The client responds to
169 the server with their device specification. This is detected by the processing
170 function and the record is updated accordingly.
171
172 \begin{figure}[H]
173 \centering
174 \begin{sequencediagram}
175 \newthread{s}{Server}
176 \newinst[4]{c}{Client}
177 \begin{call}{s}{MTSpec}{c}{MTDevSpec}
178 \end{call}
179 \end{sequencediagram}
180 \caption{Connect a device}\label{fig:handshake}
181 \end{figure}
182
183 \subsection{\glspl{Task} \& \glspl{SDS}}
184 When a \gls{Task} is sent to the device it is added to the device record
185 without an identifier. The actual identifier is added to the record when the
186 acknowledgement of the task by the device is received. The connection diagram
187 is shown in Figure~\ref{fig:tasksend}.
188
189 \begin{figure}[H]
190 \centering
191 \begin{sequencediagram}
192 \newthread{s}{Server}
193 \newinst[4]{c}{Client}
194 \begin{call}{s}{MTSDS}{c}{MTSDSAck}
195 \end{call}
196 \begin{call}{s}{MTTask}{c}{MTTaskAck}
197 \end{call}
198 \end{sequencediagram}
199 \caption{Sending a \gls{Task} to a device}\label{fig:tasksend}
200 \end{figure}
201
202 The function for sending a task to the device is shown in
203 Listing~\ref{lst:sendtask}. First the task is compiled into messages. The
204 details of the compilation process are given in Section~\ref{sec:compiler}.
205 The new shares that were made during compilation are added to the deviceshares
206 that were made during the compilation are merged with the existing shares on
207 the device. Furthermore the messages are placed in the channel share of the
208 device. This will result in sending the actual \gls{SDS} and \gls{Task}
209 specifications to the device. A \gls{Task} record is created with the
210 identifier $-1$ to denote a \gls{Task} not yet acknowledged. Finally the device
211 itself is updated with the new state and with the new \gls{Task}. When the
212 device returns an acknowledgement the \gls{Task} is updated accordingly.
213
214 \begin{lstlisting}[label={lst:sendtask},%
215 caption={Sending a \gls{Task} to a device}]
216 makeTask :: String Int -> Task MTaskTask
217 makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name,ident=ident,dateAdded=dt}
218
219 makeShare :: String Int BCValue -> MTaskShare
220 makeShare withTask identifier value = {MTaskShare |withTask=[withTask], identifier=identifier,value=value}
221
222 sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task [MTaskDevice]
223 sendTaskToDevice wta mTask (device, timeout)
224 # (msgs, newState) = toMessages timeout mTask device.deviceState
225 # shares = [makeShare wta sdsi sdsval\\{sdsi,sdsval}<-newState.sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`]
226 = updateShares device ((++) shares)
227 >>| sendMessages msgs device
228 >>| makeTask wta -1
229 >>= withDevices device o addTaskUpState newState
230 where
231 addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
232 addTaskUpState st task device = {MTaskDevice | device &
233 deviceState=st, deviceTasks=[task:device.deviceTasks]}
234 \end{lstlisting}
235
236 \subsection{Miscellaneous Messages}
237 There exists one special type of message that is sent to the device only when
238 it needs to reboot. When the server wants to stop the bond with the device it
239 sends the \CI{MTShutdown} message. The device will then clear his memory, thus
240 losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself.
241 Shortly after the shutdown message a new server can connect to the device
242 because the device is back in listening mode.