From: Mart Lubbers Date: Fri, 3 Feb 2023 13:57:04 +0000 (+0100) Subject: upadtes X-Git-Url: https://git.martlubbers.net/?a=commitdiff_plain;h=c1b9a0995445470a66854861c04df440b7d7ffb3;p=phd-thesis.git upadtes --- diff --git a/asbook.tex b/asbook.tex index b320532..11ba090 100644 --- a/asbook.tex +++ b/asbook.tex @@ -5,6 +5,6 @@ \begin{document} %\includepdf[landscape,booklet,pages={69-128}]{thesis.pdf}%chktex 29 chktex 8 -\includepdf[landscape,booklet,pages={3-}]{top/lang.pdf}%chktex 29 chktex 8 +\includepdf[landscape,booklet,pages={1-}]{top/lang.pdf}%chktex 29 chktex 8 %\includepdf[pages={211,212}]{thesis.pdf}%chktex 29 chktex 8 \end{document} diff --git a/back/summary.tex b/back/summary.tex index 91ee7b7..3fbb2a5 100644 --- a/back/summary.tex +++ b/back/summary.tex @@ -28,7 +28,6 @@ Programming edge devices benefits from \gls{TOP} as well. However, it is not straightforward to run \gls{TOP} systems on resource-constrained edge devices. This dissertation demonstrates how to orchestrate complete \gls{IOT} systems using \gls{TOP}. -% First, I present advanced \gls{DSL} embedding techniques. Then \gls{MTASK} is shown, a \gls{TOP} \gls{DSL} for \gls{IOT} edge devices, embedded in \gls{ITASK}. Tasks are constructed and compiled at run time. diff --git a/glossaries.tex b/glossaries.tex index f3733b9..5bbafb5 100644 --- a/glossaries.tex +++ b/glossaries.tex @@ -113,6 +113,10 @@ name=I\textsuperscript{2}C, description={(Inter-Integrated Circuit) {-} a simple serial communication protocol often used to connect sensors to microcontrollers} } +\newglossaryentry{WIFI}{ + name=Wi-Fi, + description={- is a family of wireless network protocols commonly used for local area networking} +} \newglossaryentry{SPI}{ name=SPI, description={(Serial Peripheral Interface) {-} a synchronous serial communication protocol often used to connect sensors to microcontrollers} diff --git a/intro/intro.tex b/intro/intro.tex index 6880f89..b726a6c 100644 --- a/intro/intro.tex +++ b/intro/intro.tex @@ -109,7 +109,7 @@ It consists of edge devices such as microcontrollers equipped with various senso In home automation this layer consists of all devices hosting sensors and actuators such as smart light bulbs, actuators to open doors, or temperature and humidity sensors. All layers are connected using the network layer. -In some applications this is implemented using conventional networking techniques such as Wi-Fi or Ethernet. +In some applications this is implemented using conventional networking techniques such as \gls{WIFI} or Ethernet. However, network technology that is tailored to the needs of the specific interconnection between two layers is increasingly popular. Examples of this are BLE, LoRa, ZigBee, and LTE-M as a communication protocol for connecting the perception layer to the application layer using \gls{IOT} transport protocols such as \gls{MQTT}. Protocols such as HTTP, AJAX, and WebSocket connecting the presentation layer to the application layer that are designed for the use in web applications. diff --git a/preamble.tex b/preamble.tex index 20cef3a..88cdcf1 100644 --- a/preamble.tex +++ b/preamble.tex @@ -361,12 +361,11 @@ \newcommand{\pkoopman}{Koopman, dr.\ P.\ (Radboud University)} \newcommand{\ptrinder}{Trinder, prof.~dr.\ P.\ (University of Glasgow)} \newcommand{\rdmentry}[5]{#1 (#2): #3. #4.\ \doifmt{#5}} -\newcommand{\rewriterate}[2]{\langle{}#1, #2\rangle{}} +\newcommand{\rewriterate}[2]{\langle{}\mathit{#1}, \mathit{#2}\rangle{}} \newcommand{\requiresGHCmod}[2][]{\footnote{Requires \GHCmod{#2} to be enabled. #1}} \newcommand{\rplasmeijer}{Plasmeijer, prof.\ dr.\ ir.\ R.\ (Radboud University)} \newcommand{\erasmusplus}{ERASMUS\raisebox{.25ex}{+}} \newcommand{\imtask}{\gls{ITASK}\slash\gls{MTASK}} -\newcommand{\Imtask}{\Gls{ITASK}\slash\gls{MTASK}} \newcommand{\citask}{\gls{CLEAN}\slash\gls{ITASK}} \newcommand{\Citask}{\Gls{CLEAN}\slash\gls{ITASK}} \newcommand{\cmtask}{\gls{CLEAN}\slash\gls{MTASK}} diff --git a/top/4iot.tex b/top/4iot.tex index 2af8bf8..25e04ad 100644 --- a/top/4iot.tex +++ b/top/4iot.tex @@ -9,7 +9,7 @@ \chapter{\texorpdfstring{\Glsxtrlong{TOP} for the \glsxtrlong{IOT}}{Task-oriented programming for the internet of things}}% \label{chp:top4iot} \begin{chapterabstract} - \noindent This chapter introduces the monograph. It compares traditional edge device programming to \gls{TOP} by: + This chapter introduces the monograph. It compares traditional edge device programming to \gls{TOP} by: \begin{itemize} \item introducing edge device programming; \item showing how to create the \emph{Hello World!} application for microcontrollers using \gls{ARDUINO} and \gls{MTASK}; diff --git a/top/finale.tex b/top/finale.tex index 8738588..de498f1 100644 --- a/top/finale.tex +++ b/top/finale.tex @@ -195,6 +195,7 @@ The table compares the solutions in the relevant categories with \gls{MTASK}. }\label{tbl:multithreadingcompare} % \begin{tabular}{lc>{\columncolor[gray]{0.95}}cc>{\columncolor[gray]{0.95}}cc>{\columncolor[gray]{0.95}}cc} \begingroup + % default is 6pt \setlength\tabcolsep{4.5pt} \begin{tabular}{lccccccc} \toprule diff --git a/top/green.tex b/top/green.tex index 83734fe..20d6ee9 100644 --- a/top/green.tex +++ b/top/green.tex @@ -9,56 +9,63 @@ \chapter{Green computing with \texorpdfstring{\gls{MTASK}}{mTask}}% \label{chp:green_computing_mtask} \begin{chapterabstract} - \noindent This chapter demonstrate the energy saving features of \gls{MTASK} by + This chapter demonstrate the energy-saving features of \gls{MTASK} by: \begin{itemize} \item giving an overview of general green computing measures for edge devices; - \item explaining \gls{MTASK}'s task scheduling, and it is shown how to customise it so suit the applications and energy needs; + \item explaining task scheduling in \gls{MTASK}, and how to tweak it so suit the applications and energy needs; \item showing how to use interrupts in \gls{MTASK} to reduce the need for polling. \end{itemize} \end{chapterabstract} -The edge layer of the \gls{IOT} contains small devices that sense and interact with the world and it is crucial to lower their energy consumption. +The edge layer of the \gls{IOT} contains small devices that sense and interact with the world. While individual devices consume little energy, the sheer number of devices in total amounts to a lot. Furthermore, many \gls{IOT} devices operate on batteries and higher energy consumption increases the amount of e-waste as \gls{IOT} edge devices are often hard to reach and consequently hard to replace \citep{nizetic_internet_2020}. - -To reduce the power consumption of an \gls{IOT} device, the specialized low-power sleep modes of the microprocessors can be leveraged. -Different sleep modes achieve different power reductions because of their different run time characteristics. -These specifics range from disabling or suspending WiFi; stopping powering (parts) of the \gls{RAM}; disabling peripherals; or even turning off the processor completely, requiring an external signal to wake up again. -Determining when exactly and for how long it is possible to sleep is expensive in the general case and often requires annotations in the source code, a real-time operating system or a handcrafted scheduler. +It is therefore crucial to lower their energy consumption. + +To reduce the power consumption of an \gls{IOT} edge device, the specialized low-power sleep modes of the microprocessors can be leveraged. +Various sleep mode achieve different power reductions because of their run time characteristics. +These specifics range from disabling or suspending \gls{WIFI}; stop powering (parts) of the \gls{RAM}; disabling peripherals; or even turning off the processor completely, requiring an external signal to wake up again. +Determining exactly when, and for how long it is possible to sleep is expensive in the general case. +In practise it means that either annotations in the source code, a \gls{RTOS}, or a handcrafted or general-purpose scheduler is required. + +\Cref{tbl:top_sleep} shows the properties and current consumption of two commonly used microcontrollers in their various sleep modes. +It uncovers that switching the \gls{WIFI} radio off yields the biggest energy savings. +In most \gls{IOT} applications, we need \gls{WIFI} for communications. +It is fine to switch it off, but after switching it on, the \gls{WIFI} protocol needs to transmit a number of messages to re-establish the connection. +This implies that it is only worthwhile to switch the radio off when this can be done for some time. +The details vary per system and situation. +As a rule of thumb, derived from experimentation, it is only worthwhile to switch the \gls{WIFI} off when it is not needed for at least some tens of seconds. \begin{table} \centering - \caption{Current use in \unit{\milli\ampere} of two microprocessor boards in various sleep modes.}% + \caption{Current use (\unit{\milli\ampere}) of two microprocessor boards in various sleep modes.}% \label{tbl:top_sleep} \small + \begingroup + % default is 6pt but this gives an overflow of 4.25816pt + \setlength\tabcolsep{5.5pt} \begin{tabular}{ccccccccc} \toprule - & \multicolumn{4}{c}{Wemos D1 mini} & \multicolumn{4}{c}{Adafruit Feather M0 Wifi} \\ + & \multicolumn{4}{c}{\Gls{WEMOS} D1 mini} + & \multicolumn{4}{c}{Adafruit Feather M0 Wifi} \\ \midrule - & active & modem & light & deep & active & modem & light & deep \\ - & & sleep & sleep & sleep & & sleep & sleep & sleep \\ + & active & modem & light & deep & active & modem & light & deep\\ + & & sleep & sleep & sleep & & sleep & sleep & sleep\\ \midrule - WiFi & on & off & off & off & on & off & off & off \\ - CPU & on & on & pending & off & on & on & idle & idle \\ - \gls{RAM} & on & on & on & off & on & on & on & on\\%low power \\ + \gls{WIFI}& on & off & off & off & on & off & off & off\\ + CPU & on & on & pend. & off & on & on & idle & idle\\ + \gls{RAM} & on & on & on & off & on & on & on & on\\ \midrule - current & 100--240 & 15 & 0.5 & 0.002 & 90--300 & 5 & 2 & 0.005\\ + current & \numrange{100}{240} & \num{15} & \num{0.5} & \num{0.002} & \numrange{90}{300} & \num{5} & \num{2} & \num{0.005}\\ \bottomrule \end{tabular} + \endgroup \end{table} -\Cref{tbl:top_sleep} shows the properties and current consumption of two commonly used microcontrollers. -It shows that switching the WiFi radio off yields the biggest energy savings. -In most \gls{IOT} applications, we need WiFi for communications. -It is fine to switch it off, but after switching it on, the WiFi protocol needs to transmit a number of messages to re-establish the connection. -This implies that it is only worthwhile to switch the radio off when this can be done for some time. -The details vary per system and situation. -As a rule of thumb, it is only worthwhile to switch the WiFi off when it is not needed for at least some tens of seconds. - \section{Green \texorpdfstring{\glsxtrshort{IOT}}{IoT} computing} The data in \cref{tbl:top_sleep} shows that it is worthwhile to put the system in some sleep mode when there is temporarily no work to be done. A deeper sleep mode saves more energy, but also requires more work to restore the software to its working state. -A processor like the ESP8266 driving the Wemos D1 mini loses the content of its \gls{RAM} in deep sleep mode. +A processor like the ESP8266 driving the \gls{WEMOS} D1 mini loses the content of its \gls{RAM} in deep sleep mode. As a result, after waking up, the program itself is preserved, since it is stored in flash memory, but the program state is lost. When there is a program state to be preserved, we must either store it elsewhere, limit us to light sleep, or use a microcontroller that keeps the \gls{RAM} intact during deep sleep. @@ -69,22 +76,16 @@ This is especially annoying when the other tasks are executing time critical par Such protocols control the communication with sensors and actuators. Without the help of an \gls{OS}, the programmer is forced to combine all subtasks into one big system that decides if it is safe to sleep for all subtasks. -\Gls{MTASK} offers abstractions for edge layer-specific details such as the heterogeneity of architectures, platforms and frameworks; peripheral access; and multitasking but also for energy consumption and scheduling. +The \gls{MTASK} language offers abstractions for edge layer-specific details such as the heterogeneity of architectures, platforms and frameworks; peripheral access; and multitasking but also for energy consumption and scheduling. In \gls{MTASK}, tasks are implemented as a rewrite system, where the work is automatically segmented in small atomic bits and stored as a task tree. Each cycle, a single rewrite step is performed on all task trees, during rewriting, tasks do a bit of their work and progress steadily, allowing interleaved and seemingly parallel operation. After a loop, the \gls{RTS} knows which task is waiting on which triggers and is thus able to determine the next execution time for each task automatically. Utilising this information, the \gls{RTS} can determine when it is possible and safe to sleep and choose the optimal sleep mode according to the sleeping time. For example, the \gls{RTS} never attempts to sleep during an \gls{I2C} communication because \gls{IO} is always contained \emph{within} a rewrite step. - -An \gls{MTASK} program is dynamically transformed to byte code. -This byte code and the initial \gls{MTASK} expression are shipped to \gls{MTASK} \gls{IOT} node. -The \gls{MTASK} rewrite engine rewrites the current expression just a single rewrite step at a time. -When subtasks are composed in parallel, all subtasks are rewritten unless the result of the first rewrite step makes the result of the other tasks superfluous. -The task design ensures such that all time critical communication with peripherals is within a single rewrite step. This is very convenient, since the system can inspect the current state of all \gls{MTASK} expressions after a rewrite and decide if sleeping and how long is possible. %As a consequence, we cannot have fair multitasking. %When a single rewrite step would take forever due to an infinite sequence of function calls, this would block the entire IoT node. -Even infinite sequences rewrite steps, as in the \cleaninline{blink} example, are perfectly fine. +Even infinite sequences rewrite steps are perfectly fine. The \gls{MTASK} system does proper tail-call optimizations to facilitate this. \section{Rewrite interval} @@ -93,17 +94,17 @@ However, there are many \gls{MTASK} programs that just specify a repeated set of A typical example is the program that reads the temperature for a sensor and sets the system \gls{LED} if the reading is below some given \cleaninline{goal}. \begin{lstClean}[caption={A basic thermostat task.},label={lst:thermostat}] -thermostat :: Main (MTask v Bool) | mtask v -thermostat = DHT I2Caddr \dht-> - {main = rpeat (temperature dht >>~. \temp. - writeD builtInLED (goal <. temp))} +thermostat :: Main (MTask v Bool) | mtask, dht v +thermostat = declarePin D8 PMOutput \ledPin-> + DHT I2Caddr \dht-> + {main = rpeat (temperature dht >>~. \temp-> + writeD ledPin (goal <. temp))} \end{lstClean} This program repeatedly reads the \gls{DHT} sensor and sets the on-board \gls{LED} based on the comparison with the \cleaninline{goal} as fast as possible on the \gls{MTASK} node. -This is a perfect solution as long as we ignore the power consumption. The \gls{MTASK} machinery ensures that if there are other tasks running on the node, they will make progress. However, this solution is far from perfect when we take power consumption into account. -In most applications, it is very unlikely that the temperature will change significantly within one minute, let alone within some milliseconds. +In most applications, it is very unlikely that the temperature changes significantly within one minute, let alone within some milliseconds. Hence, it is sufficient to repeat the measurement with an appropriate interval. There are various ways to improve this program. @@ -177,7 +178,7 @@ Based on these default rewrite rates, the system automatically derives rewrite r \subsubsection{Parallel combinators} For parallel combinators, the \emph{or}-combinator (\cleaninline{.\|\|.}) in \cref{R:or} and the \emph{and}-combinator (\cleaninline{.&&.}) in \cref{R:and}, the safe intersection (see \cref{equ:safe_intersect}) of the rewrite rates is taken to determine the rewrite rate of the complete task. The conventional intersection does not suffice here because it yields an empty intersection when the intervals do not overlap. -In that case, the safe intersection behaves will return the range with the lowest numbers. +In that case, the safe intersection returns the range with the lowest numbers. The rationale is that subtasks should not be delayed longer than their rewrite range. Evaluating a task earlier should not change its result but just consumes more energy. @@ -199,7 +200,7 @@ For the step combinator (\cref{R:step})---and all other derived sequential combi Only after stepping, the combinator rewrites to the right-hand side. \subsubsection{Repeating combinators} -The repeat combinators repeats their argument indefinitely. +The repeat combinators repeats its argument indefinitely. As the \cleaninline{rpeat} task tree node already includes a rewrite rate (set to $\rewriterate{0}{0}$ for a default \cleaninline{rpeat}), both \cleaninline{rpeat} and \cleaninline{rpeatEvery} use the same task tree node and thus only one entry is required here. The derived refresh rate of the repeat combinator is the refresh rate of the child if it is unstable. Otherwise, the refresh rate is the embedded rate time minus the start time. @@ -352,7 +353,7 @@ timedPulseNaive = declarePin D0 PMOutput \d0-> \section{Task scheduling in the \texorpdfstring{\gls{MTASK}}{mTask} engine}\label{sec:scheduling} The rewrite rates from the previous section only tell us how much the next evaluation of the task can be delayed. -An \gls{IOT} edge devices executes multiple tasks may run interleaved. +In the \gls{MTASK} system, an \gls{IOT} edge devices can run multiple tasks. In addition, it has to communicate with a server to collect new tasks and updates of \glspl{SDS}. Hence, the rewrite intervals cannot be used directly to let the microcontroller sleep. Our scheduler has the following objectives. @@ -384,7 +385,7 @@ These execution times can yield a considerable and noticeable time drift in \gls For instance, a task like \cleaninline{rpeatEvery (ExactMs 1) t} should repeat \cleaninline{t} every millisecond. The programmer might expect that \cleaninline{t} will be executed for the ${(N+1)}^{th}$ time after $N$ milliseconds. Uncompensated time drift might make this considerably later. -\Gls{MTASK} does not pretend to be a hard real-time \gls{OS}, and cannot give firm guarantees with respect to evaluation time. +The \gls{MTASK} \gls{RTS} does not pretend to be a hard real-time \gls{OS}, and cannot give firm guarantees with respect to evaluation time. Nevertheless, we try to make time handling as reliable as possible. This is achieved by adding the start time of this round of task evaluations rather than the current time to compute absolute execution intervals. @@ -394,7 +395,7 @@ When the microcontroller is active, it checks the connection and updates from th Next, the microcontroller goes to light sleep for the minimum of a predefined interval and the task delay. In general, the microcontroller node executes multiple \gls{MTASK} tasks at the same time. -\Gls{MTASK} nodes repeatedly check for inputs from servers and execute all tasks that cannot be delayed to the next evaluation round one step. +The \gls{MTASK} node repeatedly check for inputs from servers and execute all tasks that cannot be delayed to the next evaluation round one step. The tasks are stored in a priority queue to check efficiently which of them need to be stepped. The \gls{MTASK} tasks are ordered at their absolute latest start time in this queue; the earliest deadline first. We use the earliest deadline to order tasks with equal latest deadline. @@ -437,7 +438,7 @@ A task that produces a stable value is completed and is not queued again. The \cleaninline{sleep} function determines the maximum sleep time based on the top of the queue. The computed sleep time and the characteristics of the microprocessor determine the length and depth of the sleep. -For very short sleep times it might not be worthwhile to sleep. +For very short sleep times it is not be worthwhile to put the processor in sleep mode. In the current \gls{MTASK} \gls{RTS}, the thresholds are determined by experimentation but can be tuned by the programmer. On systems that lose the content of their \gls{RAM} it is not possible to go to deep sleep mode. @@ -447,9 +448,9 @@ These interrupts are hard-wired signals that can interrupt the normal flow of th While the \glspl{ISR} look like regular functions, they do come with some limitations. For example, they must be very short, in order not to miss future interrupts; can only do very limited \gls{IO}; cannot reliably check the clock; and they operate in their own stack, and thus communication must happen via global variables. After the execution of the \gls{ISR}, the normal program flow is resumed. -Interrupts are heavily used internally in the \gls{RTS} of the microcontrollers to perform timing critical operations such as WiFi, \gls{I2C}, or \gls{SPI} communication; completed \gls{ADC} conversions, software timers; exception handling; \etc. +Interrupts are heavily used internally in the \gls{RTS} of the microcontrollers to perform timing critical operations such as \gls{WIFI}, \gls{I2C}, or \gls{SPI} communication; completed \gls{ADC} conversions; software timers; exception handling; \etc. -Interrupts offer two substantial benefits: fewer missed events and better energy usage. +Using interrupts in \gls{MTASK} task offer two substantial benefits: fewer missed events and better energy usage. Sometimes an external event such as a button press only occurs for a very small duration, making it possible to miss it due to it happening right between two polls. Using interrupts is not a fool-proof way of never missing an event. Events may still be missed if they occur during the execution of an \gls{ISR} or while the microcontroller is still in the process of waking up from a triggered interrupt. @@ -521,7 +522,7 @@ void buttonPressed() {[+\label{lst:arduino_interrupt:isr_fro}+] }[+\label{lst:arduino_interrupt:isr_to}+] \end{lstArduino} -\subsection{\texorpdfstring{\Gls{MTASK}}{MTask} language} +\subsection{The \texorpdfstring{\gls{MTASK}}{mTask} language} \Cref{lst:mtask_interrupts} shows the interrupt interface in \gls{MTASK}. The \cleaninline{interrupt} class contains a single function that, given an interrupt mode and a \gls{GPIO} pin, produces a task that represents this interrupt. Lowercase variants of the various interrupt modes such as \cleaninline{change :== lit Change} are available as convenience macros (see \cref{sec:expressions}). @@ -552,7 +553,7 @@ pirSwitch = >>|. writeD ledpin true) } \end{lstClean} -\subsection{\texorpdfstring{\Gls{MTASK}}{MTask} engine} +\subsection{The \texorpdfstring{\gls{MTASK}}{mTask} engine} While interrupt tasks have their own node type in the task tree, they differ slightly from other node types because they require a more elaborate setup and teardown. Enabling and disabling interrupts is done in a general way in which tasks register themselves after creation and deregister after deletion. diff --git a/top/imp.tex b/top/imp.tex index 6f15522..88659b8 100644 --- a/top/imp.tex +++ b/top/imp.tex @@ -9,22 +9,25 @@ \chapter{Implementation}% \label{chp:implementation} \begin{chapterabstract} - \noindent This chapter shows the implementation of the \gls{MTASK} system by: + This chapter shows the implementation of the \gls{MTASK} system by: \begin{itemize} - \item gives details of the implementation of \gls{MTASK}'s \gls{TOP} engine that executes the \gls{MTASK} tasks on the microcontroller; - \item shows the implementation of the byte code compiler for \gls{MTASK}'s \gls{TOP} language; - \item explains the machinery used to automatically serialise and deserialise data to-and-fro the device. + \item giving details of the implementation of \gls{MTASK}'s \gls{TOP} engine that executes the \gls{MTASK} tasks on the microcontroller; + \item showing the implementation of the byte code compiler for \gls{MTASK}'s \gls{TOP} language; + \item explaining the machinery used to automatically serialise and deserialise data to-and-fro the device. \end{itemize} \end{chapterabstract} -Microcontrollers usually have flash-based program memory which wears out fairly quick. -For example, the atmega328p in the \gls{ARDUINO} UNO is rated for 10000 write cycles. +The \gls{MTASK} language targets resource-constrained edge devices that have little memory, processor speed and communication. +Furthermore, microcontrollers usually have flash-based program memory which wears out fairly quick. +For example, the flash memory of the popular atmega328p powering the \gls{ARDUINO} UNO is just rated for 10000 write cycles. While this sounds like a lot, if new tasks are sent to the device every minute or so, a lifetime of only seven days is guaranteed. -Hence, for dynamic applications, interpretation on the device is necessary, saving precious write cycles of the program memory. +Hence, for dynamic applications, storing the program in the \gls{RAM} of the device and interpreting this code is necessary, saving precious write cycles of the program memory. -In order to provide the device with the tools to interpret the byte code, it is programmed with a \gls{RTS}, a customisable domain-specific \gls{OS} that takes care of the execution of tasks but also low-level mechanisms such as the communication, multi tasking, and memory management. -Once the device is programmed with the \gls{MTASK} \gls{RTS}, it can continuously receive new tasks without the need for reprogramming. +In the \gls{MTASK} system, this is done by the \gls{MTASK} \gls{RTS}. +The \gls{RTS} is a customisable domain-specific \gls{OS} that takes care of the execution of tasks, but also low-level mechanisms such as the communication, multitasking, and memory management. +Once a device is programmed with the \gls{MTASK} \gls{RTS}, it can continuously receive new tasks without the need for reprogramming. The \gls{OS} is written in portable \ccpp{} and only contains a small device-specific portion. +In order to keep the abstraction level high and the hardware requirements low, much of the high-level functionality of the \gls{MTASK} language is implemented not in terms of lower-level constructs from \gls{MTASK} language but in terms of \ccpp{} code. \section{\texorpdfstring{\Glsxtrlong{RTS}}{Run time system}} The event loop of the \gls{RTS} is executed repeatedly and consists of three distinct phases. @@ -36,7 +39,7 @@ The exact communication method is a customisable device-specific option baked in The interface is deliberately kept simple and consists of a two layer interface: a link interface and a communication interface. Besides opening, closing and cleaning up, the link interface has only three functions that are shown in \cref{lst:link_interface}. Consequently, implementing this link interface is very simple but allows for many more advanced link settings such as buffering. -There are implementations for this interface for serial or Wi-Fi connections using \gls{ARDUINO} and \gls{TCP} connections for Linux. +There are implementations for this interface for serial or \gls{WIFI} connections using \gls{ARDUINO} and \gls{TCP} connections for Linux. \begin{lstArduino}[caption={Link interface of the \gls{MTASK} \gls{RTS}.},label={lst:link_interface}] bool link_input_available(void); @@ -191,7 +194,7 @@ One notable instruction is the \cleaninline{MkTask} instruction, it allocates an ... \end{lstClean} -\subsection{Compiler} +\subsection{Compiler infrastructure} The bytecode compiler interpretation for the \gls{MTASK} language is implemented as a monad stack containing a writer monad and a state monad. The writer monad is used to generate code snippets locally without having to store them in the monadic values. The state monad accumulates the code, and stores the stateful data the compiler requires. @@ -204,7 +207,7 @@ next fresh label; a list of all the used \glspl{SDS}, either local \glspl{SDS} containing the initial value (\cleaninline{Left}) or lifted \glspl{SDS} (see \cref{sec:liftsds}) containing a reference to the associated \gls{ITASK} \gls{SDS}; and finally there is a list of peripherals used. -\begin{lstClean}[label={lst:compiler_state},caption={\Gls{MTASK}'s byte code compiler type}] +\begin{lstClean}[label={lst:compiler_state},caption={The type for the \gls{MTASK} byte code compiler}] :: BCInterpret a :== StateT BCState (WriterT [BCInstr] Identity) a :: BCState = { bcs_infun :: JumpLabel @@ -597,7 +600,9 @@ Furthermore, a \ccpp{} parser and printer is generated for use on the \gls{MTASK The technique for generating the \ccpp{} parser and printer is very similar to template metaprogramming and requires a generic programming library or compiler support that includes a lot of metadata in the record and constructor nodes. \section{Conclusion} -%\todo[inline]{conclusion} +Tasks in the \gls{MTASK} system are executed on resource-constrained \gls{IOT} edge devices. + +\todo[inline]{conclusion} \input{subfilepostamble} \end{document} diff --git a/top/int.tex b/top/int.tex index 1ef5860..93a161e 100644 --- a/top/int.tex +++ b/top/int.tex @@ -9,43 +9,48 @@ \chapter{Integration with \texorpdfstring{\gls{ITASK}}{iTask}}% \label{chp:integration_with_itask} \begin{chapterabstract} - \noindent This chapter shows the integration of \gls{MTASK} with \gls{ITASK} by showing: + This chapter shows the integration of \gls{MTASK} with \gls{ITASK} by showing: \begin{itemize} \item an architectural overview \gls{MTASK} applications; \item on the interface for connecting devices; \item the interface for lifting \gls{MTASK} tasks to \gls{ITASK} tasks; - \item a interface for lifting \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS}; + \item a interface for lowering \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS}; \item and finishes with non-trivial home automation example application using all integration mechanisms; \end{itemize} \end{chapterabstract} -The \gls{MTASK} language is a multi-view \gls{DSL}, i.e.\ there are multiple interpretations possible for a single \gls{MTASK} term. -Using the byte code compiler (\cleaninline{BCInterpret}) \gls{DSL} interpretation, \gls{MTASK} tasks can be fully integrated in \gls{ITASK}. -They are executed as if they are regular \gls{ITASK} tasks and they communicate may access \glspl{SDS} from \gls{ITASK} as well. -\Gls{MTASK} devices contain a domain-specific \gls{OS} and are little \gls{TOP} engines in their own respect, being able to execute tasks. +The \gls{MTASK} language is a multi-view \gls{DSL}, there are multiple interpretations possible for a single \gls{MTASK} term. +Using the byte code compiler (\cleaninline{BCInterpret}) \gls{DSL} interpretation, \gls{MTASK} tasks are fully integrated in \gls{ITASK}. +They are executed as if they are regular \gls{ITASK} tasks and they can access \glspl{SDS} from \gls{ITASK}. +Devices in the \gls{MTASK} system contain a domain-specific \gls{OS} and are little \gls{TOP} engines in their own respect, being able to execute tasks. + \Cref{fig:mtask_integration} shows the architectural layout of a typical \gls{IOT} system created with \gls{ITASK} and \gls{MTASK}. The entire system is written as a single \gls{CLEAN} specification where multiple tasks are executed at the same time. Tasks can access \glspl{SDS} according to many-to-many communication and multiple clients can work on the same task. +The diagram contains three labelled arrows that denote the integration functions between \gls{ITASK} and \gls{MTASK}. Devices are integrated into the system using the \cleaninline{withDevice} function (see \cref{sec:withdevice}). Using \cleaninline{liftmTask}, \gls{MTASK} tasks are lifted to a device (see \cref{sec:liftmtask}). -\Gls{ITASK} \glspl{SDS} are lifted to the \gls{MTASK} device using \cleaninline{liftsds} (see \cref{sec:liftsds}). +\glspl{SDS} from \gls{ITASK} are lowered to the \gls{MTASK} device using \cleaninline{lowerSds} (see \cref{sec:liftsds}). -\begin{figure}[ht] +\begin{figure} \centering \includestandalone{mtask_integration} - \caption{\Gls{MTASK}'s integration with \gls{ITASK}.}% +\caption{An architectural view of an \imtask{} applications.}% \label{fig:mtask_integration} \end{figure} \section{Connecting edge devices}\label{sec:withdevice} -When interpreted by the byte code compiler view, an \gls{MTASK} task produces a compiler. -This compiler is exceuted at run time so that the resulting byte code can be sent to an edge device. -All communication with this device happens through a so-called \emph{channels} \gls{SDS}. +Edge devices in an \gls{MTASK} application are always coordinated by a server. +This means that they wait for a server to connect to them and send work. +The heavy lifting of connecting an \gls{MTASK} device to an \gls{ITASK} server is done with the \cleaninline{withDevice} function. +This function is given a communication specification and a function producing an \gls{ITASK} task that does something with an \gls{MTASK} device, e.g.\ lift tasks. +By using \gls{HOAS} like this, setting up and tearing down the connection to the device is fully controlled. + +In the implementation of the function, all communication with a device happens through a so-called \emph{channels} \gls{SDS}. The channels contain three fields, a queue of messages that are received, a queue of messages to send and a stop flag. Every communication method that implements the \cleaninline{channelSync} class can provide the communication with an \gls{MTASK} device. As of now, serial port communication, direct \gls{TCP} communication and \gls{MQTT} over \gls{TCP} are supported as communication providers (see \cref{lst:connection_types}). -The \cleaninline{withDevice} function transforms such a communication provider and a task that does something with this device to an \gls{ITASK} task. -Internally, the task sets up the communication, exchanges specifications with the device, executes the inner task while handling errors, and finally cleans up after closing. +Internally, the \cleaninline{withDevice} task sets up the communication, exchanges specifications with the device, executes the inner task while handling errors, and finally cleans up after closing. \Cref{lst:mtask_device} shows the types and interface to connecting devices. \begin{lstClean}[label={lst:mtask_device},caption={Device communication interface in \gls{MTASK}.}] @@ -63,7 +68,7 @@ withDevice :: a (MTDevice -> Task b) The \cleaninline{MTDevice} abstract type is internally represented as three \gls{ITASK} \gls{SDS} that contain all the current information about the tasks. The first \gls{SDS} is the information about the \gls{RTS} of the device, i.e.\ metadata on the tasks that are executing, the hardware specification and capabilities, and a list of fresh task identifiers. The second \gls{SDS} is a map storing downstream \gls{SDS} updates. -When a lifted \gls{SDS} is updated on the device, a message is sent to the server. +When a lowered \gls{SDS} is updated on the device, a message is sent to the server. This message is initially queued in the map to allow for asynchronous handling of multiple updates. Finally, the \cleaninline{MTDevices} type contains the communication channels. @@ -74,7 +79,7 @@ Then, it performs the following four tasks in parallel to monitor the edge devic Errors that occur here are converted to the proper \gls{MTASK} exception. \item Watches the channels for the shutdown flag. If the connection is lost with the device unexpectedly, an \gls{MTASK} exception is thrown. - \item Watches the channels for messages and processes them accordingly by changing the device information \gls{SDS} or adding the lifted \gls{SDS} updates to the corresponding \gls{SDS} update queue. + \item Watches the channels for messages and processes them accordingly by changing the device information \gls{SDS} or adding the lowered \gls{SDS} updates to the corresponding \gls{SDS} update queue. \item Sends a request for a specification. Once the specification is received, the device task is run. The task value of this device task is then used as the task value of the \cleaninline{withDevice} task. \end{enumerate} @@ -94,10 +99,11 @@ withDevice spec deviceTask = \end{lstClean} If at any stage an unrecoverable device error occurs, an \gls{ITASK} exception is thrown on the \cleaninline{withDevice} task. -This exception can be caught in order to device some kind of fail-safe mechanism. -For example, when a device fails, the tasks can be sent to another device as can be seen in \cref{lst:failover}. -This function executes an \gls{MTASK} task on a pool of devices. -If an error occurs during execution, the next device in the pool is tried until the pool is exhausted +This exception can be caught in order to devise fail-safe mechanisms. +For example, if a device fails, the task can be sent to another device as can be seen in \cref{lst:failover}. +This function executes an \gls{MTASK} task on a pool of devices connected through \gls{TCP}. +If a device error occurs during execution, the next device in the pool is tried until the pool is exhausted +If another error occurs, it is rethrown for a parent task to catch. \begin{lstClean}[caption={An \gls{MTASK} failover combinator.},label={lst:failover}] failover :: [TCPSettings] (Main (MTask BCInterpret a)) -> Task a @@ -108,9 +114,9 @@ where except MTEUnexpectedDisconnect = failover ds mtask \end{lstClean} \section{Lifting \texorpdfstring{\gls{MTASK}}{mTask} tasks}\label{sec:liftmtask} -Once the connection with the device is established, \gls{MTASK} tasks can be lifted to \gls{MTASK} tasks using the \cleaninline{liftmTask} family of functions (see \cref{lst:liftmtask}). +Once the connection with the device is established, \gls{MTASK} tasks are lifted to \gls{MTASK} tasks using the \cleaninline{liftmTask} family of functions (see \cref{lst:liftmtask}). Given an \gls{MTASK} task in the \cleaninline{BCInterpret} view and a device obtained from \cleaninline{withDevice}, an \gls{ITASK} task is returned. -This \gls{ITASK} task tethers the \gls{MTASK} task that is executed on the microcontroller. +This \gls{ITASK} task proxies the \gls{MTASK} task that is executed on the microcontroller. Hence, when for example observing the task value, the actual task value from the microcontroller is observed. \begin{lstClean}[label={lst:liftmtask},caption={The interface for lifting \gls{MTASK} tasks to \gls{ITASK} tasks.}] @@ -123,8 +129,8 @@ The first argument is the task and the second argument is the device which is ju First a fresh identifier for the task is generated using the device state. With this identifier, the cleanup hook can be installed. This is done to assure the task is removed from the edge device if the \gls{ITASK} task coordinating it is destroyed. -Tasks can be destroyed when for example a task executed in parallel and the parallel combinator terminates or when the condition to step holds in a sequential task combination. -Then the \gls{MTASK} compiler is invoked, its only argument besides the task is a function doing something with the results of the compilation, i.e.\ the lifted \glspl{SDS} and the messages containing the compiled and serialised task. +Tasks in \gls{ITASK} are destroyed when for example it is executed in parallel with another task and the parallel combinator terminates or when the condition to step holds in a sequential task combination. +Then the \gls{MTASK} compiler is invoked, its only argument besides the task is a function doing something with the results of the compilation, i.e.\ the lowered \glspl{SDS} and the messages containing the compiled and serialised task. With the result of the compilation, the task can be executed. First the messages are put in the channels, sending them to the device. Then, in parallel: @@ -145,39 +151,53 @@ liftmTask task (MTDevice dev sdsupdates channels) -|| watchSharesUpstream mrefs channels tid) \end{lstClean} -\todo{dis\-cuss pre\-loading} +Sending the complete byte code to the device is not always a suitable option. +For example, when the device is connected through an unstable or slow connection, sending the entire byte code may induce lots of delay. +To mitigate this problem, \gls{MTASK} tasks can be preloaded on a device. +Preloading means that the task is compiled and integrated into the device firmware. +On receiving a \cleaninline{TaskPrep}, a hashed value of the task to be sent is included. +The device then checks the preloaded task registry and uses the local version if the hash matches. +Of course this only works for tasks that are not tailor made for the current work specification and not depend on run time information. +The interface for task preloading can be found in \cref{lst:preload}. +Given an \gls{MTASK} task, a header file is created that is placed in the source code directory of the \gls{RTS} to include it in the firmware. + +\begin{lstClean}[label={lst:preload},caption={Preloading tasks in \gls{MTASK}.}] +preloadTask :: (Main (MTask BCInterpret u)) -> Task String +\end{lstClean} -\section{Lifting \texorpdfstring{\gls{ITASK}}{iTask} \texorpdfstring{\glsxtrlongpl{SDS}}{shared data sources}}\label{sec:liftsds} -Lifting \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS} is something that mostly happens at the compiler level using the \cleaninline{liftsds} function (see \cref{lst:mtask_itasksds}). +\section{Lowering \texorpdfstring{\gls{ITASK}}{iTask} \texorpdfstring{\glsxtrlongpl{SDS}}{shared data sources}}\label{sec:liftsds} +Lowering \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS} is something that mostly happens at the compiler level using the \cleaninline{lowerSds} function (see \cref{lst:mtask_itasksds}). \Glspl{SDS} in \gls{MTASK} must always have an initial value. -For regular \gls{SDS} this value is given in the source code, for lifted \gls{ITASK} \glspl{SDS} this value is obtained by reading the values once just before sending the task to the edge device. -On the device itself, there is just one difference between lifted \glspl{SDS} and regular \glspl{SDS}: after changing \pgls{SDS}, a message is sent to the server containing this new value. +For regular \gls{SDS} this value is given in the source code, for lowered \gls{ITASK} \glspl{SDS} this value is obtained by reading the values once just before sending the task to the edge device. +On the device itself, there is just one difference between lowered \glspl{SDS} and regular \glspl{SDS}: after changing \pgls{SDS}, a message is sent to the server containing this new value. The \cleaninline{withDevice} task (see \cref{sec:withdevice}) receives and processes this message by writing to the \gls{ITASK} \gls{SDS}. Tasks watching this \gls{SDS} get notified then through the normal notification mechanism of \gls{ITASK}. -\begin{lstClean}[label={lst:mtask_itasksds},caption={Lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] -class liftsds v where - liftsds :: ((v (Sds t)) -> In (Shared sds t) (Main (MTask v u))) +\begin{lstClean}[label={lst:mtask_itasksds},caption={Lowered \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] +class lowerSds v where + lowerSds :: ((v (Sds t)) -> In (Shared sds t) (Main (MTask v u))) -> Main (MTask v u) | RWShared sds \end{lstClean} -As an example, \cref{lst:mtask_liftsds_ex} shows a lightswitch function producing an \imtask{} task. -Given an \cleaninline{MTDevice} type, a device handle, an \gls{ITASK} \gls{SDS} of the type boolean is created. +As an example, \cref{lst:mtask_liftsds_ex} shows a light switch function producing an \imtask{} task when given a device handle. +First an \gls{ITASK} \gls{SDS} of the type boolean is created. This boolean represents the state of the light. The \gls{MTASK} task uses this \gls{SDS} to turn on or off the light. -An \gls{ITASK} task that runs in parallel allows interactive updating of this state. +The \gls{ITASK} task that runs in parallel allows interactive updating of this state. -\todo{dit voor\-beeld aan\-hou\-den of al\-leen die gro\-te ge\-brui\-ken} \begin{lstClean}[label={lst:mtask_liftsds_ex},caption={Interactive light switch program.}] +lightswitch :: MTDevice -> Task Bool lightswitch dev = withShared False \sh-> liftmTask (mtask sh) dev -|| updateSharedInformation [] sh <<@ Hint "Light switch" where + mtask :: (Shared sds Bool) -> Main (MTask v Bool) + | mtask, lowerSds v & RWShared sds mtask sh = declarePin D13 PMOutput \d13-> - liftsds \ls=sh + lowerSds \ls=sh In fun \f=(\st-> getSds ls >>*. [IfValue ((!=.)st) (\v->writeD d13 v)] @@ -190,15 +210,15 @@ The compilation of the code and the serialisation of the data throws away all ty \Glspl{SDS} are stored in the compiler state as a map from identifiers to either an initial value or an \cleaninline{MTLens}. The \cleaninline{MTLens} is a type synonym for a \gls{SDS} that represents the typeless serialised value of the underlying \gls{SDS}. This is done so that the \cleaninline{withDevice} task can write the received \gls{SDS} updates to the according \gls{SDS} independently. -\Gls{ITASK}'s notification mechanism then takes care of the rest. -Such a \gls{SDS} is created by using the \cleaninline{mapReadWriteError} which, given a pair of read and write functions with error handling, produces a \gls{SDS} with the lens embedded. -The read function transforms, the function that converts a typed value to a typeless serialised value, just applies the serialisation. -The write function, the function that, given the new serialised value and the old typed value, produces a new typed value. +The \gls{ITASK} notification mechanism then takes care of the rest. +Such \pgls{SDS} is created by using the \cleaninline{mapReadWriteError} which, given a pair of read and write functions with error handling, produces \pgls{SDS} with the lens embedded. +The read function transforms converts the typed value to a typeless serialised value. +The write function will, given the new serialised value and the old typed value, produce a new typed value. It tries to decode the serialised value, if that succeeds, it is written to the underlying \gls{SDS}, an error is thrown otherwise. \Cref{lst:mtask_itasksds_lens} provides the implementation for this: % VimTeX: SynIgnore on -\begin{lstClean}[label={lst:mtask_itasksds_lens},caption={Lens applied to lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] +\begin{lstClean}[label={lst:mtask_itasksds_lens},caption={Lens applied to lowered \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] lens :: (Shared sds a) -> MTLens | type a & RWShared sds lens sds = mapReadWriteError ( \r-> Ok (fromString (toByteCode{|*|} r) @@ -207,28 +227,30 @@ lens sds = mapReadWriteError \end{lstClean} % VimTeX: SynIgnore off -\Cref{lst:mtask_itasksds_lift} shows the code for the implementation of \cleaninline{liftsds} that uses the \cleaninline{lens} function shown earlier. -First, the \gls{SDS} to be lifted is extracted from the expression by bootstrapping the fixed point with a dummy value. +\Cref{lst:mtask_itasksds_lift} shows the code for the implementation of \cleaninline{lowerSds} that uses the \cleaninline{lens} function shown earlier. +First, the \gls{SDS} to be lowered is extracted from the expression by bootstrapping the fixed point with a dummy value. This is safe because the expression on the right-hand side of the \cleaninline{In} is never evaluated. Then, using \cleaninline{addSdsIfNotExist}, the identifier for this particular \gls{SDS} is either retrieved from the compiler state or generated freshly. This identifier is then used to provide a reference to the \cleaninline{def} definition to evaluate the main expression. % VimTeX: SynIgnore on -\begin{lstClean}[label={lst:mtask_itasksds_lift},caption={Lens applied to lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] - liftsds def = {main = - let (t In _) = def (abort "liftsds: expression too strict") +\begin{lstClean}[label={lst:mtask_itasksds_lift},caption={Lens applied to lowered \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] +instance lowerSds BCInterpret where + lowerSds def = {main = + let (t In _) = def (abort "lowerSds: expression too strict") in addSdsIfNotExist (Right $ lens t) >>= \sdsi->let (_ In e) = def (pure (Sds sdsi)) in e.main }\end{lstClean} % VimTeX: SynIgnore off \section{Home automation} +\todo[inline]{Staat dit hier goed of moet dit naar een andere sectie?} This section presents a interactive home automation program (\Cref{lst:example_home_automation}) to illustrate \gls{MTASK}'s integration with \gls{ITASK}. -It consists of a web interface for the user to control which tasks may be executed on either of two connected devices: an \gls{ARDUINO} UNO, connected via a serial port; and an ESP8266 based prototyping board called NodeMCU, connected via \gls{TCP} over WiFi. +It consists of a web interface for the user to control which tasks may be executed on either of two connected devices: an \gls{ARDUINO} UNO, connected via a serial port; and an ESP8266 based prototyping board called NodeMCU, connected via \gls{TCP} over \gls{WIFI}. \Crefrange{lst:example:spec1}{lst:example:spec2} show the specification for the devices. The UNO is connected via serial using the unix filepath \path{/dev/ttyACM0} and the default serial port settings. -The NodeMCU is connected via WiFi and hence the \cleaninline{TCPSettings} record is used. +The NodeMCU is connected via \gls{WIFI} and hence the \cleaninline{TCPSettings} record is used. Both types have \cleaninline{channelSync} instances. The code consists of an \gls{ITASK} part and several \gls{MTASK} parts. @@ -247,7 +269,7 @@ This task just sends a simple temperature monitoring task to the device using \c \footnotetext{\cleaninline{(>\&>) infixl 1 :: (Task a) ((SDSLens () (? a) ()) -> Task b) -> Task b \| iTask a \& iTask b}} The light switch task at \crefrange{lst:example:ls1}{lst:example:ls2} is a task that has bidirectional interaction using the definition of \cleaninline{lightswitch} shown in \cref{lst:mtask_liftsds_ex}. Using \cleaninline{liftsds}, the status of the light switch is synchronised with the user. -The task on the edge device continuously monitors the value of the lifted \gls{SDS}. +The task on the edge device continuously monitors the value of the lowered \gls{SDS}. If it is different from the current state, the new value is written to the digital \gls{GPIO} pin 13 and the monitoring function is recursively called. \begin{figure}[ht] @@ -278,12 +300,14 @@ If it is different from the current state, the new value is written to the digit \end{figure} \section{Conclusion} -\Gls{MTASK} edge devices run little \gls{TOP} engines of their own. -Using only a couple of \gls{ITASK} functions, \gls{MTASK} tasks can be integrated in \gls{ITASK} seamlessly. +When \gls{IOT} edge devices run the \gls{MTASK} \gls{RTS}, they become little \gls{TOP} engines of their own. +Using just three \gls{ITASK} functions, \gls{MTASK} devices are integrated in \gls{ITASK} seamlessly. Devices, using any supported type of connection, are integrated in \gls{ITASK} using the \cleaninline{withDevice} function. -Once connected, \gls{MTASK} tasks can be sent to the device for execution using \cleaninline{liftmTask}, lifting them to full-fledged \gls{ITASK} tasks. -Furthermore, the \gls{MTASK} tasks can interact with \gls{ITASK} \glspl{SDS} using the \cleaninline{liftsds} construct. -This together allows entire \gls{IOT} systems to be programmed from a single source. +Once connected, \gls{MTASK} tasks are sent to the device for execution using \cleaninline{liftmTask}, lifting them to full-fledged \gls{ITASK} tasks. +To lower the bandwidth, tasks can also be preloaded. +Furthermore, the \gls{MTASK} tasks interact with \gls{ITASK} \glspl{SDS} using the \cleaninline{lowerSds} construct. +All of this together allows programming all layers of an \gls{IOT} system from a single source and in a single paradigm. +All details regarding interoperation are automatically taken care of. \input{subfilepostamble} \end{document} diff --git a/top/lang.tex b/top/lang.tex index f04ad9d..4677ada 100644 --- a/top/lang.tex +++ b/top/lang.tex @@ -29,7 +29,7 @@ This particular type of embedding results in the language being is extensible bo Adding a language construct is as simple as adding a type class and adding an interpretation is done by creating a new data type and providing implementations for the various type classes. Let us illustrate this technique by taking the very simple language of literal values. This language interface can be described using a single type constructor class with a single function \cleaninline{lit}. -This function is for lifting a values, as long as it has a \cleaninline{toString} instance, from the host language to our new \gls{DSL}. +This function is for lifting values, when it has a \cleaninline{toString} instance, from the host language to our new \gls{DSL}. The type variable \cleaninline{v} of the type class represents the view on the language, the interpretation. \begin{lstClean} @@ -38,7 +38,7 @@ class literals v where \end{lstClean} Providing an evaluator is straightforward as can be seen in the following listing. -The evaluator is just a box holding a value of the computation but it can also be something more complex such as a monadic computation. +The evaluator is just a box holding a value of the computation, but it can also be something more complex such as a monadic computation. \begin{lstClean} :: Eval a = Eval a @@ -84,7 +84,7 @@ Creating such a function, e.g.\ one that both prints and evaluates an expression \section{Interpretations} This section describes all the interpretations tha the \gls{MTASK} language has. -Not all of these interpretations are necessarily \gls{TOP} engines, i.e.\ not all of the interpretations execute the resulting tasks. +Not all of these interpretations are necessarily \gls{TOP} engines, i.e.\ not all the interpretations execute the resulting tasks. Some may perform an analysis over the program or typeset the program so that a textual representation can be shown. \subsection{Pretty printer} @@ -97,7 +97,7 @@ This shortcoming is illustrated by printing a blink task that contains a functio \begin{lstClean}[caption={The entrypoint for the pretty printing interpretation.},label={lst:showmain}] :: Show a // from the mTask pretty printing library -showMain :: (Main (Show a)) -> [String] | type a +showMain :: (Main (Show a)) -> [String] \end{lstClean} \begin{lstClean}[caption={Pretty printing interpretation example.},label={lst:showexample}] @@ -107,7 +107,7 @@ blinkTask = writeD d13 state >>|. delay (lit 500) >>=. blink o Not ) In {main = blink true} -// output: +// output when printing: // fun f0 a1 = writeD(D13, a1) >>= \a2.(delay 1000) // >>| (f0 (Not a1)) in (f0 True) \end{lstClean} @@ -119,7 +119,7 @@ The simulation allows the user to (partially) execute tasks, control the simulat \begin{lstClean}[caption={The entrypoint for the simulation interpretation.},label={lst:simulatemain}] :: TraceTask a // from the mTask simulator library -simulate :: (Main (TraceTask a)) -> [String] | type a +simulate :: (Main (TraceTask a)) -> [String] \end{lstClean} \begin{figure} @@ -138,7 +138,7 @@ The integration with \gls{ITASK} is explained thoroughly later in \cref{chp:inte When using the byte code compiler interpretation in conjunction with the \gls{ITASK} integration, \gls{MTASK} is a heterogeneous \gls{DSL}. I.e.\ some components---for example the \gls{RTS} on the microcontroller that executes the tasks---is largely unaware of the other components in the system, and it is executed on a completely different architecture. The \gls{MTASK} language is based on a simply-typed $\lambda$-calculus with support for some basic types, arithmetic operations, and function definitions. -This basic language is enriched with a task language to make it \gls{TOP} (see \cref{sec:top}). +As the language is a \gls{TOP} language, is is enriched with a task language (see \cref{sec:top}). \section{Types} To leverage the type checker of the host language, types in the \gls{MTASK} language are expressed as types in the host language, to make the language type safe. @@ -147,7 +147,7 @@ The most used class constraint is the \cleaninline{type} class collection contai Many of these functions are usually automatically derived using generic programming but can be specialised when needed. An even stronger restriction on types is defined for types that have a stack representation. This \cleaninline{basicType} class has instances for many \gls{CLEAN} basic types such as \cleaninline{Int}, \cleaninline{Real} and \cleaninline{Bool}. -The class constraints for values in \gls{MTASK} are omnipresent in all functions and therefore usually omitted throughout throughout the chapters for brevity and clarity. +The class constraints for values in \gls{MTASK} are omnipresent in all functions and therefore usually omitted for brevity and clarity. \begin{table}[ht] \centering @@ -155,13 +155,13 @@ The class constraints for values in \gls{MTASK} are omnipresent in all functions \label{tbl:mtask-c-datatypes} \begin{tabular}{lll} \toprule - \cmtask{} & \ccpp{} type & \textnumero{}bits\\ + \cmtask{} & \ccpp{} type & \textnumero{}bits\\ \midrule - \cleaninline{Bool} & \cinline{bool} & 16\\ - \cleaninline{Char} & \cinline{char} & 16\\ - \cleaninline{Int} & \cinline{int16_t} & 16\\ - \cleaninline{Real} & \cinline{float} & 32\\ - \cleaninline{:: Long} & \cinline{int32_t} & 32\\ + \cleaninline{Bool} & \cinline{bool} & 16\\ + \cleaninline{Char} & \cinline{char} & 16\\ + \cleaninline{Int} & \cinline{int16_t} & 16\\ + \cleaninline{Real} & \cinline{float} & 32\\ + \cleaninline{:: Long} & \cinline{int32_t} & 32\\ \cleaninline{:: T = A \| B \| C} & \cinline{enum} & 16\\ \bottomrule \end{tabular} @@ -217,7 +217,7 @@ simulateAndPrint mt = \end{lstClean} \section{Expressions}\label{sec:expressions} -This section shows all \gls{MTASK} constructs for exppressions. +This section shows all \gls{MTASK} constructs for expressions. \Cref{lst:expressions} shows the \cleaninline{expr} class containing the functionality to lift values from the host language to the \gls{MTASK} language (\cleaninline{lit}); perform numeric and boolean arithmetics; do comparisons; and conditional execution. For every common boolean and arithmetic operator in the host language, an \gls{MTASK} variant is present, suffixed by a period to not clash with \gls{CLEAN}'s builtin operators. @@ -281,8 +281,8 @@ approxEqual x y eps = If (x ==. y) true \subsection{Data types} Most of \gls{CLEAN}'s fixed-size basic types are mapped on \gls{MTASK} types (see \cref{tbl:mtask-c-datatypes}). However, it is useful to have access to compound types as well. -All types in \gls{MTASK} must have a fixed-size representation on the stack so sum types are not (yet) supported. -While it is possible to lift any types using the \cleaninline{lit} function, you cannot do anything with the types besides passing them around but they are being produced by some parallel task combinators (see \cref{sssec:combinators_parallel}). +All types in \gls{MTASK} must have a fixed-size representation on the stack, so sum types are not (yet) supported. +While it is possible to lift any types using the \cleaninline{lit} function, you cannot do anything with the types besides passing them around, but they are being produced by some parallel task combinators (see \cref{sssec:combinators_parallel}). To be able to use types as first-class citizens, constructors, and field selectors or deconstructors are required (see \cref{chp:first-class_datatypes}). \Cref{lst:tuple_exprs} shows the scaffolding for supporting tuples in \gls{MTASK}. Besides the constructors and field selectors, there is also a helper function available that transforms a function from a tuple of \gls{MTASK} expressions to an \gls{MTASK} expression of a tuple, a deconstructor. @@ -304,7 +304,7 @@ The \gls{MTASK} language only allows first-order functions and does not allow pa This is restricted by using a multi-parameter type class where the first parameter represents the arguments and the second parameter the view or interpretation. An instance is provided for each function arity instead of providing an instance for all possible arities to enforce that all functions are first order. By using tuples for the arguments, partial function applications is eradicated. -The definition of the type class and the some instances for the pretty printer are as follows: +The definition of the type class and some instances for the pretty printer are as follows: \begin{lstClean}[caption={Functions in \gls{MTASK}.}] class fun a v :: ((a -> v s) -> In (a -> v s) (Main (MTask v u))) @@ -319,7 +319,7 @@ instance fun (Show a, Show b, Show c) Show | type a, ... where ... Deriving how to define and use functions from the type is quite the challenge even though the resulting syntax is made easier using the infix type \cleaninline{In}. \Cref{lst:function_examples} show some examples of functions to demonstrate the syntax for functions. -Splitting out the function definition for each single arity means that that for every function arity and combination of arguments, a separate class constraint must be created. +Splitting out the function definition for each single arity means that for every function arity and combination of arguments, a separate class constraint must be created. Many of the often used functions are already bundled in the \cleaninline{mtask} class constraint collection. The \cleaninline{factorial} functions shows a recursive version of the factorial function. In the \cleaninline{factorialtail} function, a tail-call optimised version of the factorial function, a manually added class constraint can be seen. @@ -357,12 +357,19 @@ swapTuple = % VimTeX: SynIgnore off \section{Tasks and task combinators}\label{sec:top} -This section describes \gls{MTASK}'s task language. -Tasks can be seen as trees that are the result of evaluating expressions. -After evaluating an expression, the resulting task is executed. -\todo[inline]{iets zeggen over values en dat hier ook een handwavy semantiek is.} -The execution semantics of tasks is explained in more detail in \cref{chp:implementation}. +This section describes the task language of \gls{MTASK}. +\Gls{TOP} languages are languages enriched with tasks. +Tasks represent abstract pieces of work and can be combined using combinators. +Creating tasks is done by evaluating expressions and this is called a task tree, a run time representation of a task. +After evaluation, the resulting task tree is \emph{rewritten}, i.e.\ they are continuously given a small slice of execution time after which a task value is yielded. +This task value is observable by other tasks and can be acted upon. +\todo{dui\-de\-lijk ge\-noeg?} + +The implementation in the \gls{MTASK} \gls{RTS} for task execution is shown in \cref{chp:implementation}. +The following sections show the definition of the functions for creating tasks. +They also show the semantics of tasks, their observable value in relation to the work that the task represents. The task language of \gls{MTASK} is divided into three categories, namely + \begin{description} \item [Basic tasks] are the leaves in the task trees. In most \gls{TOP} systems, the basic tasks are called editors, modelling the interactivity with the user. @@ -400,7 +407,9 @@ class delay v :: (v n) -> MTask v n | long v n \end{lstClean} \subsubsection{Peripherals}\label{sssec:peripherals} -For every sensor or actuator, basic tasks are available that allow interaction with the specific peripheral. +In order for the edge devices to interact with their environment, peripherals such as sensors and actuators are employed. +Some peripherals are available on the microcontroller package, others are connected with wires using protocols such as \gls{I2C}. +For every supported sensor or actuator, basic tasks are available that allow interaction with the specific peripheral. The type classes for these tasks are not included in the \cleaninline{mtask} class collection as not all devices nor all language interpretations support all peripherals connected. \Cref{lst:dht} shows the type classes for \glspl{DHT} sensors. @@ -425,12 +434,13 @@ measureTemp = DHT (DHT_SHT (i2c 0x36)) \dht-> {main=temperature dht} \end{lstClean} -\todo[inline]{Uitleggen wat GPIO is} +An example of a built-in peripheral is the \gls{GPIO} system. +This array of digital and analogue pins is controlled through software. \Gls{GPIO} access is divided into three classes: analogue \gls{IO}, digital \gls{IO} and pin modes configuration (see \cref{lst:gpio}). For all pins and pin modes an \gls{ADT} is available that enumerates the pins. The analogue \gls{GPIO} pins of a microcontroller are connected to an \gls{ADC} that translates the measured voltage to an integer. Digital \gls{GPIO} pins of a microcontroller report either a high or a low value. -Both analogue and digital \gls{GPIO} pins can be read or written to but it is advised to set the pin mode accordingly. +Both analogue and digital \gls{GPIO} pins can be read or written to, but it is advised to set the pin mode accordingly. The \cleaninline{pin} type class allows functions to be overloaded in either the analogue or digital pins, e.g.\ analogue pins can be considered as digital pins as well. For digital \gls{GPIO} interaction, the \cleaninline{dio} type class is used. @@ -480,17 +490,17 @@ task2 = declarePin D3 PMOutput \d3->{main=writeD d3 true} \subsection{Task combinators} Task combinators are used to combine multiple tasks to describe workflows. -In contrast to \gls{ITASK}, that uses super combinators to derive the simpler ones, \gls{MTASK} has a set of simpler combinators from which more complicated workflow can be derived. +The \gls{MTASK} language has a set of simpler combinators from which more complicated workflow can be derived. There are three main types of task combinators, namely: \begin{enumerate} - \item Sequential combinators that execute tasks one after the other, possibly using the result of the left hand side. - \item Parallel combinators that execute tasks at the same time combining the result. - \item Miscellaneous combinators that change the semantics of a task---e.g.\ repeat it or delay execution. + \item sequential combinators that execute tasks one after the other, possibly using the result of the left-hand side; + \item parallel combinators that execute tasks at the same time, combining the result; + \item miscellaneous combinators that change the semantics of a task---for example a combinator that repeats the child task. \end{enumerate} \subsubsection{Sequential} Sequential task combination allows the right-hand side expression to \emph{observe} the left-hand side task value. -All sequential task combinators are expressed in the \cleaninline{step} class and can be---and are by default---derived from the Swiss army knife step combinator \cleaninline{>>*.}. +All sequential task combinators are expressed in the \cleaninline{step} class and are by default derived from the Swiss Army knife step combinator \cleaninline{>>*.}. This combinator has a single task on the left-hand side and a list of \emph{task continuations} on the right-hand side. The list of task continuations is checked during evaluation of the left-hand side and if one of the predicates matches, the task continues with the result of this combination. Several shorthand combinators are derived from the step combinator. @@ -538,7 +548,7 @@ class (.||.) infixr 3 v :: (MTask v a) (MTask v a) -> MTask v a The conjunction combinator (\cleaninline{.&&.}) combines the result by putting the values from both sides in a tuple. The stability of the task depends on the stability of both children. If both children are stable, the result is stable, otherwise the result is unstable. -The disjunction combinator (\cleaninline{.\|\|.}) combines the results by picking the leftmost, stablest task. +The disjunction combinator (\cleaninline{.\|\|.}) combines the results by picking the leftmost, most stable task. The semantics are most easily described using the \gls{CLEAN} functions shown in \cref{lst:semantics_con,lst:semantics_dis}. \begin{figure}[ht] @@ -603,9 +613,13 @@ task = \end{lstClean} \subsection{\texorpdfstring{\Glsxtrlongpl{SDS}}{Shared data sources}} -\todo[inline]{Explain \glspl{SDS} shortly.} +Tasks not always communicate using their observable task values. +\Glspl{SDS} are a safe abstraction over any data that fill this gap. +It allows tasks to safely and atomically read, write and update data stores in order to share this data with other tasks in the system. \Glspl{SDS} in \gls{MTASK} are by default references to shared memory but can also be references to \glspl{SDS} in \gls{ITASK} (see \cref{sec:liftsds}). -Similar to peripherals (see \cref{sssec:peripherals}), they are constructed at the top level and are accessed through interaction tasks. +There are no combinators or user-defined \gls{SDS} types in \gls{MTASK}. +Similar to peripherals and functions, \glspl{SDS} are constructed at the top level with the \cleaninline{sds} function. +They are accessed through interaction tasks. The \cleaninline{getSds} task yields the current value of the \gls{SDS} as an unstable value. This behaviour is similar to the \cleaninline{watch} task in \gls{ITASK}. Writing a new value to \pgls{SDS} is done using \cleaninline{setSds}. diff --git a/top/mtask_integration.tex b/top/mtask_integration.tex index a952224..559854a 100644 --- a/top/mtask_integration.tex +++ b/top/mtask_integration.tex @@ -60,11 +60,11 @@ \draw [<->] (CN) -- (T1); \draw [<->] (T1) -- (S1); -% \draw [<->] (T1) -- (S2); -% \draw [<->] (T1) -- (Sn); + \draw [<->] (T1) -- (S2); + \draw [<->] (T1) -- (Sn); \draw [<->] (Tn) -- node [midway,above,fill=white] {\small\texttt{liftmTask}} (D2T1); - \draw [<->] (Sn) -- node [midway,above,fill=white] {\small\texttt{liftsds}} (D2S1); + \draw [<->] (Sn) -- node [midway,above,fill=white] {\small\texttt{lowersds}} (D2S1); \draw [<->] (D1T1) -- (D1S1); \draw [<->] (D2T1) -- (D2S1); diff --git a/tvt/tvt.tex b/tvt/tvt.tex index abc5041..e622e86 100644 --- a/tvt/tvt.tex +++ b/tvt/tvt.tex @@ -322,7 +322,7 @@ readTempTask = \includegraphics[width=\textwidth]{simpleTempSensor} \caption{Deployment diagram.} \end{subfigure} - \caption{\Gls{ITASK} SimpleTempSensor.}% + \caption{SimpleTempSensor written in \gls{ITASK}.}% \label{fig_t4t:itaskTempSimple} \end{figure} @@ -489,7 +489,7 @@ there is no \gls{OS} to start the remote task, the code of the task is too big t The \gls{MTASK} \gls{EDSL} is designed to bridge this gap: \gls{MTASK} tasks can be communicated from the server to the sensor node, to execute within the limitations of a typical microcontroller, while providing programming abstractions that are consistent with \gls{ITASK}. Like \gls{ITASK}, \gls{MTASK} is task oriented, e.g.\ there are primitive tasks that produce intermediate values, a restricted set of task combinators to compose the tasks, and (recursive) functions to construct tasks. -\Gls{MTASK} tasks communicate using task values or \glspl{SDS} that may be local or remote, and may be shared by some \gls{ITASK} tasks. +Tasks in \gls{MTASK} communicate using task values or \glspl{SDS} that may be local or remote, and may be shared by some \gls{ITASK} tasks. Apart from the \gls{EDSL}, the \gls{MTASK} system contains a featherlight domain-specific \emph{operating system} running on the microcontroller. This \gls{OS} task scheduler receives the byte code generated from one or more \gls{MTASK} programs and interleaves the execution of those tasks. The \gls{OS} also manages \gls{SDS} updates and the passing of task results. @@ -666,7 +666,7 @@ The main goal of the \gls{UOG} smart campus project is to provide a testbed for \item scale to no more than 10 sensors per sensor node and investigate further sensor options like measuring sound levels, - \item have access to communication channels like WiFi, Bluetooth and even wired networks. + \item have access to communication channels like \gls{WIFI}, Bluetooth and even wired networks. \item have a centralised database server, @@ -1111,7 +1111,7 @@ Even in low level languages some language features are disabled by default when Here we investigate the restrictions imposed by resource-constrained sensor nodes on \gls{MTASK}, in comparison with \gls{ITASK}. While \gls{ITASK} and \gls{MTASK} are by design superficially similar languages, to execute on resource-constrained sensor nodes \gls{MTASK} tasks are more restricted, and have a different semantics. -\Gls{MTASK} programs do not support user defined higher order functions, the only higher order functions available are the predefined \gls{MTASK} combinators. +Programs in \gls{MTASK} do not support user defined higher order functions, the only higher order functions available are the predefined \gls{MTASK} combinators. Programmers can, however, use any construct of the \gls{CLEAN} host language to construct an \gls{MTASK} program, including higher order functions and arbitrary data types. For example folding an \gls{MTASK} combinator over a list of tasks. The only restriction is that any higher order function must be macro expanded to a first order \gls{MTASK} program before being compiled to byte code. As an example in \cref{lst_t4t:mtasktemp} we use \cleaninline{temperature dht >>~.} \cleaninline{setSds localSds} instead of \cleaninline{temperature dht >>~.} \cleaninline{\\temp -> setSds localSds temp}. @@ -1121,9 +1121,9 @@ In contrast to \gls{ITASK}, \gls{MTASK} programs have no user defined or recursi Due to the language being shallowly embedded, pattern matching and field selection on user defined types is not readily available and thus needs to be built into the language by hand. Alleviating this limitation remains future work. -\Gls{MTASK} programs mainly use strict rather than lazy evaluation to minimise the requirement for a variable size heap. This has no significant impact for the \gls{MTASK} programs we have developed here, nor in other \gls{IOT} applications we have engineered. +Programs in \gls{MTASK} mainly use strict rather than lazy evaluation to minimise the requirement for a variable size heap. This has no significant impact for the \gls{MTASK} programs we have developed here, nor in other \gls{IOT} applications we have engineered. -\Gls{MTASK} abstractions are less easily extended than \gls{ITASK}. For example \gls{ITASK} can be extended with a new combinator that composes a specific set of tasks for some application. +Abstractions in \gls{MTASK} are less easily extended than \gls{ITASK}. For example \gls{ITASK} can be extended with a new combinator that composes a specific set of tasks for some application. Without higher order functions the equivalent combinator can often not be expressed in \gls{MTASK}, and adding it to \gls{MTASK} requires extending the \gls{DSL} rather than writing a new definition in it. On the other hand, it is possible to outsource this logic to the \gls{ITASK} program as \gls{MTASK} and \gls{ITASK} tasks are so tightly integrated.