add factorial 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} are available. Furthermore, an
3 interactive web application has been created that provides an interactive
4 management console for the \gls{mTask} system. This interface provides
5 functionality to list \glspl{SDS}, add and remove \glspl{Task}, administrate
6 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 which is in turn stored in a \gls{SDS}. This includes management for the
11 \glspl{SDS} and \glspl{Task} stored on the device. The \CI{MTaskDevice}
12 definition is shown in Listing~\ref{lst:mtaskdevice} accompanied with the
13 necessary classes and sub types. Devices added to the system must be reachable
14 asynchronously. This implies that the programmer only needs to keep hold of
15 the reference to the device and not the actual device record.
16
17 \begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
18 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
19 :: BCState = ... // Compiler state, explained in later sections
20 :: MTaskDeviceSpec = ... // Also explained in later sections
21 :: MTaskMSGRecv = ... // Message format, explained in later sections
22 :: MTaskMSGSend = ... // Also explained in later sections
23 :: MTaskResource
24 = TCPDevice TCPSettings
25 | SerialDevice TTYSettings
26 | ...
27 :: MTaskDevice =
28 { deviceTask :: Maybe TaskId
29 , deviceError :: Maybe String
30 , deviceChannels :: String
31 , deviceName :: String
32 , deviceState :: BCState
33 , deviceTasks :: [MTaskTask]
34 , deviceResource :: MTaskResource
35 , deviceSpec :: Maybe MTaskDeviceSpec
36 , deviceShares :: [MTaskShare]
37 }
38
39 channels :: MTaskDevice -> Shared Channels
40
41 class MTaskDuplex a where
42 synFun :: a (Shared Channels) -> Task ()
43 \end{lstlisting}
44
45 The \CI{deviceResource} component of the record must implement the
46 \CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
47 used for synchronizing the channels. The \CI{deviceChannels} field can be used
48 to get the memory \gls{SDS} containing the channels. This field does not
49 contain the channels itself because they update often. The field is used to
50 get a memory \gls{SDS} containing the actual channel data when calling the
51 \CI{channels} function. The \CI{deviceTask} stores the \gls{Task}-id for this
52 \gls{Task} when active so that it can be checked upon. This top-level task has
53 the duty to report exceptions and errors as they are thrown by setting the
54 \CI{deviceError} field. All communication goes via these channels. To send a
55 message to the device, the system just puts it in the channels. Messages sent
56 from the client to the server are also placed in there. In the case of the
57 \gls{TCP} device type, the \gls{Task} is just a simple wrapper around the
58 existing \CI{tcpconnect} function in \gls{iTasks}. In case of a device
59 connected by a serial connection, it uses the newly developed serial port
60 library of \gls{Clean}\footnote{\url{%
61 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}. The implementation and
62 semantics for the \CI{MTaskMSGRecv} and \CI{MTaskMSGSend} types are given in
63 Section~\ref{sec:communication}.
64
65 Besides all the communication information, the record also keeps track of the
66 \glspl{Task} currently on the device, the compiler state (see
67 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
68 the specification of the device that is received when connecting. All of this
69 is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
70 format are explained in the following section.
71
72 \subsection{Shares}
73 \Glspl{SDS} on the device can be accessed by both the device and the server.
74 While it would be possible to only store the \glspl{SDS} on the device, this
75 would require a lot of communication because every read operation will then
76 result in sending messages to-and-fro the device. Thus, the \gls{Task}
77 requesting the shared information can just be provided with the synchronized
78 value. As mentioned before, the device has to explicitly publish an update.
79 This has the implication that the server and the client can get out of sync.
80 However, this is by design and well documented. In the current system, an
81 \gls{SDS} can only reside on a single device.
82
83 % TODO
84 % Single share per share
85 There are several possible approaches for storing \glspl{SDS} on the server
86 each with their own level of control. A possible way is to --- in the device
87 record --- add a list of references to \gls{iTasks}-\glspl{SDS} that represent
88 the \gls{SDS} on the device. The problem with this is the fact that an
89 \gls{SDS} can become an orphan. The \gls{SDS} is still accessible even when the
90 device is long gone. There is no way of knowing whether the \gls{SDS} is
91 unreachable because of the device being gone, or the \gls{SDS} itself is gone
92 on the device. Accessing the \gls{SDS} happens by calling the \CI{get},
93 \CI{set} and \CI{upd} functions directory on the actual \gls{SDS}.
94
95 % Single share per device
96 Another approach would be to have reference to a \gls{SDS} containing a table
97 of \gls{SDS} values per device. This approach poses the same orphan problem as
98 before. Accessing a single \gls{SDS} on the device happens by calling the
99 \CI{get}, \CI{set} and \CI{upd} functions on the actual table \gls{SDS} with an
100 applied \CI{mapReadWrite}. Using parametric lenses can circumvent problem of
101 watchers getting notified for other shares that are written. Error handling is
102 better than the previously mentioned approach because a \gls{SDS} can know
103 whether the \gls{SDS} has really gone because it will not be available anymore
104 in the table. It still does not know whether the device is still available.
105
106 % One share to rule them all
107 Finally, all devices containing all of their \glspl{SDS} in a table could be
108 stored in a single big \gls{SDS}. While the \CI{mapReadWrite} functions require
109 a bit more logic, they can determine the source of the error and act upon it.
110 Also, the parametric lenses must contain more logic. A downside of this
111 approach is that updating a single \gls{SDS} requires an update of the entire
112 object. In practise, this is not a real issue since almost all information can
113 be reused and \gls{SDS} residing on devices are often not updated with a very
114 high frequency.
115
116 \subsection{Parametric Lenses}
117 The type for the parametric lens of the \gls{SDS} containing all devices is
118 \CI{Maybe (MTaskDevice, Int)}. There are several levels of abstraction that
119 have to be introduced. First, the \gls{SDS} responsible for storing the entire
120 list of devices is called the global \gls{SDS}. Secondly, a \gls{SDS} can focus
121 on a single device, such \glspl{SDS} are called local \glspl{SDS}. Finally, a
122 \gls{SDS} can focus on a single \gls{SDS} on a single device. These \glspl{SDS}
123 are called share \glspl{SDS}. Using parametric lenses, the notifications can be
124 directed to only the watchers interested. Moreover, using parametric lenses,
125 the \gls{SDS} can know whether it is updating a single \gls{SDS} on a single
126 device and synchronize the value with the actual device. This means that when
127 writing to a share \gls{SDS} the update is also transformed to messages that
128 are put in the channels of the corresponding device to also notify the device
129 of the update. The \gls{SDS} is tailor-made and uses an actual standard
130 \gls{SDS} that writes to a file or memory as the storage. The tailor-made read
131 and write functions are only used to detect whether it is required to send an
132 update to the actual device.
133
134 Listing~\ref{lst:actualdev} shows the implementation of the big \gls{SDS}. From
135 this \gls{SDS} all other \glspl{SDS} are derived. The following paragraphs show
136 how this is achieved for the global \gls{SDS} local \gls{SDS} and the share
137 \gls{SDS}. In the big \gls{SDS}, reading the value is just a matter of reading
138 the standard \gls{SDS} that serves as the actual storage of the \gls{SDS}. The
139 derived shares will filter the output read accordingly. Writing the share
140 requires some extra work because it might be possible that an actual device has
141 to be notified. First, the actual storage of the \gls{SDS} is written. If the
142 parameter was \CI{Nothing} --- the global \gls{SDS} --- the write operation is
143 done. If the parameter was \CI{Just (d, -1)} --- a local \gls{SDS} --- nothing
144 has to be done as well. The final case is the special case, when the parameter
145 is \CI{Just (d, i)}, this means that the \gls{SDS} was focussed on device
146 \CI{d} and \gls{SDS} \CI{i} and thus it needs to write to it. First it locates
147 the device in the list, followed by the location of the share to check whether
148 is still exists. Finally the actual update messages are added to the device
149 channels using the \CI{sendMessagesIW} function.
150
151 All of the methods share the same \CI{SDSNotifyPred p} which is a function
152 \CI{p -> Bool} and determines for the given \CI{p} whether a notification is
153 required. The predicate function has the \CI{p} of the writer curried in and
154 can determine whether the second argument --- the reader --- needs to be
155 notified. In practice, the reader only needs to be notified when the parameters
156 are exactly the same.
157
158 \begin{lstlisting}[label={lst:actualdev},%
159 caption={Device \gls{SDS}}]
160 ($<) a fb = fmap (const a) fb
161
162 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
163 deviceStore = SDSSource {SDSSource | name="deviceStore", read=realRead, write=realWrite}
164 where
165 realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
166 realRead p iw = read realDeviceStore iw
167
168 realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
169 realWrite mi w iw
170 # (merr, iw) = write w realDeviceStore iw
171 | isError merr || isNothing mi = (merr $> gEq{|*|} mi, iw)
172 # (Just (dev, ident)) = mi
173 | ident == -1 = (merr $> gEq{|*|} mi, iw)
174 = case find ((==)dev) w of
175 Nothing = (Error $ exception "Device lost", iw)
176 Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of
177 Nothing = (Error $ exception "Share lost", iw)
178 Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of
179 (Error e, iw) = (Error e, iw)
180 (Ok _, iw) = (Ok $ gEq{|*|} mi, iw)
181
182 realDeviceStore :: Shared [MTaskDevice]
183 realDeviceStore = sharedStore "mTaskDevices" []
184 \end{lstlisting}
185
186 \subsubsection{Global \glspl{SDS}}
187 Accessing the global \gls{SDS} is just a matter of focussing the
188 \CI{deviceStore} to \CI{Nothing}. In this way, \glspl{Task} watching the
189 \gls{SDS} will only be notified if a device is added or removed. The actual
190 code is as follows:
191
192 \begin{lstlisting}[caption={Global \gls{SDS}}]
193 deviceStoreNP :: Shared [MTaskDevice]
194 deviceStoreNP = sdsFocus Nothing deviceStore
195 \end{lstlisting}
196
197 \subsubsection{Local \glspl{SDS}}
198 Accessing a single device can be done using the \CI{deviceShare} function.
199 Since device comparison is shallow, the device that is given is allowed to be
200 an old version. The identification of devices is solely done on the name of the
201 channels and is unique throughout the system. This type of \gls{SDS} will only
202 be notified if the device itself changed. It will not be notified when only a
203 single \gls{SDS} on the device changes. The implementation is as follows:
204
205 \begin{lstlisting}[caption={Local \gls{SDS}}]
206 deviceShare :: MTaskDevice -> Shared MTaskDevice
207 deviceShare d = mapReadWriteError
208 ( \ds->case find ((==)d) of
209 Nothing = exception "Device lost"
210 Just d = Ok d)
211 , \w ds->case splitWith ((==)d) ds of
212 ([], _) = Error $ exception "Device lost"
213 ([_:_], ds) = Ok $ Just [w:ds])
214 $ sdsFocus (Just (d, -1)) deviceStore
215 \end{lstlisting}
216
217 \subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
218 A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
219 function. This function focusses the global \gls{SDS} on a single \gls{SDS}
220 from a single device. It can use old share references in the same fashion as
221 the local \gls{SDS} only treating it as references. It uses the
222 \CI{mapReadWrite} functions to serve the correct part of the information. When
223 a \gls{Task} writes to this \gls{SDS}, the global \gls{SDS} will know this
224 through the parameter and propagate the value to the device.
225
226 \begin{lstlisting}[caption={Local \gls{SDS}}]
227 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
228 shareShare dev share = sdsFocus ()
229 $ mapReadWriteError (read, write)
230 $ sdsFocus (Just (dev, share.identifier))
231 $ deviceStore
232 where
233 read :: [MTaskDevice] -> MaybeError TaskException BCValue
234 read devs = case find ((==)dev) devs of
235 Nothing = exception "Device lost"
236 Just d = case find ((==)share) d.deviceShares of
237 Nothing = exception "Share lost"
238 Just s = Ok s.MTaskShare.value
239
240 write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice])
241 write val devs = case partition ((==)dev) devs of
242 ([], _) = Error $ exception "Device doesn't exist anymore"
243 ([_,_:_], _) = Error $ exception "Multiple matching devices"
244 ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of
245 ([], _) = Error $ exception "Share doesn't exist anymore"
246 ([_,_:_], _) = Error $ exception "Multiple matching shares"
247 ([s], shares) = Ok $ Just [{MTaskDevice | d &
248 deviceShares=[{MTaskShare | s & value=val}:shares]}:devs]
249 \end{lstlisting}