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