828e09767afd4beda5a15743635f3c7aa536f63d
[msc-thesis1617.git] / arch.example.tex
1 \subsection{Framework}
2 Systems built with support for \gls{mTask} are often following 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 a led to
29 indicate whether the heater is on. The following code shows an implementation
30 for this. The code fully uses the framework. Note that a little bit of type
31 twiddling is required to fully us the result from the \gls{SDS}. This approach
32 is still type safe due to the type safety of \CI{Dynamic}s.
33
34 \begin{lstlisting}[caption={Thermostat example}]
35 thermos :: Task ()
36 thermos = makeDevice "nodeM" nodeMCU >>= connectDevice
37 >>= \nod-> makeDevice "stm32" stm32 >>= connectDevice
38 >>= \stm-> sendTaskToDevice "sensing" sensing (nod, OnInterval 1000)
39 >>= \(st, [t])->sendTaskToDevice "acting" acting (stm, OnInterval 1000)
40 (\(BCValue s)->set (BCValue $ dynInt (dynamic s) > 0) (shareShare nod a))
41 >>| treturn ()
42 where
43 dynInt :: Dynamic -> Int
44 dynInt (a :: Int) = a
45
46 sensing = sds \x=0 In {main=
47 x =. analogRead A0 :. pub x
48 }
49 acting = sds \cool=False In {main=
50 IF cool (ledOn LED1) (ledOff LED1) :.
51 digitalWrite D5 cool
52 }
53 nodeMCU = TCP
54 \end{lstlisting}
55
56 \subsection[Lifting mTasks to iTasks-Tasks]%
57 {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}}
58 If the user does not want to know where and when a \gls{mTask} is actually
59 executed and is just interested in the results it can lift the \gls{mTask} to
60 an \gls{iTasks}-\gls{Task}. The function is called with a name, \gls{mTask},
61 device and interval specification and it will return a \gls{Task} that finishes
62 if and only if the \gls{mTask} has returned.
63
64 \begin{lstlisting}[caption={Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}}]
65 liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task [MTaskShare]
66 liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c
67 >>= \(t, shs)->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev)
68 >>| viewInformation "Done!" [] ()
69 >>| treturn shs
70 where
71 taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks
72 \end{lstlisting}
73
74 The factorial function example from Chapter~\ref{chp:mtaskcont} can then be
75 lifted to a real \gls{iTasks}-\gls{mTask} with the following code:
76 \begin{lstlisting}[caption={Lifting the factorial \gls{Task} to \gls{iTasks}}]
77 factorial :: MTaskDevice -> Task BCValue
78 factorial dev = enterInformation "Factorial of ?" []
79 >>= \fac->liftmTask "fact" (fact fac) (dev, OnInterval 100)
80 @ fromJust o find (\x->x.humanName == "result")
81 @ \s->s.MTaskShare.value
82 where
83 fact i = sds \y=i
84 In namedsds \x=(1 Named "result")
85 In {main = IF (y <=. lit 1)
86 ( pub x :. retrn )
87 ( x =. x *. y :. y =. y -. lit 1 )}
88 \end{lstlisting}
89
90 \subsection{Heartbeat \& Oxygen Saturation Sensor}
91 As an example, the addition of a new sensor will be demonstrated. The heartbeat
92 and oxygen saturation sensor add-on is a \textsc{PCB} the size of a fingernail
93 with a red \gls{LED} and a light sensor on it. Moreover, it contains an
94 \textsc{I2C} chip to communicate. The company producing the chip provides the
95 programmer with example code for \gls{Arduino} and \textsc{mbed}. The sensor
96 emits red light and measures the returning light intensity. The microcontroller
97 hosting the device has to keep track of four seconds of samples to determine
98 the heartbeat. In the \gls{mTask}-system, an abstraction is made. The current
99 implementation runs on \textsc{mbed} supported devices.
100
101 \subsubsection{\gls{mTask} Classes}
102 First, a class has to be devised to store the functionality of the sensor. The
103 heartbeat sensor updates four values continuously, namely the heartbeat, the
104 validity of the reading, the oxygen saturation and the validity of it. For
105 every value a function is added to the new \CI{hb} class. Moreover, the
106 introduced datatype housing the values should implement the \CI{mTaskType}
107 classes. The definition is as follows:
108
109 \begin{lstlisting}[caption={The \texttt{hb} class}]
110 :: Heartbeat = HB Int
111 :: SP02 = SP02 Int
112
113 instance toByteCode Heartbeat, SP02
114 instance fromByteCode Heartbeat, SP02
115 derive class iTask Heartbeat, SP02
116
117 class hb v where
118 getHb :: (v Heartbeat Expr)
119 validHb :: (v Bool Expr)
120 getSp02 :: (v SP02 Expr)
121 validSp02 :: (v Bool Expr)
122 \end{lstlisting}
123
124 \subsubsection{Bytecode Implementation}
125 The class is available now, and the implementation can be created. The
126 implementation is trivial since the functionality is limited to retrieving
127 single values and no assignment is possible. The following code shows the
128 implementation. Dedicated bytecode instructions have been added to support the
129 functionality.
130
131 \begin{lstlisting}[caption={The \texttt{hb} bytecode instance}]
132 :: BC
133 = BCNop
134 | ...
135 | BCGetHB
136 | BCValidHB
137 | BCGetSP02
138 | BCValidSP02
139 | ...
140
141 instance hb ByteCode where
142 getHb = tell` [BCGetHB]
143 validHb = tell` [BCValidHB]
144 getSp02 = tell` [BCGetSP02]
145 validSp02 = tell` [BCValidSP02]
146 \end{lstlisting}
147
148 \subsubsection{Device Interface}
149 The bytecode instructions are added but still the functionality needs to be
150 added to the device interface to be implemented by clients. The following
151 addition to \CI{interface.h} and the interpreter shows the added instructions.
152
153 \begin{lstlisting}[caption={Adding the device interface}]
154 // interface.h
155 ...
156 #if HAVEHB == 1
157 uint16_t get_hb();
158 bool valid_hb();
159 uint16_t get_spo2();
160 bool valid_spo2();
161 #endif
162 ...
163
164 // interpret.c
165 while(pc < plen){
166 switch(program[pc++]){
167 ...
168 #if HAVEHB == 1
169 case BCGETHB:
170 stack[sp++] = get_hb();
171 break;
172 case BCVALIDHB:
173 stack[sp++] = valid_hb();
174 break;
175 case BCGETSP02:
176 stack[sp++] = get_spo2();
177 break;
178 case BCVALIDSP02:
179 stack[sp++] = valid_spo2();
180 break;
181 #endif
182 ...
183 \end{lstlisting}
184
185 \subsubsection{Client Software}
186 The device client software always executes the \CI{real\_setup} in which the
187 client software can setup the connection and peripherals. In the case of the
188 heartbeat peripheral it starts a thread running the calculations. The thread
189 started in the setup will set the global heartbeat and oxygen level variables
190 so that the interface functions for it can access it. The code is then as
191 follows:
192
193 \begin{lstlisting}[language=C,caption={}]
194 Serial pc;
195 Thread thread;
196
197 void heartbeat_thread(void) {
198 // Constant heartbeat calculations
199 }
200
201 void real_setup(void) {
202 pc.baud(19200);
203 thread.start(heartbeat_thread);
204 }
205 \end{lstlisting}