7a21471e86712207c3081cd39e053b9dfcb2fcbb
[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 implicates 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{deviceTask} stores the
48 \gls{Task}-id for this \gls{Task} when active so that it can be checked upon.
49 This top-level task has the duty to report exceptions and errors as they are
50 thrown by setting the \CI{deviceError} field. All communication goes via these
51 channels. To send a message to the device, the system just puts it
52 in the channels. Messages sent from the client to the server are also placed
53 in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a
54 simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In
55 case of a device connected by a serial connection, it uses the newly developed
56 serial port library of \gls{Clean}\footnote{\url{%
57 https://gitlab.science.ru.nl/mlubbers/CleanSerial}}.
58 The implementation and semantics for the \CI{MTaskMSGRecv} and
59 \CI{MTaskMSGSend} types is given in Section~\ref{sec:communication}.
60
61 Besides all the communication information, the record also keeps track of the
62 \glspl{Task} currently on the device, the compiler state (see
63 Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores
64 the specification of the device that is received when connecting. All of this
65 is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
66 format are explained in the following section.
67
68 \subsection{Shares}
69 \Glspl{SDS} on the device are synchronized in the server and thus stored both
70 on the server and on the client. In this way the communication is kept to the
71 bare minimum because no communication with the device is needed for a
72 \gls{SDS} read operation. In this case the \gls{Task} requesting can just be
73 provided with the synchronized value. As mentioned before, the device has to
74 explicitly publish an update. This has the implication that the server and the
75 client can get out of sync. However, this is not a problem since it is well
76 documented and by design.
77
78 % TODO
79 The system keeps track of the \glspl{SDS} stored on the client in a big
80 \gls{SDS} containing a list of devices. Client-\glspl{SDS} can be stored on one
81 device at the same time. This means that if an \gls{SDS} updates, everyone
82 watching it will be notified. This would result in a lot of notifications that
83 are not meant for the watcher. Moreover, when a client updates the \gls{SDS}
84 this is processed by the connection handler and results in an update of the
85 real \gls{SDS}. Finally, the \gls{SDS} of a client must be synchronized with
86 the actual device. Thus, when an \gls{iTasks}-\gls{Task} writes the
87 client-\gls{SDS}, it must be propagated to the real device. There are several
88 ways of tackling this problem each with their own level of granularity.
89
90 First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can
91 be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and
92 synchronizing it with the device when an update occurred. This approach is very
93 expensive as it requires a lot of listening \glspl{Task}.
94
95 Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every
96 devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite}
97 functions, a single \gls{SDS} per device can be created as a lens that allows
98 mapping on a single client-\gls{SDS}. However, this approach still requires
99 \glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written,
100 everyone is notified, even if the \gls{Task} only uses the value of a single
101 different \gls{SDS}.
102
103 Finally, the current approach --- a single \gls{SDS} for the entire system
104 --- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a
105 \CI{mapReadWrite} can be used but it suffers from the same problem as mentioned
106 before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate
107 the client-\gls{SDS} updates to the actual device. Both of these problems can
108 be solved by using a tailor made \gls{SDS} that heavily depends on parametric
109 lenses.
110
111 \subsection{Parametric Lenses}
112 The type for the parametric lens of the big \gls{SDS} is \CI{Maybe
113 (MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list
114 of devices, from now on global. Moreover, the \gls{SDS} can focus on a single
115 device, from now on local. A local \gls{SDS} can also specifically focus on a
116 single \gls{SDS} on a single device, from now on called local-share. The
117 implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}.
118 The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory.
119 Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing
120 the \gls{SDS} is a little bit more involved. If the write operation originated
121 from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must
122 also be relayed to the actual device. If the write originated from an \gls{SDS}
123 focussed the devices or on one device only, nothing needs to be done. The
124 notification predicate determines whether a watcher gets a notification update.
125
126 \begin{lstlisting}[label={lst:actualdev},%
127 caption={Device \gls{SDS}}]
128 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
129 deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite}
130 where
131 realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
132 realRead p iw = read realDeviceStore iw
133
134 realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
135 realWrite mi w iw
136 # (merr, iw) = write w realDeviceStore iw
137 | isError merr || isNothing mi = (merr $> notifyPred mi, iw)
138 # (Just (dev, ident)) = mi
139 | ident == -1 = (merr $> notifyPred mi, iw)
140 = case find ((==)dev) w of
141 Nothing = (Error $ exception "Device lost", iw)
142 Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of
143 Nothing = (Error $ exception "Share lost", iw)
144 Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of
145 (Error e, iw) = (Error e, iw)
146 (Ok _, iw) = (Ok $ notifyPred mi, iw)
147
148 notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool
149 notifyPred Nothing Nothing = True // Global watcher looking at a global event
150 notifyPred Nothing (Just _) = False // Global watcher looking at a local event
151 notifyPred (Just _) Nothing = False // Local watcher looking at a global event
152 // Local device watcher looking at a local event
153 notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2
154 // Local share watcher looking at a local share event
155 notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2
156
157 realDeviceStore :: Shared [MTaskDevice]
158 realDeviceStore = sharedStore "mTaskDevices" []
159 \end{lstlisting}
160
161 \subsubsection{Global \glspl{SDS}}
162 Accessing the global \gls{SDS} is just a matter of focussing the
163 \CI{deviceStore} with the \CI{Nothing} parameter as follows:
164
165 \begin{lstlisting}[caption={Global \gls{SDS}}]
166 deviceStoreNP :: Shared [MTaskDevice]
167 deviceStoreNP = sdsFocus Nothing deviceStore
168 \end{lstlisting}
169
170 \subsubsection{Local \glspl{SDS}}
171 Accessing a single device can be done using the \CI{mapReadWrite} function.
172 Since device comparison is shallow, the device that is given is allowed to be
173 an old version. The identification of devices is solely done on the name of the
174 channels and is unique throughout the system. The implementation is as follows:
175
176 \begin{lstlisting}[caption={Local \gls{SDS}}]
177 deviceShare :: MTaskDevice -> Shared MTaskDevice
178 deviceShare d = mapReadWriteError
179 ( \ds->case find ((==)d) of
180 Nothing = exception "Device lost"
181 Just d = Ok d)
182 , \w ds->case splitWith ((==)d) ds of
183 ([], _) = Error $ exception "Device lost"
184 ([_:_], ds) = Ok $ Just [w:ds])
185 $ sdsFocus (Just (d, -1)) deviceStore
186 \end{lstlisting}
187
188 \subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
189 A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
190 function. This function focusses the real big \gls{SDS} on a single \gls{SDS}
191 and uses the \CI{mapReadWrite} functions to serve the correct part of the
192 information.
193
194 \begin{lstlisting}[caption={Local \gls{SDS}}]
195 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
196 shareShare dev share = sdsFocus ()
197 $ mapReadWriteError (read, write)
198 $ sdsFocus (Just (dev, share.identifier))
199 $ deviceStore
200 where
201 read :: [MTaskDevice] -> MaybeError TaskException BCValue
202 read devs = case find ((==)dev) devs of
203 Nothing = exception "Device lost"
204 Just d = case find ((==)share) d.deviceShares of
205 Nothing = exception "Share lost"
206 Just s = Ok s.MTaskShare.value
207
208 write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice])
209 write val devs = case partition ((==)dev) devs of
210 ([], _) = Error $ exception "Device doesn't exist anymore"
211 ([_,_:_], _) = Error $ exception "Multiple matching devices"
212 ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of
213 ([], _) = Error $ exception "Share doesn't exist anymore"
214 ([_,_:_], _) = Error $ exception "Multiple matching shares"
215 ([s], shares) = Ok $ Just [{MTaskDevice | d &
216 deviceShares=[{MTaskShare | s & value=val}:shares]}:devs]
217 \end{lstlisting}