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.
9 \begin{lstlisting
}[language=Clean,label=
{lst:framework
},
10 caption=
{\gls{mTask
} framework for building applications
}]
12 w = makeDevice "dev1" (...) >>= connectDevice
13 >>=
\dev1->makeDevice "dev2" (...) >>= connectDevice
16 >>*
[OnAction (Action "Shutdown") $ always
17 $ deleteDevice dev1 >>| deleteDevice dev2
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.
35 \begin{lstlisting
}[language=Clean,caption=
{Thermostat example
}]
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]
44 dynInt :: Dynamic -> Int
47 sensing = sds
\x=
0 In
{main=
48 x =. analogRead A0 :. pub x
50 acting = sds
\cool=False In
{main=
51 IF cool (ledOn LED1) (ledOff LED1) :.
57 \subsection[Lifting mTasks to iTasks-Tasks
]%
58 {Lifting
\gls{mTask
}-
\glspl{Task
} to
\gls{iTasks
}-
\glspl{Task
}}
59 If the user does not want to know where and when an
\gls{mTask
} is actually
60 executed and is just interested in the results, it can lift the
\gls{mTask
} to
61 an
\gls{iTasks
}-
\gls{Task
}. The function is called with a name,
\gls{mTask
},
62 device and interval specification and it will return a
\gls{Task
} that finishes
63 if and only if the
\gls{mTask
} has returned.
65 \begin{lstlisting
}[language=Clean,caption=
{Lifting
\gls{mTask
}-
\glspl{Task
} to
\gls{iTasks
}}]
66 liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task
[MTaskShare
]
67 liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c
68 >>= \(t, shs)->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev)
69 >>| viewInformation "Done!"
[] ()
72 taskRemoved t d = isNothing $ find (
\t1->t1.ident==t.ident) d.deviceTasks
75 The factorial function example from Chapter~
\ref{chp:mtaskcont
} can then be
76 lifted to a real
\gls{iTasks
}-
\gls{Task
} with the following code:
77 \begin{lstlisting
}[language=Clean,caption=
{Lifting the factorial
\gls{Task
} to
\gls{iTasks
}}]
78 factorial :: MTaskDevice -> Task BCValue
79 factorial dev = enterInformation "Factorial of ?"
[]
80 >>=
\fac->liftmTask "fact" (fact fac) (dev, OnInterval
100)
81 @ fromJust o find (
\x->x.humanName == "result")
82 @
\s->s.MTaskShare.value
85 In namedsds
\x=(
1 Named "result")
86 In
{main = IF (y <=. lit
1)
88 ( x =. x *. y :. y =. y -. lit
1 )
}
91 \subsection{Heartbeat \& Oxygen Saturation Sensor
}
92 As an example, the addition of a new sensor will be demonstrated. The heartbeat
93 and oxygen saturation sensor add-on is a
\textsc{PCB
} the size of a fingernail
94 with a red
\gls{LED
} and a light sensor on it. Moreover, it contains an
95 \textsc{I2C
} chip to communicate. The company producing the chip provides the
96 programmer with example code for
\gls{Arduino
} and
\gls{mbed
}. The sensor
97 emits red light and measures the intensity of the light returned. The
98 microcontroller hosting the device has to keep track of four seconds of samples
99 to determine the heartbeat. In the
\gls{mTask
}-system, an abstraction is made.
100 The current implementation runs on
\gls{mbed
} supported devices.
102 \subsubsection{\gls{mTask
} Classes
}
103 First, a class has to be devised to store the functionality of the sensor. The
104 heartbeat sensor updates four values continuously, namely the heartbeat, the
105 oxygen saturation and the validity of the two. The real value and the validity
106 are combined in an
\gls{ADT
} and functions are added for both of them in the
107 new
\CI{hb
} class. The values are combined in such a way that they fit in a
16
108 bit integer with the last bit representing the validity of the reading. The
109 introduced datatype housing the values should implement the
\CI{mTaskType
}
110 classes. The definition is as follows:
112 \begin{lstlisting
}[language=Clean,
%
113 caption=
{The
\texttt{hb
} class and class implementations
}]
114 :: Heartbeat = HB Int Bool
115 :: SP02 = SP02 Int Bool
117 instance toByteCode Heartbeat
118 where toByteCode (HB i b) = "h" +++ (to16bit $ (i <<
1) bitand (if b
1 0))
119 instance toByteCode SP02 where ...
121 instance fromByteCode Heartbeat
122 where fromByteCode s = let i = fromByteCode s //The Int from bytecode
123 in HB (i >>
1) (i bitand
1 >
0)
124 instance fromByteCode SP02 where ...
126 derive class iTask Heartbeat, SP02
129 getHb :: (v Heartbeat Expr)
130 getSp02 :: (v SP02 Expr)
133 \subsubsection{Bytecode Implementation
}
134 The class is available now, and the implementation can be created. The
135 implementation is trivial since the functionality is limited to retrieving
136 single values and no assignment is possible. The following code shows the
137 implementation. Dedicated bytecode instructions have been added to support the
140 \begin{lstlisting
}[language=Clean,caption=
{The
\texttt{hb
} bytecode instance
}]
147 instance hb ByteCode where
148 getHb = tell`
[BCGetHB
]
149 getSp02 = tell`
[BCGetSP02
]
152 \subsubsection{Device Interface
}
153 The bytecode instructions are added but still the functionality needs to be
154 added to the device interface to be implemented by clients. The following
155 addition to
\CI{interface.h
} and the interpreter shows the added instructions.
156 When adding a peripheral, the devices not having the peripheral do not need to
157 have their code recompiled. New instructions always get a higher bytecode
158 number if added correctly. The peripheral byte in the device specification by
159 default shows a negative flag for every peripheral. Only the peripherals added
160 will be flagged positive.
162 \begin{lstlisting
}[language=Clean,caption=
{Adding the device interface
}]
173 switch(program
[pc++
])
{
177 stack
[sp++
] = get_hb();
180 stack
[sp++
] = get_spo2();
186 \subsubsection{Client Software
}
187 The device client software always executes the
\CI{real
\_setup} in which the
188 client software can setup the connection and peripherals. In the case of the
189 heartbeat peripheral it starts a thread running the calculations. The thread
190 started in the setup will set the global heartbeat and oxygen level variables
191 so that the interface functions for it can access it. This is listed in
192 Listing~
\ref{lst:hbding
}. If interrupts were implemented, the
\glspl{Task
}
193 using the heartbeat sensor could be executed on interrupt. The heartbeat thread
194 can fire an interrupt everytime it calculated a new heartbeat.
196 \begin{lstlisting
}[label=
{lst:hbding
},language=C,
%
197 caption=
{Heartbeat code in the client
}]
201 void heartbeat_thread(void)
{
202 // Constant heartbeat calculations
205 void real_setup(void)
{
207 thread.start(heartbeat_thread);
211 \subsubsection{Example
}
212 The following code shows a complete example of a
\gls{Task
} controlling an
213 \emph{STM
} microcontroller containing a heartbeat sensor. The web application
214 belonging to the server shows the heartbeat value and starts an alert
215 \gls{Task
} when it exceeds the value given or is no longer valid.
216 This example also shows how named
\gls{SDS
} are handled.
218 \begin{lstlisting
}[language=Clean,caption=
{Heartbeat example
}]
219 hbwatch :: (Task a) Int -> Task ()
221 = makeDevice "stm32" stm32
223 >>=
\stm ->sendTaskToDevice "monitor" monitor (stm, OnInterval
200)
224 >>= \(t, sh)->mon (fromJust $ find (
\x->x.name == "hb") sh)
225 >>*
[OnAction (Action "Shutdown") $ always $ deleteDevice stm >>| shutDown
0]
227 mon :: (Shared BCValue) -> Task ()
228 mon b = whileUnchanged (mapRead dynHB b)
229 \hb=:(HB i valid)->if (not valid || i > lim)
230 alert (viewInformation "HB Okay"
[] hb)
232 dynHB :: Dynamic -> HeartBeat
233 dynHB (a :: HeartBeat) = a
235 monitor = namedsds
\hb=(
0 Named hb) In
236 {main= hb = getHB :. pub hb
}