X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=arch.example.tex;h=b8ef049501f06cf2e90f9e679fd7e4683b36548f;hb=2498dced580be1e7af31a662dadee26c4fd159ed;hp=db9a2a7d18e86594f44e52b358d9e8220b87760c;hpb=432d1916fb34e42708252c601d77af05842eb1d9;p=msc-thesis1617.git diff --git a/arch.example.tex b/arch.example.tex index db9a2a7..b8ef049 100644 --- a/arch.example.tex +++ b/arch.example.tex @@ -1,10 +1,10 @@ \subsection{Framework} -Systems built with support for \gls{mTask} are often following the same design +Systems built with support for \gls{mTask} often follow the same design pattern. First the devices are created --- with or without the interaction of the user --- and they are then connected. When all devices are registered, the \gls{mTask}-\glspl{Task} can be sent and \gls{iTasks}-\glspl{Task} can be started to monitor the output. When everything is finished, the devices are -removed and the system is powered off. +removed and the system is shut down. \begin{lstlisting}[language=Clean,label={lst:framework}, caption={\gls{mTask} framework for building applications}] @@ -14,8 +14,7 @@ w = makeDevice "dev1" (...) >>= connectDevice >>= \dev2->... ... >>* [OnAction (Action "Shutdown") $ always - $ deleteDevice dev1 - >>| deleteDevice dev2 + $ deleteDevice dev1 >>| deleteDevice dev2 >>| ... >>| shutDown 0 ] @@ -23,18 +22,50 @@ w = makeDevice "dev1" (...) >>= connectDevice \subsection{Thermostat} The thermostat is a classic example program for showing interactions between -peripherals. The following program shows a system containing two devices. One -device is connected via \gls{TCP} and contains a temperature sensor. The second +peripherals. The following program shows a system containing two devices. The +first device --- the sensor --- contains a temperature sensor that measures the +room temperature. The second device --- the actor --- contains a heater, +connected to the digital pin \CI{D5}. Moreover, this device contains an +\gls{LED} to indicate whether the heater is on. The following code shows an +implementation for this. The code makes use of all the aspects of the +framework. Note that a little bit of type twiddling is required to fully use +the result from the \gls{SDS}. This approach is still type safe due to the type +safety of \CI{Dynamic}s. + +\begin{lstlisting}[language=Clean,caption={Thermostat example}] +thermos :: Task () +thermos = makeDevice "nodeM" nodeMCU >>= connectDevice + >>= \nod-> makeDevice "stm32" stm32 >>= connectDevice + >>= \stm-> sendTaskToDevice "sensing" sensing (nod, OnInterval 1000) + >>= \(st, [t])->sendTaskToDevice "acting" acting (stm, OnInterval 1000) + (\(BCValue s)->set (BCValue $ dynInt (dynamic s) > 0) (shareShare nod a)) + >>* [OnAction (Action "Shutdown") $ always $ deleteDevice nod >>| deleteDevice stm >>| shutDown 0] +where + dynInt :: Dynamic -> Int + dynInt (a :: Int) = a + + sensing = sds \x=0 In {main= + x =. analogRead A0 :. pub x + } + acting = sds \cool=False In {main= + IF cool (ledOn LED1) (ledOff LED1) :. + digitalWrite D5 cool + } + nodeMCU = makeDevice "NodeMCU" + (TCPDevice {host="192.168.0.12", port=8888}) + stm32 = makeDevice "Stm32" + (SerialDevice {devicePath="/dev/ttyUSB0", baudrate=B9600, ...} +\end{lstlisting} \subsection[Lifting mTasks to iTasks-Tasks]% {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}} -If the user does not want to know where and when a \gls{mTask} is actually -executed and is just interested in the results it can lift the \gls{mTask} to +If the user does not want to know where and when an \gls{mTask} is actually +executed and is just interested in the results, it can lift the \gls{mTask} to an \gls{iTasks}-\gls{Task}. The function is called with a name, \gls{mTask}, device and interval specification and it will return a \gls{Task} that finishes if and only if the \gls{mTask} has returned. -\begin{lstlisting}[caption={Starting up the devices}] +\begin{lstlisting}[language=Clean,caption={Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}}] liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task [MTaskShare] liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c >>= \(t, shs)->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev) @@ -44,9 +75,9 @@ where taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks \end{lstlisting} -The factorial example can then be lifted to a real \gls{iTasks}-\gls{mTask} -with the following code: -\begin{lstlisting}[caption={Lifting the factorial \gls{Task} to \gls{iTasks}}] +The factorial function example from Chapter~\ref{chp:mtaskcont} can then be +lifted to a real \gls{iTasks}-\gls{Task} with the following code: +\begin{lstlisting}[language=Clean,caption={Lifting the factorial \gls{Task} to \gls{iTasks}}] factorial :: MTaskDevice -> Task BCValue factorial dev = enterInformation "Factorial of ?" [] >>= \fac->liftmTask "fact" (fact fac) (dev, OnInterval 100) @@ -59,3 +90,151 @@ where ( pub x :. retrn ) ( x =. x *. y :. y =. y -. lit 1 )} \end{lstlisting} + +\subsection{Heartbeat \& Oxygen Saturation Sensor} +As an example, the addition of a new sensor will be demonstrated. The heartbeat +and oxygen saturation sensor add-on is a \textsc{PCB} the size of a fingernail +with a red \gls{LED} and a light sensor on it. Moreover, it contains an +\textsc{I2C} chip to communicate. The company producing the chip provides the +programmer with example code for \gls{Arduino} and \gls{mbed}. The sensor +emits red light and measures the intensity of the light returned. The +microcontroller hosting the device has to keep track of four seconds of samples +to determine the heartbeat. In the \gls{mTask}-system, an abstraction is made. +The current implementation runs on \gls{mbed} supported devices. + +\subsubsection{\gls{mTask} Classes} +First, a class has to be devised to store the functionality of the sensor. The +heartbeat sensor updates four values continuously, namely the heartbeat, the +oxygen saturation and the validity of the two. The real value and the validity +are combined in an \gls{ADT} and functions are added for both of them in the +new \CI{hb} class. The values are combined in such a way that they fit in a 16 +bit integer with the last bit representing the validity of the reading. The +introduced datatype housing the values should implement the \CI{mTaskType} +classes. The definition is as follows: + +\begin{lstlisting}[language=Clean,% + caption={The \texttt{hb} class and class implementations}] +:: Heartbeat = HB Int Bool +:: SP02 = SP02 Int Bool + +instance toByteCode Heartbeat + where toByteCode (HB i b) = "h" +++ (to16bit $ (i << 1) bitand (if b 1 0)) +instance toByteCode SP02 where ... + +instance fromByteCode Heartbeat + where fromByteCode s = let i = fromByteCode s //The Int from bytecode + in HB (i >> 1) (i bitand 1 > 0) +instance fromByteCode SP02 where ... + +derive class iTask Heartbeat, SP02 + +class hb v where + getHb :: (v Heartbeat Expr) + getSp02 :: (v SP02 Expr) +\end{lstlisting} + +\subsubsection{Bytecode Implementation} +The class is available now, and the implementation can be created. The +implementation is trivial since the functionality is limited to retrieving +single values and no assignment is possible. The following code shows the +implementation. Dedicated bytecode instructions have been added to support the +functionality. + +\begin{lstlisting}[language=Clean,caption={The \texttt{hb} bytecode instance}] +:: BC + = BCNop + | ... + | BCGetHB + | BCGetSP02 + +instance hb ByteCode where + getHb = tell` [BCGetHB] + getSp02 = tell` [BCGetSP02] +\end{lstlisting} + +\subsubsection{Device Interface} +The bytecode instructions are added but still the functionality needs to be +added to the device interface to be implemented by clients. The following +addition to \CI{interface.h} and the interpreter shows the added instructions. +When adding a peripheral, the devices not having the peripheral do not need to +have their code recompiled. New instructions always get a higher bytecode +number if added correctly. The peripheral byte in the device specification by +default shows a negative flag for every peripheral. Only the peripherals added +will be flagged positive. + +\begin{lstlisting}[language=Clean,caption={Adding the device interface}] +// interface.h +... +#if HAVEHB == 1 +uint16_t get_hb(); +uint16_t get_spo2(); +#endif +... + +// interpret.c + while(pc < plen){ + switch(program[pc++]){ + ... +#if HAVEHB == 1 + case BCGETHB: + stack[sp++] = get_hb(); + break; + case BCGETSP02: + stack[sp++] = get_spo2(); + break; +#endif + ... +\end{lstlisting} + +\subsubsection{Client Software} +The device client software always executes the \CI{real\_setup} in which the +client software can setup the connection and peripherals. In the case of the +heartbeat peripheral it starts a thread running the calculations. The thread +started in the setup will set the global heartbeat and oxygen level variables +so that the interface functions for it can access it. This is listed in +Listing~\ref{lst:hbding}. If interrupts were implemented, the \glspl{Task} +using the heartbeat sensor could be executed on interrupt. The heartbeat thread +can fire an interrupt everytime it calculated a new heartbeat. + +\begin{lstlisting}[label={lst:hbding},language=C,% + caption={Heartbeat code in the client}] +Serial pc; +Thread thread; + +void heartbeat_thread(void) { + // Constant heartbeat calculations +} + +void real_setup(void) { + pc.baud(19200); + thread.start(heartbeat_thread); +} +\end{lstlisting} + +\subsubsection{Example} +The following code shows a complete example of a \gls{Task} controlling an +\emph{STM} microcontroller containing a heartbeat sensor. The web application +belonging to the server shows the heartbeat value and starts an alert +\gls{Task} when it exceeds the value given or is no longer valid. +This example also shows how named \gls{SDS} are handled. + +\begin{lstlisting}[language=Clean,caption={Heartbeat example}] +hbwatch :: (Task a) Int -> Task () +hbwatch alert lim + = makeDevice "stm32" stm32 + >>= connectDevice + >>= \stm ->sendTaskToDevice "monitor" monitor (stm, OnInterval 200) + >>= \(t, sh)->mon (fromJust $ find (\x->x.name == "hb") sh) + >>* [OnAction (Action "Shutdown") $ always $ deleteDevice stm >>| shutDown 0] +where + mon :: (Shared BCValue) -> Task () + mon b = whileUnchanged (mapRead dynHB b) + \hb=:(HB i valid)->if (not valid || i > lim) + alert (viewInformation "HB Okay" [] hb) + + dynHB :: Dynamic -> HeartBeat + dynHB (a :: HeartBeat) = a + + monitor = namedsds \hb=(0 Named hb) In + {main= hb = getHB :. pub hb } +\end{lstlisting}