Sweeping through the archg
[msc-thesis1617.git] / results.arch.tex
1 The goal of the total system is to facilitate an ecosystem in which an
2 \gls{iTasks}-system can add, change and remove devices at runtime. Moreover,
3 the \gls{iTasks}-system can send \glspl{mTask} --- compiled at runtime to
4 bytecode by the \gls{mTask}-view --- to the device. The device runs an
5 interpreter which can execute the \gls{Task}'s bytecode. Device profiles should
6 be persistent during reboots of the \gls{iTasks}-system. The methods of
7 interacting with \glspl{mTask} should be analogous to interacting with
8 \gls{iTasks}-\glspl{Task}. Meaning that programmers can access the \glspl{SDS}
9 made for a device in the same way as a regular \gls{SDS} and they can execute
10 \glspl{mTask} as if it where a normal \gls{iTasks}-\gls{Task}.
11
12 The following terms will be used throughout the following chapter:
13 \begin{itemize}
14 \item Device, Client
15
16 This denotes the actual device connected to the system. This can be a
17 real device such as a microcontroller but it can also just be a program
18 on the same machine as the server functioning as a client.
19 \item Server, \gls{iTasks}-System
20
21 This is the actual executable serving the \gls{iTasks} application. The
22 system contains \glspl{Task} taking care of the communication with the
23 clients.
24 \item System
25
26 The system describes the complete ecosystem, containing both the server
27 and the clients including the communication between them.
28 \item Engine
29
30 The runtime system of the client is called the engine. This program
31 handles communicating with the server and runs the interpreter for the
32 \glspl{Task} on the client.
33 \end{itemize}
34
35 \section{Devices}
36 A device is suitable for the system if it can run the engine.
37 The engine is compiled from one codebase and devices implement (part of) the
38 device specific interface. The shared codebase only uses standard \gls{C} and
39 no special libraries or tricks are used. Therefore, the code is compilable for
40 almost any device or system. Note that it is not needed to implement a full
41 interface. The full interface --- excluding the device specific settings --- is
42 listed in Appendix~\ref{app:device-interface}. The interface works in a
43 similar fashion as the \gls{EDSL}. Devices do not have to implement all
44 functionality, this is analogous to the fact that views do not have to
45 implement all type classes in the \gls{EDSL}. When the device connects for the
46 first time with a server the specifications of what is implemented is
47 communicated.
48
49 At the time of writing the following device families are supported and can run
50 the device software.
51 \begin{itemize}
52 \item \texttt{POSIX} compatible systems connected via \gls{TCP}.
53
54 This includes systems running \emph{Linux} and \emph{MacOS}.
55 \item \texttt{STM32} family microcontrollers supported by \texttt{ChibiOS}
56 connected via serial communication.
57
58 This is tested in particular on the \texttt{STM32f7x} series \gls{ARM}
59 development board.
60 \item Microcontrollers who are programmable in the \gls{Arduino} \gls{IDE}
61 connected via serial communication or via \gls{TCP} over WiFi.
62
63 This does not only include \gls{Arduino} compatible boards but also
64 other boards capable of running \gls{Arduino} code. A port of the
65 client has been made for the \texttt{ESP8266} powered \emph{NodeMCU}
66 that is connected via \gls{TCP} over WiFi. A port also has been made
67 for the regular \gls{Arduino} \emph{UNO} board which only boasts a
68 meager \emph{2K} of \emph{RAM}. The stack size and storage for such
69 small amount of \emph{RAM} have to be smaller than default but it still
70 suitable to hold a hand full of \glspl{Task}.
71 \end{itemize}
72
73 \subsection{Client}
74 \subsubsection*{Engine}
75 The client is in a constant loop listening for input and waiting to execute
76 \gls{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}.
77
78 \todo{make algorithm}
79 \begin{algorithm}[H]
80 \KwData{\textbf{stack} stack, \textbf{time} $t, t_p$}
81
82 $t\leftarrow\text{now}()$\;
83 \Begin{
84 \While{true}{
85 $t_p\leftarrow t$\;
86 $t\leftarrow\text{now}()$\;
87 \If{notEmpty$($queue$)$}{
88 $task\leftarrow \text{queue.pop}()$\;
89 $task$.wait $\leftarrow task$.wait $-(t-t_p)$\;
90 \eIf{$task.wait>t_0$}{
91 queue.append$(task)$\;
92 }{
93 run\_task$(task)$\;
94 }
95 }
96 }
97 }
98 \caption{Engine pseudocode}\label{alg:client}
99 \end{algorithm}
100
101 \subsubsection*{Storage}
102 \glspl{Task} and \glspl{SDS} are stored on the client in one big memory space
103 that is fully allocated at the start of the program. The space could also have
104 been dynamically allocated but that would require using the heap which is
105 unwanted in small memory environments. \Glspl{Task} grow from the bottom up
106 and \glspl{SDS} grow from the top down. When a \gls{Task} or \gls{SDS} is
107 removed, all \glspl{Task} are relocated in the memory space to not leave
108 holes. Both \glspl{Task} and \glspl{SDS} are stored as structs and helper
109 functions are available to loop through them without having to fiddle in the
110 memory space. The instance for \glspl{Task} and \glspl{SDS} are shown in
111 Listing~\ref{lst:structs} accompanied by the helper functions for \glspl{Task}.
112 \Glspl{Task} consist the length, interval, last run time, id and the bytecode.
113 \Glspl{SDS} consist just of an id, value and type. The pointer to the bytecode
114 of the \gls{Task} always points to the location in the memory space.
115
116 \begin{lstlisting}[language=C,label={lst:structs},%
117 caption={The data type storing the \glspl{Task}}]
118 struct task {
119 uint16_t tasklength;
120 uint16_t interval;
121 unsigned long lastrun;
122 uint8_t taskid;
123 uint8_t *bc;
124 };
125
126 struct task *task_head(void);
127 struct task *task_next(struct task *t);
128
129 struct sds {
130 int id;
131 int value;
132 char type;
133 };
134
135 struct sds *sds_head(void);
136 struct sds *sds_next(struct sds *s);
137 \end{lstlisting}
138
139 \subsubsection*{Interpretation}
140 Execution of a \gls{Task} always start with prepared the stack and the program
141 counter and stack pointer are set to zero and the bottom respectively. When
142 finished, the interpreter executes one step at the time while the program
143 counter is smaller than the program length. The code for this is listed in
144 Listing~\ref{lst:interpr}. One execution step is basically a big switch
145 statement going over all possible bytecode instructions. Some instructions are
146 detailed upon in the listing. The \CI{BCPush} instruction is a little more
147 complicated in the real code because some decoding will take place as not all
148 \CI{BCValue}'s are of the same length and are encoded.
149
150 \begin{lstlisting}[language=C,label={lst:interpr},%
151 caption={Rough code outline for interpretation}]
152 #define f16(p) program[pc]*265+program[pc+1]
153
154 void run_task(struct task *t){
155 uint8_t *program = t->bc;
156 int plen = t->tasklength;
157 int pc = 0;
158 int sp = 0;
159 while(pc < plen){
160 switch(program[pc++]){
161 case BCNOP:
162 break;
163 case BCPUSH:
164 stack[sp++] = pc++ //Simplified
165 break;
166 case BCPOP:
167 sp--;
168 break;
169 case BCSDSSTORE:
170 sds_store(f16(pc), stack[--sp]);
171 pc+=2;
172 break;
173 // ...
174 case BCADD: trace("add");
175 stack[sp-2] = stack[sp-2] + stack[sp-1];
176 sp -= 1;
177 break;
178 // ...
179 case BCJMPT: trace("jmpt to %d", program[pc]);
180 pc = stack[--sp] ? program[pc]-1 : pc+1;
181 break;
182 }
183 \end{lstlisting}
184
185 \subsection{Specification}
186 The server stores a description for every device available in a record type
187 which are stored in a \gls{SDS}. From the macro settings in
188 the interface file, a profile is created for the device that describes the
189 specification. When a connection between the server and a client is established
190 the server will send a request for specification. The client will serialize his
191 specification and send it to the server so that the server knows what the
192 client is capable of. The exact specification is shown in
193 Listing~\ref{lst:devicespec} and stores the peripheral availability, the memory
194 available for storing \glspl{Task} and \glspl{SDS} and the size of the stack.
195
196 \begin{lstlisting}[label={lst:devicespec},
197 caption={Device specification for \glspl{mTask}}]
198 :: MTaskDeviceSpec =
199 { haveLed :: Bool
200 , haveLcd :: Bool
201 , have...
202 , bytesMemory :: Int
203 , stackSize :: Int
204 , aPins :: Int
205 , dPins :: Int
206 }
207 \end{lstlisting}
208
209 \subsection{Device Storage}
210 All devices available in the system are stored in a big \gls{SDS} that contains
211 a list of \CI{MTaskDevice}s. The exact specification is defined as in
212 Listing~\ref{lst:mtaskdevice} accompanied with the used classes and types.
213
214 The \CI{deviceResource} component of the record must implement the
215 \CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
216 used for synchronizing the channels. The \CI{deviceTask} stores the
217 \gls{Task}-id for this \gls{Task} when active so that it can be checked upon.
218 This top-level task has the duty to report exceptions and errors as they are
219 thrown by setting the \CI{deviceError} field. All communication goes via these
220 channels. If the system wants to send a message to the device, it just puts it
221 in the channels. Messages sent from the client to the server are also placed
222 in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a
223 simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In
224 case of a device connected by a serial connection, it uses the newly developed
225 serial port library of \gls{Clean}\footnote{\url{%
226 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
227
228 Besides all the communication information, the record also keeps track of the
229 \glspl{Task} currently on the device, the compiler state (see
230 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
231 the specification of the device that is received when connecting. All of this
232 is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
233 format are explained in the following section. Specialized shares are available
234 per device. The internal mechanism for this is given in
235 Chapter~\ref{chp:itasksint}.
236
237 \begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
238 deviceStoreNP :: Shared [MTaskDevice]
239 deviceShare :: MTaskDevice -> Shared MTaskDevice
240
241 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
242 :: BCState = ... // Compiler state, explained in later sections
243 :: MTaskResource
244 = TCPDevice TCPSettings
245 | SerialDevice TTYSettings
246 :: MTaskDevice =
247 { deviceTask :: Maybe TaskId
248 , deviceError :: Maybe String
249 , deviceChannels :: String
250 , deviceName :: String
251 , deviceState :: BCState
252 , deviceTasks :: [MTaskTask]
253 , deviceData :: MTaskResource
254 , deviceSpec :: Maybe MTaskDeviceSpec
255 , deviceShares :: [MTaskShare]
256 }
257
258 channels :: MTaskDevice -> Shared Channels
259
260 class MTaskDuplex a where
261 synFun :: a (Shared Channels) -> Task ()
262 \end{lstlisting}
263
264 \section{iTasks}
265 The server part of the system is written in \gls{iTasks}. Functions for
266 managing devices, \glspl{Task} and \glspl{SDS} have been created to support the
267 functionality. An interactive application has been created that allows an
268 interactive management console for the \gls{mTask} system. This interface
269 provides functionality to list \glspl{SDS}, add \glspl{Task}, remove
270 \glspl{Task}, administrate devices and view the state of the system.
271
272 \subsection{Integration}
273 When the system starts up the devices from the previous execution still
274 residing in the \gls{SDS} must be cleaned up. It might be the case that they
275 contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in
276 this run. A user or programmer can later choose to reconnect to some devices.
277
278 \begin{lstlisting}[caption={Starting up the devices},%
279 label={lst:startupdevs}]
280 startupDevices :: Task [MTaskDevice]
281 startupDevices = upd (map reset) deviceStoreNP
282 where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing}
283 \end{lstlisting}
284
285 An image of the management interface is shown in Figure~\ref{lst:manage}.
286 The system management is done by a single \gls{Task} called \CI{mTaskManager}.
287 To manage the system, a couple of different functionalities are needed and
288 are launched. The left sidebar of the interface shows the list of example
289 \glspl{Task} that are present in the system. When clicking a \gls{Task}, a
290 dialog opens in which you can select the device to send the \gls{Task} to. The
291 dialog might contain user specified variables. All example \glspl{mTask} are of
292 the type \CI{Task (Main (ByteCode () Stmt))} and can thus ask for user input
293 first.
294
295 The bottom panel shows the device information. In this panel, the devices can
296 be created and modified. Moreover, this panel allows the user to reconnect with
297 a device after a restart of the server application.
298
299 \begin{figure}[H]
300 \centering
301 \includegraphics[width=\linewidth]{manage}
302 \caption{The device management interface}\label{lst:manage}
303 \end{figure}
304
305 \subsection{Shares}
306 The architecture of the system needs to keep track of the \glspl{SDS} stored on
307 the client. \glspl{SDS} can be stored on only one device at the same time.
308 that also stores the of devices. This means that if a \gls{SDS} updates,
309 everyone watching it will be notified. This would result in to a lot of
310 notifications that are not ment to be for the listener. Moreover, when a client
311 updates the \gls{SDS} this is processed by the connection handler and results
312 in an update of the real \gls{SDS}.
313 Finally, the \gls{SDS} of a client must be synchronized with the actual device.
314 There are several ways of tackling this problem each with their own pros and
315 cons and their own level of abstraction.
316
317 \begin{itemize}
318 \item Instantiate an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used
319 in a client.
320
321 \item Instantiate a \gls{iTasks}-\gls{SDS} for every device that stores all
322 their \glspl{SDS}.
323
324 \item Use only one \gls{iTasks}-\gls{SDS} for all devices.
325 \end{itemize}
326
327 \begin{lstlisting}[label={lst:actualdev},%
328 caption={Real types for the device \gls{SDS}}]
329 deviceStoreNP :: Shared [MTaskDevice]
330 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
331 \end{lstlisting}
332
333 \subsection{Parametric Lenses}
334 The type of the parametric lens is \CI{Maybe (MTaskDevice, Int)}. The \gls{SDS}
335 can be responsible for the entire list of devices, from now on global.
336 Moreover, the \gls{SDS} can focus on a single device, from now on local. A
337 local \gls{SDS} can also specifically focus on a single \gls{SDS} on a single
338 device, from now on called local-share.
339
340 \paragraph{Global \glspl{SDS}: }
341 Accessing the global \gls{SDS} is just a matter of focussing the
342 \CI{deviceStore} with the \CI{Nothing} parameter. The signature for
343 \CI{deviceStore} was given in Chapter~\ref{chp:arch}. The actual implementation
344 is as in Listing~\ref{lst:global}
345
346 \begin{lstlisting}[label={lst:shareimpl},%
347 caption={Base share implementation}]
348 deviceStoreNP :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
349 deviceStoreNP = sdsFocus Nothing deviceStore
350 \end{lstlisting}
351
352
353
354
355 \paragraph{Local \glspl{SDS}: }
356 \paragraph{Local-share specific \glspl{SDS}: }
357
358 The implementation for the share is shown in Listing~\ref{lst:shareimpl}. The
359 \CI{realDeviceStore} \gls{SDS} is not exported through the header files. This
360 \gls{SDS} contains the actual \gls{SDS} that writes to disk or memory.
361 \CI{Int} is the identifier of the \gls{SDS}. The \gls{iTasks} way of applying
362 lenses is through the \CI{sdsFocus} function and through the \CI{sdsLens}
363 functions. \CI{sdsFocus} allows the programmer to fix the parameter.
364 \CI{sdsLens} is basically a \CI{mapReadWrite} that has access to the parameter.
365 This allows the programmer to create filters and lenses. Both of the methods
366 are not good enough for the device \gls{SDS} because they do not achieve the
367 writing to the actual device. Writing to a device requires being able to write
368 to \glspl{SDS}. To solve this problem, a real base \gls{SDS} is created. All
369 the details are visible in Listing~\ref{lst:shareimpl}.
370
371 \section{Communication}
372 The communication from the server to the client and vice versa is just a
373 character stream containing encoded \gls{mTask} messages. The specific encoding
374 is visible in Appendix~\ref{app:communication-protocol}. The type holding the
375 messages in Listing~\ref{lst:avmsg}. Detailed explanation about the message
376 types will be given in the following subsections.
377
378 \begin{lstlisting}[label={lst:avmsg},caption={Available messages}]
379 :: MTaskId :== Int
380 :: MSDSId :== Int
381 :: MTaskFreeBytes :== Int
382 :: MTaskMSGRecv
383 = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId
384 | MTSDSAck MSDSId | MTSDSDelAck MSDSId
385 | MTPub MSDSId BCValue | MTMessage String
386 | MTDevSpec MTaskDeviceSpec | MTEmpty
387
388 :: MTaskMSGSend
389 = MTTask MTaskInterval String | MTTaskDel MTaskId
390 | MTShutdown | MTSds MSDSId BCValue
391 | MTUpd MSDSId BCValue | MTSpec
392
393 :: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int
394 \end{lstlisting}
395
396 \subsection{Add a device}
397 A device can be added by filling in the \CI{MTaskDevice} record as much as
398 possible and running the \CI{connectDevice} function. This function grabs the
399 channels, starts the synchronization \gls{Task}, makes sure the errors are
400 handled when needed and runs a processing function in parallel to react on the
401 incoming messages. Moreover, it sends a specification request to the device in
402 question to determine the details of the device and updates the record to
403 contain the top-level \gls{Task}-id. All the device functionality heavily
404 depends on the specific \CI{deviceShare} function that applies a function a device in
405 the \gls{SDS} when they are equal. Device equality is defined as equality on
406 their channels. This allows you to give an old device record to the function
407 and still update the latest instance. Listing~\ref{lst:connectDevice} shows the
408 connection function.
409
410 \begin{lstlisting}[label={lst:connectDevice},%
411 caption={Connect a device}]
412 withDevices :: MTaskDevice (MTaskDevice -> MTaskDevice) -> Task [MTaskDevice]
413
414 connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels
415 connectDevice procFun device = let ch = channels device
416 in appendTopLevelTask 'DM'.newMap True
417 (procFun device ch -||- catchAll (getSynFun d.deviceData ch) errHdl)
418 >>= \tid->withDevices device (\d->{d&deviceTask=Just tid,deviceError=Nothing})
419 >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch
420 where
421 errHdl e = withDevices device (\d->{d & deviceTask=Nothing, deviceError=Just e}) @! ()
422 \end{lstlisting}
423
424 Figure~\ref{fig:handshake} shows the connection diagram. The client responds to
425 the server with their device specification. This is detected by the processing
426 function and the record is updated accordingly.
427
428 \begin{figure}[H]
429 \centering
430 \begin{sequencediagram}
431 \newthread{s}{Server}
432 \newinst[4]{c}{Client}
433 \begin{call}{s}{MTSpec}{c}{MTDevSpec}
434 \end{call}
435 \end{sequencediagram}
436 \caption{Connect a device}\label{fig:handshake}
437 \end{figure}
438
439 \subsection{\glspl{Task} \& \glspl{SDS}}
440 When a \gls{Task} is sent to the device it is added to the device record
441 without an identifier. The actual identifier is added to the record when the
442 acknowledgement of the \gls{Task} by the device is received. The connection
443 diagram is shown in Figure~\ref{fig:tasksend}.
444
445 \begin{figure}[H]
446 \centering
447 \begin{sequencediagram}
448 \newthread{s}{Server}
449 \newinst[4]{c}{Client}
450 \begin{call}{s}{MTSDS}{c}{MTSDSAck}
451 \end{call}
452 \begin{call}{s}{MTTask}{c}{MTTaskAck}
453 \end{call}
454 \end{sequencediagram}
455 \caption{Sending a \gls{Task} to a device}\label{fig:tasksend}
456 \end{figure}
457
458 The function for sending a \gls{Task} to the device is shown in
459 Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The
460 details of the compilation process are given in Section~\ref{sec:compiler}.
461 The new \glspl{SDS} that were made during compilation are added to the
462 deviceshares that were made during the compilation are merged with the existing
463 shares on the device. Furthermore the messages are placed in the channel share
464 of the device. This will result in sending the actual \gls{SDS} specification
465 and \gls{Task} specifications to the device. A \gls{Task} record is created
466 with the identifier $-1$ to denote a \gls{Task} not yet acknowledged. Finally
467 the device itself is updated with the new state and with the new \gls{Task}.
468 When the device returns an acknowledgement the \gls{Task} is updated
469 accordingly.
470
471 \begin{lstlisting}[label={lst:sendtask},%
472 caption={Sending a \gls{Task} to a device}]
473 makeTask :: String Int -> Task MTaskTask
474 makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt}
475
476 makeShare :: String Int BCValue -> MTaskShare
477 makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value}
478
479 sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task [MTaskDevice]
480 sendTaskToDevice wta mTask (device, timeout)
481 # (msgs, newState) = toMessages timeout mTask device.deviceState
482 # shares = [makeShare wta sdsi sdsval\\{sdsi,sdsval}<-newState.sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`]
483 = updateShares device ((++) shares)
484 >>| sendMessages msgs device
485 >>| makeTask wta -1
486 >>= withDevices device o addTaskUpState newState
487 where
488 addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
489 addTaskUpState st task device = {MTaskDevice | device &
490 deviceState=st, deviceTasks=[task:device.deviceTasks]}
491 \end{lstlisting}
492
493 \subsection{Miscellaneous Messages}
494 There exists one special type of message that is sent to the device only when
495 it needs to reboot. When the server wants to stop the bond with the device it
496 sends the \CI{MTShutdown} message. The device will then clear his memory, thus
497 losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself.
498 Shortly after the shutdown message a new server can connect to the device
499 because the device is back in listening mode.
500
501 \section{Lifting mTask to iTasks}
502 \todo{task lifting}
503
504 \section{Examples}
505 \todo{example program (demo)}