-The goal of the system as a whole is to offer a framework of functions with
-which an \gls{iTasks}-system can add, change and remove devices at runtime.
-Moreover, the \gls{iTasks}-system can send \gls{mTask}-\glspl{Task} ---
-compiled at runtime to bytecode by the \gls{mTask}-view --- to the device. The
-device runs an interpreter which can execute the \gls{Task}'s bytecode. Device
-profiles should be persistent during reboots of the \gls{iTasks}-system. The
-methods of interacting with \gls{mTask}-\gls{Task} should be analogous to
-interacting with \gls{iTasks}-\glspl{Task}. This means that programmers can
-access the \glspl{SDS} made for a device in the same way as regular \glspl{SDS}
-and they can execute \gls{mTask}-\glspl{Task} as if they where normal
-\gls{iTasks}-\glspl{Task}.
-
-The following terms will be used throughout the following chapter:
-\begin{itemize}
- \item Device, Client
-
- These terms denotes the actual device connected to the system. This can
- be a real device such as a microcontroller but it can also just be a
- program on the same machine as the server functioning as a client.
- \item Server, \gls{iTasks}-System
-
- This is the actual executable serving the \gls{iTasks} application. The
- system contains \glspl{Task} taking care of the communication with the
- clients.
- \item System
-
- The system describes the complete ecosystem, containing both the server
- and the clients including the communication between them.
- \item Engine
-
- The runtime system of the client is called the engine. This program
- handles communicating with the server and runs the interpreter for the
- \glspl{Task} on the client.
-\end{itemize}
-
-\section{Devices}
-A device is suitable for the system if it can run the engine.
-The engine is compiled from one codebase and devices implement (part of) the
-device specific interface. The shared codebase only uses standard \gls{C} and
-no special libraries or tricks are used. Therefore, the code is compilable for
-almost any device or system. Note that it is not needed to implement a full
-interface. The full interface --- excluding the device specific settings --- is
-listed in Appendix~\ref{app:device-interface}. The interface works in a
-similar fashion as the \gls{EDSL}. Devices do not have to implement all
-functionality, this is analogous to the fact that views do not have to
-implement all type classes in the \gls{EDSL}. When the device connects with
-the server for the first time, the specifications of what is implemented is
-communicated.
-
-At the time of writing the following device families are supported and can run
-the device software.
-\begin{itemize}
- \item \texttt{POSIX} compatible systems connected via the \gls{TCP}.
-
- This includes systems running \emph{Linux} and \emph{MacOS}.
- \item The \texttt{STM32} microcontrollers family supported by
- \texttt{ChibiOS} connected via serial communication.
-
- This is tested in particular on the \texttt{STM32f7x} series \gls{ARM}
- development board.
- \item Microcontrollers which are programmable in the \gls{Arduino} \gls{IDE}
- connected via serial communication or via \gls{TCP} over WiFi or
- Ethernet.
-
- This does not only include \gls{Arduino} compatible boards but also
- other boards capable of running \gls{Arduino} code. A port of the
- client has been made for the \texttt{ESP8266} powered \emph{NodeMCU}
- that is connected via \gls{TCP} over WiFi. A port also has been made
- for the regular \gls{Arduino} \emph{UNO} board which only boasts a
- meager \emph{2K} \emph{RAM}. The stack size and storage available for
- devices boasting this little \emph{RAM} has to be smaller than default
- but are still suitable to hold a hand full of \glspl{Task}.
-\end{itemize}
-
-\subsection{Client}
-\subsubsection{Engine}
-The client is in a constant loop listening for input and waiting to execute
-\glspl{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}.
-The \CI{input\_available} function waits for input, but has a timeout set which
-can be interrupted. The timeout of the function determines the amount of loops
-per time interval and is a parameter that can be set during compilation for a
-device.
-
-\begin{algorithm}
- \KwData{
- \textbf{list} $tasks$,
- \textbf{time} $tm$
- }
-
- \Begin{
- \While{true}{
- \If{input\_available$()$}{
- receive\_data()\;
- }
-
- $tm\leftarrow \text{now}()$\;
- \ForEach{$t\leftarrow tasks$}{
- \uIf{is\_interrupt$(t)$ \textbf{and} had\_interrupt$(t)$}{
- run\_task$(t)$\;
- }
- \ElseIf{$tm-t.\text{lastrun} > t.\text{interval}$}{
- run\_task$(t)$\;
- \uIf{$t.\text{interval}==0$}{
- delete\_task$(t)$\;
- }\Else{
- $t.\text{lastrun}\leftarrow t$\;
- }
- }
- }
- }
- }
- \caption{Engine pseudocode}\label{alg:client}
-\end{algorithm}
-
-\subsubsection{Storage}
-\glspl{Task} and \glspl{SDS} are stored on the client in memory. Some devices
-have very little memory and therefore memory space is very expensive and needs
-to be used optimally. Almost all microcontrollers support heaps nowadays,
-however, the functions for allocating and freeing the memory on the heap are
-not very space optimal and often leave holes in the heap if allocations are not
-freed in reverse order. To overcome this problem the client will allocate a big
-memory segment in the global data block. This block of memory resides under the
-stack and its size can be set in the interface implementation. This block of
-memory will be managed in a similar way as the entire memory space of the
-device is managed. \Glspl{Task} will grow from the bottom up and \glspl{SDS}
-will grow from the top down.
-
-When a \gls{Task} is received, the program will traverse the memory space from
-the bottom up, jumping over all \glspl{Task}. A \gls{Task} is stored as the
-structure followed directly by its bytecode. Therefore it only takes two jumps
-to determine the size of the \gls{Task}. When the program arrived at the last
-\gls{Task}, this place is returned and the newly received \gls{Task} can be
-copied to there. This method is analogously applied for \glspl{SDS}, however,
-the \glspl{SDS} grow from the bottom down.
-
-When a \gls{Task} or \gls{SDS} is removed, all remaining objects are compressed
-again. This means that if the first received \gls{Task} is removed, all
-\glspl{Task} received later will have to move back. Obviously, this is quite
-time intensive but it can not be permitted to leave holes in the memory since
-the memory space is so limited. This techniques allows for even the smallest
-tested microcontrollers with only $2K$ \emph{RAM} to hold several \glspl{Task}
-and \glspl{SDS}. If this technique would not be used the memory space will
-decrease over time and the client can then not run for very long since holes
-are evidently created at some point.
-
-The structure instances and helper functions for traversing them in memory for
-\glspl{Task} and \glspl{SDS} are shown in Listing~\ref{lst:structs}.
-
-\begin{lstlisting}[language=C,label={lst:structs},%
- caption={The data type storing the \glspl{Task}},float]
-struct task {
- uint16_t tasklength;
- uint16_t interval;
- unsigned long lastrun;
- uint8_t taskid;
- uint8_t *bc;
-};
-
-struct task *task_head(void);
-struct task *task_next(struct task *t);
-
-struct sds {
- int id;
- int value;
- char type;
-};
-
-struct sds *sds_head(void);
-struct sds *sds_next(struct sds *s);
-\end{lstlisting}
-
-\subsubsection{Interpretation}
-The execution of a \gls{Task} is started by running the \CI{run\_task} function
-and always starts with setting the program counter and stack
-pointer to zero and the bottom respectively. When finished, the
-interpreter executes one step at the time while the program counter is smaller
-than the program length. This code is listed in Listing~\ref{lst:interpr}. One
-execution step is basically a big switch statement going over all possible
-bytecode instructions. Of some instructions, the implementations are shown in
-the listing. The \CI{BCPush} instruction is a little more complicated in the
-real code because some decoding will take place as not all \CI{BCValue}s are of
-the same length and are encoded.
-
-\begin{lstlisting}[language=C,label={lst:interpr},%
- caption={Rough code outline for interpretation}]
-#define f16(p) program[pc]*265+program[pc+1]
-
-void run_task(struct task *t){
- uint8_t *program = t->bc;
- int plen = t->tasklength;
- int pc = 0;
- int sp = 0;
- while(pc < plen){
- switch(program[pc++]){
- case BCNOP:
- break;
- case BCPUSH:
- stack[sp++] = pc++ //Simplified
- break;
- case BCPOP:
- sp--;
- break;
- case BCSDSSTORE:
- sds_store(f16(pc), stack[--sp]);
- pc+=2;
- break;
- // ...
- case BCADD: trace("add");
- stack[sp-2] = stack[sp-2] + stack[sp-1];
- sp -= 1;
- break;
- // ...
- case BCJMPT: trace("jmpt to %d", program[pc]);
- pc = stack[--sp] ? program[pc]-1 : pc+1;
- break;
-}
-\end{lstlisting}
-
-\subsection{Specification}
-The server stores a description for every device available in a record type.
-From the macro settings in the interface file, a profile is created that
-describes the specification of the device. When the connection between the
-server and a client is established, the server will send a request for
-specification. The client serializes its specification and send it to the
-server so that the server knows what the client is capable of. The exact
-specification is shown in Listing~\ref{lst:devicespec} and stores the
-peripheral availability, the memory available for storing \glspl{Task} and
-\glspl{SDS} and the size of the stack.
-
-\begin{lstlisting}[label={lst:devicespec},
- caption={Device specification for \gls{mTask}-\glspl{Task}}]
-:: MTaskDeviceSpec =
- { haveLed :: Bool
- , haveLCD :: Bool
- , have...
- , bytesMemory :: Int
- , stackSize :: Int
- , aPins :: Int
- , dPins :: Int
- }
-\end{lstlisting}
-
-\section{iTasks}
-The server part of the system is written in \gls{iTasks}. Functions for
-managing devices, \glspl{Task} and \glspl{SDS} have been created to support the
-functionality. An interactive web application has been created that provides an
-interactive management console for the \gls{mTask} system. This interface
-provides functionality to list \glspl{SDS}, add and remove \glspl{Task},
-administrate devices and view the state of the system.
-
-\subsection{Device Storage}
-Everything that a device encompasses is stored in the \CI{MTaskDevice} record
-type. This includes management for the \glspl{SDS} and \glspl{Task} stored on
-the device. The \CI{MTaskDevice} definition is shown in
-Listing~\ref{lst:mtaskdevice} accompanied with the necessary classes and sub
-types.
-
-\begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
-:: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
-:: BCState = ... // Compiler state, explained in later sections
-:: MTaskResource
- = TCPDevice TCPSettings
- | SerialDevice TTYSettings
- | ...
-:: MTaskDevice =
- { deviceTask :: Maybe TaskId
- , deviceError :: Maybe String
- , deviceChannels :: String
- , deviceName :: String
- , deviceState :: BCState
- , deviceTasks :: [MTaskTask]
- , deviceResource :: MTaskResource
- , deviceSpec :: Maybe MTaskDeviceSpec
- , deviceShares :: [MTaskShare]
- }
-
-channels :: MTaskDevice -> Shared Channels
-
-class MTaskDuplex a where
- synFun :: a (Shared Channels) -> Task ()
-\end{lstlisting}
-
-The \CI{deviceResource} component of the record must implement the
-\CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
-used for synchronizing the channels. The \CI{deviceTask} stores the
-\gls{Task}-id for this \gls{Task} when active so that it can be checked upon.
-This top-level task has the duty to report exceptions and errors as they are
-thrown by setting the \CI{deviceError} field. All communication goes via these
-channels. To send a message to the device, the system just puts it
-in the channels. Messages sent from the client to the server are also placed
-in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a
-simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In
-case of a device connected by a serial connection, it uses the newly developed
-serial port library of \gls{Clean}\footnote{\url{%
-https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
-
-Besides all the communication information, the record also keeps track of the
-\glspl{Task} currently on the device, the compiler state (see
-Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
-the specification of the device that is received when connecting. All of this
-is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
-format are explained in the following section.
-
-\subsection{Shares}
-The system keeps track of the \glspl{SDS} stored on
-the client in a big \gls{SDS} containing a list of devices. Client-\glspl{SDS}
-can be stored on one device at the same time. This means that if an \gls{SDS}
-updates, everyone watching it will be notified. This would result in a lot
-of notifications that are not meant for the watcher. Moreover, when a client
-updates the \gls{SDS} this is processed by the connection handler and results
-in an update of the real \gls{SDS}. Finally, the \gls{SDS} of a client must be
-synchronized with the actual device. Thus, when an \gls{iTasks}-\gls{Task}
-writes the client-\gls{SDS}, it must be propagated to the real device. There
-are several ways of tackling this problem each with their own level of
-granularity.
-
-First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can
-be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and
-synchronizing it with the device when an update occurred. This approach is very
-expensive as it requires a lot of listening \glspl{Task}.
-
-Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every
-devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite}
-functions, a single \gls{SDS} per device can be created as a lens that allows
-mapping on a single client-\gls{SDS}. However, this approach still requires
-\glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written,
-everyone is notified, even if the \gls{Task} only uses the value of a single
-different \gls{SDS}.
-
-Finally, the current approach --- a single \gls{SDS} for the entire system
---- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a
-\CI{mapReadWrite} can be used but it suffers from the same problem as mentioned
-before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate
-the client-\gls{SDS} updates to the actual device. Both of these problems can
-be solved by using a tailor made \gls{SDS} that heavily depends on parametric
-lenses.
-
-\subsection{Parametric Lenses}
-The type for the parametric lens of the big \gls{SDS} is \CI{Maybe
-(MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list
-of devices, from now on global. Moreover, the \gls{SDS} can focus on a single
-device, from now on local. A local \gls{SDS} can also specifically focus on a
-single \gls{SDS} on a single device, from now on called local-share. The
-implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}.
-The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory.
-Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing
-the \gls{SDS} is a little bit more involved. If the write operation originated
-from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must
-also be relayed to the actual device. If the write originated from an \gls{SDS}
-focussed the devices or on one device only, nothing needs to be done. The
-notification predicate determines whether a watcher gets a notification update.
-
-\begin{lstlisting}[label={lst:actualdev},%
- caption={Device \gls{SDS}}]
-deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
-deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite}
-where
- realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
- realRead p iw = read realDeviceStore iw
-
- realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
- realWrite mi w iw
- # (merr, iw) = write w realDeviceStore iw
- | isError merr || isNothing mi = (merr $> notifyPred mi, iw)
- # (Just (dev, ident)) = mi
- | ident == -1 = (merr $> notifyPred mi, iw)
- = case find ((==)dev) w of
- Nothing = (Error $ exception "Device lost", iw)
- Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of
- Nothing = (Error $ exception "Share lost", iw)
- Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of
- (Error e, iw) = (Error e, iw)
- (Ok _, iw) = (Ok $ notifyPred mi, iw)
-
- notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool
- notifyPred Nothing Nothing = True // Global watcher looking at a global event
- notifyPred Nothing (Just _) = False // Global watcher looking at a local event
- notifyPred (Just _) Nothing = False // Local watcher looking at a global event
- // Local device watcher looking at a local event
- notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2
- // Local share watcher looking at a local share event
- notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2
-
- realDeviceStore :: Shared [MTaskDevice]
- realDeviceStore = sharedStore "mTaskDevices" []
-\end{lstlisting}
-
-\subsubsection{Global \glspl{SDS}}
-Accessing the global \gls{SDS} is just a matter of focussing the
-\CI{deviceStore} with the \CI{Nothing} parameter as follows:
-
-\begin{lstlisting}[caption={Global \gls{SDS}}]
-deviceStoreNP :: Shared [MTaskDevice]
-deviceStoreNP = sdsFocus Nothing deviceStore
-\end{lstlisting}
-
-\subsubsection{Local \glspl{SDS}}
-Accessing a single device can be done using the \CI{mapReadWrite} function.
-Since device comparison is shallow, the device that is given is allowed to be
-an old version. The identification of devices is solely done on the name of the
-channels and is unique throughout the system. The implementation is as follows:
-
-\begin{lstlisting}[caption={Local \gls{SDS}}]
-deviceShare :: MTaskDevice -> Shared MTaskDevice
-deviceShare d = mapReadWriteError
- ( \ds->case find ((==)d) of
- Nothing = exception "Device lost"
- Just d = Ok d)
- , \w ds->case splitWith ((==)d) ds of
- ([], _) = Error $ exception "Device lost"
- ([_:_], ds) = Ok $ Just [w:ds])
- $ sdsFocus (Just (d, -1)) deviceStore
-\end{lstlisting}
-
-\subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
-A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
-function. This function focusses the real big \gls{SDS} on a single \gls{SDS}
-and uses the \CI{mapReadWrite} functions to serve the correct part of the
-information.
-
-\begin{lstlisting}[caption={Local \gls{SDS}}]
-shareShare :: MTaskDevice MTaskShare -> Shared BCValue
-shareShare dev share = sdsFocus ()
- $ mapReadWriteError (read, write)
- $ sdsFocus (Just (dev, share.identifier))
- $ deviceStore
-where
- read :: [MTaskDevice] -> MaybeError TaskException BCValue
- read devs = case find ((==)dev) devs of
- Nothing = exception "Device lost"
- Just d = case find ((==)share) d.deviceShares of
- Nothing = exception "Share lost"
- Just s = Ok s.MTaskShare.value
-
- write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice])
- write val devs = case partition ((==)dev) devs of
- ([], _) = Error $ exception "Device doesn't exist anymore"
- ([_,_:_], _) = Error $ exception "Multiple matching devices"
- ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of
- ([], _) = Error $ exception "Share doesn't exist anymore"
- ([_,_:_], _) = Error $ exception "Multiple matching shares"
- ([s], shares) = Ok $ Just [{MTaskDevice | d &
- deviceShares=[{MTaskShare | s & value=val}:shares]}:devs]
-\end{lstlisting}
-
-\section{Communication}
-The communication from the server to the client and vice versa is just a
-character stream containing encoded \gls{mTask} messages. The \CI{synFun}
-belonging to the device is responsible for sending the content in the left
-channel and putting received messages in the right channel. Moreover, the
-boolean value should be set to \CI{True} when the connection is terminated. The
-specific encoding of the messages is visible in
-Appendix~\ref{app:communication-protocol}. The type holding the messages is
-shown in Listing~\ref{lst:avmsg}. Detailed explanation about the message types
-and according actions will be given in the following subsections.
-
-\begin{lstlisting}[label={lst:avmsg},caption={Available messages}]
-:: MTaskId :== Int
-:: MSDSId :== Int
-:: MTaskFreeBytes :== Int
-:: MTaskMSGRecv
- = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId
- | MTSDSAck MSDSId | MTSDSDelAck MSDSId
- | MTPub MSDSId BCValue | MTMessage String
- | MTDevSpec MTaskDeviceSpec | MTEmpty
-
-:: MTaskMSGSend
- = MTTask MTaskInterval String | MTTaskDel MTaskId
- | MTShutdown | MTSds MSDSId BCValue
- | MTUpd MSDSId BCValue | MTSpec
-
-:: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int
-\end{lstlisting}
-
-\subsection{Add a device}
-A device can be added by filling in the \CI{MTaskDevice} record as much as
-possible and running the \CI{connectDevice} function. This function grabs the
-channels, starts the synchronization \gls{Task} (\CI{synFun}), makes sure the
-errors are handled when needed and runs a processing function in parallel to
-react on the incoming messages. Moreover, it sends a specification request to
-the device in question to determine the details of the device and updates the
-record to contain the top-level \gls{Task}-id. All device functionality
-heavily depends on the specific \CI{deviceShare} function that generates an
-\gls{SDS} for a specific device. This allows giving an old device record to the
-function and still update the latest instance. Listing~\ref{lst:connectDevice}
-shows the connection function.
-
-\begin{lstlisting}[label={lst:connectDevice},%
- caption={Connect a device}]
-connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels
-connectDevice procFun device = set ([], [], False) ch
- >>| appendTopLevelTask 'DM'.newMap True
- ( procFun device ch -||- catchAll (getSynFun device.deviceData ch) errHdl)
- >>= \tid->upd (\d->{d&deviceTask=Just tid,deviceError=Nothing}) (deviceShare device)
- >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch
-where
- errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! ()
- ch = channels device
-\end{lstlisting}
-
-Figure~\ref{fig:handshake} shows the connection diagram. The client responds to
-the server with their device specification. This is detected by the processing
-function and the record is updated accordingly.
-
-\begin{figure}[H]
- \centering
- \begin{sequencediagram}
- \newthread{s}{Server}
- \newinst[4]{c}{Client}
- \begin{call}{s}{MTSpec}{c}{MTDevSpec}
- \end{call}
- \end{sequencediagram}
- \caption{Connect a device}\label{fig:handshake}
-\end{figure}
-
-\subsection{\glspl{Task} \& \glspl{SDS}}
-When a \gls{Task} is sent to the device it is added to the device record
-without an identifier. The actual identifier is added to the record when the
-acknowledgement of the \gls{Task} by the device is received. The connection
-diagram is shown in Figure~\ref{fig:tasksend}.
-
-\begin{figure}[H]
- \centering
- \begin{sequencediagram}
- \newthread{s}{Server}
- \newinst[4]{c}{Client}
- \begin{call}{s}{MTSDS}{c}{MTSDSAck}
- \end{call}
- \begin{call}{s}{MTTask}{c}{MTTaskAck}
- \end{call}
- \end{sequencediagram}
- \caption{Sending a \gls{Task} to a device}\label{fig:tasksend}
-\end{figure}
-
-The function for sending a \gls{Task} to the device is shown in
-Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The
-details of the compilation process are given in Section~\ref{sec:compiler}.
-The new \glspl{SDS} that were generated during compilation are merged with the
-existing device's \glspl{SDS}. Furthermore the messages are placed in the
-channel \gls{SDS} of the device. This will result in sending the actual \gls{SDS}
-specification and \gls{Task} specifications to the device. A \gls{Task} record
-is created with the identifier $-1$ to denote a \gls{Task} not yet
-acknowledged. Finally the device itself is updated with the new state and with
-the new \gls{Task}. After waiting for the acknowledgement the device is
-updated again and the \gls{Task} returns.
-
-\begin{lstlisting}[label={lst:sendtask},%
- caption={Sending a \gls{Task} to a device}]
-makeTask :: String Int -> Task MTaskTask
-makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt}
-
-makeShare :: String Int BCValue -> MTaskShare
-makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value}
-
-sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task MTaskTask
-sendTaskToDevice wta mTask (device, timeout)
-# (msgs, newState=:{sdss}) = toMessages timeout mTask device.deviceState
-# shares = [makeShare wta "" sdsi sdsval\\{sdsi,sdsval}<-sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`]
-= updateShares device ((++) shares)
- >>| sendMessages msgs device
- >>| makeTask wta -1
- >>= \t->upd (addTaskUpState newState t) (deviceShare device)
- >>| wait "Waiting for task to be acked" (taskAcked t) (deviceShare device)
- >>| treturn t
-where
- addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
- addTaskUpState st task device = {MTaskDevice | device & deviceState=st, deviceTasks=[task:device.deviceTasks]}
- taskAcked t d = maybe True (\t->t.ident <> -1) $ find (eq t) d.deviceTasks
- eq t1 t2 = t1.dateAdded == t2.dateAdded && t1.MTaskTask.name == t2.MTaskTask.name
-\end{lstlisting}
-
-\subsection{Miscellaneous Messages}
-One special type of message is available which is sent to the device only when
-it needs to reboot. When the server wants to stop the bond with the device it
-sends the \CI{MTShutdown} message. The device will then clear its memory, thus
-losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself.
-Shortly after the shutdown message a new server can connect to the device
-because the device is back in listening mode.
-
-\subsection{Integration}
-When the system starts up, the devices from the previous execution still
-residing in the \gls{SDS} must be cleaned up. It might be the case that they
-contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in
-this run. A user or programmer can later choose to reconnect to some devices.
-
-\begin{lstlisting}[caption={Starting up the devices},%
- label={lst:startupdevs}]
-startupDevices :: Task [MTaskDevice]
-startupDevices = upd (map reset) deviceStoreNP
- where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing}
-\end{lstlisting}
-
-The system's management is done through the interface of a single \gls{Task}
-called \CI{mTaskManager}. To manage the system, a couple of different
-functionalities are necessary and are launched. An image of the management
-interface is shown in Figure~\ref{lst:manage}. The left sidebar of the
-interface shows the list of example \glspl{Task} that are present in the
-system. When clicking a \gls{Task}, a dialog opens in which a device can be
-selected to send the \gls{Task} to. The dialog might contain user specified
-variables. All example \gls{mTask}-\glspl{Task} are of the type \CI{Task (Main
-(ByteCode () Stmt))} and can thus ask for user input first if needed for
-parameterized \gls{mTask}-\glspl{Task}. The bottom panel shows the device
-information. In this panel, the devices can be created and modified. Moreover,
-this panel allows the user to reconnect with a device after a restart of the
-server application.
-
-\begin{figure}[H]
- \centering
- \includegraphics[width=\linewidth]{manage}
- \caption{The device management interface}\label{lst:manage}
-\end{figure}
-
-\section[Lifting mTasks to iTasks-Tasks]%
- {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}}
-If the user does not want to know where and when a \gls{mTask} is actually
-executed and is just interested in the results it can lift the \gls{mTask} to
-an \gls{iTasks}-\gls{Task}. The function is called with a name, \gls{mTask},
-device and interval specification and it will return a \gls{Task} that finishes
-if and only if the \gls{mTask} has returned.
-
-\begin{lstlisting}[caption={Starting up the devices}]
-liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task ()
-liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c
- >>= \t->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev)
- >>| viewInformation "Done!" [] ()
-where
- taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks
-\end{lstlisting}
-
-\section{Examples}
-Here comes a description of the demo program.