Merge branch 'master' of git.martlubbers.net:msc-thesis1617
[msc-thesis1617.git] / arch.communication.tex
1 The communication from the server to the client and vice versa is just a
2 character stream containing encoded \gls{mTask} messages. The \CI{synFun}
3 belonging to the device is responsible for sending the content in the left
4 channel and putting received messages in the right channel. Moreover, the
5 boolean flag in the channel type should be set to \CI{True} when the connection
6 is terminated. The specific encoding of the messages is visible in
7 Appendix~\ref{app:communication-protocol}. The type holding the messages is
8 shown in Listing~\ref{lst:avmsg}. Detailed explanation about the message types
9 and according actions will be given in the following subsections.
10
11 \begin{lstlisting}[language=Clean,label={lst:avmsg},caption={Available messages}]
12 :: MTaskId :== Int
13 :: MSDSId :== Int
14 :: MTaskFreeBytes :== Int
15 :: MTaskMSGRecv
16 = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId
17 | MTSDSAck MSDSId | MTSDSDelAck MSDSId
18 | MTPub MSDSId BCValue | MTMessage String
19 | MTDevSpec MTaskDeviceSpec | MTEmpty
20
21 :: MTaskMSGSend
22 = MTTask MTaskInterval String | MTTaskDel MTaskId
23 | MTShutdown | MTSds MSDSId BCValue
24 | MTUpd MSDSId BCValue | MTSpec
25
26 :: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int
27 \end{lstlisting}
28
29 \subsection{Device Specification}
30 The server stores a description for every device available in a record type.
31 From the macro settings in the client --- in the interface file--- a profile
32 is created that describes the specification of the device. When the connection
33 between the server and a client is established, the server will send a request
34 for specification. The client serializes its specification and send it to the
35 server so that the server knows what the client is capable of. The exact
36 specification is shown in Listing~\ref{lst:devicespec} and stores the
37 peripheral availability, the memory available for storing \glspl{Task} and
38 \glspl{SDS} and the size of the stack. Not all peripheral flags are shown for
39 brevity.
40
41 \begin{lstlisting}[language=Clean,label={lst:devicespec},
42 caption={Device specification for \gls{mTask}-\glspl{Task}}]
43 :: MTaskDeviceSpec =
44 { haveLed :: Bool
45 , haveLCD :: Bool
46 , have...
47 , bytesMemory :: Int
48 , stackSize :: Int
49 , aPins :: Int
50 , dPins :: Int
51 }
52 \end{lstlisting}
53
54 The code on the device generates the specification. When a device does not have
55 a specific peripheral, the code will also not be on the device. In the
56 interface file, the code for peripherals is always guarded by macros. Thus, if
57 the peripheral is not there, the macro is set accordingly and the code will not
58 be included. To illustrate this, Listings~\ref{lst:macro}-\ref{lst:macro3}
59 show parts of the interface file and device specification generation function
60 for the \emph{NodeMCU} microcontroller which only boasts a single analog pin
61 and eight digital pins.
62
63 \begin{minipage}{.49\textwidth}
64 \begin{lstlisting}[label={lst:macro},language=C,%
65 caption={Specification in the interface}]
66 ...
67 #elif defined ARDUINO_ESP8266_NODEMCU
68 #define APINS 1
69 #define DPINS 8
70 #define STACKSIZE 1024
71 #define MEMSIZE 1024
72 #define HAVELED 0
73 #define HAVEHB 0
74
75 #if APINS > 0
76 void write_apin(uint8_t p, uint8_t v);
77 uint8_t read_apin(uint8_t pin);
78 #endif
79 \end{lstlisting}
80 \end{minipage}
81 \begin{minipage}{.49\textwidth}
82 \begin{lstlisting}[label={lst:macro3},language=C,%
83 caption={Actual generation}]
84 ...
85 void spec_send(void) {
86 write_byte('c');
87 write_byte(0 | (HAVELED << 0)
88 | (HAVELCD << 1)
89 | (HAVEHB << 2)
90 | ...);
91 write16(MEMSIZE);
92 write16(STACKSIZE);
93 write_byte(APINS);
94 write_byte(DPINS);
95 write_byte('\n');
96 }
97 \end{lstlisting}
98 \end{minipage}
99
100 \subsection{Add a device}
101 A device can be added by filling in the \CI{MTaskDevice} record as much as
102 possible and running the \CI{connectDevice} function. This function grabs and
103 clears the channels, starts the synchronization \gls{Task} (\CI{synFun}), makes
104 sure the errors are handled when needed and runs a processing function in
105 parallel to react on the incoming messages. Moreover, it sends a specification
106 request to the device in question to determine the details of the device and
107 updates the record to contain the top-level \gls{Task}-id. All device
108 functionality heavily depends on the specific \CI{deviceShare} function that
109 generates an \gls{SDS} for a specific device. This allows giving an old device
110 record to the function and still update the latest instance.
111 Listing~\ref{lst:connectDevice} shows the connection function.
112
113 \begin{lstlisting}[language=Clean,label={lst:connectDevice},%
114 caption={Connect a device}]
115 process :: MTaskDevice (Shared Channels) -> Task ()
116 process device ch = forever $ wait "process" (not o isEmpty o fst3) ch
117 >>= \(r,s,ss)->upd (appFst3 (const [])) ch >>| proc r
118 where
119 proc :: [MTaskMSGRecv] -> Task ()
120 proc [] = treturn ()
121 proc [m:ms] = (case m of
122 MTPub i val = updateShareFromPublish device i val @! ()
123 ...
124 MTDevSpec s = deviceAddSpec device s @! ()
125 ) >>| proc ms
126
127 connectDevice :: MTaskDevice -> Task MTaskDevice
128 connectDevice device = set ([], [], False) ch
129 >>| appendTopLevelTask 'DM'.newMap True
130 ( process device ch -||- catchAll (getSynFun device.deviceData ch) errHdl)
131 >>= \tid->upd (\d->{d&deviceTask=Just tid,deviceError=Nothing}) (deviceShare device)
132 >>| set (r,[MTSpec],ss) ch
133 >>| treturn device
134 where
135 errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! ()
136 ch = channels device
137 \end{lstlisting}
138
139 Figure~\ref{fig:handshake} shows the connection diagram. The client responds to
140 the server with their device specification. This is detected by the processing
141 function and the record is updated accordingly.
142
143 \begin{figure}[H]
144 \centering
145 \begin{sequencediagram}
146 \newthread{s}{Server}
147 \newinst[4]{c}{Client}
148 \begin{call}{s}{MTSpec}{c}{MTDevSpec}
149 \end{call}
150 \end{sequencediagram}
151 \caption{Connect a device}\label{fig:handshake}
152 \end{figure}
153
154 \subsection{\glspl{Task} \& \glspl{SDS}}
155 When a \gls{Task} is sent to the device it is added to the device record
156 without an identifier. The actual identifier is added to the record when the
157 acknowledgement of the \gls{Task} by the device is received. The connection
158 diagram is shown in Figure~\ref{fig:tasksend}.
159
160 \begin{figure}[H]
161 \centering
162 \begin{sequencediagram}
163 \newthread{s}{Server}
164 \newinst[4]{c}{Client}
165 \begin{call}{s}{MTSDS}{c}{MTSDSAck}
166 \end{call}
167 \begin{call}{s}{MTTask}{c}{MTTaskAck}
168 \end{call}
169 \end{sequencediagram}
170 \caption{Sending a \gls{Task} to a device}\label{fig:tasksend}
171 \end{figure}
172
173 The function for sending a \gls{Task} to the device is shown in
174 Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The
175 details of the compilation process are given in Section~\ref{sec:compiler}.
176 The new \glspl{SDS} that were generated during compilation are merged with the
177 existing device's \glspl{SDS}. Furthermore the messages are placed in the
178 channel \gls{SDS} of the device. This will result in sending the actual
179 \gls{SDS} specification and \gls{Task} specifications to the device. A
180 \gls{Task} record is created with the identifier $-1$ to denote a \gls{Task}
181 not yet acknowledged. Finally the device itself is updated with the new state
182 and with the new \gls{Task}. After waiting for the acknowledgement the device
183 is updated again and the \gls{Task} returns.
184
185 \begin{lstlisting}[language=Clean,label={lst:sendtask},%
186 caption={Sending a \gls{Task} to a device}]
187 makeTask :: String Int -> Task MTaskTask
188 makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt}
189
190 makeShare :: String Int BCValue -> MTaskShare
191 makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value}
192
193 sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task (MTaskTask, [MTaskShare])
194 sendTaskToDevice wta mTask (device, timeout)
195 # (msgs, newState=:{sdss}) = toMessages timeout mTask device.deviceState
196 # shares = [makeShare wta "" sdsi sdsval\\{sdsi,sdsval}<-sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`]
197 = updateShares device ((++) shares)
198 >>| sendMessages msgs device
199 >>| makeTask wta -1
200 >>= \t->upd (addTaskUpState newState t) (deviceShare device)
201 >>| wait "Waiting for task to be acked" (taskAcked t) (deviceShare device)
202 >>| treturn (t, shares)
203 where
204 addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
205 addTaskUpState st task device = {MTaskDevice | device & deviceState=st, deviceTasks=[task:device.deviceTasks]}
206 taskAcked t d = maybe True (\t->t.ident <> -1) $ find (eq t) d.deviceTasks
207 eq t1 t2 = t1.dateAdded == t2.dateAdded && t1.MTaskTask.name == t2.MTaskTask.name
208 \end{lstlisting}
209
210 \subsection{Miscellaneous Messages}
211 One special type of message is available which is sent to the device only when
212 it needs to reboot. When the server wants to stop the bond with the device it
213 sends the \CI{MTShutdown} message. The device will then clear its memory, thus
214 losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself.
215 Shortly after the shutdown message a new server can connect to the device
216 because the device is back in listening mode.
217
218 \subsection{Integration}
219 When the system starts up, the devices from the previous execution still
220 residing in the \gls{SDS} must be cleaned up. It might be the case that they
221 contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in
222 this run. A user or programmer can later choose to reconnect to some devices.
223
224 \begin{lstlisting}[language=Clean,caption={Starting up the devices},%
225 label={lst:startupdevs}]
226 startupDevices :: Task [MTaskDevice]
227 startupDevices = upd (map reset) deviceStoreNP
228 where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing}
229 \end{lstlisting}
230
231 The system's management is done through the interface of a single \gls{Task}
232 called \CI{mTaskManager}. To manage the system, a couple of different
233 functionalities are necessary and are launched. An image of the management
234 interface is shown in Figure~\ref{lst:manage}. The left sidebar of the
235 interface shows the list of example \glspl{Task} that are present in the
236 system. When clicking a \gls{Task}, a dialog opens in which a device can be
237 selected to send the \gls{Task} to. The dialog might contain user specified
238 variables. All example \gls{mTask}-\glspl{Task} are of the type
239 \CI{Task (Main (ByteCode () Stmt))} and can thus ask for user input first if
240 needed for parameterized \gls{mTask}-\glspl{Task}. The bottom panel shows the
241 device information. In this panel, the devices can be created and modified.
242 Moreover, this panel allows the user to reconnect with a device after a restart
243 of the server application.
244
245 \begin{figure}[ht]
246 \centering
247 \includegraphics[width=\linewidth]{manage}
248 \caption{The device management interface}\label{lst:manage}
249 \end{figure}