Merge remote-tracking branch 'refs/remotes/origin/master'
[msc-thesis1617.git] / results.arch.tex
1 The goal of the system as a whole is to offer a framework of functions with
2 which an \gls{iTasks}-system can add, change and remove devices at runtime.
3 Moreover, the \gls{iTasks}-system can send \gls{mTask}-\glspl{Task} ---
4 compiled at runtime to bytecode by the \gls{mTask}-view --- to the device. The
5 device runs an interpreter which can execute the \gls{Task}'s bytecode. Device
6 profiles should be persistent during reboots of the \gls{iTasks}-system. The
7 methods of interacting with \gls{mTask}-\gls{Task} should be analogous to
8 interacting with \gls{iTasks}-\glspl{Task}. This means that programmers can
9 access the \glspl{SDS} made for a device in the same way as regular \glspl{SDS}
10 and they can execute \gls{mTask}-\glspl{Task} as if they where normal
11 \gls{iTasks}-\glspl{Task}.
12
13 The following terms will be used throughout the following chapter:
14 \begin{itemize}
15 \item Device, Client
16
17 These terms denotes the actual device connected to the system. This can
18 be a real device such as a microcontroller but it can also just be a
19 program on the same machine as the server functioning as a client.
20 \item Server, \gls{iTasks}-System
21
22 This is the actual executable serving the \gls{iTasks} application. The
23 system contains \glspl{Task} taking care of the communication with the
24 clients.
25 \item System
26
27 The system describes the complete ecosystem, containing both the server
28 and the clients including the communication between them.
29 \item Engine
30
31 The runtime system of the client is called the engine. This program
32 handles communicating with the server and runs the interpreter for the
33 \glspl{Task} on the client.
34 \end{itemize}
35
36 \section{Devices}
37 A device is suitable for the system if it can run the engine.
38 The engine is compiled from one codebase and devices implement (part of) the
39 device specific interface. The shared codebase only uses standard \gls{C} and
40 no special libraries or tricks are used. Therefore, the code is compilable for
41 almost any device or system. Note that it is not needed to implement a full
42 interface. The full interface --- excluding the device specific settings --- is
43 listed in Appendix~\ref{app:device-interface}. The interface works in a
44 similar fashion as the \gls{EDSL}. Devices do not have to implement all
45 functionality, this is analogous to the fact that views do not have to
46 implement all type classes in the \gls{EDSL}. When the device connects with
47 the server for the first time, the specifications of what is implemented is
48 communicated.
49
50 At the time of writing the following device families are supported and can run
51 the device software.
52 \begin{itemize}
53 \item \texttt{POSIX} compatible systems connected via the \gls{TCP}.
54
55 This includes systems running \emph{Linux} and \emph{MacOS}.
56 \item The \texttt{STM32} microcontrollers family supported by
57 \texttt{ChibiOS} connected via serial communication.
58
59 This is tested in particular on the \texttt{STM32f7x} series \gls{ARM}
60 development board.
61 \item Microcontrollers which are programmable in the \gls{Arduino} \gls{IDE}
62 connected via serial communication or via \gls{TCP} over WiFi or
63 Ethernet.
64
65 This does not only include \gls{Arduino} compatible boards but also
66 other boards capable of running \gls{Arduino} code. A port of the
67 client has been made for the \texttt{ESP8266} powered \emph{NodeMCU}
68 that is connected via \gls{TCP} over WiFi. A port also has been made
69 for the regular \gls{Arduino} \emph{UNO} board which only boasts a
70 meager \emph{2K} \emph{RAM}. The stack size and storage available for
71 devices boasting this little \emph{RAM} has to be smaller than default
72 but are still suitable to hold a hand full of \glspl{Task}.
73 \end{itemize}
74
75 \subsection{Client}
76 \subsubsection{Engine}
77 The client is in a constant loop listening for input and waiting to execute
78 \glspl{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}.
79 The \CI{input\_available} function waits for input, but has a timeout set which
80 can be interrupted. The timeout of the function determines the amount of loops
81 per time interval and is a parameter that can be set during compilation for a
82 device.
83
84 \begin{algorithm}
85 \KwData{
86 \textbf{list} $tasks$,
87 \textbf{time} $tm$
88 }
89
90 \Begin{
91 \While{true}{
92 \If{input\_available$()$}{
93 receive\_data()\;
94 }
95
96 $tm\leftarrow \text{now}()$\;
97 \ForEach{$t\leftarrow tasks$}{
98 \uIf{is\_interrupt$(t)$ \textbf{and} had\_interrupt$(t)$}{
99 run\_task$(t)$\;
100 }
101 \ElseIf{$tm-t.\text{lastrun} > t.\text{interval}$}{
102 run\_task$(t)$\;
103 \uIf{$t.\text{interval}==0$}{
104 delete\_task$(t)$\;
105 }\Else{
106 $t.\text{lastrun}\leftarrow t$\;
107 }
108 }
109 }
110 }
111 }
112 \caption{Engine pseudocode}\label{alg:client}
113 \end{algorithm}
114
115 \subsubsection{Storage}
116 \glspl{Task} and \glspl{SDS} are stored on the client in memory. Some devices
117 have very little memory and therefore memory space is very expensive and needs
118 to be used optimally. Almost all microcontrollers support heaps nowadays,
119 however, the functions for allocating and freeing the memory on the heap are
120 not very space optimal and often leave holes in the heap if allocations are not
121 freed in reverse order. To overcome this problem the client will allocate a big
122 memory segment in the global data block. This block of memory resides under the
123 stack and its size can be set in the interface implementation. This block of
124 memory will be managed in a similar way as the entire memory space of the
125 device is managed. \Glspl{Task} will grow from the bottom up and \glspl{SDS}
126 will grow from the top down.
127
128 When a \gls{Task} is received, the program will traverse the memory space from
129 the bottom up, jumping over all \glspl{Task}. A \gls{Task} is stored as the
130 structure followed directly by its bytecode. Therefore it only takes two jumps
131 to determine the size of the \gls{Task}. When the program arrived at the last
132 \gls{Task}, this place is returned and the newly received \gls{Task} can be
133 copied to there. This method is analogously applied for \glspl{SDS}, however,
134 the \glspl{SDS} grow from the bottom down.
135
136 When a \gls{Task} or \gls{SDS} is removed, all remaining objects are compressed
137 again. This means that if the first received \gls{Task} is removed, all
138 \glspl{Task} received later will have to move back. Obviously, this is quite
139 time intensive but it can not be permitted to leave holes in the memory since
140 the memory space is so limited. This techniques allows for even the smallest
141 tested microcontrollers with only $2K$ \emph{RAM} to hold several \glspl{Task}
142 and \glspl{SDS}. If this technique would not be used the memory space will
143 decrease over time and the client can then not run for very long since holes
144 are evidently created at some point.
145
146 The structure instances and helper functions for traversing them in memory for
147 \glspl{Task} and \glspl{SDS} are shown in Listing~\ref{lst:structs}.
148
149 \begin{lstlisting}[language=C,label={lst:structs},%
150 caption={The data type storing the \glspl{Task}},float]
151 struct task {
152 uint16_t tasklength;
153 uint16_t interval;
154 unsigned long lastrun;
155 uint8_t taskid;
156 uint8_t *bc;
157 };
158
159 struct task *task_head(void);
160 struct task *task_next(struct task *t);
161
162 struct sds {
163 int id;
164 int value;
165 char type;
166 };
167
168 struct sds *sds_head(void);
169 struct sds *sds_next(struct sds *s);
170 \end{lstlisting}
171
172 \subsubsection{Interpretation}
173 The execution of a \gls{Task} is started by running the \CI{run\_task} function
174 and always starts with setting the program counter and stack
175 pointer to zero and the bottom respectively. When finished, the
176 interpreter executes one step at the time while the program counter is smaller
177 than the program length. This code is listed in Listing~\ref{lst:interpr}. One
178 execution step is basically a big switch statement going over all possible
179 bytecode instructions. Of some instructions, the implementations are shown in
180 the listing. The \CI{BCPush} instruction is a little more complicated in the
181 real code because some decoding will take place as not all \CI{BCValue}s are of
182 the same length and are encoded.
183
184 \begin{lstlisting}[language=C,label={lst:interpr},%
185 caption={Rough code outline for interpretation}]
186 #define f16(p) program[pc]*265+program[pc+1]
187
188 void run_task(struct task *t){
189 uint8_t *program = t->bc;
190 int plen = t->tasklength;
191 int pc = 0;
192 int sp = 0;
193 while(pc < plen){
194 switch(program[pc++]){
195 case BCNOP:
196 break;
197 case BCPUSH:
198 stack[sp++] = pc++ //Simplified
199 break;
200 case BCPOP:
201 sp--;
202 break;
203 case BCSDSSTORE:
204 sds_store(f16(pc), stack[--sp]);
205 pc+=2;
206 break;
207 // ...
208 case BCADD: trace("add");
209 stack[sp-2] = stack[sp-2] + stack[sp-1];
210 sp -= 1;
211 break;
212 // ...
213 case BCJMPT: trace("jmpt to %d", program[pc]);
214 pc = stack[--sp] ? program[pc]-1 : pc+1;
215 break;
216 }
217 \end{lstlisting}
218
219 \subsection{Specification}
220 The server stores a description for every device available in a record type.
221 From the macro settings in the interface file, a profile is created that
222 describes the specification of the device. When the connection between the
223 server and a client is established, the server will send a request for
224 specification. The client serializes its specification and send it to the
225 server so that the server knows what the client is capable of. The exact
226 specification is shown in Listing~\ref{lst:devicespec} and stores the
227 peripheral availability, the memory available for storing \glspl{Task} and
228 \glspl{SDS} and the size of the stack.
229
230 \begin{lstlisting}[label={lst:devicespec},
231 caption={Device specification for \gls{mTask}-\glspl{Task}}]
232 :: MTaskDeviceSpec =
233 { haveLed :: Bool
234 , haveLCD :: Bool
235 , have...
236 , bytesMemory :: Int
237 , stackSize :: Int
238 , aPins :: Int
239 , dPins :: Int
240 }
241 \end{lstlisting}
242
243 \section{iTasks}
244 The server part of the system is written in \gls{iTasks}. Functions for
245 managing devices, \glspl{Task} and \glspl{SDS} have been created to support the
246 functionality. An interactive web application has been created that provides an
247 interactive management console for the \gls{mTask} system. This interface
248 provides functionality to list \glspl{SDS}, add and remove \glspl{Task},
249 administrate devices and view the state of the system.
250
251 \subsection{Device Storage}
252 Everything that a device encompasses is stored in the \CI{MTaskDevice} record
253 type. This includes management for the \glspl{SDS} and \glspl{Task} stored on
254 the device. The \CI{MTaskDevice} definition is shown in
255 Listing~\ref{lst:mtaskdevice} accompanied with the necessary classes and sub
256 types.
257
258 \begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
259 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
260 :: BCState = ... // Compiler state, explained in later sections
261 :: MTaskResource
262 = TCPDevice TCPSettings
263 | SerialDevice TTYSettings
264 | ...
265 :: MTaskDevice =
266 { deviceTask :: Maybe TaskId
267 , deviceError :: Maybe String
268 , deviceChannels :: String
269 , deviceName :: String
270 , deviceState :: BCState
271 , deviceTasks :: [MTaskTask]
272 , deviceResource :: MTaskResource
273 , deviceSpec :: Maybe MTaskDeviceSpec
274 , deviceShares :: [MTaskShare]
275 }
276
277 channels :: MTaskDevice -> Shared Channels
278
279 class MTaskDuplex a where
280 synFun :: a (Shared Channels) -> Task ()
281 \end{lstlisting}
282
283 The \CI{deviceResource} component of the record must implement the
284 \CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
285 used for synchronizing the channels. The \CI{deviceTask} stores the
286 \gls{Task}-id for this \gls{Task} when active so that it can be checked upon.
287 This top-level task has the duty to report exceptions and errors as they are
288 thrown by setting the \CI{deviceError} field. All communication goes via these
289 channels. To send a message to the device, the system just puts it
290 in the channels. Messages sent from the client to the server are also placed
291 in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a
292 simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In
293 case of a device connected by a serial connection, it uses the newly developed
294 serial port library of \gls{Clean}\footnote{\url{%
295 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
296
297 Besides all the communication information, the record also keeps track of the
298 \glspl{Task} currently on the device, the compiler state (see
299 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
300 the specification of the device that is received when connecting. All of this
301 is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
302 format are explained in the following section.
303
304 \subsection{Shares}
305 The system keeps track of the \glspl{SDS} stored on
306 the client in a big \gls{SDS} containing a list of devices. Client-\glspl{SDS}
307 can be stored on one device at the same time. This means that if an \gls{SDS}
308 updates, everyone watching it will be notified. This would result in a lot
309 of notifications that are not meant for the watcher. Moreover, when a client
310 updates the \gls{SDS} this is processed by the connection handler and results
311 in an update of the real \gls{SDS}. Finally, the \gls{SDS} of a client must be
312 synchronized with the actual device. Thus, when an \gls{iTasks}-\gls{Task}
313 writes the client-\gls{SDS}, it must be propagated to the real device. There
314 are several ways of tackling this problem each with their own level of
315 granularity.
316
317 First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can
318 be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and
319 synchronizing it with the device when an update occurred. This approach is very
320 expensive as it requires a lot of listening \glspl{Task}.
321
322 Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every
323 devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite}
324 functions, a single \gls{SDS} per device can be created as a lens that allows
325 mapping on a single client-\gls{SDS}. However, this approach still requires
326 \glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written,
327 everyone is notified, even if the \gls{Task} only uses the value of a single
328 different \gls{SDS}.
329
330 Finally, the current approach --- a single \gls{SDS} for the entire system
331 --- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a
332 \CI{mapReadWrite} can be used but it suffers from the same problem as mentioned
333 before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate
334 the client-\gls{SDS} updates to the actual device. Both of these problems can
335 be solved by using a tailor made \gls{SDS} that heavily depends on parametric
336 lenses.
337
338 \subsection{Parametric Lenses}
339 The type for the parametric lens of the big \gls{SDS} is \CI{Maybe
340 (MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list
341 of devices, from now on global. Moreover, the \gls{SDS} can focus on a single
342 device, from now on local. A local \gls{SDS} can also specifically focus on a
343 single \gls{SDS} on a single device, from now on called local-share. The
344 implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}.
345 The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory.
346 Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing
347 the \gls{SDS} is a little bit more involved. If the write operation originated
348 from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must
349 also be relayed to the actual device. If the write originated from an \gls{SDS}
350 focussed the devices or on one device only, nothing needs to be done. The
351 notification predicate determines whether a watcher gets a notification update.
352
353 \begin{lstlisting}[label={lst:actualdev},%
354 caption={Device \gls{SDS}}]
355 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
356 deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite}
357 where
358 realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
359 realRead p iw = read realDeviceStore iw
360
361 realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
362 realWrite mi w iw
363 # (merr, iw) = write w realDeviceStore iw
364 | isError merr || isNothing mi = (merr $> notifyPred mi, iw)
365 # (Just (dev, ident)) = mi
366 | ident == -1 = (merr $> notifyPred mi, iw)
367 = case find ((==)dev) w of
368 Nothing = (Error $ exception "Device lost", iw)
369 Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of
370 Nothing = (Error $ exception "Share lost", iw)
371 Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of
372 (Error e, iw) = (Error e, iw)
373 (Ok _, iw) = (Ok $ notifyPred mi, iw)
374
375 notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool
376 notifyPred Nothing Nothing = True // Global watcher looking at a global event
377 notifyPred Nothing (Just _) = False // Global watcher looking at a local event
378 notifyPred (Just _) Nothing = False // Local watcher looking at a global event
379 // Local device watcher looking at a local event
380 notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2
381 // Local share watcher looking at a local share event
382 notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2
383
384 realDeviceStore :: Shared [MTaskDevice]
385 realDeviceStore = sharedStore "mTaskDevices" []
386 \end{lstlisting}
387
388 \subsubsection{Global \glspl{SDS}}
389 Accessing the global \gls{SDS} is just a matter of focussing the
390 \CI{deviceStore} with the \CI{Nothing} parameter as follows:
391
392 \begin{lstlisting}[caption={Global \gls{SDS}}]
393 deviceStoreNP :: Shared [MTaskDevice]
394 deviceStoreNP = sdsFocus Nothing deviceStore
395 \end{lstlisting}
396
397 \subsubsection{Local \glspl{SDS}}
398 Accessing a single device can be done using the \CI{mapReadWrite} function.
399 Since device comparison is shallow, the device that is given is allowed to be
400 an old version. The identification of devices is solely done on the name of the
401 channels and is unique throughout the system. The implementation is as follows:
402
403 \begin{lstlisting}[caption={Local \gls{SDS}}]
404 deviceShare :: MTaskDevice -> Shared MTaskDevice
405 deviceShare d = mapReadWriteError
406 ( \ds->case find ((==)d) of
407 Nothing = exception "Device lost"
408 Just d = Ok d)
409 , \w ds->case splitWith ((==)d) ds of
410 ([], _) = Error $ exception "Device lost"
411 ([_:_], ds) = Ok $ Just [w:ds])
412 $ sdsFocus (Just (d, -1)) deviceStore
413 \end{lstlisting}
414
415 \subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
416 A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
417 function. This function focusses the real big \gls{SDS} on a single \gls{SDS}
418 and uses the \CI{mapReadWrite} functions to serve the correct part of the
419 information.
420
421 \begin{lstlisting}[caption={Local \gls{SDS}}]
422 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
423 shareShare dev share = sdsFocus ()
424 $ mapReadWriteError (read, write)
425 $ sdsFocus (Just (dev, share.identifier))
426 $ deviceStore
427 where
428 read :: [MTaskDevice] -> MaybeError TaskException BCValue
429 read devs = case find ((==)dev) devs of
430 Nothing = exception "Device lost"
431 Just d = case find ((==)share) d.deviceShares of
432 Nothing = exception "Share lost"
433 Just s = Ok s.MTaskShare.value
434
435 write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice])
436 write val devs = case partition ((==)dev) devs of
437 ([], _) = Error $ exception "Device doesn't exist anymore"
438 ([_,_:_], _) = Error $ exception "Multiple matching devices"
439 ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of
440 ([], _) = Error $ exception "Share doesn't exist anymore"
441 ([_,_:_], _) = Error $ exception "Multiple matching shares"
442 ([s], shares) = Ok $ Just [{MTaskDevice | d &
443 deviceShares=[{MTaskShare | s & value=val}:shares]}:devs]
444 \end{lstlisting}
445
446 \section{Communication}
447 The communication from the server to the client and vice versa is just a
448 character stream containing encoded \gls{mTask} messages. The \CI{synFun}
449 belonging to the device is responsible for sending the content in the left
450 channel and putting received messages in the right channel. Moreover, the
451 boolean value should be set to \CI{True} when the connection is terminated. The
452 specific encoding of the messages is visible in
453 Appendix~\ref{app:communication-protocol}. The type holding the messages is
454 shown in Listing~\ref{lst:avmsg}. Detailed explanation about the message types
455 and according actions will be given in the following subsections.
456
457 \begin{lstlisting}[label={lst:avmsg},caption={Available messages}]
458 :: MTaskId :== Int
459 :: MSDSId :== Int
460 :: MTaskFreeBytes :== Int
461 :: MTaskMSGRecv
462 = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId
463 | MTSDSAck MSDSId | MTSDSDelAck MSDSId
464 | MTPub MSDSId BCValue | MTMessage String
465 | MTDevSpec MTaskDeviceSpec | MTEmpty
466
467 :: MTaskMSGSend
468 = MTTask MTaskInterval String | MTTaskDel MTaskId
469 | MTShutdown | MTSds MSDSId BCValue
470 | MTUpd MSDSId BCValue | MTSpec
471
472 :: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int
473 \end{lstlisting}
474
475 \subsection{Add a device}
476 A device can be added by filling in the \CI{MTaskDevice} record as much as
477 possible and running the \CI{connectDevice} function. This function grabs the
478 channels, starts the synchronization \gls{Task} (\CI{synFun}), makes sure the
479 errors are handled when needed and runs a processing function in parallel to
480 react on the incoming messages. Moreover, it sends a specification request to
481 the device in question to determine the details of the device and updates the
482 record to contain the top-level \gls{Task}-id. All device functionality
483 heavily depends on the specific \CI{deviceShare} function that generates an
484 \gls{SDS} for a specific device. This allows giving an old device record to the
485 function and still update the latest instance. Listing~\ref{lst:connectDevice}
486 shows the connection function.
487
488 \begin{lstlisting}[label={lst:connectDevice},%
489 caption={Connect a device}]
490 connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels
491 connectDevice procFun device = set ([], [], False) ch
492 >>| appendTopLevelTask 'DM'.newMap True
493 ( procFun device ch -||- catchAll (getSynFun device.deviceData ch) errHdl)
494 >>= \tid->upd (\d->{d&deviceTask=Just tid,deviceError=Nothing}) (deviceShare device)
495 >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch
496 where
497 errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! ()
498 ch = channels device
499 \end{lstlisting}
500
501 Figure~\ref{fig:handshake} shows the connection diagram. The client responds to
502 the server with their device specification. This is detected by the processing
503 function and the record is updated accordingly.
504
505 \begin{figure}[H]
506 \centering
507 \begin{sequencediagram}
508 \newthread{s}{Server}
509 \newinst[4]{c}{Client}
510 \begin{call}{s}{MTSpec}{c}{MTDevSpec}
511 \end{call}
512 \end{sequencediagram}
513 \caption{Connect a device}\label{fig:handshake}
514 \end{figure}
515
516 \subsection{\glspl{Task} \& \glspl{SDS}}
517 When a \gls{Task} is sent to the device it is added to the device record
518 without an identifier. The actual identifier is added to the record when the
519 acknowledgement of the \gls{Task} by the device is received. The connection
520 diagram is shown in Figure~\ref{fig:tasksend}.
521
522 \begin{figure}[H]
523 \centering
524 \begin{sequencediagram}
525 \newthread{s}{Server}
526 \newinst[4]{c}{Client}
527 \begin{call}{s}{MTSDS}{c}{MTSDSAck}
528 \end{call}
529 \begin{call}{s}{MTTask}{c}{MTTaskAck}
530 \end{call}
531 \end{sequencediagram}
532 \caption{Sending a \gls{Task} to a device}\label{fig:tasksend}
533 \end{figure}
534
535 The function for sending a \gls{Task} to the device is shown in
536 Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The
537 details of the compilation process are given in Section~\ref{sec:compiler}.
538 The new \glspl{SDS} that were generated during compilation are merged with the
539 existing device's \glspl{SDS}. Furthermore the messages are placed in the
540 channel \gls{SDS} of the device. This will result in sending the actual \gls{SDS}
541 specification and \gls{Task} specifications to the device. A \gls{Task} record
542 is created with the identifier $-1$ to denote a \gls{Task} not yet
543 acknowledged. Finally the device itself is updated with the new state and with
544 the new \gls{Task}. After waiting for the acknowledgement the device is
545 updated again and the \gls{Task} returns.
546
547 \begin{lstlisting}[label={lst:sendtask},%
548 caption={Sending a \gls{Task} to a device}]
549 makeTask :: String Int -> Task MTaskTask
550 makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt}
551
552 makeShare :: String Int BCValue -> MTaskShare
553 makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value}
554
555 sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task MTaskTask
556 sendTaskToDevice wta mTask (device, timeout)
557 # (msgs, newState=:{sdss}) = toMessages timeout mTask device.deviceState
558 # shares = [makeShare wta "" sdsi sdsval\\{sdsi,sdsval}<-sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`]
559 = updateShares device ((++) shares)
560 >>| sendMessages msgs device
561 >>| makeTask wta -1
562 >>= \t->upd (addTaskUpState newState t) (deviceShare device)
563 >>| wait "Waiting for task to be acked" (taskAcked t) (deviceShare device)
564 >>| treturn t
565 where
566 addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
567 addTaskUpState st task device = {MTaskDevice | device & deviceState=st, deviceTasks=[task:device.deviceTasks]}
568 taskAcked t d = maybe True (\t->t.ident <> -1) $ find (eq t) d.deviceTasks
569 eq t1 t2 = t1.dateAdded == t2.dateAdded && t1.MTaskTask.name == t2.MTaskTask.name
570 \end{lstlisting}
571
572 \subsection{Miscellaneous Messages}
573 One special type of message is available which is sent to the device only when
574 it needs to reboot. When the server wants to stop the bond with the device it
575 sends the \CI{MTShutdown} message. The device will then clear its memory, thus
576 losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself.
577 Shortly after the shutdown message a new server can connect to the device
578 because the device is back in listening mode.
579
580 \subsection{Integration}
581 When the system starts up, the devices from the previous execution still
582 residing in the \gls{SDS} must be cleaned up. It might be the case that they
583 contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in
584 this run. A user or programmer can later choose to reconnect to some devices.
585
586 \begin{lstlisting}[caption={Starting up the devices},%
587 label={lst:startupdevs}]
588 startupDevices :: Task [MTaskDevice]
589 startupDevices = upd (map reset) deviceStoreNP
590 where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing}
591 \end{lstlisting}
592
593 The system's management is done through the interface of a single \gls{Task}
594 called \CI{mTaskManager}. To manage the system, a couple of different
595 functionalities are necessary and are launched. An image of the management
596 interface is shown in Figure~\ref{lst:manage}. The left sidebar of the
597 interface shows the list of example \glspl{Task} that are present in the
598 system. When clicking a \gls{Task}, a dialog opens in which a device can be
599 selected to send the \gls{Task} to. The dialog might contain user specified
600 variables. All example \gls{mTask}-\glspl{Task} are of the type \CI{Task (Main
601 (ByteCode () Stmt))} and can thus ask for user input first if needed for
602 parameterized \gls{mTask}-\glspl{Task}. The bottom panel shows the device
603 information. In this panel, the devices can be created and modified. Moreover,
604 this panel allows the user to reconnect with a device after a restart of the
605 server application.
606
607 \begin{figure}[H]
608 \centering
609 \includegraphics[width=\linewidth]{manage}
610 \caption{The device management interface}\label{lst:manage}
611 \end{figure}
612
613 \section[Lifting mTasks to iTasks-Tasks]%
614 {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}}
615 If the user does not want to know where and when a \gls{mTask} is actually
616 executed and is just interested in the results it can lift the \gls{mTask} to
617 an \gls{iTasks}-\gls{Task}. The function is called with a name, \gls{mTask},
618 device and interval specification and it will return a \gls{Task} that finishes
619 if and only if the \gls{mTask} has returned.
620
621 \begin{lstlisting}[caption={Starting up the devices}]
622 liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task ()
623 liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c
624 >>= \t->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev)
625 >>| viewInformation "Done!" [] ()
626 where
627 taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks
628 \end{lstlisting}
629
630 \section{Examples}
631 Here comes a description of the demo program.