X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=arch.itasks.tex;fp=arch.itasks.tex;h=f7fc915e2a4fb11c9b42aa7688a83838cd2b139e;hb=6548a5ec9ce8e0df67fc4019625ab5238eb1bf71;hp=0000000000000000000000000000000000000000;hpb=f54205bc29f7dff01f97618d1d83812937333bc4;p=msc-thesis1617.git diff --git a/arch.itasks.tex b/arch.itasks.tex new file mode 100644 index 0000000..f7fc915 --- /dev/null +++ b/arch.itasks.tex @@ -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}