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