X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=arch.itasks.tex;h=b8f4b089bffbc02ef5ad1a384e7bf3070db7414a;hb=8755061062b9738c80da1f5966c93ac6ec832a5c;hp=7f9ac60df778c3654739ec2e946e2f3ff38f1f84;hpb=b3f05abd18320bba213273d3433e8fb2c2ac4228;p=msc-thesis1617.git diff --git a/arch.itasks.tex b/arch.itasks.tex index 7f9ac60..b8f4b08 100644 --- a/arch.itasks.tex +++ b/arch.itasks.tex @@ -1,60 +1,40 @@ 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 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. Not all peripheral flags are shown for -brevity. The encoding for this interface - -\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} +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. 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. +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) -:: BCState = ... // Compiler state, explained in later sections +:: BCState = ... // Compiler state, explained in later sections +:: 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] - } + { 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 @@ -64,17 +44,23 @@ class MTaskDuplex a where 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}}. +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 @@ -84,57 +70,96 @@ 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. +\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 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. +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 fb = fmap (const a) fb + deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice] -deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite} +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 @@ -142,25 +167,16 @@ where 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) + | isError merr || isNothing mi = (merr $> gEq{|*|} mi, iw) # (Just (dev, ident)) = mi - | ident == -1 = (merr $> notifyPred mi, iw) + | 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 $ 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 + (Ok _, iw) = (Ok $ gEq{|*|} mi, iw) realDeviceStore :: Shared [MTaskDevice] realDeviceStore = sharedStore "mTaskDevices" [] @@ -168,7 +184,9 @@ where \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: +\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] @@ -176,10 +194,12 @@ deviceStoreNP = sdsFocus Nothing deviceStore \end{lstlisting} \subsubsection{Local \glspl{SDS}} -Accessing a single device can be done using the \CI{mapReadWrite} function. +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. The implementation is as follows: +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 @@ -195,9 +215,12 @@ deviceShare d = mapReadWriteError \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. +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