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