X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=results.arch.tex;h=e4e5d38f142c41ff4ee2249e3fc1c3da7d6483a4;hb=36149fe97302e46248ceecfd8ef39faaea19991b;hp=0573033638e074fbaa8250d8c52544e6291ac5e2;hpb=ebab80fc256101c7c9e4b51f3c5d12c1763d21d7;p=msc-thesis1617.git diff --git a/results.arch.tex b/results.arch.tex index 0573033..e4e5d38 100644 --- a/results.arch.tex +++ b/results.arch.tex @@ -71,26 +71,38 @@ the device software. \end{itemize} \subsection{Client} -\subsubsection*{Engine} +\subsubsection{Engine} The client is in a constant loop listening for input and waiting to execute -\gls{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}. +\gls{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. -\todo{make algorithm} \begin{algorithm}[H] - \KwData{\textbf{stack} stack, \textbf{time} $t, t_p$} + \KwData{ + \textbf{list} $tasks$, + \textbf{time} $t$ + } - $t\leftarrow\text{now}()$\; \Begin{ \While{true}{ - $t_p\leftarrow t$\; - $t\leftarrow\text{now}()$\; - \If{notEmpty$($queue$)$}{ - $task\leftarrow \text{queue.pop}()$\; - $task$.wait $\leftarrow task$.wait $-(t-t_p)$\; - \eIf{$task.wait>t_0$}{ - queue.append$(task)$\; - }{ - run\_task$(task)$\; + \If{input\_available$()$}{ + receive\_data()\; + } + + $t\leftarrow \text{now}()$\; + \ForEach{$t\leftarrow tasks$}{ + \uIf{is\_interrupt$(t) \&\& $had\_interrupt$(t)$}{ + run\_task$(t)$\; + } + \ElseIf{$t-t.\text{lastrun} > t.\text{interval}$}{ + run\_task$(t)$\; + \uIf{$t.\text{interval}==0$}{ + delete\_task$(t)$\; + }\Else{ + $t.\text{lastrun}\leftarrow t$\; + } } } } @@ -98,20 +110,21 @@ The client is in a constant loop listening for input and waiting to execute \caption{Engine pseudocode}\label{alg:client} \end{algorithm} -\subsubsection*{Storage} +\subsubsection{Storage} \glspl{Task} and \glspl{SDS} are stored on the client in one big memory space -that is fully allocated at the start of the program. The space could also have -been dynamically allocated but that would require using the heap which is -unwanted in small memory environments. \Glspl{Task} grow from the bottom up -and \glspl{SDS} grow from the top down. When a \gls{Task} or \gls{SDS} is -removed, all \glspl{Task} are relocated in the memory space to not leave -holes. Both \glspl{Task} and \glspl{SDS} are stored as structs and helper -functions are available to loop through them without having to fiddle in the -memory space. The instance for \glspl{Task} and \glspl{SDS} are shown in -Listing~\ref{lst:structs} accompanied by the helper functions for \glspl{Task}. -\Glspl{Task} consist the length, interval, last run time, id and the bytecode. -\Glspl{SDS} consist just of an id, value and type. The pointer to the bytecode -of the \gls{Task} always points to the location in the memory space. +that is reserved at the start of the program. The space could also have been +dynamically allocated but that would require using the heap which is unwanted +in small memory environments. \Glspl{Task} grow from the bottom up and +\glspl{SDS} grow from the top down. When a \gls{Task} or \gls{SDS} is removed, +all \glspl{Task} are relocated in the memory space to not leave holes. Both +\glspl{Task} and \glspl{SDS} are stored as structures that are linked in the +memory space, helper functions are available to loop through them without +having to fiddle in the memory space itself. The instance for \glspl{Task} and +\glspl{SDS} are shown in Listing~\ref{lst:structs} accompanied by the helper +functions for \glspl{Task}. \Glspl{Task} consist the length, interval, last run +time, id and the bytecode. \Glspl{SDS} consist just of an id, value and type. +The pointer to the bytecode of the \gls{Task} always points to the location in +the memory space. \begin{lstlisting}[language=C,label={lst:structs},% caption={The data type storing the \glspl{Task}}] @@ -136,16 +149,17 @@ struct sds *sds_head(void); struct sds *sds_next(struct sds *s); \end{lstlisting} -\subsubsection*{Interpretation} -Execution of a \gls{Task} always start with prepared the stack and the program -counter and stack pointer are set 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. The code for this is listed in +\subsubsection{Interpretation} +The execution of a \gls{Task} is started by running the \CI{run\_task} function +and always start with prepared the stack and the program counter and stack +pointer are set 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. The code for this is listed in Listing~\ref{lst:interpr}. One execution step is basically a big switch statement going over all possible bytecode instructions. Some instructions are detailed upon 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. +\CI{BCValue}s are of the same length and are encoded. \begin{lstlisting}[language=C,label={lst:interpr},% caption={Rough code outline for interpretation}] @@ -183,21 +197,21 @@ void run_task(struct task *t){ \end{lstlisting} \subsection{Specification} -The server stores a description for every device available in a record type -which are stored in a \gls{SDS}. From the macro settings in -the interface file, a profile is created for the device that describes the -specification. When a connection between the server and a client is established -the server will send a request for specification. The client will serialize his -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. +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 will serialize his 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 \glspl{mTask}}] :: MTaskDeviceSpec = { haveLed :: Bool - , haveLcd :: Bool + , haveLCD :: Bool , have... , bytesMemory :: Int , stackSize :: Int @@ -206,43 +220,28 @@ available for storing \glspl{Task} and \glspl{SDS} and the size of the stack. } \end{lstlisting} -\subsection{Device Storage} -All devices available in the system are stored in a big \gls{SDS} that contains -a list of \CI{MTaskDevice}s. The exact specification is defined as in -Listing~\ref{lst:mtaskdevice} accompanied with the used classes and types. - -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. If the system wants to send a message to the device, it 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}}. +\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 application has been created that allows an +interactive management console for the \gls{mTask} system. This interface +provides functionality to list \glspl{SDS}, add \glspl{Task}, remove +\glspl{Task}, administrate devices and view the state of the system. -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. Specialized shares are available -per device. The internal mechanism for this is given in -Chapter~\ref{chp:itasksint}. +\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}] -deviceStoreNP :: Shared [MTaskDevice] -deviceShare :: MTaskDevice -> Shared MTaskDevice - :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool) :: BCState = ... // Compiler state, explained in later sections -:: MTaskResource +:: MTaskResource = TCPDevice TCPSettings | SerialDevice TTYSettings + | ... :: MTaskDevice = { deviceTask :: Maybe TaskId , deviceError :: Maybe String @@ -261,119 +260,179 @@ class MTaskDuplex a where synFun :: a (Shared Channels) -> Task () \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 application has been created that allows an -interactive management console for the \gls{mTask} system. This interface -provides functionality to list \glspl{SDS}, add \glspl{Task}, remove -\glspl{Task}, administrate devices and view the state of the system. - -\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} - -An image of the management interface is shown in Figure~\ref{lst:manage}. -The system management is done by a single \gls{Task} called \CI{mTaskManager}. -To manage the system, a couple of different functionalities are needed and -are launched. 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 you can select the device to send the \gls{Task} to. The -dialog might contain user specified variables. All example \glspl{mTask} are of -the type \CI{Task (Main (ByteCode () Stmt))} and can thus ask for user input -first. - -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. +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. If the system wants to send a message to the device, it 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}}. -\begin{figure}[H] - \centering - \includegraphics[width=\linewidth]{manage} - \caption{The device management interface}\label{lst:manage} -\end{figure} +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 architecture of the system needs to keep track of the \glspl{SDS} stored on -the client. \glspl{SDS} can be stored on only one device at the same time. -that also stores the of devices. This means that if a \gls{SDS} updates, -everyone watching it will be notified. This would result in to a lot of -notifications that are not ment to be for the listener. Moreover, when a client +The architecture of 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 a \gls{SDS} +updates, everyone watching it will be notified. This would result in to a lot +of notifications that are not meant for the listener. 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. -There are several ways of tackling this problem each with their own pros and -cons and their own level of abstraction. - -\begin{itemize} - \item Instantiate an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used - in a client. - - \item Instantiate a \gls{iTasks}-\gls{SDS} for every device that stores all - their \glspl{SDS}. - - \item Use only one \gls{iTasks}-\gls{SDS} for all devices. -\end{itemize} - -\begin{lstlisting}[label={lst:actualdev},% - caption={Real types for the device \gls{SDS}}] -deviceStoreNP :: Shared [MTaskDevice] -deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice] -\end{lstlisting} +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 pros and cons and +their own level of abstraction. + +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 occured. 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 a \gls{SDS} is written, +everyone is notified, even if the \gls{Task} wanted to only watch a single +different \gls{SDS}. + +Ultimately, 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 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 share that heavily depends on parametric +lenses. \subsection{Parametric Lenses} The type of the parametric lens is \CI{Maybe (MTaskDevice, Int)}. The \gls{SDS} can be responsible for 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. +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 a \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 a \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} -\paragraph{Global \glspl{SDS}: } +\subsubsection{Global \glspl{SDS}} Accessing the global \gls{SDS} is just a matter of focussing the -\CI{deviceStore} with the \CI{Nothing} parameter. The signature for -\CI{deviceStore} was given in Chapter~\ref{chp:arch}. The actual implementation -is as in Listing~\ref{lst:global} +\CI{deviceStore} with the \CI{Nothing} parameter as follows: -\begin{lstlisting}[label={lst:shareimpl},% - caption={Base share implementation}] -deviceStoreNP :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice] +\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} - -\paragraph{Local \glspl{SDS}: } -\paragraph{Local-share specific \glspl{SDS}: } - -The implementation for the share is shown in Listing~\ref{lst:shareimpl}. The -\CI{realDeviceStore} \gls{SDS} is not exported through the header files. This -\gls{SDS} contains the actual \gls{SDS} that writes to disk or memory. -\CI{Int} is the identifier of the \gls{SDS}. The \gls{iTasks} way of applying -lenses is through the \CI{sdsFocus} function and through the \CI{sdsLens} -functions. \CI{sdsFocus} allows the programmer to fix the parameter. -\CI{sdsLens} is basically a \CI{mapReadWrite} that has access to the parameter. -This allows the programmer to create filters and lenses. Both of the methods -are not good enough for the device \gls{SDS} because they do not achieve the -writing to the actual device. Writing to a device requires being able to write -to \glspl{SDS}. To solve this problem, a real base \gls{SDS} is created. All -the details are visible in Listing~\ref{lst:shareimpl}. +\subsubsection{Local-share 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 share 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 specific encoding -is visible in Appendix~\ref{app:communication-protocol}. The type holding the -messages in Listing~\ref{lst:avmsg}. Detailed explanation about the message -types will be given in the following subsections. +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, it should +set the boolean value 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 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 @@ -396,29 +455,26 @@ types will be given in the following subsections. \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}, 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 the device functionality heavily -depends on the specific \CI{deviceShare} function that applies a function a device in -the \gls{SDS} when they are equal. Device equality is defined as equality on -their channels. This allows you to give an old device record to the function -and still update the latest instance. Listing~\ref{lst:connectDevice} shows the -connection function. +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 the device functionality +heavily depends on the specific \CI{deviceShare} function that generates a +\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}] -withDevices :: MTaskDevice (MTaskDevice -> MTaskDevice) -> Task [MTaskDevice] - connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels connectDevice procFun device = let ch = channels device - in appendTopLevelTask 'DM'.newMap True - (procFun device ch -||- catchAll (getSynFun d.deviceData ch) errHdl) - >>= \tid->withDevices device (\d->{d&deviceTask=Just tid,deviceError=Nothing}) + in traceValue "connectDevice" >>| 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 = withDevices device (\d->{d & deviceTask=Nothing, deviceError=Just e}) @! () +where + errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! () \end{lstlisting} Figure~\ref{fig:handshake} shows the connection diagram. The client responds to @@ -465,8 +521,8 @@ 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}. -When the device returns an acknowledgement the \gls{Task} is updated -accordingly. +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}] @@ -476,18 +532,21 @@ makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=i 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 [MTaskDevice] +sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task MTaskTask sendTaskToDevice wta mTask (device, timeout) -# (msgs, newState) = toMessages timeout mTask device.deviceState -# shares = [makeShare wta sdsi sdsval\\{sdsi,sdsval}<-newState.sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`] +# (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 - >>= withDevices device o addTaskUpState newState - where - addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice - addTaskUpState st task device = {MTaskDevice | device & - deviceState=st, deviceTasks=[task:device.deviceTasks]} + >>= \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} @@ -498,8 +557,55 @@ 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. -\section{Lifting mTask to iTasks} -\todo{task lifting} +\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 needed 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 you can select the +device to send the \gls{Task} to. The dialog might contain user specified +variables. All example \glspl{mTask} are of the type \CI{Task (Main (ByteCode +() Stmt))} and can thus ask for user input first if needed for parameterized +\glspl{mTask}. 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. + +\todo{redo this image} +\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 \glspl{mTask} 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}-\glspl{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} \todo{example program (demo)}