The server part of the system is written in \gls{iTasks}. Functions for
managing devices, \glspl{Task} and \glspl{SDS} are available. Furthermore, 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 which is in turn stored in an \gls{SDS}. 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. Devices added to the system must be reachable
asynchronously. This implies that the programmer only needs to keep hold of
the reference to the device and not the actual device record.

\begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
:: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
:: MTaskDeviceSpec = ... // Also explained in later sections
:: MTaskMSGRecv = ...    // Message format, explained in later sections
:: MTaskMSGSend = ...    // Also 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{deviceChannels} field can be used
to get the memory \gls{SDS} containing the channels. This field does not
contain the channels itself because they update often. The field is used to
get a memory \gls{SDS} containing the actual channel data when calling the
\CI{channels} function. 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}}. The implementation and
semantics for the \CI{MTaskMSGRecv} and \CI{MTaskMSGSend} types are given in
Section~\ref{sec:communication}.

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}
\Glspl{SDS} on the device can be accessed by both the device and the server.
While it would be possible to only store the \glspl{SDS} on the device, this
would require a lot of communication because every read operation will then
result in sending messages to-and-fro the device. Thus, the \gls{Task}
requesting the shared information can just be provided with the synchronized
value. As mentioned before, the device has to explicitly publish an update.
This has the implication that the server and the client can get out of sync.
However, this is by design and well documented. In the current system, an
\gls{SDS} can only reside on a single device.

% Single share per share
There are several possible approaches for storing \glspl{SDS} on the server
each with their own level of control. A possible way is to --- in the device
record --- add a list of references to \gls{iTasks}-\glspl{SDS} that represent
the \gls{SDS} on the device. The problem with this is the fact that an
\gls{SDS} can become an orphan. The \gls{SDS} is still accessible even when the
device is long gone. There is no way of knowing whether the \gls{SDS} is
unreachable because of the device being gone, or the \gls{SDS} itself is gone
on the device. Accessing the \gls{SDS} happens by calling the \CI{get},
\CI{set} and \CI{upd} functions directory on the actual \gls{SDS}.

% Single share per device
Another approach would be to have reference to an \gls{SDS} containing a table
of \gls{SDS} values per device. This approach suffers the same orphan problem
as before. Accessing a single \gls{SDS} on the device happens by calling the
\CI{get}, \CI{set} and \CI{upd} functions on the actual table \gls{SDS} with an
applied \CI{mapReadWrite}. Using parametric lenses can circumvent the problem
of watchers getting notified for other shares that are written. Error handling
is better than the previously mentioned approach because an \gls{SDS} can know
whether the \gls{SDS} has really gone because it will not be available anymore
in the table. It still does not know whether the device is still available.

% One share to rule them all
Finally, all devices containing all of their \glspl{SDS} in a table could be
stored in a single big \gls{SDS}. While the \CI{mapReadWrite} functions require
a bit more logic, they can determine the source of the error and act upon it.
Also, the parametric lenses must contain more logic. A downside of this
approach is that updating a single \gls{SDS} requires an update of the entire
object. In practise, this is not a real issue since almost all information can
be reused and \gls{SDS} residing on devices are often not updated with a very
high frequency.

\subsection{Parametric Lenses}
The type for the parametric lens of the \gls{SDS} containing all devices is
\CI{Maybe (MTaskDevice, Int)}. There are several levels of abstraction that
have to be introduced. First, the \gls{SDS} responsible for storing the entire
list of devices is called the global \gls{SDS}. Secondly, an \gls{SDS} can focus
on a single device, such \glspl{SDS} are called local \glspl{SDS}. Finally, an
\gls{SDS} can focus on a single \gls{SDS} on a single device. These \glspl{SDS}
are called share \glspl{SDS}. Using parametric lenses, the notifications can be
directed to only the watchers interested. Moreover, using parametric lenses,
the \gls{SDS} can know whether it is updating a single \gls{SDS} on a single
device and synchronize the value with the actual device. This means that when
writing to a share \gls{SDS} the update is also transformed to messages that
are put in the channels of the corresponding device to also notify the device
of the update. The \gls{SDS} is tailor-made and uses an actual standard
\gls{SDS} that writes to a file or memory as the storage. The tailor-made read
and write functions are only used to detect whether it is required to send an
update to the actual device. 

Listing~\ref{lst:actualdev} shows the implementation of the big \gls{SDS}. From
this \gls{SDS} all other \glspl{SDS} are derived. The following paragraphs show
how this is achieved for the global \gls{SDS} local \gls{SDS} and the share
\gls{SDS}. In the big \gls{SDS}, reading the value is just a matter of reading
the standard \gls{SDS} that serves as the actual storage of the \gls{SDS}. The
derived shares will filter the output read accordingly. Writing the share
requires some extra work because it might be possible that an actual device has
to be notified. First, the actual storage of the \gls{SDS} is written. If the
parameter was \CI{Nothing} --- the global \gls{SDS} --- the write operation is
done. If the parameter was \CI{Just (d, -1)} --- a local \gls{SDS} --- nothing
has to be done as well. The final case is the special case, when the parameter
is \CI{Just (d, i)}, this means that the \gls{SDS} was focussed on device
\CI{d} and \gls{SDS} \CI{i} and thus it needs to write to it. First it locates
the device in the list, followed by the location of the share to check whether
is still exists. Finally the actual update messages are added to the device
channels using the \CI{sendMessagesIW} function.

All of the methods share the same \CI{SDSNotifyPred p} which is a function
\CI{p -> Bool} and determines for the given \CI{p} whether a notification is
required. The predicate function has the \CI{p} of the writer curried in and
can determine whether the second argument --- the reader --- needs to be
notified. In practice, the reader only needs to be notified when the parameters
are exactly the same.

\begin{lstlisting}[label={lst:actualdev},%
	caption={Device \gls{SDS}}]
($<) :: a (f a) -> (f b)
($<) a fb = fmap (const a) fb

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 $> gEq{|*|} mi, iw)
	# (Just (dev, ident)) = mi
	| ident == -1 = (merr $> gEq{|*|} 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 $ gEq{|*|} mi, iw)

	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} to \CI{Nothing}. In this way, \glspl{Task} watching the
\gls{SDS} will only be notified if a device is added or removed. The actual
code is 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{deviceShare} 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. This type of \gls{SDS} will only
be notified if the device itself changed. It will not be notified when only a
single \gls{SDS} on the device changes. The implementation is as follows:

\begin{lstlisting}[caption={Local \gls{SDS}}]
deviceShare :: MTaskDevice -> Shared MTaskDevice
deviceShare d = mapReadWriteError
	( \ds->case find ((==)d) ds 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 global \gls{SDS} on a single \gls{SDS}
from a single device. It can use old share references in the same fashion as
the local \gls{SDS} only treating it as references. It uses the
\CI{mapReadWrite} functions to serve the correct part of the information. When
a \gls{Task} writes to this \gls{SDS}, the global \gls{SDS} will know this
through the parameter and propagate the value to the device.

\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}
