add bigger mtask example
[msc-thesis1617.git] / arch.itasks.tex
1 The server part of the system is written in \gls{iTasks}. Functions for
2 managing devices, \glspl{Task} and \glspl{SDS} have been created to support the
3 functionality. An interactive web application has been created that provides an
4 interactive management console for the \gls{mTask} system. This interface
5 provides functionality to list \glspl{SDS}, add and remove \glspl{Task},
6 administrate devices and view the state of the system.
7
8 \subsection{Device Storage}
9 Everything that a device encompasses is stored in the \CI{MTaskDevice} record
10 type. This includes management for the \glspl{SDS} and \glspl{Task} stored on
11 the device. The \CI{MTaskDevice} definition is shown in
12 Listing~\ref{lst:mtaskdevice} accompanied with the necessary classes and sub
13 types.
14
15 \begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
16 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
17 :: BCState = ... // Compiler state, explained in later sections
18 :: MTaskResource
19 = TCPDevice TCPSettings
20 | SerialDevice TTYSettings
21 | ...
22 :: MTaskDevice =
23 { deviceTask :: Maybe TaskId
24 , deviceError :: Maybe String
25 , deviceChannels :: String
26 , deviceName :: String
27 , deviceState :: BCState
28 , deviceTasks :: [MTaskTask]
29 , deviceResource :: MTaskResource
30 , deviceSpec :: Maybe MTaskDeviceSpec
31 , deviceShares :: [MTaskShare]
32 }
33
34 channels :: MTaskDevice -> Shared Channels
35
36 class MTaskDuplex a where
37 synFun :: a (Shared Channels) -> Task ()
38 \end{lstlisting}
39
40 The \CI{deviceResource} component of the record must implement the
41 \CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
42 used for synchronizing the channels. The \CI{deviceTask} stores the
43 \gls{Task}-id for this \gls{Task} when active so that it can be checked upon.
44 This top-level task has the duty to report exceptions and errors as they are
45 thrown by setting the \CI{deviceError} field. All communication goes via these
46 channels. To send a message to the device, the system just puts it
47 in the channels. Messages sent from the client to the server are also placed
48 in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a
49 simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In
50 case of a device connected by a serial connection, it uses the newly developed
51 serial port library of \gls{Clean}\footnote{\url{%
52 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
53
54 Besides all the communication information, the record also keeps track of the
55 \glspl{Task} currently on the device, the compiler state (see
56 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
57 the specification of the device that is received when connecting. All of this
58 is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
59 format are explained in the following section.
60
61 \subsection{Shares}
62 The system keeps track of the \glspl{SDS} stored on
63 the client in a big \gls{SDS} containing a list of devices. Client-\glspl{SDS}
64 can be stored on one device at the same time. This means that if an \gls{SDS}
65 updates, everyone watching it will be notified. This would result in a lot
66 of notifications that are not meant for the watcher. Moreover, when a client
67 updates the \gls{SDS} this is processed by the connection handler and results
68 in an update of the real \gls{SDS}. Finally, the \gls{SDS} of a client must be
69 synchronized with the actual device. Thus, when an \gls{iTasks}-\gls{Task}
70 writes the client-\gls{SDS}, it must be propagated to the real device. There
71 are several ways of tackling this problem each with their own level of
72 granularity.
73
74 First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can
75 be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and
76 synchronizing it with the device when an update occurred. This approach is very
77 expensive as it requires a lot of listening \glspl{Task}.
78
79 Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every
80 devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite}
81 functions, a single \gls{SDS} per device can be created as a lens that allows
82 mapping on a single client-\gls{SDS}. However, this approach still requires
83 \glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written,
84 everyone is notified, even if the \gls{Task} only uses the value of a single
85 different \gls{SDS}.
86
87 Finally, the current approach --- a single \gls{SDS} for the entire system
88 --- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a
89 \CI{mapReadWrite} can be used but it suffers from the same problem as mentioned
90 before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate
91 the client-\gls{SDS} updates to the actual device. Both of these problems can
92 be solved by using a tailor made \gls{SDS} that heavily depends on parametric
93 lenses.
94
95 \subsection{Parametric Lenses}
96 The type for the parametric lens of the big \gls{SDS} is \CI{Maybe
97 (MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list
98 of devices, from now on global. Moreover, the \gls{SDS} can focus on a single
99 device, from now on local. A local \gls{SDS} can also specifically focus on a
100 single \gls{SDS} on a single device, from now on called local-share. The
101 implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}.
102 The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory.
103 Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing
104 the \gls{SDS} is a little bit more involved. If the write operation originated
105 from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must
106 also be relayed to the actual device. If the write originated from an \gls{SDS}
107 focussed the devices or on one device only, nothing needs to be done. The
108 notification predicate determines whether a watcher gets a notification update.
109
110 \begin{lstlisting}[label={lst:actualdev},%
111 caption={Device \gls{SDS}}]
112 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
113 deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite}
114 where
115 realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
116 realRead p iw = read realDeviceStore iw
117
118 realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
119 realWrite mi w iw
120 # (merr, iw) = write w realDeviceStore iw
121 | isError merr || isNothing mi = (merr $> notifyPred mi, iw)
122 # (Just (dev, ident)) = mi
123 | ident == -1 = (merr $> notifyPred mi, iw)
124 = case find ((==)dev) w of
125 Nothing = (Error $ exception "Device lost", iw)
126 Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of
127 Nothing = (Error $ exception "Share lost", iw)
128 Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of
129 (Error e, iw) = (Error e, iw)
130 (Ok _, iw) = (Ok $ notifyPred mi, iw)
131
132 notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool
133 notifyPred Nothing Nothing = True // Global watcher looking at a global event
134 notifyPred Nothing (Just _) = False // Global watcher looking at a local event
135 notifyPred (Just _) Nothing = False // Local watcher looking at a global event
136 // Local device watcher looking at a local event
137 notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2
138 // Local share watcher looking at a local share event
139 notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2
140
141 realDeviceStore :: Shared [MTaskDevice]
142 realDeviceStore = sharedStore "mTaskDevices" []
143 \end{lstlisting}
144
145 \subsubsection{Global \glspl{SDS}}
146 Accessing the global \gls{SDS} is just a matter of focussing the
147 \CI{deviceStore} with the \CI{Nothing} parameter as follows:
148
149 \begin{lstlisting}[caption={Global \gls{SDS}}]
150 deviceStoreNP :: Shared [MTaskDevice]
151 deviceStoreNP = sdsFocus Nothing deviceStore
152 \end{lstlisting}
153
154 \subsubsection{Local \glspl{SDS}}
155 Accessing a single device can be done using the \CI{mapReadWrite} function.
156 Since device comparison is shallow, the device that is given is allowed to be
157 an old version. The identification of devices is solely done on the name of the
158 channels and is unique throughout the system. The implementation is as follows:
159
160 \begin{lstlisting}[caption={Local \gls{SDS}}]
161 deviceShare :: MTaskDevice -> Shared MTaskDevice
162 deviceShare d = mapReadWriteError
163 ( \ds->case find ((==)d) of
164 Nothing = exception "Device lost"
165 Just d = Ok d)
166 , \w ds->case splitWith ((==)d) ds of
167 ([], _) = Error $ exception "Device lost"
168 ([_:_], ds) = Ok $ Just [w:ds])
169 $ sdsFocus (Just (d, -1)) deviceStore
170 \end{lstlisting}
171
172 \subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
173 A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
174 function. This function focusses the real big \gls{SDS} on a single \gls{SDS}
175 and uses the \CI{mapReadWrite} functions to serve the correct part of the
176 information.
177
178 \begin{lstlisting}[caption={Local \gls{SDS}}]
179 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
180 shareShare dev share = sdsFocus ()
181 $ mapReadWriteError (read, write)
182 $ sdsFocus (Just (dev, share.identifier))
183 $ deviceStore
184 where
185 read :: [MTaskDevice] -> MaybeError TaskException BCValue
186 read devs = case find ((==)dev) devs of
187 Nothing = exception "Device lost"
188 Just d = case find ((==)share) d.deviceShares of
189 Nothing = exception "Share lost"
190 Just s = Ok s.MTaskShare.value
191
192 write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice])
193 write val devs = case partition ((==)dev) devs of
194 ([], _) = Error $ exception "Device doesn't exist anymore"
195 ([_,_:_], _) = Error $ exception "Multiple matching devices"
196 ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of
197 ([], _) = Error $ exception "Share doesn't exist anymore"
198 ([_,_:_], _) = Error $ exception "Multiple matching shares"
199 ([s], shares) = Ok $ Just [{MTaskDevice | d &
200 deviceShares=[{MTaskShare | s & value=val}:shares]}:devs]
201 \end{lstlisting}