From: Mart Lubbers Date: Thu, 29 Jun 2017 18:42:22 +0000 (+0200) Subject: restructure files X-Git-Tag: hand-in~34 X-Git-Url: https://git.martlubbers.net/?a=commitdiff_plain;h=6548a5ec9ce8e0df67fc4019625ab5238eb1bf71;p=msc-thesis1617.git restructure files --- 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}