From 6548a5ec9ce8e0df67fc4019625ab5238eb1bf71 Mon Sep 17 00:00:00 2001 From: Mart Lubbers Date: Thu, 29 Jun 2017 20:42:22 +0200 Subject: [PATCH] restructure files --- arch.communication.tex | 165 ++++++ arch.devices | 205 +++++++ arch.example.tex | 1 + arch.itasks.tex | 201 +++++++ arch.lift.tex | 14 + arch.tex | 50 ++ conclusion.conclusion.tex | 26 + conclusion.discussion.tex | 113 ++++ conclusion.tex | 141 +---- edsl.class.tex | 57 ++ edsl.deep.tex | 44 ++ edsl.shallow.tex | 37 ++ edsl.tex | 16 + intro.doc.tex | 33 ++ intro.intro.tex | 51 ++ intro.problem.tex | 10 + intro.related.tex | 45 ++ intro.tex | 11 + introduction.tex | 146 ----- methods.dsl.tex | 151 ----- methods.mtask.tex | 238 -------- methods.top.tex | 209 ------- mtask.control.tex | 34 ++ mtask.examples.tex | 31 + mtask.expr.tex | 22 + mtask.io.tex | 47 ++ mtask.semantics | 57 ++ mtask.tex | 52 ++ results.mtask.tex => mtaskext.bytecode.tex | 104 +--- mtaskext.examples.tex | 15 + mtaskext.sdssem.tex | 23 + mtaskext.tasksem.tex | 28 + mtaskext.tex | 20 + results.arch.tex | 631 --------------------- thesis.tex | 12 +- top.combinators.tex | 54 ++ top.itasks.tex | 68 +++ top.sds.tex | 83 +++ top.tex | 8 + 39 files changed, 1640 insertions(+), 1613 deletions(-) create mode 100644 arch.communication.tex create mode 100644 arch.devices create mode 100644 arch.example.tex create mode 100644 arch.itasks.tex create mode 100644 arch.lift.tex create mode 100644 arch.tex create mode 100644 conclusion.conclusion.tex create mode 100644 conclusion.discussion.tex create mode 100644 edsl.class.tex create mode 100644 edsl.deep.tex create mode 100644 edsl.shallow.tex create mode 100644 edsl.tex create mode 100644 intro.doc.tex create mode 100644 intro.intro.tex create mode 100644 intro.problem.tex create mode 100644 intro.related.tex create mode 100644 intro.tex delete mode 100644 introduction.tex delete mode 100644 methods.dsl.tex delete mode 100644 methods.mtask.tex delete mode 100644 methods.top.tex create mode 100644 mtask.control.tex create mode 100644 mtask.examples.tex create mode 100644 mtask.expr.tex create mode 100644 mtask.io.tex create mode 100644 mtask.semantics create mode 100644 mtask.tex rename results.mtask.tex => mtaskext.bytecode.tex (69%) create mode 100644 mtaskext.examples.tex create mode 100644 mtaskext.sdssem.tex create mode 100644 mtaskext.tasksem.tex create mode 100644 mtaskext.tex delete mode 100644 results.arch.tex create mode 100644 top.combinators.tex create mode 100644 top.itasks.tex create mode 100644 top.sds.tex create mode 100644 top.tex diff --git a/arch.communication.tex b/arch.communication.tex new file mode 100644 index 0000000..61a7e2e --- /dev/null +++ b/arch.communication.tex @@ -0,0 +1,165 @@ +The communication from the server to the client and vice versa is just a +character stream containing encoded \gls{mTask} messages. The \CI{synFun} +belonging to the device is responsible for sending the content in the left +channel and putting received messages in the right channel. Moreover, the +boolean value should be set to \CI{True} when the connection is terminated. The +specific encoding of the messages is visible in +Appendix~\ref{app:communication-protocol}. The type holding the messages is +shown in Listing~\ref{lst:avmsg}. Detailed explanation about the message types +and according actions will be given in the following subsections. + +\begin{lstlisting}[label={lst:avmsg},caption={Available messages}] +:: MTaskId :== Int +:: MSDSId :== Int +:: MTaskFreeBytes :== Int +:: MTaskMSGRecv + = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId + | MTSDSAck MSDSId | MTSDSDelAck MSDSId + | MTPub MSDSId BCValue | MTMessage String + | MTDevSpec MTaskDeviceSpec | MTEmpty + +:: MTaskMSGSend + = MTTask MTaskInterval String | MTTaskDel MTaskId + | MTShutdown | MTSds MSDSId BCValue + | MTUpd MSDSId BCValue | MTSpec + +:: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int +\end{lstlisting} + +\subsection{Add a device} +A device can be added by filling in the \CI{MTaskDevice} record as much as +possible and running the \CI{connectDevice} function. This function grabs the +channels, starts the synchronization \gls{Task} (\CI{synFun}), makes sure the +errors are handled when needed and runs a processing function in parallel to +react on the incoming messages. Moreover, it sends a specification request to +the device in question to determine the details of the device and updates the +record to contain the top-level \gls{Task}-id. All device functionality +heavily depends on the specific \CI{deviceShare} function that generates an +\gls{SDS} for a specific device. This allows giving an old device record to the +function and still update the latest instance. Listing~\ref{lst:connectDevice} +shows the connection function. + +\begin{lstlisting}[label={lst:connectDevice},% + caption={Connect a device}] +connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels +connectDevice procFun device = set ([], [], False) ch + >>| appendTopLevelTask 'DM'.newMap True + ( procFun device ch -||- catchAll (getSynFun device.deviceData ch) errHdl) + >>= \tid->upd (\d->{d&deviceTask=Just tid,deviceError=Nothing}) (deviceShare device) + >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch +where + errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! () + ch = channels device +\end{lstlisting} + +Figure~\ref{fig:handshake} shows the connection diagram. The client responds to +the server with their device specification. This is detected by the processing +function and the record is updated accordingly. + +\begin{figure}[H] + \centering + \begin{sequencediagram} + \newthread{s}{Server} + \newinst[4]{c}{Client} + \begin{call}{s}{MTSpec}{c}{MTDevSpec} + \end{call} + \end{sequencediagram} + \caption{Connect a device}\label{fig:handshake} +\end{figure} + +\subsection{\glspl{Task} \& \glspl{SDS}} +When a \gls{Task} is sent to the device it is added to the device record +without an identifier. The actual identifier is added to the record when the +acknowledgement of the \gls{Task} by the device is received. The connection +diagram is shown in Figure~\ref{fig:tasksend}. + +\begin{figure}[H] + \centering + \begin{sequencediagram} + \newthread{s}{Server} + \newinst[4]{c}{Client} + \begin{call}{s}{MTSDS}{c}{MTSDSAck} + \end{call} + \begin{call}{s}{MTTask}{c}{MTTaskAck} + \end{call} + \end{sequencediagram} + \caption{Sending a \gls{Task} to a device}\label{fig:tasksend} +\end{figure} + +The function for sending a \gls{Task} to the device is shown in +Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The +details of the compilation process are given in Section~\ref{sec:compiler}. +The new \glspl{SDS} that were generated during compilation are merged with the +existing device's \glspl{SDS}. Furthermore the messages are placed in the +channel \gls{SDS} of the device. This will result in sending the actual \gls{SDS} +specification and \gls{Task} specifications to the device. A \gls{Task} record +is created with the identifier $-1$ to denote a \gls{Task} not yet +acknowledged. Finally the device itself is updated with the new state and with +the new \gls{Task}. After waiting for the acknowledgement the device is +updated again and the \gls{Task} returns. + +\begin{lstlisting}[label={lst:sendtask},% + caption={Sending a \gls{Task} to a device}] +makeTask :: String Int -> Task MTaskTask +makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt} + +makeShare :: String Int BCValue -> MTaskShare +makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value} + +sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task MTaskTask +sendTaskToDevice wta mTask (device, timeout) +# (msgs, newState=:{sdss}) = toMessages timeout mTask device.deviceState +# shares = [makeShare wta "" sdsi sdsval\\{sdsi,sdsval}<-sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`] += updateShares device ((++) shares) + >>| sendMessages msgs device + >>| makeTask wta -1 + >>= \t->upd (addTaskUpState newState t) (deviceShare device) + >>| wait "Waiting for task to be acked" (taskAcked t) (deviceShare device) + >>| treturn t +where + addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice + addTaskUpState st task device = {MTaskDevice | device & deviceState=st, deviceTasks=[task:device.deviceTasks]} + taskAcked t d = maybe True (\t->t.ident <> -1) $ find (eq t) d.deviceTasks + eq t1 t2 = t1.dateAdded == t2.dateAdded && t1.MTaskTask.name == t2.MTaskTask.name +\end{lstlisting} + +\subsection{Miscellaneous Messages} +One special type of message is available which is sent to the device only when +it needs to reboot. When the server wants to stop the bond with the device it +sends the \CI{MTShutdown} message. The device will then clear its memory, thus +losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself. +Shortly after the shutdown message a new server can connect to the device +because the device is back in listening mode. + +\subsection{Integration} +When the system starts up, the devices from the previous execution still +residing in the \gls{SDS} must be cleaned up. It might be the case that they +contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in +this run. A user or programmer can later choose to reconnect to some devices. + +\begin{lstlisting}[caption={Starting up the devices},% + label={lst:startupdevs}] +startupDevices :: Task [MTaskDevice] +startupDevices = upd (map reset) deviceStoreNP + where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing} +\end{lstlisting} + +The system's management is done through the interface of a single \gls{Task} +called \CI{mTaskManager}. To manage the system, a couple of different +functionalities are necessary and are launched. An image of the management +interface is shown in Figure~\ref{lst:manage}. The left sidebar of the +interface shows the list of example \glspl{Task} that are present in the +system. When clicking a \gls{Task}, a dialog opens in which a device can be +selected to send the \gls{Task} to. The dialog might contain user specified +variables. All example \gls{mTask}-\glspl{Task} are of the type \CI{Task (Main +(ByteCode () Stmt))} and can thus ask for user input first if needed for +parameterized \gls{mTask}-\glspl{Task}. The bottom panel shows the device +information. In this panel, the devices can be created and modified. Moreover, +this panel allows the user to reconnect with a device after a restart of the +server application. + +\begin{figure}[H] + \centering + \includegraphics[width=\linewidth]{manage} + \caption{The device management interface}\label{lst:manage} +\end{figure} diff --git a/arch.devices b/arch.devices new file mode 100644 index 0000000..cca4449 --- /dev/null +++ b/arch.devices @@ -0,0 +1,205 @@ +A device is suitable for the system if it can run the engine. +The engine is compiled from one codebase and devices implement (part of) the +device specific interface. The shared codebase only uses standard \gls{C} and +no special libraries or tricks are used. Therefore, the code is compilable for +almost any device or system. Note that it is not needed to implement a full +interface. The full interface --- excluding the device specific settings --- is +listed in Appendix~\ref{app:device-interface}. The interface works in a +similar fashion as the \gls{EDSL}. Devices do not have to implement all +functionality, this is analogous to the fact that views do not have to +implement all type classes in the \gls{EDSL}. When the device connects with +the server for the first time, the specifications of what is implemented is +communicated. + +At the time of writing the following device families are supported and can run +the device software. +\begin{itemize} + \item \texttt{POSIX} compatible systems connected via the \gls{TCP}. + + This includes systems running \emph{Linux} and \emph{MacOS}. + \item The \texttt{STM32} microcontrollers family supported by + \texttt{ChibiOS} connected via serial communication. + + This is tested in particular on the \texttt{STM32f7x} series \gls{ARM} + development board. + \item Microcontrollers which are programmable in the \gls{Arduino} \gls{IDE} + connected via serial communication or via \gls{TCP} over WiFi or + Ethernet. + + This does not only include \gls{Arduino} compatible boards but also + other boards capable of running \gls{Arduino} code. A port of the + client has been made for the \texttt{ESP8266} powered \emph{NodeMCU} + that is connected via \gls{TCP} over WiFi. A port also has been made + for the regular \gls{Arduino} \emph{UNO} board which only boasts a + meager \emph{2K} \emph{RAM}. The stack size and storage available for + devices boasting this little \emph{RAM} has to be smaller than default + but are still suitable to hold a hand full of \glspl{Task}. +\end{itemize} + +\subsection{Client} +\subsubsection{Engine} +The client is in a constant loop listening for input and waiting to execute +\glspl{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}. +The \CI{input\_available} function waits for input, but has a timeout set which +can be interrupted. The timeout of the function determines the amount of loops +per time interval and is a parameter that can be set during compilation for a +device. + +\begin{algorithm} + \KwData{ + \textbf{list} $tasks$, + \textbf{time} $tm$ + } + + \Begin{ + \While{true}{ + \If{input\_available$()$}{ + receive\_data()\; + } + + $tm\leftarrow \text{now}()$\; + \ForEach{$t\leftarrow tasks$}{ + \uIf{is\_interrupt$(t)$ \textbf{and} had\_interrupt$(t)$}{ + run\_task$(t)$\; + } + \ElseIf{$tm-t.\text{lastrun} > t.\text{interval}$}{ + run\_task$(t)$\; + \uIf{$t.\text{interval}==0$}{ + delete\_task$(t)$\; + }\Else{ + $t.\text{lastrun}\leftarrow t$\; + } + } + } + } + } + \caption{Engine pseudocode}\label{alg:client} +\end{algorithm} + +\subsubsection{Storage} +\glspl{Task} and \glspl{SDS} are stored on the client in memory. Some devices +have very little memory and therefore memory space is very expensive and needs +to be used optimally. Almost all microcontrollers support heaps nowadays, +however, the functions for allocating and freeing the memory on the heap are +not very space optimal and often leave holes in the heap if allocations are not +freed in reverse order. To overcome this problem the client will allocate a big +memory segment in the global data block. This block of memory resides under the +stack and its size can be set in the interface implementation. This block of +memory will be managed in a similar way as the entire memory space of the +device is managed. \Glspl{Task} will grow from the bottom up and \glspl{SDS} +will grow from the top down. + +When a \gls{Task} is received, the program will traverse the memory space from +the bottom up, jumping over all \glspl{Task}. A \gls{Task} is stored as the +structure followed directly by its bytecode. Therefore it only takes two jumps +to determine the size of the \gls{Task}. When the program arrived at the last +\gls{Task}, this place is returned and the newly received \gls{Task} can be +copied to there. This method is analogously applied for \glspl{SDS}, however, +the \glspl{SDS} grow from the bottom down. + +When a \gls{Task} or \gls{SDS} is removed, all remaining objects are compressed +again. This means that if the first received \gls{Task} is removed, all +\glspl{Task} received later will have to move back. Obviously, this is quite +time intensive but it can not be permitted to leave holes in the memory since +the memory space is so limited. This techniques allows for even the smallest +tested microcontrollers with only $2K$ \emph{RAM} to hold several \glspl{Task} +and \glspl{SDS}. If this technique would not be used the memory space will +decrease over time and the client can then not run for very long since holes +are evidently created at some point. + +The structure instances and helper functions for traversing them in memory for +\glspl{Task} and \glspl{SDS} are shown in Listing~\ref{lst:structs}. + +\begin{lstlisting}[language=C,label={lst:structs},% + caption={The data type storing the \glspl{Task}},float] +struct task { + uint16_t tasklength; + uint16_t interval; + unsigned long lastrun; + uint8_t taskid; + uint8_t *bc; +}; + +struct task *task_head(void); +struct task *task_next(struct task *t); + +struct sds { + int id; + int value; + char type; +}; + +struct sds *sds_head(void); +struct sds *sds_next(struct sds *s); +\end{lstlisting} + +\subsubsection{Interpretation} +The execution of a \gls{Task} is started by running the \CI{run\_task} function +and always starts with setting the program counter and stack +pointer to zero and the bottom respectively. When finished, the +interpreter executes one step at the time while the program counter is smaller +than the program length. This code is listed in Listing~\ref{lst:interpr}. One +execution step is basically a big switch statement going over all possible +bytecode instructions. Of some instructions, the implementations are shown in +the listing. The \CI{BCPush} instruction is a little more complicated in the +real code because some decoding will take place as not all \CI{BCValue}s are of +the same length and are encoded. + +\begin{lstlisting}[language=C,label={lst:interpr},% + caption={Rough code outline for interpretation}] +#define f16(p) program[pc]*265+program[pc+1] + +void run_task(struct task *t){ + uint8_t *program = t->bc; + int plen = t->tasklength; + int pc = 0; + int sp = 0; + while(pc < plen){ + switch(program[pc++]){ + case BCNOP: + break; + case BCPUSH: + stack[sp++] = pc++ //Simplified + break; + case BCPOP: + sp--; + break; + case BCSDSSTORE: + sds_store(f16(pc), stack[--sp]); + pc+=2; + break; + // ... + case BCADD: trace("add"); + stack[sp-2] = stack[sp-2] + stack[sp-1]; + sp -= 1; + break; + // ... + case BCJMPT: trace("jmpt to %d", program[pc]); + pc = stack[--sp] ? program[pc]-1 : pc+1; + break; +} +\end{lstlisting} + +\subsection{Specification} +The server stores a description for every device available in a record type. +From the macro settings in the interface file, a profile is created that +describes the specification of the device. When the connection between the +server and a client is established, the server will send a request for +specification. The client serializes its specification and send it to the +server so that the server knows what the client is capable of. The exact +specification is shown in Listing~\ref{lst:devicespec} and stores the +peripheral availability, the memory available for storing \glspl{Task} and +\glspl{SDS} and the size of the stack. + +\begin{lstlisting}[label={lst:devicespec}, + caption={Device specification for \gls{mTask}-\glspl{Task}}] +:: MTaskDeviceSpec = + { haveLed :: Bool + , haveLCD :: Bool + , have... + , bytesMemory :: Int + , stackSize :: Int + , aPins :: Int + , dPins :: Int + } +\end{lstlisting} diff --git a/arch.example.tex b/arch.example.tex new file mode 100644 index 0000000..4b4f48e --- /dev/null +++ b/arch.example.tex @@ -0,0 +1 @@ +Here comes a description of the demo program. diff --git a/arch.itasks.tex b/arch.itasks.tex new file mode 100644 index 0000000..f7fc915 --- /dev/null +++ b/arch.itasks.tex @@ -0,0 +1,201 @@ +The server part of the system is written in \gls{iTasks}. Functions for +managing devices, \glspl{Task} and \glspl{SDS} have been created to support the +functionality. An interactive web application has been created that provides an +interactive management console for the \gls{mTask} system. This interface +provides functionality to list \glspl{SDS}, add and remove \glspl{Task}, +administrate devices and view the state of the system. + +\subsection{Device Storage} +Everything that a device encompasses is stored in the \CI{MTaskDevice} record +type. This includes management for the \glspl{SDS} and \glspl{Task} stored on +the device. The \CI{MTaskDevice} definition is shown in +Listing~\ref{lst:mtaskdevice} accompanied with the necessary classes and sub +types. + +\begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}] +:: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool) +:: BCState = ... // Compiler state, explained in later sections +:: MTaskResource + = TCPDevice TCPSettings + | SerialDevice TTYSettings + | ... +:: MTaskDevice = + { deviceTask :: Maybe TaskId + , deviceError :: Maybe String + , deviceChannels :: String + , deviceName :: String + , deviceState :: BCState + , deviceTasks :: [MTaskTask] + , deviceResource :: MTaskResource + , deviceSpec :: Maybe MTaskDeviceSpec + , deviceShares :: [MTaskShare] + } + +channels :: MTaskDevice -> Shared Channels + +class MTaskDuplex a where + synFun :: a (Shared Channels) -> Task () +\end{lstlisting} + +The \CI{deviceResource} component of the record must implement the +\CI{MTaskDuplex} interface that provides a function that launches a \gls{Task} +used for synchronizing the channels. The \CI{deviceTask} stores the +\gls{Task}-id for this \gls{Task} when active so that it can be checked upon. +This top-level task has the duty to report exceptions and errors as they are +thrown by setting the \CI{deviceError} field. All communication goes via these +channels. To send a message to the device, the system just puts it +in the channels. Messages sent from the client to the server are also placed +in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a +simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In +case of a device connected by a serial connection, it uses the newly developed +serial port library of \gls{Clean}\footnote{\url{% +https://gitlab.science.ru.nl/mlubbers/CleanSerial}}. + +Besides all the communication information, the record also keeps track of the +\glspl{Task} currently on the device, the compiler state (see +Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores +the specification of the device that is received when connecting. All of this +is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message +format are explained in the following section. + +\subsection{Shares} +The system keeps track of the \glspl{SDS} stored on +the client in a big \gls{SDS} containing a list of devices. Client-\glspl{SDS} +can be stored on one device at the same time. This means that if an \gls{SDS} +updates, everyone watching it will be notified. This would result in a lot +of notifications that are not meant for the watcher. Moreover, when a client +updates the \gls{SDS} this is processed by the connection handler and results +in an update of the real \gls{SDS}. Finally, the \gls{SDS} of a client must be +synchronized with the actual device. Thus, when an \gls{iTasks}-\gls{Task} +writes the client-\gls{SDS}, it must be propagated to the real device. There +are several ways of tackling this problem each with their own level of +granularity. + +First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can +be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and +synchronizing it with the device when an update occurred. This approach is very +expensive as it requires a lot of listening \glspl{Task}. + +Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every +devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite} +functions, a single \gls{SDS} per device can be created as a lens that allows +mapping on a single client-\gls{SDS}. However, this approach still requires +\glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written, +everyone is notified, even if the \gls{Task} only uses the value of a single +different \gls{SDS}. + +Finally, the current approach --- a single \gls{SDS} for the entire system +--- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a +\CI{mapReadWrite} can be used but it suffers from the same problem as mentioned +before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate +the client-\gls{SDS} updates to the actual device. Both of these problems can +be solved by using a tailor made \gls{SDS} that heavily depends on parametric +lenses. + +\subsection{Parametric Lenses} +The type for the parametric lens of the big \gls{SDS} is \CI{Maybe +(MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list +of devices, from now on global. Moreover, the \gls{SDS} can focus on a single +device, from now on local. A local \gls{SDS} can also specifically focus on a +single \gls{SDS} on a single device, from now on called local-share. The +implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}. +The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory. +Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing +the \gls{SDS} is a little bit more involved. If the write operation originated +from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must +also be relayed to the actual device. If the write originated from an \gls{SDS} +focussed the devices or on one device only, nothing needs to be done. The +notification predicate determines whether a watcher gets a notification update. + +\begin{lstlisting}[label={lst:actualdev},% + caption={Device \gls{SDS}}] +deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice] +deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite} +where + realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld) + realRead p iw = read realDeviceStore iw + + realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld) + realWrite mi w iw + # (merr, iw) = write w realDeviceStore iw + | isError merr || isNothing mi = (merr $> notifyPred mi, iw) + # (Just (dev, ident)) = mi + | ident == -1 = (merr $> notifyPred mi, iw) + = case find ((==)dev) w of + Nothing = (Error $ exception "Device lost", iw) + Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of + Nothing = (Error $ exception "Share lost", iw) + Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of + (Error e, iw) = (Error e, iw) + (Ok _, iw) = (Ok $ notifyPred mi, iw) + + notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool + notifyPred Nothing Nothing = True // Global watcher looking at a global event + notifyPred Nothing (Just _) = False // Global watcher looking at a local event + notifyPred (Just _) Nothing = False // Local watcher looking at a global event + // Local device watcher looking at a local event + notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2 + // Local share watcher looking at a local share event + notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2 + + realDeviceStore :: Shared [MTaskDevice] + realDeviceStore = sharedStore "mTaskDevices" [] +\end{lstlisting} + +\subsubsection{Global \glspl{SDS}} +Accessing the global \gls{SDS} is just a matter of focussing the +\CI{deviceStore} with the \CI{Nothing} parameter as follows: + +\begin{lstlisting}[caption={Global \gls{SDS}}] +deviceStoreNP :: Shared [MTaskDevice] +deviceStoreNP = sdsFocus Nothing deviceStore +\end{lstlisting} + +\subsubsection{Local \glspl{SDS}} +Accessing a single device can be done using the \CI{mapReadWrite} function. +Since device comparison is shallow, the device that is given is allowed to be +an old version. The identification of devices is solely done on the name of the +channels and is unique throughout the system. The implementation is as follows: + +\begin{lstlisting}[caption={Local \gls{SDS}}] +deviceShare :: MTaskDevice -> Shared MTaskDevice +deviceShare d = mapReadWriteError + ( \ds->case find ((==)d) of + Nothing = exception "Device lost" + Just d = Ok d) + , \w ds->case splitWith ((==)d) ds of + ([], _) = Error $ exception "Device lost" + ([_:_], ds) = Ok $ Just [w:ds]) + $ sdsFocus (Just (d, -1)) deviceStore +\end{lstlisting} + +\subsubsection{Local-\gls{SDS} specific \glspl{SDS}} +A single \gls{SDS} on a single device can be accessed using the \CI{shareShare} +function. This function focusses the real big \gls{SDS} on a single \gls{SDS} +and uses the \CI{mapReadWrite} functions to serve the correct part of the +information. + +\begin{lstlisting}[caption={Local \gls{SDS}}] +shareShare :: MTaskDevice MTaskShare -> Shared BCValue +shareShare dev share = sdsFocus () + $ mapReadWriteError (read, write) + $ sdsFocus (Just (dev, share.identifier)) + $ deviceStore +where + read :: [MTaskDevice] -> MaybeError TaskException BCValue + read devs = case find ((==)dev) devs of + Nothing = exception "Device lost" + Just d = case find ((==)share) d.deviceShares of + Nothing = exception "Share lost" + Just s = Ok s.MTaskShare.value + + write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice]) + write val devs = case partition ((==)dev) devs of + ([], _) = Error $ exception "Device doesn't exist anymore" + ([_,_:_], _) = Error $ exception "Multiple matching devices" + ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of + ([], _) = Error $ exception "Share doesn't exist anymore" + ([_,_:_], _) = Error $ exception "Multiple matching shares" + ([s], shares) = Ok $ Just [{MTaskDevice | d & + deviceShares=[{MTaskShare | s & value=val}:shares]}:devs] +\end{lstlisting} diff --git a/arch.lift.tex b/arch.lift.tex new file mode 100644 index 0000000..1e96bab --- /dev/null +++ b/arch.lift.tex @@ -0,0 +1,14 @@ +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 +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}] +liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task () +liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c + >>= \t->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev) + >>| viewInformation "Done!" [] () +where + taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks +\end{lstlisting} diff --git a/arch.tex b/arch.tex new file mode 100644 index 0000000..3e1ea9f --- /dev/null +++ b/arch.tex @@ -0,0 +1,50 @@ +The goal of the system as a whole is to offer a framework of functions with +which an \gls{iTasks}-system can add, change and remove devices at runtime. +Moreover, the \gls{iTasks}-system can send \gls{mTask}-\glspl{Task} --- +compiled at runtime to bytecode by the \gls{mTask}-view --- to the device. The +device runs an interpreter which can execute the \gls{Task}'s bytecode. Device +profiles should be persistent during reboots of the \gls{iTasks}-system. The +methods of interacting with \gls{mTask}-\gls{Task} should be analogous to +interacting with \gls{iTasks}-\glspl{Task}. This means that programmers can +access the \glspl{SDS} made for a device in the same way as regular \glspl{SDS} +and they can execute \gls{mTask}-\glspl{Task} as if they where normal +\gls{iTasks}-\glspl{Task}. + +The following terms will be used throughout the following chapter: +\begin{itemize} + \item Device, Client + + These terms denotes the actual device connected to the system. This can + be a real device such as a microcontroller but it can also just be a + program on the same machine as the server functioning as a client. + \item Server, \gls{iTasks}-System + + This is the actual executable serving the \gls{iTasks} application. The + system contains \glspl{Task} taking care of the communication with the + clients. + \item System + + The system describes the complete ecosystem, containing both the server + and the clients including the communication between them. + \item Engine + + The runtime system of the client is called the engine. This program + handles communicating with the server and runs the interpreter for the + \glspl{Task} on the client. +\end{itemize} + +\section{Devices} +\input{arch.devices} + +\section{iTasks} +\input{arch.itasks} + +\section{Communication} +\input{arch.communication} + +\section[Lifting mTasks to iTasks-Tasks]% + {Lifting \gls{mTask}-\glspl{Task} to \gls{iTasks}-\glspl{Task}} +\input{arch.lift} + +\section{Example} +\input{arch.example} diff --git a/conclusion.conclusion.tex b/conclusion.conclusion.tex new file mode 100644 index 0000000..4fde493 --- /dev/null +++ b/conclusion.conclusion.tex @@ -0,0 +1,26 @@ +This thesis introduces a novel system for adding \gls{IoT} functionality to +the \gls{TOP} implementation \gls{iTasks}. A new view for the existing +\gls{mTask}-\gls{EDSL} has been created which compiles the program +into bytecode that can be interpreted by a client. Clients have +been written for several microcontrollers and consumer architectures which can +be connected through various means of communication such as serial port, +wifi and wired network communication. The bytecode on the devices is +interpreted using a stack machine and provides the programmer with interfaces +to the peripherals. The semantics for \gls{mTask} try to resemble the +\gls{iTasks} semantics as close as possible. + +The host language has a proven efficient compiler and code generator. The +compilation is linear in the amount of instructions generated and is therefore +also scalable. Moreover, compiling \glspl{Task} is fast because it is nothing +more than running some functions native to the host language and there is no +intermediate \gls{AST}. + +The dynamic nature of the client allows the microcontroller to be programmed +once and used many times. The program memory of microcontrollers often +guarantees around $10.000$ write or upload cycles and therefore existing +techniques such as generating \gls{C} code are not suitable for dynamic +\gls{Task} environments. The dynamic nature also allows the programmer to +design fail-over mechanisms. When a device is assigned a \gls{Task} but another +device suddenly becomes unusable, the \gls{iTasks} system can reassign a new +\gls{mTask}-\gls{Task} to another device that is also suitable for running the +\gls{Task} without needing to recompile the code. diff --git a/conclusion.discussion.tex b/conclusion.discussion.tex new file mode 100644 index 0000000..3b1657f --- /dev/null +++ b/conclusion.discussion.tex @@ -0,0 +1,113 @@ +The system is still a crude prototype and a proof of concept. Improvements and +extension for the system are amply available in several fields of study. + +\subsection{Simulation} +An additional simulation view to the \gls{mTask}-\gls{EDSL} could be added that +works in the same way as the existing \gls{C}-backed simulation. It simulates +the bytecode interpretation. Moreover, it would also be possible to let the +simulator function as a real device, thus handling all communication through +the existing \gls{SDS}-based systems. At the moment the \emph{POSIX}-client is +the reference client and contains debugging code. Adding a simulation view to +the system allows for easy interactive debugging. However, it might not be +easy to devise a simulation tool that accurately simulates the \gls{mTask} +system on some levels. The semantics can be simulated but timing and peripheral +input/output are more difficult to simulate properly. + +\subsection{Optimization} +\paragraph{Multitasking on the client:} +True multitasking could be added to the client software. This allows +\gls{mTask}-\glspl{Task} to run truly parallel. All \gls{mTask}-\glspl{Task} +get slices of execution time and will each have their own interpreter state +instead of a single system-wide state which is reset after am \gls{mTask} +finishes. This does require separate stacks for each \gls{Task} and therefore +increases the system requirements of the client software. However, it could be +implemented as a compile-time option and exchanged during the handshake so that +the server knows the multithreading capabilities of the client. Multithreading +allows \glspl{Task} to be truly interruptible by other \glspl{Task}. +Furthermore, this allows for more fine-grained timing control of \glspl{Task}. + +\paragraph{Optimizing the interpreter:} +Due to time constraints and focus, hardly any work has been done in the +interpreter. The current interpreter is a no nonsense stack machine. A lot of +improvements can be done in this part. For example, precomputed \emph{gotos} +can improve jumping to the correct part of the code corresponding to the +correct instruction. Moreover, the stack currently consists of 16-bit values. +All operations work on 16-bit values and this simplifies the interpreter +implementation. A memory improvement can be made by converting the stack to +8-bit values. This does pose some problems since an equality instruction must +work on single-byte booleans \emph{and} two-byte integers. Adding specialized +instructions per word size could overcome this problem. + +\subsection{Resources} +\paragraph{Resource analysis: } +Resource analysis during compilation can be useful to determine if an +\gls{mTask}-\gls{Task} is suitable for a specific device. If the device does +not contain the correct peripherals --- such as an \gls{LCD} --- then the +\gls{mTask}-\gls{Task} should be rejected and feedback to the user must be +given. It might even be possible to do this statically on the type level. The +current system does not have any of this built-in. Sending a \gls{Task} that +uses the \gls{LCD} to a device not containing one will result in the device +just skipping the \gls{LCD} related instructions. + +\paragraph{Extended resource analysis: } +The previous idea could be extended to the analysis of stack size and possibly +communication bandwidth. With this functionality ever more reliable fail-over +systems can be designed. When the system knows precise bounds it can allocate +more \glspl{Task} on a device whilst staying within safe memory bounds. The +resource allocation can be done at runtime within the backend itself or a +general backend can be devised that can calculate the resources needed for a +given \gls{mTask}. A specific \gls{mTask} cannot have multiple views at the +same time due to the restrictions of class based shallow embedding. It might +even be possible to encode the resource allocation in the type system itself +using forms of dependant types. + +\subsection{Functionality} +\paragraph{Add more combinators: } +More \gls{Task}-combinators --- already existing in the \gls{iTasks}-system --- +could be added to the \gls{mTask}-system to allow for more fine-grained control +flow between \gls{mTask}-\glspl{Task}. In this way the new system follows the +\gls{TOP} paradigm even more and makes programming \gls{mTask}-\glspl{Task} for +\gls{TOP}-programmers more seamless. Some of the combinators require previously +mentioned extension such as the parallel combinator. Others might be achieved +using simple syntactic transformations. + +\paragraph{Launch \glspl{Task} from a \gls{Task}:} +Currently the \gls{C}-view allows \glspl{Task} to launch other \glspl{Task}. In +the current system this type of logic has to take place on the server side. +Adding this functionality to the bytecode-view allows greater flexibility, +easier programming and less communication resources. Adding these semantics +requires modifications to the client software and extensions to the +communication protocol since relations between \glspl{Task} also need to be +encoded and communicated. + +The \gls{SDS} functionality in the current system is bare. There is no easy way +of reusing an \gls{SDS} for another \gls{Task} on the same device or on another +device. Such functionality can be implemented in a crude way by tying the +\glspl{SDS} together in the \gls{iTasks} environment. However, this will result +in a slow updating system. Functionality for reusing shares from a device +should be added. This requires rethinking the storage because some typedness is +lost when the \gls{SDS} is stored after compilation. A possibility would be to +use runtime typing with \CI{Dynamic}s or the encoding technique currently used +for \CI{BCValue}s. Using \glspl{SDS} for multiple \glspl{Task} within one +device is solved when the previous point is implemented. + +\subsection{Robustness} +\paragraph{Reconnect with lost devices:} +The robustness of the system can be greatly improved. Devices that lose +connection are not well supported in the current system. The device will stop +functioning and has to be emptied for a reconnect. \Glspl{Task} residing on a +device that disconnected should be kept on the server to allow a swift +reconnect and restoration of the \glspl{Task}. This holds the same for the +client software. The client drops all existing \glspl{Task} on a shutdown +request. An extra specialization of the shutdown could be added that drops the +connection but keeps the \glspl{Task} in memory. During the downtime the +\glspl{Task} can still be executed but publications need to be delayed. If the +same server connects to the client the delayed publications can be sent +anyways. + +\paragraph{Reverse \gls{Task} sending:} +Furthermore, devices could send their current \glspl{Task} back to the +server to synchronize it. This allows interchanging servers without +interrupting the client. Allowing the client to send \glspl{Task} to the server +is something to handle with care because it can easily cause high bandwidth +usage. diff --git a/conclusion.tex b/conclusion.tex index 0cc405e..3f0b3e5 100644 --- a/conclusion.tex +++ b/conclusion.tex @@ -1,142 +1,5 @@ \section{Discussion \& Future Research} -The system is still a crude prototype and a proof of concept. Improvements and -extension for the system are amply available in several fields of study. - -\subsection{Simulation} -An additional simulation view to the \gls{mTask}-\gls{EDSL} could be added that -works in the same way as the existing \gls{C}-backed simulation. It simulates -the bytecode interpretation. Moreover, it would also be possible to let the -simulator function as a real device, thus handling all communication through -the existing \gls{SDS}-based systems. At the moment the \emph{POSIX}-client is -the reference client and contains debugging code. Adding a simulation view to -the system allows for easy interactive debugging. However, it might not be -easy to devise a simulation tool that accurately simulates the \gls{mTask} -system on some levels. The semantics can be simulated but timing and peripheral -input/output are more difficult to simulate properly. - -\subsection{Optimization} -\paragraph{Multitasking on the client:} -True multitasking could be added to the client software. This allows -\gls{mTask}-\glspl{Task} to run truly parallel. All \gls{mTask}-\glspl{Task} -get slices of execution time and will each have their own interpreter state -instead of a single system-wide state which is reset after am \gls{mTask} -finishes. This does require separate stacks for each \gls{Task} and therefore -increases the system requirements of the client software. However, it could be -implemented as a compile-time option and exchanged during the handshake so that -the server knows the multithreading capabilities of the client. Multithreading -allows \glspl{Task} to be truly interruptible by other \glspl{Task}. -Furthermore, this allows for more fine-grained timing control of \glspl{Task}. - -\paragraph{Optimizing the interpreter:} -Due to time constraints and focus, hardly any work has been done in the -interpreter. The current interpreter is a no nonsense stack machine. A lot of -improvements can be done in this part. For example, precomputed \emph{gotos} -can improve jumping to the correct part of the code corresponding to the -correct instruction. Moreover, the stack currently consists of 16-bit values. -All operations work on 16-bit values and this simplifies the interpreter -implementation. A memory improvement can be made by converting the stack to -8-bit values. This does pose some problems since an equality instruction must -work on single-byte booleans \emph{and} two-byte integers. Adding specialized -instructions per word size could overcome this problem. - -\subsection{Resources} -\paragraph{Resource analysis: } -Resource analysis during compilation can be useful to determine if an -\gls{mTask}-\gls{Task} is suitable for a specific device. If the device does -not contain the correct peripherals --- such as an \gls{LCD} --- then the -\gls{mTask}-\gls{Task} should be rejected and feedback to the user must be -given. It might even be possible to do this statically on the type level. The -current system does not have any of this built-in. Sending a \gls{Task} that -uses the \gls{LCD} to a device not containing one will result in the device -just skipping the \gls{LCD} related instructions. - -\paragraph{Extended resource analysis: } -The previous idea could be extended to the analysis of stack size and possibly -communication bandwidth. With this functionality ever more reliable fail-over -systems can be designed. When the system knows precise bounds it can allocate -more \glspl{Task} on a device whilst staying within safe memory bounds. The -resource allocation can be done at runtime within the backend itself or a -general backend can be devised that can calculate the resources needed for a -given \gls{mTask}. A specific \gls{mTask} cannot have multiple views at the -same time due to the restrictions of class based shallow embedding. It might -even be possible to encode the resource allocation in the type system itself -using forms of dependant types. - -\subsection{Functionality} -\paragraph{Add more combinators: } -More \gls{Task}-combinators --- already existing in the \gls{iTasks}-system --- -could be added to the \gls{mTask}-system to allow for more fine-grained control -flow between \gls{mTask}-\glspl{Task}. In this way the new system follows the -\gls{TOP} paradigm even more and makes programming \gls{mTask}-\glspl{Task} for -\gls{TOP}-programmers more seamless. Some of the combinators require previously -mentioned extension such as the parallel combinator. Others might be achieved -using simple syntactic transformations. - -\paragraph{Launch \glspl{Task} from a \gls{Task}:} -Currently the \gls{C}-view allows \glspl{Task} to launch other \glspl{Task}. In -the current system this type of logic has to take place on the server side. -Adding this functionality to the bytecode-view allows greater flexibility, -easier programming and less communication resources. Adding these semantics -requires modifications to the client software and extensions to the -communication protocol since relations between \glspl{Task} also need to be -encoded and communicated. - -The \gls{SDS} functionality in the current system is bare. There is no easy way -of reusing an \gls{SDS} for another \gls{Task} on the same device or on another -device. Such functionality can be implemented in a crude way by tying the -\glspl{SDS} together in the \gls{iTasks} environment. However, this will result -in a slow updating system. Functionality for reusing shares from a device -should be added. This requires rethinking the storage because some typedness is -lost when the \gls{SDS} is stored after compilation. A possibility would be to -use runtime typing with \CI{Dynamic}s or the encoding technique currently used -for \CI{BCValue}s. Using \glspl{SDS} for multiple \glspl{Task} within one -device is solved when the previous point is implemented. - -\subsection{Robustness} -\paragraph{Reconnect with lost devices:} -The robustness of the system can be greatly improved. Devices that lose -connection are not well supported in the current system. The device will stop -functioning and has to be emptied for a reconnect. \Glspl{Task} residing on a -device that disconnected should be kept on the server to allow a swift -reconnect and restoration of the \glspl{Task}. This holds the same for the -client software. The client drops all existing \glspl{Task} on a shutdown -request. An extra specialization of the shutdown could be added that drops the -connection but keeps the \glspl{Task} in memory. During the downtime the -\glspl{Task} can still be executed but publications need to be delayed. If the -same server connects to the client the delayed publications can be sent -anyways. - -\paragraph{Reverse \gls{Task} sending:} -Furthermore, devices could send their current \glspl{Task} back to the -server to synchronize it. This allows interchanging servers without -interrupting the client. Allowing the client to send \glspl{Task} to the server -is something to handle with care because it can easily cause high bandwidth -usage. +\input{conclusion.discussion} \section{Conclusion} -This thesis introduces a novel system for adding \gls{IoT} functionality to -the \gls{TOP} implementation \gls{iTasks}. A new view for the existing -\gls{mTask}-\gls{EDSL} has been created which compiles the program -into bytecode that can be interpreted by a client. Clients have -been written for several microcontrollers and consumer architectures which can -be connected through various means of communication such as serial port, -wifi and wired network communication. The bytecode on the devices is -interpreted using a stack machine and provides the programmer with interfaces -to the peripherals. The semantics for \gls{mTask} try to resemble the -\gls{iTasks} semantics as close as possible. - -The host language has a proven efficient compiler and code generator. The -compilation is linear in the amount of instructions generated and is therefore -also scalable. Moreover, compiling \glspl{Task} is fast because it is nothing -more than running some functions native to the host language and there is no -intermediate \gls{AST}. - -The dynamic nature of the client allows the microcontroller to be programmed -once and used many times. The program memory of microcontrollers often -guarantees around $10.000$ write or upload cycles and therefore existing -techniques such as generating \gls{C} code are not suitable for dynamic -\gls{Task} environments. The dynamic nature also allows the programmer to -design fail-over mechanisms. When a device is assigned a \gls{Task} but another -device suddenly becomes unusable, the \gls{iTasks} system can reassign a new -\gls{mTask}-\gls{Task} to another device that is also suitable for running the -\gls{Task} without needing to recompile the code. +\input{conclusion.conclusion} diff --git a/edsl.class.tex b/edsl.class.tex new file mode 100644 index 0000000..aafd355 --- /dev/null +++ b/edsl.class.tex @@ -0,0 +1,57 @@ +The third type of embedding is called class-based shallow embedding and has the +advantages of both shallow and deep +embedding~\cite{svenningsson_combining_2012}. In class-based shallow embedding +the language constructs are defined as type classes. This language is shown +with the new method in Listing~\ref{lst:exclassshallow}. + +This type of embedding inherits the ease of adding views from shallow +embedding. A view is just a different data type implementing one or more of the +type classes as shown in the aforementioned Listing where an evaluator and a +pretty printer are implemented. + +Just as with \glspl{GADT}, type safety is guaranteed in deep embedding. Type +constraints are enforced through phantom types. One can add as many phantom +types as necessary. Lastly, extensions can be added easily, just as in +shallow embedding. When an extension is made in an existing class, all views +must be updated accordingly to prevent possible runtime errors. When an +extension is added in a new class, this problem does not arise and views can +choose to implement only parts of the collection of classes. + +In contrast to deep embedding, it is very well possible to have multiple views +applied on the same expression. This is also shown in the following listing. + +\begin{lstlisting}[label={lst:exclassshallow},% + caption={A minimal class based shallow \gls{EDSL}}] +:: Env = ... // Some environment +:: Evaluator a = Evaluator (Env -> a) +:: PrettyPrinter a = PP String + +class intArith where + lit :: t -> v t | toString t + add :: (v t) (v t) -> (v t) | + t + minus :: (v t) (v t) -> (v t) | - t + +class boolArith where + and :: (v Bool) (v Bool) -> (v Bool) + eq :: (v t) (v t) -> (v Bool) | == t + +instance intArith Evaluator where + lit x = \e->x + add x y = ... + +instance intArith PrettyPrinter where + lit x = toString x + add x y = x +++ "+" +++ y + ... + +... + +Start :: (PP String, Bool) +Start = (print e0, eval e0) +where + e0 :: a Bool | intArith, boolArith a + e0 = eq (lit 42) (lit 21 +. lit 21) + + print (PP p) = p + eval (Evaluator e) env = e env +\end{lstlisting} diff --git a/edsl.deep.tex b/edsl.deep.tex new file mode 100644 index 0000000..fa9d380 --- /dev/null +++ b/edsl.deep.tex @@ -0,0 +1,44 @@ +A deep \gls{EDSL} is a language represented as an \gls{ADT}. Views are +functions that transform something to the datatype or the other way around. As +an example, take the simple arithmetic \gls{EDSL} shown in +Listing~\ref{lst:exdeep}. + +\begin{lstlisting}[label={lst:exdeep},% + caption={A minimal deep \gls{EDSL}}] +:: DSL + = LitI Int + | LitB Bool + | Var String + | Plus DSL DSL + | Minus DSL DSL + | And DSL DSL + | Eq DSL +\end{lstlisting} + +Deep embedding has the advantage that it is easy to build and views +are easy to add. To the downside, the expressions created with this language +are not type-safe. In the given language it is possible to create an expression +such as \CI{Plus (LitI 4) (LitB True)} that adds a boolean to an integer. +Evermore so, extending the \gls{ADT} is easy and convenient but extending the +views accordingly is tedious and has to be done individually for all views. + +The first downside of this type of \gls{EDSL} can be overcome by using +\glspl{GADT}~\cite{cheney_first-class_2003}. Listing~\ref{lst:exdeepgadt} shows +the same language, but type-safe with a \gls{GADT}. \glspl{GADT} are not +supported in the current version of \gls{Clean} and therefore the syntax is +hypothetical. However, it has been shown that \glspl{GADT} can be simulated +using bimaps or projection pairs~\cite{cheney_lightweight_2002}. Unfortunately +the lack of extendability remains a problem. If a language construct is added, +no compile time guarantee is given that all views support it. + +\begin{lstlisting}[label={lst:exdeepgadt},% + caption={A minimal deep \gls{EDSL} using \glspl{GADT}}] +:: DSL a + = LitI Int -> DSL Int + | LitB Bool -> DSL Bool + | E.e: Var String -> DSL e + | Plus (DSL Int) (DSL Int) -> DSL Int + | Minus (DSL Int) (DSL Int) -> DSL Int + | And (DSL Bool) (DSL Bool) -> DSL Bool + | E.e: Eq (DSL e) (DSL e) -> DSL Bool & == e +\end{lstlisting} diff --git a/edsl.shallow.tex b/edsl.shallow.tex new file mode 100644 index 0000000..b7616b6 --- /dev/null +++ b/edsl.shallow.tex @@ -0,0 +1,37 @@ +In a shallow \gls{EDSL} all language constructs are expressed as functions in +the host language. An evaluator view for the example language then can be +implemented as the code shown in Listing~\ref{lst:exshallow}. Note that much of +the internals of the language can be hidden using monads. + +\begin{lstlisting}[label={lst:exshallow},% + caption={A minimal shallow \gls{EDSL}}] +:: Env = ... // Some environment +:: DSL a = DSL (Env -> a) + +Lit :: a -> DSL a +Lit x = \e -> x + +Var :: String -> DSL Int +Var i = \e -> retrEnv e i + +Plus :: (DSL Int) (DSL Int) -> DSL Int +Plus x y = \e -> x e + y e + +... + +Eq :: (DSL a) (DSL a) -> DSL Bool | == a +Eq x y = \e -> x e + y e +\end{lstlisting} + +The advantage of shallowly embedding a language in a host language is its +extendability. It is very easy to add functionality and compile time checks +of the host language guarantee whether or not the functionality is available +when used. Moreover, the language is type safe as it is directly typed in the +host language. + +The downside of this method is extending the language with views. It is nearly +impossible to add views to a shallowly embedded language. The only way of +achieving this is by decorating the datatype for the \gls{EDSL} with all the +information for all the views. This will mean that every component will have to +implement all views rendering it slow for multiple views and complex to +implement. diff --git a/edsl.tex b/edsl.tex new file mode 100644 index 0000000..e66577d --- /dev/null +++ b/edsl.tex @@ -0,0 +1,16 @@ +An \gls{EDSL} is a language embedded in a host language. \glspl{EDSL} can have +one or more backends or views. Commonly used views are pretty printing, +compiling, simulating, verifying and proving the program. There are several +techniques available for creating \glspl{EDSL}. They all have their own +advantages and disadvantages in terms of extendability, typedness and view +support. In the following subsections each of the main techniques are briefly +explained. + +\section{Deep embedding} +\input{edsl.deep} + +\section{Shallow embedding} +\input{edsl.shallow} + +\section{Class based shallow embedding} +\input{edsl.class} diff --git a/intro.doc.tex b/intro.doc.tex new file mode 100644 index 0000000..7350f17 --- /dev/null +++ b/intro.doc.tex @@ -0,0 +1,33 @@ +The structure of this thesis is as follows. + +Chapter~\ref{chp:introduction} contains the problem statement, motivation, +related work and the structure of the document. +Chapter~\ref{chp:top} introduces the reader to the basics of \gls{TOP} and +\gls{iTasks}. +Chapter~\ref{chp:dsl} discusses the pros and cons of different embedding +methods to create \gls{EDSL}. +Chapter~\ref{chp:mtask} shows the existing \gls{mTask}-\gls{EDSL} on which is +extended upon in this dissertation. +Chapter~\ref{chp:mtaskcont} describes the view and functionality for +the \gls{mTask}-\gls{EDSL} that were added and used in the system. +Chapter~\ref{chp:arch} shows the architecture used for \gls{IoT}-devices that +are a part of the new \gls{mTask}-system. It covers the client software running +on the device and the server written in \gls{iTasks}. +Chapter~\ref{chp:conclusion} concludes by answering the research questions +and discusses future research. +Appendix~\ref{app:communication-protocol} shows the concrete protocol used for +communicating between the server and client. +Appendix~\ref{app:device-interface} shows the concrete interface for the +devices. + +Text written using the \CI{Teletype} font indicates code and is often +referring to a listing. \emph{Emphasized} text is used for proper nouns and +words that have a unexpected meaning. + +The complete source code of this thesis can be found in the following git +repository:\\ +\url{https://git.martlubbers.net/msc-thesis1617.git} + +The complete source code of the \gls{mTask}-system can be found in the +following git repository: +\url{https://git.martlubbers.net/mTask.git} diff --git a/intro.intro.tex b/intro.intro.tex new file mode 100644 index 0000000..3574b58 --- /dev/null +++ b/intro.intro.tex @@ -0,0 +1,51 @@ +\Gls{IoT} technology is emerging rapidly. It offers myriads of solutions +and transforms the way we interact with technology. + +Initially the term was coined to describe \gls{RFID} devices and the +communication between them. However, currently the term \gls{IoT} encompasses +all small devices that communicate with each other and the world. These devices +are often equipped with sensors, \gls{GNSS} modules\footnote{e.g.\ the American +\gls{GPS} or the Russian \gls{GLONASS}.} and +actuators~\cite{da_xu_internet_2014}. With these new technologies information +can be tracked accurately using little power and bandwidth. Moreover, \gls{IoT} +technology is coming into people's homes, clothes and +healthcare~\cite{riazul_islam_internet_2015}. For example, for a few euros a +consumer ready fitness tracker watch can be bought that tracks heartbeat and +respiration levels. + +The \gls{TOP} paradigm and the corresponding \gls{iTasks} implementation offer +a high abstraction level for real world workflow +tasks~\cite{plasmeijer_itasks:_2007}. These workflow tasks can be described +through an \gls{EDSL} and modeled as \glspl{Task}. The system will generate a +multi-user web app from the specification. This web service can be accessed +through a browser and is used to complete these \glspl{Task}. Familiar workflow +patterns like sequential, parallel and conditional \glspl{Task} can be modelled +using combinators. + +\gls{iTasks} has proven to be useful in many fields of operation such as +incident management~\cite{lijnse_top_2013}. Interfaces are automatically +generated for the types of data which makes rapid development possible. +\Glspl{Task} in the \gls{iTasks} system are modelled after real life workflow +tasks but the modelling is applied on a high level. Therefore it is difficult +to connect \gls{iTasks}-\glspl{Task} to real world \glspl{Task} and allow them +to interact. A lot of the actual tasks could be performed by small \gls{IoT} +devices. Nevertheless, adding such devices to the current system is difficult +to say the least as it was not designed to cope with these devices. + +In the current system such adapters connecting devices to \gls{iTasks} --- in +principle --- can be written as \glspl{SDS}\footnote{Similar as to resources +such as time are available in the current \gls{iTasks} implementation.}. +However, this requires a very specific adapter to be written for every device +and function. This forces a fixed logic in the device that is set at compile +time. Many small \gls{IoT} devices have limited processing power but are still +powerful enough for decision making. Recompiling the code for a small +\gls{IoT} device is expensive and therefore it is difficult to use a device +dynamically for multiple purposes. Oortgiese et al.\ lifted \gls{iTasks} from a +single server model to a distributed server architecture that is also runnable +on small devices such as those powered by +\gls{ARM}~\cite{oortgiese_distributed_2017}. However, this is limited to +fairly high performance devices that are equipped with high speed communication +channels because it requires the device to run the entire \gls{iTasks} core. +Devices in \gls{IoT} often have only Low Throughput Network communication with +low bandwidth and a very limited amount of processing power and are therefore +not suitable to run an entire \gls{iTasks} core. diff --git a/intro.problem.tex b/intro.problem.tex new file mode 100644 index 0000000..cfb8494 --- /dev/null +++ b/intro.problem.tex @@ -0,0 +1,10 @@ +The updates to the \gls{mTask}-system~\cite{koopman_type-safe_nodate} will +bridge this gap in the current system by introducing a new communication +protocol, device application and \glspl{Task} synchronizing the two. The system +can run on devices as small as \gls{Arduino} +microcontrollers~\cite{noauthor_arduino_nodate} and operates via the same +paradigms and patterns as regular \glspl{Task} in the \gls{TOP} paradigm. +Devices in the \gls{mTask}-system can run small imperative programs written in +an \gls{EDSL} and have access to \glspl{SDS}. \Glspl{Task} are sent to the +device at runtime, avoiding recompilation and thus write cycles on the program +memory. diff --git a/intro.related.tex b/intro.related.tex new file mode 100644 index 0000000..e2af6e2 --- /dev/null +++ b/intro.related.tex @@ -0,0 +1,45 @@ +Similar research has been conducted on the subject. +For example, microcontrollers such as the \gls{Arduino} can be remotely +controlled by the \gls{Firmata}-protocol\footnote{``firmata/protocol: +Documentation of the Firmata protocol.'' +(\url{https://github.com/firmata/protocol}). [Accessed: 23-May-2017].}. This +protocol is designed to expose the peripherals such as sensors to the server. +This allows very fine grained control but with the cost of excessive +communication overhead since no code is executed on the device, only the +peripherals are queried. A \gls{Haskell} implementation of the protocol is +also available\footnote{``hArduino by LeventErkok.'' (\url{% +https://leventerkok.github.io/hArduino}). [Accessed: 23-May-2017].}. + +\Gls{Clean} has a history of interpretation and there is a lot of research +happening on the intermediate language \gls{SAPL}. \Gls{SAPL} is a purely +functional intermediate language that has interpreters written in +\gls{C++}~\cite{jansen_efficient_2007}, \gls{Javascript}% +~\cite{domoszlai_implementing_2011} and \gls{Clean} and \gls{Haskell} compiler +backends~\cite{domoszlai_compiling_2012}. However, interpreting the resulting +code is still heap-heavy and therefore not directly suitable for devices with +as little as $2K$ of RAM such as the \gls{Arduino} \emph{Uno}. It might be +possible to compile the \gls{SAPL} code into efficient machine language or +\gls{C} but then the system would lose its dynamic properties since the +microcontroller then would have to be reprogrammed every time a new \gls{Task} +is sent to the device. + +\Glspl{EDSL} have often been used to generate \gls{C} code for microcontroller +environments. This work uses parts of the existing \gls{mTask}-\gls{EDSL} which +generates \gls{C} code to run a \gls{TOP}-like system on microcontrollers% +~\cite{plasmeijer_shallow_2016}~\cite{koopman_type-safe_nodate}. Again, this +requires a reprogramming cycle every time the \gls{Task}-specification is +changed. + +Another \gls{EDSL} designed to generate low-level high-assurance programs is +called \gls{Ivory} and uses \gls{Haskell} as a host language% +~\cite{elliott_guilt_2015}. The language uses the \gls{Haskell} type-system to +make unsafe languages type safe. For example, \gls{Ivory} has been used in the +automotive industry to program parts of an autopilot% +~\cite{pike_programming_2014}~\cite{hickey_building_2014}. \Gls{Ivory}'s syntax +is deeply embedded but the type system is shallowly embedded. This requires +several \gls{Haskell} extensions that offer dependent type constructions. The +process of compiling an \gls{Ivory} program happens in stages. The embedded +code is transformed into an \gls{AST} that is sent to a backend. In the new +system, the \gls{mTask}-\gls{EDSL} transforms the embedded code during +compile-time directly into the backend which is often a state transformer that +will execute on runtime. diff --git a/intro.tex b/intro.tex new file mode 100644 index 0000000..1a93b40 --- /dev/null +++ b/intro.tex @@ -0,0 +1,11 @@ +\section{Introduction} +\input{intro.intro} + +\section{Problem statement} +\input{intro.problem} + +\section{Document structure} +\input{intro.doc} + +\section{Related work} +\input{intro.related} diff --git a/introduction.tex b/introduction.tex deleted file mode 100644 index 9243d2a..0000000 --- a/introduction.tex +++ /dev/null @@ -1,146 +0,0 @@ -\section{Introduction} -\Gls{IoT} technology is emerging rapidly. It offers myriads of solutions -and transforms the way we interact with technology. - -Initially the term was coined to describe \gls{RFID} devices and the -communication between them. However, currently the term \gls{IoT} encompasses -all small devices that communicate with each other and the world. These devices -are often equipped with sensors, \gls{GNSS} modules\footnote{e.g.\ the American -\gls{GPS} or the Russian \gls{GLONASS}.} and -actuators~\cite{da_xu_internet_2014}. With these new technologies information -can be tracked accurately using little power and bandwidth. Moreover, \gls{IoT} -technology is coming into people's homes, clothes and -healthcare~\cite{riazul_islam_internet_2015}. For example, for a few euros a -consumer ready fitness tracker watch can be bought that tracks heartbeat and -respiration levels. - -The \gls{TOP} paradigm and the corresponding \gls{iTasks} implementation offer -a high abstraction level for real world workflow -tasks~\cite{plasmeijer_itasks:_2007}. These workflow tasks can be described -through an \gls{EDSL} and modeled as \glspl{Task}. The system will generate a -multi-user web app from the specification. This web service can be accessed -through a browser and is used to complete these \glspl{Task}. Familiar workflow -patterns like sequential, parallel and conditional \glspl{Task} can be modelled -using combinators. - -\gls{iTasks} has proven to be useful in many fields of operation such as -incident management~\cite{lijnse_top_2013}. Interfaces are automatically -generated for the types of data which makes rapid development possible. -\Glspl{Task} in the \gls{iTasks} system are modelled after real life workflow -tasks but the modelling is applied on a high level. Therefore it is difficult -to connect \gls{iTasks}-\glspl{Task} to real world \glspl{Task} and allow them -to interact. A lot of the actual tasks could be performed by small \gls{IoT} -devices. Nevertheless, adding such devices to the current system is difficult -to say the least as it was not designed to cope with these devices. - -In the current system such adapters connecting devices to \gls{iTasks} --- in -principle --- can be written as \glspl{SDS}\footnote{Similar as to resources -such as time are available in the current \gls{iTasks} implementation.}. -However, this requires a very specific adapter to be written for every device -and function. This forces a fixed logic in the device that is set at compile -time. Many small \gls{IoT} devices have limited processing power but are still -powerful enough for decision making. Recompiling the code for a small -\gls{IoT} device is expensive and therefore it is difficult to use a device -dynamically for multiple purposes. Oortgiese et al.\ lifted \gls{iTasks} from a -single server model to a distributed server architecture that is also runnable -on small devices such as those powered by -\gls{ARM}~\cite{oortgiese_distributed_2017}. However, this is limited to -fairly high performance devices that are equipped with high speed communication -channels because it requires the device to run the entire \gls{iTasks} core. -Devices in \gls{IoT} often have only Low Throughput Network communication with -low bandwidth and a very limited amount of processing power and are therefore -not suitable to run an entire \gls{iTasks} core. - -\section{Problem statement} -The updates to the \gls{mTask}-system~\cite{koopman_type-safe_nodate} will -bridge this gap in the current system by introducing a new communication -protocol, device application and \glspl{Task} synchronizing the two. The system -can run on devices as small as \gls{Arduino} -microcontrollers~\cite{noauthor_arduino_nodate} and operates via the same -paradigms and patterns as regular \glspl{Task} in the \gls{TOP} paradigm. -Devices in the \gls{mTask}-system can run small imperative programs written in -an \gls{EDSL} and have access to \glspl{SDS}. \Glspl{Task} are sent to the -device at runtime, avoiding recompilation and thus write cycles on the program -memory. - -\section{Document structure} -The structure of this thesis is as follows. - -Chapter~\ref{chp:introduction} contains the problem statement, motivation, -related work and the structure of the document. -Chapter~\ref{chp:top} introduces the reader to the basics of \gls{TOP} and -\gls{iTasks}. -Chapter~\ref{chp:dsl} discusses the pros and cons of different embedding -methods to create \gls{EDSL}. -Chapter~\ref{chp:mtask} shows the existing \gls{mTask}-\gls{EDSL} on which is -extended upon in this dissertation. -Chapter~\ref{chp:mtaskcont} describes the view and functionality for -the \gls{mTask}-\gls{EDSL} that were added and used in the system. -Chapter~\ref{chp:arch} shows the architecture used for \gls{IoT}-devices that -are a part of the new \gls{mTask}-system. It covers the client software running -on the device and the server written in \gls{iTasks}. -Chapter~\ref{chp:conclusion} concludes by answering the research questions -and discusses future research. -Appendix~\ref{app:communication-protocol} shows the concrete protocol used for -communicating between the server and client. -Appendix~\ref{app:device-interface} shows the concrete interface for the -devices. - -Text written using the \CI{Teletype} font indicates code and is often -referring to a listing. \emph{Emphasized} text is used for proper nouns and -words that have a unexpected meaning. - -The complete source code of this thesis can be found in the following git -repository:\\ -\url{https://git.martlubbers.net/msc-thesis1617.git} - -The complete source code of the \gls{mTask}-system can be found in the -following git repository: -\url{https://git.martlubbers.net/mTask.git} - -\section{Related work} -Similar research has been conducted on the subject. -For example, microcontrollers such as the \gls{Arduino} can be remotely -controlled by the \gls{Firmata}-protocol\footnote{``firmata/protocol: -Documentation of the Firmata protocol.'' -(\url{https://github.com/firmata/protocol}). [Accessed: 23-May-2017].}. This -protocol is designed to expose the peripherals such as sensors to the server. -This allows very fine grained control but with the cost of excessive -communication overhead since no code is executed on the device, only the -peripherals are queried. A \gls{Haskell} implementation of the protocol is -also available\footnote{``hArduino by LeventErkok.'' (\url{% -https://leventerkok.github.io/hArduino}). [Accessed: 23-May-2017].}. - -\Gls{Clean} has a history of interpretation and there is a lot of research -happening on the intermediate language \gls{SAPL}. \Gls{SAPL} is a purely -functional intermediate language that has interpreters written in -\gls{C++}~\cite{jansen_efficient_2007}, \gls{Javascript}% -~\cite{domoszlai_implementing_2011} and \gls{Clean} and \gls{Haskell} compiler -backends~\cite{domoszlai_compiling_2012}. However, interpreting the resulting -code is still heap-heavy and therefore not directly suitable for devices with -as little as $2K$ of RAM such as the \gls{Arduino} \emph{Uno}. It might be -possible to compile the \gls{SAPL} code into efficient machine language or -\gls{C} but then the system would lose its dynamic properties since the -microcontroller then would have to be reprogrammed every time a new \gls{Task} -is sent to the device. - -\Glspl{EDSL} have often been used to generate \gls{C} code for microcontroller -environments. This work uses parts of the existing \gls{mTask}-\gls{EDSL} which -generates \gls{C} code to run a \gls{TOP}-like system on microcontrollers% -~\cite{plasmeijer_shallow_2016}~\cite{koopman_type-safe_nodate}. Again, this -requires a reprogramming cycle every time the \gls{Task}-specification is -changed. - -Another \gls{EDSL} designed to generate low-level high-assurance programs is -called \gls{Ivory} and uses \gls{Haskell} as a host language% -~\cite{elliott_guilt_2015}. The language uses the \gls{Haskell} type-system to -make unsafe languages type safe. For example, \gls{Ivory} has been used in the -automotive industry to program parts of an autopilot% -~\cite{pike_programming_2014}~\cite{hickey_building_2014}. \Gls{Ivory}'s syntax -is deeply embedded but the type system is shallowly embedded. This requires -several \gls{Haskell} extensions that offer dependent type constructions. The -process of compiling an \gls{Ivory} program happens in stages. The embedded -code is transformed into an \gls{AST} that is sent to a backend. In the new -system, the \gls{mTask}-\gls{EDSL} transforms the embedded code during -compile-time directly into the backend which is often a state transformer that -will execute on runtime. diff --git a/methods.dsl.tex b/methods.dsl.tex deleted file mode 100644 index 9101293..0000000 --- a/methods.dsl.tex +++ /dev/null @@ -1,151 +0,0 @@ -An \gls{EDSL} is a language embedded in a host language. \glspl{EDSL} can have -one or more backends or views. Commonly used views are pretty printing, -compiling, simulating, verifying and proving the program. There are several -techniques available for creating \glspl{EDSL}. They all have their own -advantages and disadvantages in terms of extendability, typedness and view -support. In the following subsections each of the main techniques are briefly -explained. - -\section{Deep embedding} -A deep \gls{EDSL} is a language represented as an \gls{ADT}. Views are -functions that transform something to the datatype or the other way around. As -an example, take the simple arithmetic \gls{EDSL} shown in -Listing~\ref{lst:exdeep}. - -\begin{lstlisting}[label={lst:exdeep},% - caption={A minimal deep \gls{EDSL}}] -:: DSL - = LitI Int - | LitB Bool - | Var String - | Plus DSL DSL - | Minus DSL DSL - | And DSL DSL - | Eq DSL -\end{lstlisting} - -Deep embedding has the advantage that it is easy to build and views -are easy to add. To the downside, the expressions created with this language -are not type-safe. In the given language it is possible to create an expression -such as \CI{Plus (LitI 4) (LitB True)} that adds a boolean to an integer. -Evermore so, extending the \gls{ADT} is easy and convenient but extending the -views accordingly is tedious and has to be done individually for all views. - -The first downside of this type of \gls{EDSL} can be overcome by using -\glspl{GADT}~\cite{cheney_first-class_2003}. Listing~\ref{lst:exdeepgadt} shows -the same language, but type-safe with a \gls{GADT}. \glspl{GADT} are not -supported in the current version of \gls{Clean} and therefore the syntax is -hypothetical. However, it has been shown that \glspl{GADT} can be simulated -using bimaps or projection pairs~\cite{cheney_lightweight_2002}. Unfortunately -the lack of extendability remains a problem. If a language construct is added, -no compile time guarantee is given that all views support it. - -\begin{lstlisting}[label={lst:exdeepgadt},% - caption={A minimal deep \gls{EDSL} using \glspl{GADT}}] -:: DSL a - = LitI Int -> DSL Int - | LitB Bool -> DSL Bool - | E.e: Var String -> DSL e - | Plus (DSL Int) (DSL Int) -> DSL Int - | Minus (DSL Int) (DSL Int) -> DSL Int - | And (DSL Bool) (DSL Bool) -> DSL Bool - | E.e: Eq (DSL e) (DSL e) -> DSL Bool & == e -\end{lstlisting} - -\section{Shallow embedding} -In a shallow \gls{EDSL} all language constructs are expressed as functions in -the host language. An evaluator view for the example language then can be -implemented as the code shown in Listing~\ref{lst:exshallow}. Note that much of -the internals of the language can be hidden using monads. - -\begin{lstlisting}[label={lst:exshallow},% - caption={A minimal shallow \gls{EDSL}}] -:: Env = ... // Some environment -:: DSL a = DSL (Env -> a) - -Lit :: a -> DSL a -Lit x = \e -> x - -Var :: String -> DSL Int -Var i = \e -> retrEnv e i - -Plus :: (DSL Int) (DSL Int) -> DSL Int -Plus x y = \e -> x e + y e - -... - -Eq :: (DSL a) (DSL a) -> DSL Bool | == a -Eq x y = \e -> x e + y e -\end{lstlisting} - -The advantage of shallowly embedding a language in a host language is its -extendability. It is very easy to add functionality and compile time checks -of the host language guarantee whether or not the functionality is available -when used. Moreover, the language is type safe as it is directly typed in the -host language. - -The downside of this method is extending the language with views. It is nearly -impossible to add views to a shallowly embedded language. The only way of -achieving this is by decorating the datatype for the \gls{EDSL} with all the -information for all the views. This will mean that every component will have to -implement all views rendering it slow for multiple views and complex to -implement. - -\section{Class based shallow embedding} -The third type of embedding is called class-based shallow embedding and has the -advantages of both shallow and deep -embedding~\cite{svenningsson_combining_2012}. In class-based shallow embedding -the language constructs are defined as type classes. This language is shown -with the new method in Listing~\ref{lst:exclassshallow}. - -This type of embedding inherits the ease of adding views from shallow -embedding. A view is just a different data type implementing one or more of the -type classes as shown in the aforementioned Listing where an evaluator and a -pretty printer are implemented. - -Just as with \glspl{GADT}, type safety is guaranteed in deep embedding. Type -constraints are enforced through phantom types. One can add as many phantom -types as necessary. Lastly, extensions can be added easily, just as in -shallow embedding. When an extension is made in an existing class, all views -must be updated accordingly to prevent possible runtime errors. When an -extension is added in a new class, this problem does not arise and views can -choose to implement only parts of the collection of classes. - -In contrast to deep embedding, it is very well possible to have multiple views -applied on the same expression. This is also shown in the following listing. - -\begin{lstlisting}[label={lst:exclassshallow},% - caption={A minimal class based shallow \gls{EDSL}}] -:: Env = ... // Some environment -:: Evaluator a = Evaluator (Env -> a) -:: PrettyPrinter a = PP String - -class intArith where - lit :: t -> v t | toString t - add :: (v t) (v t) -> (v t) | + t - minus :: (v t) (v t) -> (v t) | - t - -class boolArith where - and :: (v Bool) (v Bool) -> (v Bool) - eq :: (v t) (v t) -> (v Bool) | == t - -instance intArith Evaluator where - lit x = \e->x - add x y = ... - -instance intArith PrettyPrinter where - lit x = toString x - add x y = x +++ "+" +++ y - ... - -... - -Start :: (PP String, Bool) -Start = (print e0, eval e0) -where - e0 :: a Bool | intArith, boolArith a - e0 = eq (lit 42) (lit 21 +. lit 21) - - print (PP p) = p - eval (Evaluator e) env = e env -\end{lstlisting} diff --git a/methods.mtask.tex b/methods.mtask.tex deleted file mode 100644 index 70cca5b..0000000 --- a/methods.mtask.tex +++ /dev/null @@ -1,238 +0,0 @@ -The \gls{mTask}-\gls{EDSL} is the language used for the proposed system. The -\gls{mTask}-\gls{EDSL} was created by Koopman et al.\ and supports several -views such as an \gls{iTasks} simulation and a \gls{C}-code generator. The -\gls{EDSL} was designed to generate a ready-to-compile \gls{TOP}-like program -for microcontrollers such as the \gls{Arduino}~\cite{koopman_type-safe_nodate}% -\cite{plasmeijer_shallow_2016}. - -The \gls{mTask}-\gls{EDSL} is a shallowly embedded class based \gls{EDSL} and -therefore it is very suitable to have a new backend that partly implements the -classes given. The following sections show the details of the \gls{EDSL} that -is used in this extension. The parts of the \gls{EDSL} that are not used will -not be discussed and the details of those parts can be found in the cited -literature. - -A view for the \gls{mTask}-\gls{EDSL} is a type with two free type -variables\footnote{kind \CI{*->*->*}.} that implements some of the classes -given. The types do not have to be present as fields in the view and can, and -will most often, be exclusively phantom types. Thus, views are of the -form:\\\CI{:: v t r = ...}. The first type variable will be the type of the -view. The second type variable will be the type of the \gls{EDSL}-expression -and the third type variable represents the role of the expression. Currently -the role of the expressions form a hierarchy. The three roles and their -hierarchy are shown in Listing~\ref{lst:exprhier}. This implies that everything -is a statement, only an \CI{Upd} and an \CI{Expr} are expressions. The \CI{Upd} -restriction describes updatable expressions such as \gls{GPIO} pins and -\glspl{SDS}. - -\begin{lstlisting}[% - label={lst:exprhier},caption={Expression role hierarchy}] -:: Upd = Upd -:: Expr = Expr -:: Stmt = Stmt - -class isExpr a :: a -> Int -instance isExpr Upd -instance isExpr Expr -\end{lstlisting} - -\section{Expressions} -Expressions in the \gls{mTask}-\gls{EDSL} are divided into two types, namely -boolean expressions and arithmetic expressions. The class of arithmetic -language constructs also contains the function \CI{lit} that lifts a -host-language value into the \gls{EDSL} domain. All standard arithmetic -functions are included in the \gls{EDSL} but are omitted in the example for -brevity. Moreover, the class restrictions are only shown in the first functions -and are omitted in subsequent functions. Both the boolean expression and -arithmetic expression classes are shown in Listing~\ref{lst:arithbool}. - -\begin{lstlisting}[label={lst:arithbool}, - caption={Basic classes for expressions}] -class arith v where - lit :: t -> v t Expr - (+.) infixl 6 :: (v t p) (v t q) -> v t Expr | +, zero t & isExpr p & isExpr q - (-.) infixl 6 :: (v t p) (v t q) -> v t Expr | -, zero t & ... - ... -class boolExpr v where - Not :: (v Bool p) -> v Bool Expr | ... - (&.) infixr 3 :: (v Bool p) (v Bool q) -> v Bool Expr | ... - ... - (==.) infix 4 :: (v a p) (v a q) -> v Bool Expr | ==, toCode a & ... -\end{lstlisting} - -\section{Control flow} -Looping of \glspl{Task} happens because \glspl{Task} are executed after waiting -a specified amount of time or when they are launched by another \gls{Task} or -even themselves. Therefore there is no need for loop control flow functionality -such as \emph{while} or \emph{for} constructions. The main control flow -operators are the sequence operator and the \emph{if} statement. Both are shown -in Listing~\ref{lst:control}. The first class of \emph{If} statements describes -the regular \emph{if} statement. The expressions given can have any role. The -functional dependency on \CI{s} determines the return type of the statement. -The listing includes examples of implementations that illustrate this -dependency. A special \emph{If} statement --- only used for statements --- is -also added under the name \CI{IF}, of which the \CI{?} is a conditional -statement to execute. - -The sequence operator is straightforward and its only function is to tie -two expressions together. The left expression is executed first, followed by -the right expression. - -\begin{lstlisting}[% - label={lst:control},caption={Control flow operators}] -class If v q r ~s where - If :: (v Bool p) (v t q) (v t r) -> v t s | ... - -class IF v where - IF :: (v Bool p) (v t q) (v s r) -> v () Stmt | ... - (?) infix 1 :: (v Bool p) (v t q) -> v () Stmt | ... - -instance If Code Stmt Stmt Stmt -instance If Code e Stmt Stmt -instance If Code Stmt e Stmt -instance If Code x y Expr - -class seq v where - (:.) infixr 0 :: (v t p) (v u q) -> v u Stmt | ... -\end{lstlisting} - -\section{Input/Output and class extensions} -Values can be assigned to all expressions that have an \CI{Upd} role. Examples -of such expressions are \glspl{SDS} and \gls{GPIO} pins. Moreover, class -extensions can be created for specific peripherals such as built-in -\glspl{LED}. The classes facilitating this are shown in -Listing~\ref{lst:sdsio}. In this way the assignment is the same for every -assignable entity. - -\begin{lstlisting}[% - label={lst:sdsio},caption={Input/Output classes}] -:: DigitalPin = D0 | D1 | D2 | D3 | D4 | D5 |D6 | D7 | D8 | D9 | D10 | D11 | D12 | D13 -:: AnalogPin = A0 | A1 | A2 | A3 | A4 | A5 -:: UserLED = LED1 | LED2 | LED3 - -class dIO v where dIO :: DigitalPin -> v Bool Upd -class aIO v where aIO :: AnalogPin -> v Int Upd -class analogRead v where - analogRead :: AnalogPin -> v Int Expr - analogWrite :: AnalogPin (v Int p) -> v Int Expr -class digitalRead v where - digitalRead :: DigitalPin -> v Bin Expr - digitalWrite :: DigitalPin (v Bool p) -> v Int Expr - -:: UserLED = LED1 | LED2 | LED3 -class userLed v where - ledOn :: (v UserLED q) -> (v () Stmt) - ledOff :: (v UserLED q) -> (v () Stmt) - -class assign v where - (=.) infixr 2 :: (v t Upd) (v t p) -> v t Expr | ... -\end{lstlisting} - -One way of storing data in \gls{mTask}-\glspl{Task} is using \glspl{SDS}. -\glspl{SDS} serve as variables in \gls{mTask} and maintain their value across -executions. \glspl{SDS} can be used by multiple \glspl{Task} and can be used -to share data. The classes associated with \glspl{SDS} are listed in -Listing~\ref{lst:sdsclass}. The \CI{Main} type is introduced to box an -\gls{mTask} and make it recognizable by the type system by separating programs -and decorations such as \glspl{SDS}. - -\begin{lstlisting}[% - label={lst:sdsclass},caption={\glspl{SDS} in \gls{mTask}}] -:: In a b = In infix 0 a b -:: Main a = {main :: a} - -class sds v where - sds :: ((v t Upd)->In t (Main (v c s))) -> (Main (v c s)) | ... -\end{lstlisting} - -\section{Semantics} -The \gls{C}-backend of the \gls{mTask}-system has an engine that is generated -alongside the code for the \glspl{Task}. This engine will execute the -\gls{mTask}-\glspl{Task} according to certain rules and semantics. -\gls{mTask}-\glspl{Task} do not behave like functions but more like -\gls{iTasks}-\glspl{Task}. An \gls{mTask} is queued when either its timer runs -out or when it is launched by another \gls{mTask}. When an \gls{mTask} is -queued it does not block the execution and it will return immediately while the -actual \gls{Task} will be executed anytime in the future. - -The \gls{iTasks}-backend simulates the \gls{C}-backend and thus uses the same -semantics. This engine expressed in pseudocode is listed as -Algorithm~\ref{lst:engine}. All the \glspl{Task} are inspected on their waiting -time. When the waiting time has not passed; the delta is subtracted and the -\gls{Task} gets pushed to the end of the queue. When the waiting has surpassed -they are executed. When an \gls{mTask} opts to queue another \gls{mTask} it -can just append it to the queue. - -~\\ -\begin{algorithm}[H] - \KwData{\textbf{queue} queue, \textbf{time} $t, t_p$} - - $t\leftarrow\text{now}()$\; - \Begin{ - \While{true}{ - $t_p\leftarrow t$\; - $t\leftarrow\text{now}()$\; - \If{notEmpty$($queue$)$}{ - $task\leftarrow \text{queue.pop}()$\; - $task$.wait $\leftarrow task$.wait $-(t-t_p)$\; - \eIf{$task.wait>t_0$}{ - queue.append$(task)$\; - }{ - run\_task$(task)$\; - } - } - } - } - \caption{Engine pseudocode for the \gls{C}- and - \gls{iTasks}-view}\label{lst:engine} -\end{algorithm} -~\\ - -To achieve this in the \gls{EDSL} a \gls{Task} class is added that work in a -similar fashion as the \texttt{sds} class. This class is listed in -Listing~\ref{lst:taskclass}. \glspl{Task} can have an argument and always have -to specify a delay or waiting time. The type signature of the \CI{mtask} is -complex and therefore an example is given. The aforementioned Listing -shows a simple specification containing one \gls{Task} that increments a value -indefinitely every one seconds. - -\begin{lstlisting}[label={lst:taskclass},% - caption={The classes for defining \glspl{Task}}] -class mtask v a where - task :: (((v delay r) a->v MTask Expr)->In (a->v u p) (Main (v t q))) -> Main (v t q) | ... - -count = task \count = (\n.count (lit 1000) (n +. One)) In {main = count (lit 1000) Zero} -\end{lstlisting} - -\section{Example mTask} -Some example \gls{mTask}-\glspl{Task} using almost all of their functionality -are shown in Listing~\ref{lst:exmtask}. The \gls{mTask}-\glspl{Task} shown in -the example do not belong to a particular view and therefore are of the type -\CI{View t r}. The \CI{blink} \gls{mTask} show the classic \gls{Arduino} -blinking led application that blinks a certain \gls{LED} every second. The -\CI{thermostat} expression will enable a digital pin powering a cooling fan -when the analog pin representing a temperature sensor is too high. -\CI{thermostat`} shows the same expression but now using the assignment style -\gls{GPIO} technique. The \CI{thermostat} example also shows that it is not -necessary to run everything as a \CI{task}. The main program code can also just -consist of the contents of the root \CI{main} itself. - -\begin{lstlisting}[% - label={lst:exmtask},caption={Some example \gls{mTask}-\glspl{Task}}] -blink = task \blink=(\x. - IF (x ==. lit True) (ledOn led) (ledOff led) :. - blink (lit 1000) (Not x) - In {main=blink (lit 1000) True} - -thermostat :: Main (View () Stmt) -thermostat = {main = - IF (analogRead A0 >. lit 50) - ( digitalWrite D0 (lit True) ) - ( digitalWrite D0 (lit False) ) - } - -thermostat` :: Main (View () Stmt) -thermostat` = let - a0 = aIO A0 - d0 = dIO D0 in {main = IF (a0 >. lit 50) (d0 =. lit True) (d0 =. lit False) } -\end{lstlisting} diff --git a/methods.top.tex b/methods.top.tex deleted file mode 100644 index 9d07eec..0000000 --- a/methods.top.tex +++ /dev/null @@ -1,209 +0,0 @@ -\section{iTasks} -\gls{TOP} is a novel programming paradigm implemented as -\gls{iTasks}~\cite{achten_introduction_2015} in the pure lazy functional -language \gls{Clean}~\cite{brus_cleanlanguage_1987}. \gls{iTasks} is an -\gls{EDSL} to model workflow tasks in the broadest sense. A \gls{Task} is just -a function that --- given some state --- returns the observable \CI{TaskValue}. -The \CI{TaskValue} of a \CI{Task} can have different states. Not all state -transitions are possible as shown in Figure~\ref{fig:taskvalue}. Once a value -is stable it can never become unstable again. Stability is often reached by -pressing a confirmation button. \glspl{Task} yielding a constant value are -immediately stable. - -A simple \gls{iTasks} example illustrating the route to stability of a -\gls{Task} in which the user has to enter a full name is shown in -Listing~\ref{lst:taskex}. The code is accompanied by screenshots showing the -user interface in Figure~\ref{fig:taskex1},~\ref{fig:taskex2} -and~\ref{fig:taskex3}. The \CI{TaskValue} of the \gls{Task} is in the first -image in the \CI{NoValue} state, the second image does not have all the fields -filled in and therefore the \CI{TaskValue} remains \CI{NoValue}. In the third -image all fields are entered and the \CI{TaskValue} transitions to the -\CI{Unstable} state. When the user presses \emph{Continue} the value becomes -\CI{Stable} and cannot be changed any further. - -\begin{figure}[H] - \centering - \includegraphics[width=.5\linewidth]{fig-taskvalue} - \caption{The states of a \CI{TaskValue}}\label{fig:taskvalue} -\end{figure} - -\begin{lstlisting}[label={lst:taskex},% - caption={An example \gls{Task} for entering a name}] -:: Name = { firstname :: String - , lastname :: String - } - -derive class iTask Name - -enterInformation :: String [EnterOption m] -> (Task m) | iTask m - -enterName :: Task Name -enterName = enterInformation "Enter your name" [] -\end{lstlisting} - -\begin{figure}[H] - \centering - \begin{subfigure}{.25\textwidth} - \centering - \includegraphics[width=.9\linewidth]{taskex1} - \caption{Initial interface}\label{fig:taskex1} - \end{subfigure} - \begin{subfigure}{.25\textwidth} - \centering - \includegraphics[width=.9\linewidth]{taskex2} - \caption{Incomplete entrance}\label{fig:taskex2} - \end{subfigure} - \begin{subfigure}{.25\textwidth} - \centering - \includegraphics[width=.9\linewidth]{taskex3} - \caption{Complete entry}\label{fig:taskex3} - \end{subfigure} - \caption{Example of a generated user interface} -\end{figure} - -For a type to be suitable, it must have instances for a collection of generic -functions that is captured in the class \CI{iTask}. Basic types have -specialization instances for these functions and show an interface accordingly. -Derived interfaces can be modified with decoration operators or specializations -can be created. - -\section{Combinators} -\Glspl{Task} can be combined using so called \gls{Task}-combinators. -Combinators describe relations between \glspl{Task}. There are only two basic -types of combinators; parallel and sequence. All other combinators are -derived from the basic combinators. Type signatures of simplified versions of -the basic combinators and their derivations are given in -Listing~\ref{lst:combinators} - -\begin{lstlisting}[% - caption={\Gls{Task}-combinators},label={lst:combinators}] -//Step combinator -(>>=) infixl 1 :: (Task a) (a -> Task b) -> Task b | iTask a & iTask b -(>>*) infixl 1 :: (Task a) [TaskCont a (Task b)] -> Task b | iTask a & iTask b -:: TaskCont a b - = OnValue ((TaskValue a) -> Maybe b) - | OnAction Action ((TaskValue a) -> Maybe b) - | E.e: OnException (e -> b) & iTask e - | OnAllExceptions (String -> b) -:: Action = Action String - -//Parallel combinators -(-||-) infixr 3 :: (Task a) (Task a) -> Task a | iTask a -(||-) infixr 3 :: (Task a) (Task b) -> Task b | iTask a & iTask b -(-||) infixl 3 :: (Task a) (Task b) -> Task a | iTask a & iTask b -(-&&-) infixr 4 :: (Task a) (Task b) -> Task (a,b) | iTask a & iTask b -\end{lstlisting} - -\paragraph{Sequence:} -The implementation for the sequence combinator is called the -\CI{step} (\CI{>>*}). This combinator runs the left-hand \gls{Task} and -starts the right-hand side when a certain predicate holds. Predicates -can be propositions about the \CI{TaskValue}, user actions from within -the web browser or a thrown exception. The familiar -bind-combinator is an example of a sequence combinator. This combinator -runs the left-hand side and continues to the right-hand \gls{Task} if -there is an \CI{UnStable} value and the user presses continue or when -the value is \CI{Stable}. The combinator could have been implemented -as follows: -\begin{lstlisting}[language=Clean] -(>>=) infixl 1 :: (Task a) (a -> (Task b)) -> (Task b) | iTask a & iTask b -(>>=) ta f = ta >>* [OnAction "Continue" onValue, OnValue onStable] - where - onValue (Value a _) = Just (f a) - onValue _ = Nothing - - onStable (Value a True) = Just (f a) - onStable _ = Nothing -\end{lstlisting} - -\paragraph{Parallel:} -The parallel combinator allows for concurrent \glspl{Task}. The -\glspl{Task} combined with these operators will appear at the same time -in the web browser of the user and the results are combined as the type -dictates. All parallel combinators used are derived from the basic parallel -combinator that is very complex and only used internally. - -\section{Shared Data Sources} -\Glspl{SDS} are an abstraction over resources that are available in the world -or in the \gls{iTasks} system. The shared data can be a file on disk, the -system time, a random integer or just some data stored in memory. The actual -\gls{SDS} is just a record containing functions on how to read and write the -source. In these functions the \CI{*IWorld} --- which in turn contains the real -\CI{*World} --- is available. Accessing the outside world is required for -interacting with it and thus the functions can access files on disk, raw -memory, other \glspl{SDS} and hardware. - -The basic operations for \glspl{SDS} are get, set and update. The signatures -for these functions are shown in Listing~\ref{lst:shares}. By default, all -\glspl{SDS} are files containing a \gls{JSON} encoded version of the object and -thus are persistent between restarts of the program. Library functions for -shares residing in memory are available as well. The three main operations on -shares are atomic in the sense that during reading no other \glspl{Task} are -executed. The system provides useful functions to transform, map and combine -\glspl{SDS} using combinators. The system also provides functionality to -inspect the value of an \gls{SDS} and act upon a change. \Glspl{Task} waiting on -an \gls{SDS} to change are notified when needed. This results in low resource -usage because \glspl{Task} are never constantly inspecting \gls{SDS} values but -are notified. - -\begin{lstlisting}[% - label={lst:shares},caption={\Gls{SDS} functions}] -:: RWShared p r w = ... -:: ReadWriteShared r w :== RWShared () r w -:: ROShared p r :== RWShared p () r -:: ReadOnlyShared r :== ROShared () r - -:: Shared r :== ReadWriteShared r r - -get :: (ReadWriteShared r w) -> Task r | iTask r -set :: w (ReadWriteShared r w) -> Task w | iTask w -upd :: (r -> w) (ReadWriteShared r w) -> Task w | iTask r & iTask w - -sharedStore :: String a -> Shared a | JSONEncode{|*|}, JSONDecode{|*|} -\end{lstlisting} - -\section{Parametric Lenses} -\Glspl{SDS} can contain complex data structures such as lists, trees and even -resources in the outside world. Sometimes, an update action only updates a part -of the resource. When this happens, all waiting \glspl{Task} looking at the -resource are notified of the update. However, it may be the case that -\glspl{Task} were only looking at parts of the structure that was not updated. -To solve this problem, parametric lenses were -introduced~\cite{domoszlai_parametric_2014}. - -Parametric lenses add a type variable to the \gls{SDS}. This type variable is -fixed to the void type (i.e. \CI{()}) in the given functions. When an \gls{SDS} -executes a write operation, it also provides the system with a notification -predicate. This notification predicate is a function \CI{p -> Bool} where -\CI{p} is the parametric lens type. This allows programmers to create a big -\gls{SDS}, and have \glspl{Task} only look at parts of the big \gls{SDS}. This -technique is used in the current system in memory shares. The \CI{IWorld} -contains a map that is accessible through an \gls{SDS}. While all data is -stored in the map, only \glspl{Task} looking at a specific entry are notified -when the structure is updated. The type of the parametric lens is the key in -the map. - -Functionality for setting parameters is available in the system. The most -important functions are the \CI{sdsFocus} and the \CI{sdsLens} function. These -functions are listed in Listing~\ref{lst:focus}. \CI{sdsFocus} allows the -programmer to fix a parametric lens value. \CI{sdsLens} is a kind of -\CI{mapReadWrite} including access to the parametric lens value. This allows -the creation of, for example, \glspl{SDS} that only read and write to parts of -the original \gls{SDS}. - -\begin{lstlisting}[label={lst:focus}, - caption={Parametric lens functions}] -sdsFocus :: p (RWShared p r w) -> RWShared p` r w | iTask p - -:: SDSNotifyPred p :== p -> Bool - -:: SDSLensRead p r rs = SDSRead (p -> rs -> MaybeError TaskException r) - | SDSReadConst (p -> r) -:: SDSLensWrite p w rs ws = SDSWrite (p -> rs -> w -> MaybeError TaskException (Maybe ws)) - | SDSWriteConst (p -> w -> MaybeError TaskException (Maybe ws)) -:: SDSLensNotify p w rs = SDSNotify (p -> rs -> w -> SDSNotifyPred p) - | SDSNotifyConst (p -> w -> SDSNotifyPred p) - -sdsLens :: String (p -> ps) (SDSLensRead p r rs) (SDSLensWrite p w rs ws) (SDSLensNotify p w rs) - (RWShared ps rs ws) -> RWShared p r w | iTask ps -\end{lstlisting} diff --git a/mtask.control.tex b/mtask.control.tex new file mode 100644 index 0000000..680e10f --- /dev/null +++ b/mtask.control.tex @@ -0,0 +1,34 @@ +Looping of \glspl{Task} happens because \glspl{Task} are executed after waiting +a specified amount of time or when they are launched by another \gls{Task} or +even themselves. Therefore there is no need for loop control flow functionality +such as \emph{while} or \emph{for} constructions. The main control flow +operators are the sequence operator and the \emph{if} statement. Both are shown +in Listing~\ref{lst:control}. The first class of \emph{If} statements describes +the regular \emph{if} statement. The expressions given can have any role. The +functional dependency on \CI{s} determines the return type of the statement. +The listing includes examples of implementations that illustrate this +dependency. A special \emph{If} statement --- only used for statements --- is +also added under the name \CI{IF}, of which the \CI{?} is a conditional +statement to execute. + +The sequence operator is straightforward and its only function is to tie +two expressions together. The left expression is executed first, followed by +the right expression. + +\begin{lstlisting}[% + label={lst:control},caption={Control flow operators}] +class If v q r ~s where + If :: (v Bool p) (v t q) (v t r) -> v t s | ... + +class IF v where + IF :: (v Bool p) (v t q) (v s r) -> v () Stmt | ... + (?) infix 1 :: (v Bool p) (v t q) -> v () Stmt | ... + +instance If Code Stmt Stmt Stmt +instance If Code e Stmt Stmt +instance If Code Stmt e Stmt +instance If Code x y Expr + +class seq v where + (:.) infixr 0 :: (v t p) (v u q) -> v u Stmt | ... +\end{lstlisting} diff --git a/mtask.examples.tex b/mtask.examples.tex new file mode 100644 index 0000000..925b66b --- /dev/null +++ b/mtask.examples.tex @@ -0,0 +1,31 @@ +Some example \gls{mTask}-\glspl{Task} using almost all of their functionality +are shown in Listing~\ref{lst:exmtask}. The \gls{mTask}-\glspl{Task} shown in +the example do not belong to a particular view and therefore are of the type +\CI{View t r}. The \CI{blink} \gls{mTask} show the classic \gls{Arduino} +blinking led application that blinks a certain \gls{LED} every second. The +\CI{thermostat} expression will enable a digital pin powering a cooling fan +when the analog pin representing a temperature sensor is too high. +\CI{thermostat`} shows the same expression but now using the assignment style +\gls{GPIO} technique. The \CI{thermostat} example also shows that it is not +necessary to run everything as a \CI{task}. The main program code can also just +consist of the contents of the root \CI{main} itself. + +\begin{lstlisting}[% + label={lst:exmtask},caption={Some example \gls{mTask}-\glspl{Task}}] +blink = task \blink=(\x. + IF (x ==. lit True) (ledOn led) (ledOff led) :. + blink (lit 1000) (Not x) + In {main=blink (lit 1000) True} + +thermostat :: Main (View () Stmt) +thermostat = {main = + IF (analogRead A0 >. lit 50) + ( digitalWrite D0 (lit True) ) + ( digitalWrite D0 (lit False) ) + } + +thermostat` :: Main (View () Stmt) +thermostat` = let + a0 = aIO A0 + d0 = dIO D0 in {main = IF (a0 >. lit 50) (d0 =. lit True) (d0 =. lit False) } +\end{lstlisting} diff --git a/mtask.expr.tex b/mtask.expr.tex new file mode 100644 index 0000000..62e9502 --- /dev/null +++ b/mtask.expr.tex @@ -0,0 +1,22 @@ +Expressions in the \gls{mTask}-\gls{EDSL} are divided into two types, namely +boolean expressions and arithmetic expressions. The class of arithmetic +language constructs also contains the function \CI{lit} that lifts a +host-language value into the \gls{EDSL} domain. All standard arithmetic +functions are included in the \gls{EDSL} but are omitted in the example for +brevity. Moreover, the class restrictions are only shown in the first functions +and are omitted in subsequent functions. Both the boolean expression and +arithmetic expression classes are shown in Listing~\ref{lst:arithbool}. + +\begin{lstlisting}[label={lst:arithbool}, + caption={Basic classes for expressions}] +class arith v where + lit :: t -> v t Expr + (+.) infixl 6 :: (v t p) (v t q) -> v t Expr | +, zero t & isExpr p & isExpr q + (-.) infixl 6 :: (v t p) (v t q) -> v t Expr | -, zero t & ... + ... +class boolExpr v where + Not :: (v Bool p) -> v Bool Expr | ... + (&.) infixr 3 :: (v Bool p) (v Bool q) -> v Bool Expr | ... + ... + (==.) infix 4 :: (v a p) (v a q) -> v Bool Expr | ==, toCode a & ... +\end{lstlisting} diff --git a/mtask.io.tex b/mtask.io.tex new file mode 100644 index 0000000..07eb3ea --- /dev/null +++ b/mtask.io.tex @@ -0,0 +1,47 @@ +Values can be assigned to all expressions that have an \CI{Upd} role. Examples +of such expressions are \glspl{SDS} and \gls{GPIO} pins. Moreover, class +extensions can be created for specific peripherals such as built-in +\glspl{LED}. The classes facilitating this are shown in +Listing~\ref{lst:sdsio}. In this way the assignment is the same for every +assignable entity. + +\begin{lstlisting}[% + label={lst:sdsio},caption={Input/Output classes}] +:: DigitalPin = D0 | D1 | D2 | D3 | D4 | D5 |D6 | D7 | D8 | D9 | D10 | D11 | D12 | D13 +:: AnalogPin = A0 | A1 | A2 | A3 | A4 | A5 +:: UserLED = LED1 | LED2 | LED3 + +class dIO v where dIO :: DigitalPin -> v Bool Upd +class aIO v where aIO :: AnalogPin -> v Int Upd +class analogRead v where + analogRead :: AnalogPin -> v Int Expr + analogWrite :: AnalogPin (v Int p) -> v Int Expr +class digitalRead v where + digitalRead :: DigitalPin -> v Bin Expr + digitalWrite :: DigitalPin (v Bool p) -> v Int Expr + +:: UserLED = LED1 | LED2 | LED3 +class userLed v where + ledOn :: (v UserLED q) -> (v () Stmt) + ledOff :: (v UserLED q) -> (v () Stmt) + +class assign v where + (=.) infixr 2 :: (v t Upd) (v t p) -> v t Expr | ... +\end{lstlisting} + +One way of storing data in \gls{mTask}-\glspl{Task} is using \glspl{SDS}. +\glspl{SDS} serve as variables in \gls{mTask} and maintain their value across +executions. \glspl{SDS} can be used by multiple \glspl{Task} and can be used +to share data. The classes associated with \glspl{SDS} are listed in +Listing~\ref{lst:sdsclass}. The \CI{Main} type is introduced to box an +\gls{mTask} and make it recognizable by the type system by separating programs +and decorations such as \glspl{SDS}. + +\begin{lstlisting}[% + label={lst:sdsclass},caption={\glspl{SDS} in \gls{mTask}}] +:: In a b = In infix 0 a b +:: Main a = {main :: a} + +class sds v where + sds :: ((v t Upd)->In t (Main (v c s))) -> (Main (v c s)) | ... +\end{lstlisting} diff --git a/mtask.semantics b/mtask.semantics new file mode 100644 index 0000000..2cee3cc --- /dev/null +++ b/mtask.semantics @@ -0,0 +1,57 @@ +The \gls{C}-backend of the \gls{mTask}-system has an engine that is generated +alongside the code for the \glspl{Task}. This engine will execute the +\gls{mTask}-\glspl{Task} according to certain rules and semantics. +\gls{mTask}-\glspl{Task} do not behave like functions but more like +\gls{iTasks}-\glspl{Task}. An \gls{mTask} is queued when either its timer runs +out or when it is launched by another \gls{mTask}. When an \gls{mTask} is +queued it does not block the execution and it will return immediately while the +actual \gls{Task} will be executed anytime in the future. + +The \gls{iTasks}-backend simulates the \gls{C}-backend and thus uses the same +semantics. This engine expressed in pseudocode is listed as +Algorithm~\ref{lst:engine}. All the \glspl{Task} are inspected on their waiting +time. When the waiting time has not passed; the delta is subtracted and the +\gls{Task} gets pushed to the end of the queue. When the waiting has surpassed +they are executed. When an \gls{mTask} opts to queue another \gls{mTask} it +can just append it to the queue. + +~\\ +\begin{algorithm}[H] + \KwData{\textbf{queue} queue, \textbf{time} $t, t_p$} + + $t\leftarrow\text{now}()$\; + \Begin{ + \While{true}{ + $t_p\leftarrow t$\; + $t\leftarrow\text{now}()$\; + \If{notEmpty$($queue$)$}{ + $task\leftarrow \text{queue.pop}()$\; + $task$.wait $\leftarrow task$.wait $-(t-t_p)$\; + \eIf{$task.wait>t_0$}{ + queue.append$(task)$\; + }{ + run\_task$(task)$\; + } + } + } + } + \caption{Engine pseudocode for the \gls{C}- and + \gls{iTasks}-view}\label{lst:engine} +\end{algorithm} +~\\ + +To achieve this in the \gls{EDSL} a \gls{Task} class is added that work in a +similar fashion as the \texttt{sds} class. This class is listed in +Listing~\ref{lst:taskclass}. \glspl{Task} can have an argument and always have +to specify a delay or waiting time. The type signature of the \CI{mtask} is +complex and therefore an example is given. The aforementioned Listing +shows a simple specification containing one \gls{Task} that increments a value +indefinitely every one seconds. + +\begin{lstlisting}[label={lst:taskclass},% + caption={The classes for defining \glspl{Task}}] +class mtask v a where + task :: (((v delay r) a->v MTask Expr)->In (a->v u p) (Main (v t q))) -> Main (v t q) | ... + +count = task \count = (\n.count (lit 1000) (n +. One)) In {main = count (lit 1000) Zero} +\end{lstlisting} diff --git a/mtask.tex b/mtask.tex new file mode 100644 index 0000000..12bf498 --- /dev/null +++ b/mtask.tex @@ -0,0 +1,52 @@ +The \gls{mTask}-\gls{EDSL} is the language used for the proposed system. The +\gls{mTask}-\gls{EDSL} was created by Koopman et al.\ and supports several +views such as an \gls{iTasks} simulation and a \gls{C}-code generator. The +\gls{EDSL} was designed to generate a ready-to-compile \gls{TOP}-like program +for microcontrollers such as the \gls{Arduino}~\cite{koopman_type-safe_nodate}% +\cite{plasmeijer_shallow_2016}. + +The \gls{mTask}-\gls{EDSL} is a shallowly embedded class based \gls{EDSL} and +therefore it is very suitable to have a new backend that partly implements the +classes given. The following sections show the details of the \gls{EDSL} that +is used in this extension. The parts of the \gls{EDSL} that are not used will +not be discussed and the details of those parts can be found in the cited +literature. + +A view for the \gls{mTask}-\gls{EDSL} is a type with two free type +variables\footnote{kind \CI{*->*->*}.} that implements some of the classes +given. The types do not have to be present as fields in the view and can, and +will most often, be exclusively phantom types. Thus, views are of the +form:\\\CI{:: v t r = ...}. The first type variable will be the type of the +view. The second type variable will be the type of the \gls{EDSL}-expression +and the third type variable represents the role of the expression. Currently +the role of the expressions form a hierarchy. The three roles and their +hierarchy are shown in Listing~\ref{lst:exprhier}. This implies that everything +is a statement, only an \CI{Upd} and an \CI{Expr} are expressions. The \CI{Upd} +restriction describes updatable expressions such as \gls{GPIO} pins and +\glspl{SDS}. + +\begin{lstlisting}[% + label={lst:exprhier},caption={Expression role hierarchy}] +:: Upd = Upd +:: Expr = Expr +:: Stmt = Stmt + +class isExpr a :: a -> Int +instance isExpr Upd +instance isExpr Expr +\end{lstlisting} + +\section{Expressions} +\input{mtask.expr} + +\section{Control flow} +\input{mtask.control} + +\section{Input/Output and class extensions} +\input{mtask.io} + +\section{Semantics} +\input{mtask.semantics} + +\section{Example mTask} +\input{mtask.examples} diff --git a/results.mtask.tex b/mtaskext.bytecode.tex similarity index 69% rename from results.mtask.tex rename to mtaskext.bytecode.tex index 6ec25a4..111be6f 100644 --- a/results.mtask.tex +++ b/mtaskext.bytecode.tex @@ -1,68 +1,3 @@ -The \glspl{Task} suitable for a client are called \gls{mTask}-\gls{Task} and -are written in the aforementioned \gls{mTask}-\gls{EDSL}. Some functionality of -the original \gls{mTask}-\gls{EDSL} will not be used in this system. -Conversely, some functionality needed was not available in the existing -\gls{EDSL}. Due to the nature of class based shallow embedding this obstacle is -easy to solve. A type --- housing the \gls{EDSL} --- does not have to implement -all the available classes. Moreover, classes can be added at will without -interfering with the existing views. - -\section{\gls{Task} Semantics} -The current \gls{mTask} engine for devices does not support \glspl{Task} in the -sense that the \gls{C}-view does. \Glspl{Task} used with the \gls{C}-view are a -main program that executes code and launches \glspl{Task}. It was also possible -to just have a main program. The current \gls{mTask}-system only supports main -programs. Sending a \gls{Task} always goes together with choosing a scheduling -strategy. This strategy can be one of the following three strategies as -reflected in the \CI{MTTask} message type. - -\begin{itemize} - \item\CI{OneShot} - - \CI{OneShot} takes no parameters and means that the \gls{Task} will run - once and will then be removed automatically. This type of scheduling - could be useful, for example, in retrieving sensor information on - request of a user. - \item\CI{OnInterval} - - \CI{OnInterval} has the number of milliseconds to wait in between - executions as a parameter. \Glspl{Task} running with this scheduling - method are executed at predetermined intervals. - \item\CI{OnInterrupt} - - The last scheduling method is running \glspl{Task} on a specific - interrupt. Unfortunatly, due to time constraints and focus, none of the - current client implementations support this. Interrupt scheduling is - useful for \glspl{Task} that have to react on a certain type of - hardware event such as the press of a button. -\end{itemize} - -\section{\gls{SDS} semantics} -\Gls{mTask}-\glspl{SDS} on a client are available on the server as in the form -of regular \gls{iTasks}-\glspl{SDS}. However, the same freedom that an -\gls{SDS} has in the \gls{iTasks}-system is not given for \glspl{SDS} that -reside on the client. Not all types are suitable to be located on a client, -simply because it needs to be representable on clients. Moreover, \glspl{SDS} -behave a little different in an \gls{mTask} device compared to in the -\gls{iTasks} system. In an \gls{iTasks} system, when the \gls{SDS} is updated, -a broadcast to all watching \glspl{Task} in the system is made to notify them -of the update. \glspl{SDS} can update often and the update might not be the -final value it will get. Implementing the same functionality on the \gls{mTask} -client would result in a lot of expensive unneeded bandwidth usage. Therefore -a device must publish the \gls{SDS} explicitly to save bandwidth. - -To add this functionality, the \CI{sds} class could be extended. However, this -would result in having to update all existing views that use the \CI{sds} -class. Therefore, an extra class is added that contains the extra -functionality. Programmers can choose to implement it for existing views in the -future but are not obliged to. The publication function has the following -signature: -\begin{lstlisting}[caption={The \texttt{sdspub} class}] -class sdspub v where - pub :: (v t Upd) -> v t Expr | type t -\end{lstlisting} - -\section{Bytecode compilation view}\label{sec:compiler} The \gls{mTask}-\glspl{Task} are sent to the device in bytecode and are saved in the memory of the device. To compile the \gls{EDSL} code to bytecode, a view is added to the \gls{mTask}-system encapsulated in the type \CI{ByteCode}. As @@ -118,8 +53,8 @@ instance serial ByteCode \subsection{Instruction Set}\label{sec:instruction} The instruction set is given in Listing~\ref{bc:instr}. The instruction set is -kept large, but under $255$, to get as much expressive power as possible while -keeping all instruction within one byte. +kept large, but the number of instructions stays under $255$ to get as much +expressive power while keeping all instruction within one byte. The interpreter running in the client is a stack machine. The virtual instruction \CI{BCLab} is added to allow for an easy implementation of jumping. @@ -277,15 +212,15 @@ addSDS sds v s = {s & sdss=[{sds & sdsval=BCValue v}:s.sdss]} \end{lstlisting} All assignable types compile to a \gls{RWST} which writes the specific fetch -instruction(s). For example, using an \gls{SDS} always results in an expression -of the form \CI{sds \x=4 In ...}. The actual \CI{x} is the \gls{RWST} that -always writes one \CI{BCSdsFetch} instruction with the correctly embedded -\gls{SDS}. Assigning to an analog pin will result in the \gls{RWST} containing -the \CI{BCAnalogRead} instruction. When the operation on the assignable is not -a read operation from but an assign operation, the instruction(s) will be -rewritten accordingly. This results in a \CI{BCSdsStore} or \CI{BCAnalogWrite} -instruction respectively. The implementation for this is given in -Listing~\ref{lst:assignmentview}. +instruction(s). For example, using an \gls{SDS} always results in an +expression of the form \CI{sds \x=4 In ...}. The actual \CI{x} is the +\gls{RWST} that always writes one \CI{BCSdsFetch} instruction with the +correctly embedded \gls{SDS}. Assigning to an analog pin will result in the +\gls{RWST} containing the \CI{BCAnalogRead} instruction. When the operation on +the assignable is not a read operation from but an assign operation, the +instruction(s) will be rewritten accordingly. This results in a \CI{BCSdsStore} +or \CI{BCAnalogWrite} instruction respectively. The implementation for this is +given in Listing~\ref{lst:assignmentview}. \begin{lstlisting}[label={lst:assignmentview},% caption={Bytecode view implementation for assignment.}] @@ -333,20 +268,3 @@ toMessages interval x oldstate # newsdss = difference newstate.sdss oldstate.sdss = ([MTSds sdsi e\\{sdsi,sdsval=e}<-newsdss] ++ [MTTask interval bc], newstate) \end{lstlisting} - -\section{Examples} -The thermostat example given previously in Listing~\ref{lst:exmtask} would be -compiled to the following code. The left column indicates the -position in the program memory. - -\begin{lstlisting}[caption={Thermostat bytecode},language=c] - 1-2 : BCAnalogRead (Analog A0) - 3-6 : BCPush (Int 50) - 7 : BCGre - 8-9 : BCJmpF 17 //Jump to else -10-12: BCPush (Bool 1) -13-14: BCDigitalWrite (Digital D0) -15-16: BCJmp 21 //Jump to end of if -17-19: BCPush (Bool 0) //Else label -20 : BCDigitalWrite (Digital D0) -\end{lstlisting} diff --git a/mtaskext.examples.tex b/mtaskext.examples.tex new file mode 100644 index 0000000..66c2466 --- /dev/null +++ b/mtaskext.examples.tex @@ -0,0 +1,15 @@ +The thermostat example given previously in Listing~\ref{lst:exmtask} would be +compiled to the following code. The left column indicates the +position in the program memory. + +\begin{lstlisting}[caption={Thermostat bytecode},language=c] + 1-2 : BCAnalogRead (Analog A0) + 3-6 : BCPush (Int 50) + 7 : BCGre + 8-9 : BCJmpF 17 //Jump to else +10-12: BCPush (Bool 1) +13-14: BCDigitalWrite (Digital D0) +15-16: BCJmp 21 //Jump to end of if +17-19: BCPush (Bool 0) //Else label +20 : BCDigitalWrite (Digital D0) +\end{lstlisting} diff --git a/mtaskext.sdssem.tex b/mtaskext.sdssem.tex new file mode 100644 index 0000000..5ba33ad --- /dev/null +++ b/mtaskext.sdssem.tex @@ -0,0 +1,23 @@ +\Gls{mTask}-\glspl{SDS} on a client are available on the server as in the form +of regular \gls{iTasks}-\glspl{SDS}. However, the same freedom that an +\gls{SDS} has in the \gls{iTasks}-system is not given for \glspl{SDS} that +reside on the client. Not all types are suitable to be located on a client, +simply because it needs to be representable on clients. Moreover, \glspl{SDS} +behave a little different in an \gls{mTask} device compared to in the +\gls{iTasks} system. In an \gls{iTasks} system, when the \gls{SDS} is updated, +a broadcast to all watching \glspl{Task} in the system is made to notify them +of the update. \glspl{SDS} can update often and the update might not be the +final value it will get. Implementing the same functionality on the \gls{mTask} +client would result in a lot of expensive unneeded bandwidth usage. Therefore +a device must publish the \gls{SDS} explicitly to save bandwidth. + +To add this functionality, the \CI{sds} class could be extended. However, this +would result in having to update all existing views that use the \CI{sds} +class. Therefore, an extra class is added that contains the extra +functionality. Programmers can choose to implement it for existing views in the +future but are not obliged to. The publication function has the following +signature: +\begin{lstlisting}[caption={The \texttt{sdspub} class}] +class sdspub v where + pub :: (v t Upd) -> v t Expr | type t +\end{lstlisting} diff --git a/mtaskext.tasksem.tex b/mtaskext.tasksem.tex new file mode 100644 index 0000000..470a257 --- /dev/null +++ b/mtaskext.tasksem.tex @@ -0,0 +1,28 @@ +The current \gls{mTask} engine for devices does not support \glspl{Task} in the +sense that the \gls{C}-view does. \Glspl{Task} used with the \gls{C}-view are a +main program that executes code and launches \glspl{Task}. It was also possible +to just have a main program. The current \gls{mTask}-system only supports main +programs. Sending a \gls{Task} always goes together with choosing a scheduling +strategy. This strategy can be one of the following three strategies as +reflected in the \CI{MTTask} message type. + +\begin{itemize} + \item\CI{OneShot} + + \CI{OneShot} takes no parameters and means that the \gls{Task} will run + once and will then be removed automatically. This type of scheduling + could be useful, for example, in retrieving sensor information on + request of a user. + \item\CI{OnInterval} + + \CI{OnInterval} has the number of milliseconds to wait in between + executions as a parameter. \Glspl{Task} running with this scheduling + method are executed at predetermined intervals. + \item\CI{OnInterrupt} + + The last scheduling method is running \glspl{Task} on a specific + interrupt. Unfortunatly, due to time constraints and focus, none of the + current client implementations support this. Interrupt scheduling is + useful for \glspl{Task} that have to react on a certain type of + hardware event such as the press of a button. +\end{itemize} diff --git a/mtaskext.tex b/mtaskext.tex new file mode 100644 index 0000000..a8d525f --- /dev/null +++ b/mtaskext.tex @@ -0,0 +1,20 @@ +The \glspl{Task} suitable for a client are called \gls{mTask}-\gls{Task} and +are written in the aforementioned \gls{mTask}-\gls{EDSL}. Some functionality of +the original \gls{mTask}-\gls{EDSL} will not be used in this system. +Conversely, some functionality needed was not available in the existing +\gls{EDSL}. Due to the nature of class based shallow embedding this obstacle is +easy to solve. A type --- housing the \gls{EDSL} --- does not have to implement +all the available classes. Moreover, classes can be added at will without +interfering with the existing views. + +\section{\gls{Task} Semantics} +\input{mtaskext.tasksem} + +\section{\gls{SDS} semantics} +\input{mtaskext.sdssem} + +\section{Bytecode compilation view}\label{sec:compiler} +\input{mtaskext.bytecode} + +\section{Examples} +\input{mtaskext.examples} diff --git a/results.arch.tex b/results.arch.tex deleted file mode 100644 index b934158..0000000 --- a/results.arch.tex +++ /dev/null @@ -1,631 +0,0 @@ -The goal of the system as a whole is to offer a framework of functions with -which an \gls{iTasks}-system can add, change and remove devices at runtime. -Moreover, the \gls{iTasks}-system can send \gls{mTask}-\glspl{Task} --- -compiled at runtime to bytecode by the \gls{mTask}-view --- to the device. The -device runs an interpreter which can execute the \gls{Task}'s bytecode. Device -profiles should be persistent during reboots of the \gls{iTasks}-system. The -methods of interacting with \gls{mTask}-\gls{Task} should be analogous to -interacting with \gls{iTasks}-\glspl{Task}. This means that programmers can -access the \glspl{SDS} made for a device in the same way as regular \glspl{SDS} -and they can execute \gls{mTask}-\glspl{Task} as if they where normal -\gls{iTasks}-\glspl{Task}. - -The following terms will be used throughout the following chapter: -\begin{itemize} - \item Device, Client - - These terms denotes the actual device connected to the system. This can - be a real device such as a microcontroller but it can also just be a - program on the same machine as the server functioning as a client. - \item Server, \gls{iTasks}-System - - This is the actual executable serving the \gls{iTasks} application. The - system contains \glspl{Task} taking care of the communication with the - clients. - \item System - - The system describes the complete ecosystem, containing both the server - and the clients including the communication between them. - \item Engine - - The runtime system of the client is called the engine. This program - handles communicating with the server and runs the interpreter for the - \glspl{Task} on the client. -\end{itemize} - -\section{Devices} -A device is suitable for the system if it can run the engine. -The engine is compiled from one codebase and devices implement (part of) the -device specific interface. The shared codebase only uses standard \gls{C} and -no special libraries or tricks are used. Therefore, the code is compilable for -almost any device or system. Note that it is not needed to implement a full -interface. The full interface --- excluding the device specific settings --- is -listed in Appendix~\ref{app:device-interface}. The interface works in a -similar fashion as the \gls{EDSL}. Devices do not have to implement all -functionality, this is analogous to the fact that views do not have to -implement all type classes in the \gls{EDSL}. When the device connects with -the server for the first time, the specifications of what is implemented is -communicated. - -At the time of writing the following device families are supported and can run -the device software. -\begin{itemize} - \item \texttt{POSIX} compatible systems connected via the \gls{TCP}. - - This includes systems running \emph{Linux} and \emph{MacOS}. - \item The \texttt{STM32} microcontrollers family supported by - \texttt{ChibiOS} connected via serial communication. - - This is tested in particular on the \texttt{STM32f7x} series \gls{ARM} - development board. - \item Microcontrollers which are programmable in the \gls{Arduino} \gls{IDE} - connected via serial communication or via \gls{TCP} over WiFi or - Ethernet. - - This does not only include \gls{Arduino} compatible boards but also - other boards capable of running \gls{Arduino} code. A port of the - client has been made for the \texttt{ESP8266} powered \emph{NodeMCU} - that is connected via \gls{TCP} over WiFi. A port also has been made - for the regular \gls{Arduino} \emph{UNO} board which only boasts a - meager \emph{2K} \emph{RAM}. The stack size and storage available for - devices boasting this little \emph{RAM} has to be smaller than default - but are still suitable to hold a hand full of \glspl{Task}. -\end{itemize} - -\subsection{Client} -\subsubsection{Engine} -The client is in a constant loop listening for input and waiting to execute -\glspl{Task}. The pseudocode for this is shown in Algorithm~\ref{alg:client}. -The \CI{input\_available} function waits for input, but has a timeout set which -can be interrupted. The timeout of the function determines the amount of loops -per time interval and is a parameter that can be set during compilation for a -device. - -\begin{algorithm} - \KwData{ - \textbf{list} $tasks$, - \textbf{time} $tm$ - } - - \Begin{ - \While{true}{ - \If{input\_available$()$}{ - receive\_data()\; - } - - $tm\leftarrow \text{now}()$\; - \ForEach{$t\leftarrow tasks$}{ - \uIf{is\_interrupt$(t)$ \textbf{and} had\_interrupt$(t)$}{ - run\_task$(t)$\; - } - \ElseIf{$tm-t.\text{lastrun} > t.\text{interval}$}{ - run\_task$(t)$\; - \uIf{$t.\text{interval}==0$}{ - delete\_task$(t)$\; - }\Else{ - $t.\text{lastrun}\leftarrow t$\; - } - } - } - } - } - \caption{Engine pseudocode}\label{alg:client} -\end{algorithm} - -\subsubsection{Storage} -\glspl{Task} and \glspl{SDS} are stored on the client in memory. Some devices -have very little memory and therefore memory space is very expensive and needs -to be used optimally. Almost all microcontrollers support heaps nowadays, -however, the functions for allocating and freeing the memory on the heap are -not very space optimal and often leave holes in the heap if allocations are not -freed in reverse order. To overcome this problem the client will allocate a big -memory segment in the global data block. This block of memory resides under the -stack and its size can be set in the interface implementation. This block of -memory will be managed in a similar way as the entire memory space of the -device is managed. \Glspl{Task} will grow from the bottom up and \glspl{SDS} -will grow from the top down. - -When a \gls{Task} is received, the program will traverse the memory space from -the bottom up, jumping over all \glspl{Task}. A \gls{Task} is stored as the -structure followed directly by its bytecode. Therefore it only takes two jumps -to determine the size of the \gls{Task}. When the program arrived at the last -\gls{Task}, this place is returned and the newly received \gls{Task} can be -copied to there. This method is analogously applied for \glspl{SDS}, however, -the \glspl{SDS} grow from the bottom down. - -When a \gls{Task} or \gls{SDS} is removed, all remaining objects are compressed -again. This means that if the first received \gls{Task} is removed, all -\glspl{Task} received later will have to move back. Obviously, this is quite -time intensive but it can not be permitted to leave holes in the memory since -the memory space is so limited. This techniques allows for even the smallest -tested microcontrollers with only $2K$ \emph{RAM} to hold several \glspl{Task} -and \glspl{SDS}. If this technique would not be used the memory space will -decrease over time and the client can then not run for very long since holes -are evidently created at some point. - -The structure instances and helper functions for traversing them in memory for -\glspl{Task} and \glspl{SDS} are shown in Listing~\ref{lst:structs}. - -\begin{lstlisting}[language=C,label={lst:structs},% - caption={The data type storing the \glspl{Task}},float] -struct task { - uint16_t tasklength; - uint16_t interval; - unsigned long lastrun; - uint8_t taskid; - uint8_t *bc; -}; - -struct task *task_head(void); -struct task *task_next(struct task *t); - -struct sds { - int id; - int value; - char type; -}; - -struct sds *sds_head(void); -struct sds *sds_next(struct sds *s); -\end{lstlisting} - -\subsubsection{Interpretation} -The execution of a \gls{Task} is started by running the \CI{run\_task} function -and always starts with setting the program counter and stack -pointer to zero and the bottom respectively. When finished, the -interpreter executes one step at the time while the program counter is smaller -than the program length. This code is listed in Listing~\ref{lst:interpr}. One -execution step is basically a big switch statement going over all possible -bytecode instructions. Of some instructions, the implementations are shown in -the listing. The \CI{BCPush} instruction is a little more complicated in the -real code because some decoding will take place as not all \CI{BCValue}s are of -the same length and are encoded. - -\begin{lstlisting}[language=C,label={lst:interpr},% - caption={Rough code outline for interpretation}] -#define f16(p) program[pc]*265+program[pc+1] - -void run_task(struct task *t){ - uint8_t *program = t->bc; - int plen = t->tasklength; - int pc = 0; - int sp = 0; - while(pc < plen){ - switch(program[pc++]){ - case BCNOP: - break; - case BCPUSH: - stack[sp++] = pc++ //Simplified - break; - case BCPOP: - sp--; - break; - case BCSDSSTORE: - sds_store(f16(pc), stack[--sp]); - pc+=2; - break; - // ... - case BCADD: trace("add"); - stack[sp-2] = stack[sp-2] + stack[sp-1]; - sp -= 1; - break; - // ... - case BCJMPT: trace("jmpt to %d", program[pc]); - pc = stack[--sp] ? program[pc]-1 : pc+1; - break; -} -\end{lstlisting} - -\subsection{Specification} -The server stores a description for every device available in a record type. -From the macro settings in the interface file, a profile is created that -describes the specification of the device. When the connection between the -server and a client is established, the server will send a request for -specification. The client serializes its specification and send it to the -server so that the server knows what the client is capable of. The exact -specification is shown in Listing~\ref{lst:devicespec} and stores the -peripheral availability, the memory available for storing \glspl{Task} and -\glspl{SDS} and the size of the stack. - -\begin{lstlisting}[label={lst:devicespec}, - caption={Device specification for \gls{mTask}-\glspl{Task}}] -:: MTaskDeviceSpec = - { haveLed :: Bool - , haveLCD :: Bool - , have... - , bytesMemory :: Int - , stackSize :: Int - , aPins :: Int - , dPins :: Int - } -\end{lstlisting} - -\section{iTasks} -The server part of the system is written in \gls{iTasks}. Functions for -managing devices, \glspl{Task} and \glspl{SDS} have been created to support the -functionality. An interactive web application has been created that provides an -interactive management console for the \gls{mTask} system. This interface -provides functionality to list \glspl{SDS}, add and remove \glspl{Task}, -administrate devices and view the state of the system. - -\subsection{Device Storage} -Everything that a device encompasses is stored in the \CI{MTaskDevice} record -type. This includes management for the \glspl{SDS} and \glspl{Task} stored on -the device. The \CI{MTaskDevice} definition is shown in -Listing~\ref{lst:mtaskdevice} accompanied with the necessary classes and sub -types. - -\begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}] -:: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool) -:: BCState = ... // Compiler state, explained in later sections -:: MTaskResource - = TCPDevice TCPSettings - | SerialDevice TTYSettings - | ... -:: MTaskDevice = - { deviceTask :: Maybe TaskId - , deviceError :: Maybe String - , deviceChannels :: String - , deviceName :: String - , deviceState :: BCState - , deviceTasks :: [MTaskTask] - , deviceResource :: MTaskResource - , deviceSpec :: Maybe MTaskDeviceSpec - , deviceShares :: [MTaskShare] - } - -channels :: MTaskDevice -> Shared Channels - -class MTaskDuplex a where - synFun :: a (Shared Channels) -> Task () -\end{lstlisting} - -The \CI{deviceResource} component of the record must implement the -\CI{MTaskDuplex} interface that provides a function that launches a \gls{Task} -used for synchronizing the channels. The \CI{deviceTask} stores the -\gls{Task}-id for this \gls{Task} when active so that it can be checked upon. -This top-level task has the duty to report exceptions and errors as they are -thrown by setting the \CI{deviceError} field. All communication goes via these -channels. To send a message to the device, the system just puts it -in the channels. Messages sent from the client to the server are also placed -in there. In the case of the \gls{TCP} device type, the \gls{Task} is just a -simple wrapper around the existing \CI{tcpconnect} function in \gls{iTasks}. In -case of a device connected by a serial connection, it uses the newly developed -serial port library of \gls{Clean}\footnote{\url{% -https://gitlab.science.ru.nl/mlubbers/CleanSerial}}. - -Besides all the communication information, the record also keeps track of the -\glspl{Task} currently on the device, the compiler state (see -Section~\ref{sec:compiler}) and the according \glspl{SDS}. Finally, it stores -the specification of the device that is received when connecting. All of this -is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message -format are explained in the following section. - -\subsection{Shares} -The system keeps track of the \glspl{SDS} stored on -the client in a big \gls{SDS} containing a list of devices. Client-\glspl{SDS} -can be stored on one device at the same time. This means that if an \gls{SDS} -updates, everyone watching it will be notified. This would result in a lot -of notifications that are not meant for the watcher. Moreover, when a client -updates the \gls{SDS} this is processed by the connection handler and results -in an update of the real \gls{SDS}. Finally, the \gls{SDS} of a client must be -synchronized with the actual device. Thus, when an \gls{iTasks}-\gls{Task} -writes the client-\gls{SDS}, it must be propagated to the real device. There -are several ways of tackling this problem each with their own level of -granularity. - -First an actual \gls{iTasks}-\gls{SDS} for every \gls{SDS} used in a client can -be instantiated with one \gls{iTasks}-\gls{Task} listening to the \gls{SDS} and -synchronizing it with the device when an update occurred. This approach is very -expensive as it requires a lot of listening \glspl{Task}. - -Improved on this, a single \gls{iTasks}-\gls{SDS} can be created for every -devices that stores the respective \glspl{SDS}. Using the \CI{mapReadWrite} -functions, a single \gls{SDS} per device can be created as a lens that allows -mapping on a single client-\gls{SDS}. However, this approach still requires -\glspl{Task} listening to the \gls{SDS} and when an \gls{SDS} is written, -everyone is notified, even if the \gls{Task} only uses the value of a single -different \gls{SDS}. - -Finally, the current approach --- a single \gls{SDS} for the entire system ---- was explored. To create \glspl{SDS} per device or per client-\glspl{SDS} a -\CI{mapReadWrite} can be used but it suffers from the same problem as mentioned -before. Moreover, a \gls{Task} still has to watch the \gls{SDS} and communicate -the client-\gls{SDS} updates to the actual device. Both of these problems can -be solved by using a tailor made \gls{SDS} that heavily depends on parametric -lenses. - -\subsection{Parametric Lenses} -The type for the parametric lens of the big \gls{SDS} is \CI{Maybe -(MTaskDevice, Int)}. The \gls{SDS} is responsible for storing the entire list -of devices, from now on global. Moreover, the \gls{SDS} can focus on a single -device, from now on local. A local \gls{SDS} can also specifically focus on a -single \gls{SDS} on a single device, from now on called local-share. The -implementation of the real \gls{SDS} is given in Listing~\ref{lst:actualdev}. -The \gls{SDS} is a lens on an actual \gls{SDS} that writes to a file or memory. -Reading the \gls{SDS} is nothing more than reading the real \gls{SDS}. Writing -the \gls{SDS} is a little bit more involved. If the write operation originated -from an \gls{SDS} focussed on a single client-\gls{SDS}, the write action must -also be relayed to the actual device. If the write originated from an \gls{SDS} -focussed the devices or on one device only, nothing needs to be done. The -notification predicate determines whether a watcher gets a notification update. - -\begin{lstlisting}[label={lst:actualdev},% - caption={Device \gls{SDS}}] -deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice] -deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite} -where - realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld) - realRead p iw = read realDeviceStore iw - - realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld) - realWrite mi w iw - # (merr, iw) = write w realDeviceStore iw - | isError merr || isNothing mi = (merr $> notifyPred mi, iw) - # (Just (dev, ident)) = mi - | ident == -1 = (merr $> notifyPred mi, iw) - = case find ((==)dev) w of - Nothing = (Error $ exception "Device lost", iw) - Just {deviceShares} = case find (\d->d.identifier == ident) deviceShares of - Nothing = (Error $ exception "Share lost", iw) - Just s = case sendMessagesIW [MTUpd ident s.MTaskShare.value] dev iw of - (Error e, iw) = (Error e, iw) - (Ok _, iw) = (Ok $ notifyPred mi, iw) - - notifyPred :: (Maybe (MTaskDevice, Int)) (Maybe (MTaskDevice, Int)) -> Bool - notifyPred Nothing Nothing = True // Global watcher looking at a global event - notifyPred Nothing (Just _) = False // Global watcher looking at a local event - notifyPred (Just _) Nothing = False // Local watcher looking at a global event - // Local device watcher looking at a local event - notifyPred (Just (d1, -1)) (Just (d2, _)) = d1 == d2 - // Local share watcher looking at a local share event - notifyPred (Just (d1, i1)) (Just (d2, i2)) = d1 == d2 && i1 == i2 - - realDeviceStore :: Shared [MTaskDevice] - realDeviceStore = sharedStore "mTaskDevices" [] -\end{lstlisting} - -\subsubsection{Global \glspl{SDS}} -Accessing the global \gls{SDS} is just a matter of focussing the -\CI{deviceStore} with the \CI{Nothing} parameter as follows: - -\begin{lstlisting}[caption={Global \gls{SDS}}] -deviceStoreNP :: Shared [MTaskDevice] -deviceStoreNP = sdsFocus Nothing deviceStore -\end{lstlisting} - -\subsubsection{Local \glspl{SDS}} -Accessing a single device can be done using the \CI{mapReadWrite} function. -Since device comparison is shallow, the device that is given is allowed to be -an old version. The identification of devices is solely done on the name of the -channels and is unique throughout the system. The implementation is as follows: - -\begin{lstlisting}[caption={Local \gls{SDS}}] -deviceShare :: MTaskDevice -> Shared MTaskDevice -deviceShare d = mapReadWriteError - ( \ds->case find ((==)d) of - Nothing = exception "Device lost" - Just d = Ok d) - , \w ds->case splitWith ((==)d) ds of - ([], _) = Error $ exception "Device lost" - ([_:_], ds) = Ok $ Just [w:ds]) - $ sdsFocus (Just (d, -1)) deviceStore -\end{lstlisting} - -\subsubsection{Local-\gls{SDS} specific \glspl{SDS}} -A single \gls{SDS} on a single device can be accessed using the \CI{shareShare} -function. This function focusses the real big \gls{SDS} on a single \gls{SDS} -and uses the \CI{mapReadWrite} functions to serve the correct part of the -information. - -\begin{lstlisting}[caption={Local \gls{SDS}}] -shareShare :: MTaskDevice MTaskShare -> Shared BCValue -shareShare dev share = sdsFocus () - $ mapReadWriteError (read, write) - $ sdsFocus (Just (dev, share.identifier)) - $ deviceStore -where - read :: [MTaskDevice] -> MaybeError TaskException BCValue - read devs = case find ((==)dev) devs of - Nothing = exception "Device lost" - Just d = case find ((==)share) d.deviceShares of - Nothing = exception "Share lost" - Just s = Ok s.MTaskShare.value - - write :: BCValue [MTaskDevice] -> MaybeError TaskException (Maybe [MTaskDevice]) - write val devs = case partition ((==)dev) devs of - ([], _) = Error $ exception "Device doesn't exist anymore" - ([_,_:_], _) = Error $ exception "Multiple matching devices" - ([d=:{deviceShares}], devs) = case partition ((==)share) deviceShares of - ([], _) = Error $ exception "Share doesn't exist anymore" - ([_,_:_], _) = Error $ exception "Multiple matching shares" - ([s], shares) = Ok $ Just [{MTaskDevice | d & - deviceShares=[{MTaskShare | s & value=val}:shares]}:devs] -\end{lstlisting} - -\section{Communication} -The communication from the server to the client and vice versa is just a -character stream containing encoded \gls{mTask} messages. The \CI{synFun} -belonging to the device is responsible for sending the content in the left -channel and putting received messages in the right channel. Moreover, the -boolean value should be set to \CI{True} when the connection is terminated. The -specific encoding of the messages is visible in -Appendix~\ref{app:communication-protocol}. The type holding the messages is -shown in Listing~\ref{lst:avmsg}. Detailed explanation about the message types -and according actions will be given in the following subsections. - -\begin{lstlisting}[label={lst:avmsg},caption={Available messages}] -:: MTaskId :== Int -:: MSDSId :== Int -:: MTaskFreeBytes :== Int -:: MTaskMSGRecv - = MTTaskAck MTaskId MTaskFreeBytes | MTTaskDelAck MTaskId - | MTSDSAck MSDSId | MTSDSDelAck MSDSId - | MTPub MSDSId BCValue | MTMessage String - | MTDevSpec MTaskDeviceSpec | MTEmpty - -:: MTaskMSGSend - = MTTask MTaskInterval String | MTTaskDel MTaskId - | MTShutdown | MTSds MSDSId BCValue - | MTUpd MSDSId BCValue | MTSpec - -:: MTaskInterval = OneShot | OnInterval Int | OnInterrupt Int -\end{lstlisting} - -\subsection{Add a device} -A device can be added by filling in the \CI{MTaskDevice} record as much as -possible and running the \CI{connectDevice} function. This function grabs the -channels, starts the synchronization \gls{Task} (\CI{synFun}), makes sure the -errors are handled when needed and runs a processing function in parallel to -react on the incoming messages. Moreover, it sends a specification request to -the device in question to determine the details of the device and updates the -record to contain the top-level \gls{Task}-id. All device functionality -heavily depends on the specific \CI{deviceShare} function that generates an -\gls{SDS} for a specific device. This allows giving an old device record to the -function and still update the latest instance. Listing~\ref{lst:connectDevice} -shows the connection function. - -\begin{lstlisting}[label={lst:connectDevice},% - caption={Connect a device}] -connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels -connectDevice procFun device = set ([], [], False) ch - >>| appendTopLevelTask 'DM'.newMap True - ( procFun device ch -||- catchAll (getSynFun device.deviceData ch) errHdl) - >>= \tid->upd (\d->{d&deviceTask=Just tid,deviceError=Nothing}) (deviceShare device) - >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch -where - errHdl e = upd (\d->{d & deviceTask=Nothing, deviceError=Just e}) (deviceShare device) @! () - ch = channels device -\end{lstlisting} - -Figure~\ref{fig:handshake} shows the connection diagram. The client responds to -the server with their device specification. This is detected by the processing -function and the record is updated accordingly. - -\begin{figure}[H] - \centering - \begin{sequencediagram} - \newthread{s}{Server} - \newinst[4]{c}{Client} - \begin{call}{s}{MTSpec}{c}{MTDevSpec} - \end{call} - \end{sequencediagram} - \caption{Connect a device}\label{fig:handshake} -\end{figure} - -\subsection{\glspl{Task} \& \glspl{SDS}} -When a \gls{Task} is sent to the device it is added to the device record -without an identifier. The actual identifier is added to the record when the -acknowledgement of the \gls{Task} by the device is received. The connection -diagram is shown in Figure~\ref{fig:tasksend}. - -\begin{figure}[H] - \centering - \begin{sequencediagram} - \newthread{s}{Server} - \newinst[4]{c}{Client} - \begin{call}{s}{MTSDS}{c}{MTSDSAck} - \end{call} - \begin{call}{s}{MTTask}{c}{MTTaskAck} - \end{call} - \end{sequencediagram} - \caption{Sending a \gls{Task} to a device}\label{fig:tasksend} -\end{figure} - -The function for sending a \gls{Task} to the device is shown in -Listing~\ref{lst:sendtask}. First the \gls{Task} is compiled into messages. The -details of the compilation process are given in Section~\ref{sec:compiler}. -The new \glspl{SDS} that were generated during compilation are merged with the -existing device's \glspl{SDS}. Furthermore the messages are placed in the -channel \gls{SDS} of the device. This will result in sending the actual \gls{SDS} -specification and \gls{Task} specifications to the device. A \gls{Task} record -is created with the identifier $-1$ to denote a \gls{Task} not yet -acknowledged. Finally the device itself is updated with the new state and with -the new \gls{Task}. After waiting for the acknowledgement the device is -updated again and the \gls{Task} returns. - -\begin{lstlisting}[label={lst:sendtask},% - caption={Sending a \gls{Task} to a device}] -makeTask :: String Int -> Task MTaskTask -makeTask name ident = get currentDateTime @ \dt->{MTaskTask | name=name, ident=ident, dateAdded=dt} - -makeShare :: String Int BCValue -> MTaskShare -makeShare withTask identifier value = {MTaskShare | withTask=[withTask], identifier=identifier, value=value} - -sendTaskToDevice :: String (Main (ByteCode a Stmt)) (MTaskDevice, MTaskInterval) -> Task MTaskTask -sendTaskToDevice wta mTask (device, timeout) -# (msgs, newState=:{sdss}) = toMessages timeout mTask device.deviceState -# shares = [makeShare wta "" sdsi sdsval\\{sdsi,sdsval}<-sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`] -= updateShares device ((++) shares) - >>| sendMessages msgs device - >>| makeTask wta -1 - >>= \t->upd (addTaskUpState newState t) (deviceShare device) - >>| wait "Waiting for task to be acked" (taskAcked t) (deviceShare device) - >>| treturn t -where - addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice - addTaskUpState st task device = {MTaskDevice | device & deviceState=st, deviceTasks=[task:device.deviceTasks]} - taskAcked t d = maybe True (\t->t.ident <> -1) $ find (eq t) d.deviceTasks - eq t1 t2 = t1.dateAdded == t2.dateAdded && t1.MTaskTask.name == t2.MTaskTask.name -\end{lstlisting} - -\subsection{Miscellaneous Messages} -One special type of message is available which is sent to the device only when -it needs to reboot. When the server wants to stop the bond with the device it -sends the \CI{MTShutdown} message. The device will then clear its memory, thus -losing all the \glspl{SDS} and \glspl{Task} that were stored and reset itself. -Shortly after the shutdown message a new server can connect to the device -because the device is back in listening mode. - -\subsection{Integration} -When the system starts up, the devices from the previous execution still -residing in the \gls{SDS} must be cleaned up. It might be the case that they -contain \glspl{Task}, \glspl{SDS} or errors that are no longer applicable in -this run. A user or programmer can later choose to reconnect to some devices. - -\begin{lstlisting}[caption={Starting up the devices},% - label={lst:startupdevs}] -startupDevices :: Task [MTaskDevice] -startupDevices = upd (map reset) deviceStoreNP - where reset d = {d & deviceTask=Nothing, deviceTasks=[], deviceError=Nothing} -\end{lstlisting} - -The system's management is done through the interface of a single \gls{Task} -called \CI{mTaskManager}. To manage the system, a couple of different -functionalities are necessary and are launched. An image of the management -interface is shown in Figure~\ref{lst:manage}. The left sidebar of the -interface shows the list of example \glspl{Task} that are present in the -system. When clicking a \gls{Task}, a dialog opens in which a device can be -selected to send the \gls{Task} to. The dialog might contain user specified -variables. All example \gls{mTask}-\glspl{Task} are of the type \CI{Task (Main -(ByteCode () Stmt))} and can thus ask for user input first if needed for -parameterized \gls{mTask}-\glspl{Task}. The bottom panel shows the device -information. In this panel, the devices can be created and modified. Moreover, -this panel allows the user to reconnect with a device after a restart of the -server application. - -\begin{figure}[H] - \centering - \includegraphics[width=\linewidth]{manage} - \caption{The device management interface}\label{lst:manage} -\end{figure} - -\section[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 -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}] -liftmTask :: String (Main (ByteCode () Stmt)) (MTaskDevice, MTaskInterval) -> Task () -liftmTask wta mTask c=:(dev, _)= sendTaskToDevice wta mTask c - >>= \t->wait "Waiting for mTask to return" (taskRemoved t) (deviceShare dev) - >>| viewInformation "Done!" [] () -where - taskRemoved t d = isNothing $ find (\t1->t1.ident==t.ident) d.deviceTasks -\end{lstlisting} - -\section{Examples} -Here comes a description of the demo program. diff --git a/thesis.tex b/thesis.tex index 088549b..9229059 100644 --- a/thesis.tex +++ b/thesis.tex @@ -38,22 +38,22 @@ \mainmatter{} \glsresetall{} \chapter{Introduction}\label{chp:introduction} -\input{introduction} +\input{intro} \chapter{Task Oriented Programming}\label{chp:top} -\input{methods.top} +\input{top} \chapter{Embedded Domain Specific Languages}\label{chp:dsl} -\input{methods.dsl} +\input{edsl} \chapter[The mTask-EDSL]{The \gls{mTask}-\gls{EDSL}}\label{chp:mtask} -\input{methods.mtask} +\input{mtask} \chapter[Extending the mTask EDSL]{Extending the \gls{mTask} {EDSL}}\label{chp:mtaskcont} -\input{results.mtask} +\input{mtaskext} \chapter{System Architecture}\label{chp:arch} -\input{results.arch} +\input{arch} \chapter{Discussion \& Conclusion}\label{chp:conclusion} \input{conclusion} diff --git a/top.combinators.tex b/top.combinators.tex new file mode 100644 index 0000000..64aa98a --- /dev/null +++ b/top.combinators.tex @@ -0,0 +1,54 @@ +\Glspl{Task} can be combined using so called \gls{Task}-combinators. +Combinators describe relations between \glspl{Task}. There are only two basic +types of combinators; parallel and sequence. All other combinators are +derived from the basic combinators. Type signatures of simplified versions of +the basic combinators and their derivations are given in +Listing~\ref{lst:combinators} + +\begin{lstlisting}[% + caption={\Gls{Task}-combinators},label={lst:combinators}] +//Step combinator +(>>=) infixl 1 :: (Task a) (a -> Task b) -> Task b | iTask a & iTask b +(>>*) infixl 1 :: (Task a) [TaskCont a (Task b)] -> Task b | iTask a & iTask b +:: TaskCont a b + = OnValue ((TaskValue a) -> Maybe b) + | OnAction Action ((TaskValue a) -> Maybe b) + | E.e: OnException (e -> b) & iTask e + | OnAllExceptions (String -> b) +:: Action = Action String + +//Parallel combinators +(-||-) infixr 3 :: (Task a) (Task a) -> Task a | iTask a +(||-) infixr 3 :: (Task a) (Task b) -> Task b | iTask a & iTask b +(-||) infixl 3 :: (Task a) (Task b) -> Task a | iTask a & iTask b +(-&&-) infixr 4 :: (Task a) (Task b) -> Task (a,b) | iTask a & iTask b +\end{lstlisting} + +\paragraph{Sequence:} +The implementation for the sequence combinator is called the +\CI{step} (\CI{>>*}). This combinator runs the left-hand \gls{Task} and +starts the right-hand side when a certain predicate holds. Predicates +can be propositions about the \CI{TaskValue}, user actions from within +the web browser or a thrown exception. The familiar +bind-combinator is an example of a sequence combinator. This combinator +runs the left-hand side and continues to the right-hand \gls{Task} if +there is an \CI{UnStable} value and the user presses continue or when +the value is \CI{Stable}. The combinator could have been implemented +as follows: +\begin{lstlisting}[language=Clean] +(>>=) infixl 1 :: (Task a) (a -> (Task b)) -> (Task b) | iTask a & iTask b +(>>=) ta f = ta >>* [OnAction "Continue" onValue, OnValue onStable] + where + onValue (Value a _) = Just (f a) + onValue _ = Nothing + + onStable (Value a True) = Just (f a) + onStable _ = Nothing +\end{lstlisting} + +\paragraph{Parallel:} +The parallel combinator allows for concurrent \glspl{Task}. The +\glspl{Task} combined with these operators will appear at the same time +in the web browser of the user and the results are combined as the type +dictates. All parallel combinators used are derived from the basic parallel +combinator that is very complex and only used internally. diff --git a/top.itasks.tex b/top.itasks.tex new file mode 100644 index 0000000..891fd96 --- /dev/null +++ b/top.itasks.tex @@ -0,0 +1,68 @@ +\gls{TOP} is a novel programming paradigm implemented as +\gls{iTasks}~\cite{achten_introduction_2015} in the pure lazy functional +language \gls{Clean}~\cite{brus_cleanlanguage_1987}. \gls{iTasks} is an +\gls{EDSL} to model workflow tasks in the broadest sense. A \gls{Task} is just +a function that --- given some state --- returns the observable \CI{TaskValue}. +The \CI{TaskValue} of a \CI{Task} can have different states. Not all state +transitions are possible as shown in Figure~\ref{fig:taskvalue}. Once a value +is stable it can never become unstable again. Stability is often reached by +pressing a confirmation button. \glspl{Task} yielding a constant value are +immediately stable. + +A simple \gls{iTasks} example illustrating the route to stability of a +\gls{Task} in which the user has to enter a full name is shown in +Listing~\ref{lst:taskex}. The code is accompanied by screenshots showing the +user interface in Figure~\ref{fig:taskex1},~\ref{fig:taskex2} +and~\ref{fig:taskex3}. The \CI{TaskValue} of the \gls{Task} is in the first +image in the \CI{NoValue} state, the second image does not have all the fields +filled in and therefore the \CI{TaskValue} remains \CI{NoValue}. In the third +image all fields are entered and the \CI{TaskValue} transitions to the +\CI{Unstable} state. When the user presses \emph{Continue} the value becomes +\CI{Stable} and cannot be changed any further. + +\begin{figure}[H] + \centering + \includegraphics[width=.5\linewidth]{fig-taskvalue} + \caption{The states of a \CI{TaskValue}}\label{fig:taskvalue} +\end{figure} + +\begin{lstlisting}[label={lst:taskex},% + caption={An example \gls{Task} for entering a name}] +:: Name = { firstname :: String + , lastname :: String + } + +derive class iTask Name + +enterInformation :: String [EnterOption m] -> (Task m) | iTask m + +enterName :: Task Name +enterName = enterInformation "Enter your name" [] +\end{lstlisting} + +\begin{figure}[H] + \centering + \begin{subfigure}{.25\textwidth} + \centering + \includegraphics[width=.9\linewidth]{taskex1} + \caption{Initial interface}\label{fig:taskex1} + \end{subfigure} + \begin{subfigure}{.25\textwidth} + \centering + \includegraphics[width=.9\linewidth]{taskex2} + \caption{Incomplete entrance}\label{fig:taskex2} + \end{subfigure} + \begin{subfigure}{.25\textwidth} + \centering + \includegraphics[width=.9\linewidth]{taskex3} + \caption{Complete entry}\label{fig:taskex3} + \end{subfigure} + \caption{Example of a generated user interface} +\end{figure} + +For a type to be suitable, it must have instances for a collection of generic +functions that is captured in the class \CI{iTask}. Basic types have +specialization instances for these functions and show an interface accordingly. +Derived interfaces can be modified with decoration operators or specializations +can be created. + diff --git a/top.sds.tex b/top.sds.tex new file mode 100644 index 0000000..2f30a85 --- /dev/null +++ b/top.sds.tex @@ -0,0 +1,83 @@ +\Glspl{SDS} are an abstraction over resources that are available in the world +or in the \gls{iTasks} system. The shared data can be a file on disk, the +system time, a random integer or just some data stored in memory. The actual +\gls{SDS} is just a record containing functions on how to read and write the +source. In these functions the \CI{*IWorld} --- which in turn contains the real +\CI{*World} --- is available. Accessing the outside world is required for +interacting with it and thus the functions can access files on disk, raw +memory, other \glspl{SDS} and hardware. + +The basic operations for \glspl{SDS} are get, set and update. The signatures +for these functions are shown in Listing~\ref{lst:shares}. By default, all +\glspl{SDS} are files containing a \gls{JSON} encoded version of the object and +thus are persistent between restarts of the program. Library functions for +shares residing in memory are available as well. The three main operations on +shares are atomic in the sense that during reading no other \glspl{Task} are +executed. The system provides useful functions to transform, map and combine +\glspl{SDS} using combinators. The system also provides functionality to +inspect the value of an \gls{SDS} and act upon a change. \Glspl{Task} waiting on +an \gls{SDS} to change are notified when needed. This results in low resource +usage because \glspl{Task} are never constantly inspecting \gls{SDS} values but +are notified. + +\begin{lstlisting}[% + label={lst:shares},caption={\Gls{SDS} functions}] +:: RWShared p r w = ... +:: ReadWriteShared r w :== RWShared () r w +:: ROShared p r :== RWShared p () r +:: ReadOnlyShared r :== ROShared () r + +:: Shared r :== ReadWriteShared r r + +get :: (ReadWriteShared r w) -> Task r | iTask r +set :: w (ReadWriteShared r w) -> Task w | iTask w +upd :: (r -> w) (ReadWriteShared r w) -> Task w | iTask r & iTask w + +sharedStore :: String a -> Shared a | JSONEncode{|*|}, JSONDecode{|*|} +\end{lstlisting} + +\section{Parametric Lenses} +\Glspl{SDS} can contain complex data structures such as lists, trees and even +resources in the outside world. Sometimes, an update action only updates a part +of the resource. When this happens, all waiting \glspl{Task} looking at the +resource are notified of the update. However, it may be the case that +\glspl{Task} were only looking at parts of the structure that was not updated. +To solve this problem, parametric lenses were +introduced~\cite{domoszlai_parametric_2014}. + +Parametric lenses add a type variable to the \gls{SDS}. This type variable is +fixed to the void type (i.e. \CI{()}) in the given functions. When an \gls{SDS} +executes a write operation, it also provides the system with a notification +predicate. This notification predicate is a function \CI{p -> Bool} where +\CI{p} is the parametric lens type. This allows programmers to create a big +\gls{SDS}, and have \glspl{Task} only look at parts of the big \gls{SDS}. This +technique is used in the current system in memory shares. The \CI{IWorld} +contains a map that is accessible through an \gls{SDS}. While all data is +stored in the map, only \glspl{Task} looking at a specific entry are notified +when the structure is updated. The type of the parametric lens is the key in +the map. + +Functionality for setting parameters is available in the system. The most +important functions are the \CI{sdsFocus} and the \CI{sdsLens} function. These +functions are listed in Listing~\ref{lst:focus}. \CI{sdsFocus} allows the +programmer to fix a parametric lens value. \CI{sdsLens} is a kind of +\CI{mapReadWrite} including access to the parametric lens value. This allows +the creation of, for example, \glspl{SDS} that only read and write to parts of +the original \gls{SDS}. + +\begin{lstlisting}[label={lst:focus}, + caption={Parametric lens functions}] +sdsFocus :: p (RWShared p r w) -> RWShared p` r w | iTask p + +:: SDSNotifyPred p :== p -> Bool + +:: SDSLensRead p r rs = SDSRead (p -> rs -> MaybeError TaskException r) + | SDSReadConst (p -> r) +:: SDSLensWrite p w rs ws = SDSWrite (p -> rs -> w -> MaybeError TaskException (Maybe ws)) + | SDSWriteConst (p -> w -> MaybeError TaskException (Maybe ws)) +:: SDSLensNotify p w rs = SDSNotify (p -> rs -> w -> SDSNotifyPred p) + | SDSNotifyConst (p -> w -> SDSNotifyPred p) + +sdsLens :: String (p -> ps) (SDSLensRead p r rs) (SDSLensWrite p w rs ws) (SDSLensNotify p w rs) + (RWShared ps rs ws) -> RWShared p r w | iTask ps +\end{lstlisting} diff --git a/top.tex b/top.tex new file mode 100644 index 0000000..816f442 --- /dev/null +++ b/top.tex @@ -0,0 +1,8 @@ +\section{iTasks} +\input{top.itasks} + +\section{Combinators} +\input{top.combinators} + +\section{Shared Data Sources} +\input{top.sds} -- 2.20.1