restructure files
[msc-thesis1617.git] / arch.itasks.tex
diff --git a/arch.itasks.tex b/arch.itasks.tex
new file mode 100644 (file)
index 0000000..f7fc915
--- /dev/null
@@ -0,0 +1,201 @@
+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}