demo'
[msc-thesis1617.git] / arch.example.tex
1 \subsection{Framework}
2 Systems built with support for \gls{mTask} often follow the same design
3 pattern. First the devices are created --- with or without the interaction of
4 the user --- and they are then connected. When all devices are registered, the
5 \gls{mTask}-\glspl{Task} can be sent and \gls{iTasks}-\glspl{Task} can be
6 started to monitor the output. When everything is finished, the devices are
7 removed and the system is shut down.
8
9 \begin{lstlisting}[language=Clean,label={lst:framework},
10 caption={\gls{mTask} framework for building applications}]
11 w :: Task ()
12 w = makeDevice "dev1" (...) >>= connectDevice
13 >>= \dev1->makeDevice "dev2" (...) >>= connectDevice
14 >>= \dev2->...
15 ...
16 >>* [OnAction (Action "Shutdown") $ always
17 $ deleteDevice dev1 >>| deleteDevice dev2
18 >>| ...
19 >>| shutDown 0
20 ]
21 \end{lstlisting}
22
23 \subsection{Thermostat}
24 The thermostat is a classic example program for showing interactions between
25 peripherals. The following program shows a system containing two devices. The
26 first device --- the sensor --- contains a temperature sensor that measures the
27 room temperature. The second device --- the actor --- contains a heater,
28 connected to the digital pin \CI{D5}. Moreover, this device contains an
29 \gls{LED} to indicate whether the heater is on. The following code shows an
30 implementation for this. The code makes use of all the aspects of the
31 framework. Note that a little bit of type twiddling is required to fully use
32 the result from the \gls{SDS}. This approach is still type safe due to the type
33 safety of \CI{Dynamic}s.
34
35 \begin{lstlisting}[language=Clean,caption={Thermostat example}]
36 thermos :: Task ()
37 thermos = makeDevice "nodeM" nodeMCU >>= connectDevice
38 >>= \nod-> makeDevice "stm32" stm32 >>= connectDevice
39 >>= \stm-> sendTaskToDevice "sensing" sensing (nod, OnInterval 1000)
40 >>= \(st, [t])->sendTaskToDevice "acting" acting (stm, OnInterval 1000)
41 (\(BCValue s)->set (BCValue $ dynInt (dynamic s) > 0) (shareShare nod a))
42 >>* [OnAction (Action "Shutdown") $ always $ deleteDevice nod >>| deleteDevice stm >>| shutDown 0]
43 where
44 dynInt :: Dynamic -> Int
45 dynInt (a :: Int) = a
46
47 sensing = sds \x=0 In {main=
48 x =. analogRead A0 :. pub x
49 }
50 acting = sds \cool=False In {main=
51 IF cool (ledOn LED1) (ledOff LED1) :.
52 digitalWrite D5 cool
53 }
54 nodeMCU = makeDevice "NodeMCU"
55 (TCPDevice {host="192.168.0.12", port=8888})
56 stm32 = makeDevice "Stm32"
57 (SerialDevice {devicePath="/dev/ttyUSB0", baudrate=B9600, ...}
58 \end{lstlisting}
59
60 \subsection[Lifting mTasks to iTasks-Tasks]%
61 {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}}
62 If the user does not want to know where and when an \gls{mTask} is actually
63 executed and is just interested in the results, it can lift the \gls{mTask} to
64 an \gls{iTasks}-\gls{Task}. The function is called with a name, \gls{mTask},
65 device and interval specification and it will return a \gls{Task} that finishes
66 if and only if the \gls{mTask} has returned.
67
68 \begin{lstlisting}[language=Clean,caption={Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}}]
69 liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task [MTaskShare]
70 liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c
71 >>= \(t, shs)->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev)
72 >>| viewInformation "Done!" [] ()
73 >>| treturn shs
74 where
75 taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks
76 \end{lstlisting}
77
78 The factorial function example from Chapter~\ref{chp:mtaskcont} can then be
79 lifted to a real \gls{iTasks}-\gls{Task} with the following code:
80 \begin{lstlisting}[language=Clean,caption={Lifting the factorial \gls{Task} to \gls{iTasks}}]
81 factorial :: MTaskDevice -> Task BCValue
82 factorial dev = enterInformation "Factorial of ?" []
83 >>= \fac->liftmTask "fact" (fact fac) (dev, OnInterval 100)
84 @ fromJust o find (\x->x.humanName == "result")
85 @ \s->s.MTaskShare.value
86 where
87 fact i = sds \y=i
88 In namedsds \x=(1 Named "result")
89 In {main = IF (y <=. lit 1)
90 ( pub x :. retrn )
91 ( x =. x *. y :. y =. y -. lit 1 )}
92 \end{lstlisting}
93
94 \subsection{Heartbeat \& Oxygen Saturation Sensor}
95 As an example, the addition of a new sensor will be demonstrated. The heartbeat
96 and oxygen saturation sensor add-on is a \textsc{PCB} the size of a fingernail
97 with a red \gls{LED} and a light sensor on it. Moreover, it contains an
98 \textsc{I2C} chip to communicate. The company producing the chip provides the
99 programmer with example code for \gls{Arduino} and \gls{mbed}. The sensor
100 emits red light and measures the intensity of the light returned. The
101 microcontroller hosting the device has to keep track of four seconds of samples
102 to determine the heartbeat. In the \gls{mTask}-system, an abstraction is made.
103 The current implementation runs on \gls{mbed} supported devices.
104
105 \subsubsection{\gls{mTask} Classes}
106 First, a class has to be devised to store the functionality of the sensor. The
107 heartbeat sensor updates four values continuously, namely the heartbeat, the
108 oxygen saturation and the validity of the two. The real value and the validity
109 are combined in an \gls{ADT} and functions are added for both of them in the
110 new \CI{hb} class. The values are combined in such a way that they fit in a 16
111 bit integer with the last bit representing the validity of the reading. The
112 introduced datatype housing the values should implement the \CI{mTaskType}
113 classes. The definition is as follows:
114
115 \begin{lstlisting}[language=Clean,%
116 caption={The \texttt{hb} class and class implementations}]
117 :: Heartbeat = HB Int Bool
118 :: SP02 = SP02 Int Bool
119
120 instance toByteCode Heartbeat
121 where toByteCode (HB i b) = "h" +++ (to16bit $ (i << 1) bitand (if b 1 0))
122 instance toByteCode SP02 where ...
123
124 instance fromByteCode Heartbeat
125 where fromByteCode s = let i = fromByteCode s //The Int from bytecode
126 in HB (i >> 1) (i bitand 1 > 0)
127 instance fromByteCode SP02 where ...
128
129 derive class iTask Heartbeat, SP02
130
131 class hb v where
132 getHb :: (v Heartbeat Expr)
133 getSp02 :: (v SP02 Expr)
134 \end{lstlisting}
135
136 \subsubsection{Bytecode Implementation}
137 The class is available now, and the implementation can be created. The
138 implementation is trivial since the functionality is limited to retrieving
139 single values and no assignment is possible. The following code shows the
140 implementation. Dedicated bytecode instructions have been added to support the
141 functionality.
142
143 \begin{lstlisting}[language=Clean,caption={The \texttt{hb} bytecode instance}]
144 :: BC
145 = BCNop
146 | ...
147 | BCGetHB
148 | BCGetSP02
149
150 instance hb ByteCode where
151 getHb = tell` [BCGetHB]
152 getSp02 = tell` [BCGetSP02]
153 \end{lstlisting}
154
155 \subsubsection{Device Interface}
156 The bytecode instructions are added but still the functionality needs to be
157 added to the device interface to be implemented by clients. The following
158 addition to \CI{interface.h} and the interpreter shows the added instructions.
159 When adding a peripheral, the devices not having the peripheral do not need to
160 have their code recompiled. New instructions always get a higher bytecode
161 number if added correctly. The peripheral byte in the device specification by
162 default shows a negative flag for every peripheral. Only the peripherals added
163 will be flagged positive.
164
165 \begin{lstlisting}[language=Clean,caption={Adding the device interface}]
166 // interface.h
167 ...
168 #if HAVEHB == 1
169 uint16_t get_hb();
170 uint16_t get_spo2();
171 #endif
172 ...
173
174 // interpret.c
175 while(pc < plen){
176 switch(program[pc++]){
177 ...
178 #if HAVEHB == 1
179 case BCGETHB:
180 stack[sp++] = get_hb();
181 break;
182 case BCGETSP02:
183 stack[sp++] = get_spo2();
184 break;
185 #endif
186 ...
187 \end{lstlisting}
188
189 \subsubsection{Client Software}
190 The device client software always executes the \CI{real\_setup} in which the
191 client software can setup the connection and peripherals. In the case of the
192 heartbeat peripheral it starts a thread running the calculations. The thread
193 started in the setup will set the global heartbeat and oxygen level variables
194 so that the interface functions for it can access it. This is listed in
195 Listing~\ref{lst:hbding}. If interrupts were implemented, the \glspl{Task}
196 using the heartbeat sensor could be executed on interrupt. The heartbeat thread
197 can fire an interrupt everytime it calculated a new heartbeat.
198
199 \begin{lstlisting}[label={lst:hbding},language=C,%
200 caption={Heartbeat code in the client}]
201 Serial pc;
202 Thread thread;
203
204 void heartbeat_thread(void) {
205 // Constant heartbeat calculations
206 }
207
208 void real_setup(void) {
209 pc.baud(19200);
210 thread.start(heartbeat_thread);
211 }
212 \end{lstlisting}
213
214 \subsubsection{Example}
215 The following code shows a complete example of a \gls{Task} controlling an
216 \emph{STM} microcontroller containing a heartbeat sensor. The web application
217 belonging to the server shows the heartbeat value and starts an alert
218 \gls{Task} when it exceeds the value given or is no longer valid.
219 This example also shows how named \gls{SDS} are handled.
220
221 \begin{lstlisting}[language=Clean,caption={Heartbeat example}]
222 hbwatch :: (Task a) Int -> Task ()
223 hbwatch alert lim
224 = makeDevice "stm32" stm32
225 >>= connectDevice
226 >>= \stm ->sendTaskToDevice "monitor" monitor (stm, OnInterval 200)
227 >>= \(t, sh)->mon (fromJust $ find (\x->x.name == "hb") sh)
228 >>* [OnAction (Action "Shutdown") $ always $ deleteDevice stm >>| shutDown 0]
229 where
230 mon :: (Shared BCValue) -> Task ()
231 mon b = whileUnchanged (mapRead dynHB b)
232 \hb=:(HB i valid)->if (not valid || i > lim)
233 alert (viewInformation "HB Okay" [] hb)
234
235 dynHB :: Dynamic -> HeartBeat
236 dynHB (a :: HeartBeat) = a
237
238 monitor = namedsds \hb=(0 Named hb) In
239 {main= hb = getHB :. pub hb }
240 \end{lstlisting}