X-Git-Url: https://git.martlubbers.net/?a=blobdiff_plain;f=top%2Fint.tex;h=33f781316790265e50da7aeca8ff9c9bc9f122c1;hb=d37f1f0d625c081afa66e43d43df55c3741001ae;hp=a1742fc6a1019c42ae968a2c784bbd3f0575608b;hpb=d6c8d6cbc9c88d449ac784eda20a213fd6b46669;p=phd-thesis.git diff --git a/top/int.tex b/top/int.tex index a1742fc..33f7813 100644 --- a/top/int.tex +++ b/top/int.tex @@ -10,7 +10,7 @@ \begin{chapterabstract} This chapter shows the integration of \gls{MTASK} with \gls{ITASK} by showing: \begin{itemize} - \item an architectural overview of \gls{MTASK}; + \item an architectural overview \gls{MTASK} applications; \item on the interface for connecting devices; \item the interface for lifting \gls{MTASK} tasks to \gls{ITASK} tasks; \item and interface for lifting \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS}. @@ -20,7 +20,7 @@ The \gls{MTASK} language is a multi-view \gls{DSL}, i.e.\ there are multiple interpretations possible for a single \gls{MTASK} term. Using the byte code compiler (\cleaninline{BCInterpret}) \gls{DSL} interpretation, \gls{MTASK} tasks can be fully integrated in \gls{ITASK}. They are executed as if they are regular \gls{ITASK} tasks and they communicate may access \glspl{SDS} from \gls{ITASK} as well. -\Gls{MTASK} devices contain a domain-specific \gls{OS} (\gls{RTS}) and are little \gls{TOP} engines in their own respect, being able to execute tasks. +\Gls{MTASK} devices contain a domain-specific \gls{OS} and are little \gls{TOP} engines in their own respect, being able to execute tasks. \Cref{fig:mtask_integration} shows the architectural layout of a typical \gls{IOT} system created with \gls{ITASK} and \gls{MTASK}. The entire system is written as a single \gls{CLEAN} specification where multiple tasks are executed at the same time. Tasks can access \glspl{SDS} according to many-to-many communication and multiple clients can work on the same task. @@ -35,15 +35,15 @@ Using \cleaninline{liftmTask}, \gls{MTASK} tasks are lifted to a device (see \cr \label{fig:mtask_integration} \end{figure} -\section{Devices}\label{sec:withdevice} +\section{Connecting edge devices}\label{sec:withdevice} When interpreted by the byte code compiler view, an \gls{MTASK} task produces a compiler. This compiler is exceuted at run time so that the resulting byte code can be sent to an edge device. All communication with this device happens through a so-called \emph{channels} \gls{SDS}. The channels contain three fields, a queue of messages that are received, a queue of messages to send and a stop flag. Every communication method that implements the \cleaninline{channelSync} class can provide the communication with an \gls{MTASK} device. -As of now, serial port communication, direct \gls{TCP} communication and \gls{MQTT} over \gls{TCP} are supported as communication providers. -The \cleaninline{withDevice} function transforms a communication provider and a task that does something with this device to an \gls{ITASK} task. -Internally, the task sets up the communication, exchanges specifications, executes the inner task while handling errors, and finally cleans up after closing. +As of now, serial port communication, direct \gls{TCP} communication and \gls{MQTT} over \gls{TCP} are supported as communication providers (see \cref{lst:connection_types}). +The \cleaninline{withDevice} function transforms such a communication provider and a task that does something with this device to an \gls{ITASK} task. +Internally, the task sets up the communication, exchanges specifications with the device, executes the inner task while handling errors, and finally cleans up after closing. \Cref{lst:mtask_device} shows the types and interface to connecting devices. \begin{lstClean}[label={lst:mtask_device},caption={Device communication interface in \gls{MTASK}.}] @@ -52,10 +52,48 @@ Internally, the task sets up the communication, exchanges specifications, execut class channelSync a :: a (Shared sds Channels) -> Task () | RWShared sds -withDevice :: a (MTDevice -> Task b) -> Task b | iTask b & channelSync, iTask a +withDevice :: a (MTDevice -> Task b) + -> Task b | iTask b & channelSync, iTask a \end{lstClean} -\section{Lifting tasks}\label{sec:liftmtask} +\subsection{Implementation} +The \cleaninline{MTDevice} abstract type is internally represented as three \gls{ITASK} \gls{SDS} that contain all the current information about the tasks. +The first \gls{SDS} is the information about the \gls{RTS} of the device, i.e.\ metadata on the tasks that are executing, the hardware specification and capabilities, and a list of fresh task identifiers. +The second \gls{SDS} is a map storing downstream \gls{SDS} updates. +When a lifted \gls{SDS} is updated on the device, a message is sent to the server. +This message is initially queued in the map to allow for asynchronous handling of multiple updates. +Finally, the \cleaninline{MTDevices} type contains the communication channels. + +The \cleaninline{withDevice} task itself first constructs the \glspl{SDS} using the \gls{ITASK} function \cleaninline{withShared} to create anonymous local \glspl{SDS}. +Then, it performs the following four tasks in parallel to monitor the edge device. +\begin{enumerate} + \item It synchronises the channels using the \cleaninline{channelSync} overloaded function. + Errors that occur here are converted to the proper \gls{MTASK} exception. + \item Watches the channels for the shutdown flag. + If the connection is lost with the device unexpectedly, an \gls{MTASK} exception is thrown. + \item Watches the channels for messages and processes them accordingly by changing the device information \gls{SDS} or adding the lifted \gls{SDS} updates to the corresponding \gls{SDS} update queue. + \item Sends a request for a specification. Once the specification is received, the device task is run. + The task value of this device task is then used as the task value of the \cleaninline{withDevice} task. +\end{enumerate} + +If at any stage an unrecoverable device error occurs, an \gls{ITASK} exception is thrown on the \cleaninline{withDevice} task. +This exception can be caught in order to device some kind of fail-safe mechanism. +For example, when a device fails, the tasks can be sent to another device. +\todo[inline]{Example of failsafe?} + +%withDevice spec deviceTask +% withShared newMap \sdsupdates-> +% withShared ([], [MTTSpecRequest], False) \channels-> +% withShared default \dev->parallel +% [ channelSync spec // unexpected disconnect +% , watchForShutdown // unexpected disconnect +% , watchChannels // process messages +% , waitForSpecification +% >>| deviceTask +% >>* [ifStable: issueShutdown] +% ] + +\section{Lifting \texorpdfstring{\gls{MTASK}}{mTask} tasks}\label{sec:liftmtask} Once the connection with the device is established, \gls{MTASK} tasks can be lifted to \gls{MTASK} tasks using the \cleaninline{liftmTask} family of functions (see \cref{lst:liftmtask}). Given an \gls{MTASK} task in the \cleaninline{BCInterpret} view and a device obtained from \cleaninline{withDevice}, an \gls{ITASK} task is returned. This \gls{ITASK} task tethers the \gls{MTASK} task that is executed on the microcontroller. @@ -65,18 +103,68 @@ Hence, when for example observing the task value, the actual task value from the liftmTask :: (Main (MTask BCInterpret u)) MTDevice -> Task u | iTask u \end{lstClean} -Under the hood, \cleaninline{liftmTask} compiler the \gls{MTASK} task to byte code, gathers the initial values for the lifted \glspl{SDS} and sends the data to the device. -Once acknowledged, the \gls{MTASK} task value and the \glspl{SDS} are monitored. -If the \gls{ITASK} server updates the value of a lifted \gls{SDS}, the appropriate message is sent to the \gls{MTASK} to notify it of the change. -Furthermore, the \gls{MTASK} device may update the \gls{SDS}, and in that case the message is picked up by the \cleaninline{liftmTask} task and the \gls{SDS} on the \gls{ITASK} server updated accordingly. +Under the hood, \cleaninline{liftmTask}: +\begin{itemize} + \item Generates a fresh task identifier for the device. + \item Compiles the task and fetches the values for the tethered \glspl{SDS}. + \item Sends the task to the device + \item Watches, in parallel: the tethered \glspl{SDS} in \gls{ITASK}, if they are updated, a message is sent to the device; the \gls{SDS} update queue, if there is a downstream update, the \gls{ITASK} \gls{SDS} it references is updated as well; and the task value. +\end{itemize} + +The task value of the \cleaninline{liftmTask} task is the task value of the task on the edge device. + +\section{Lifting \texorpdfstring{\gls{ITASK}}{iTask} \texorpdfstring{\glsxtrlongpl{SDS}}{shared data sources}}\label{sec:liftsds} +Lifting \gls{ITASK} \glspl{SDS} to \gls{MTASK} \glspl{SDS} is something that mostly happens at the compiler level using the \cleaninline{liftsds} function (see \cref{lst:mtask_itasksds}). +\Glspl{SDS} in \gls{MTASK} must always have an initial value. +For regular \gls{SDS} this value is given in the source code, for lifted \gls{ITASK} \glspl{SDS} this value is obtained by reading the values once just before sending the task to the edge device. +On the device itself, there is just one difference between lifted \glspl{SDS} and regular \glspl{SDS}: after changing \pgls{SDS}, a message is sent to the server containing this new value. +The \cleaninline{withDevice} task (see \cref{sec:withdevice}) receives and processes this message by writing to the \gls{ITASK} \gls{SDS}. +Tasks watching this \gls{SDS} get notified then through the normal notification mechanism of \gls{ITASK}. -\section{Lifting \texorpdfstring{\glsxtrlongpl{SDS}}{shared data sources}}\label{sec:liftsds} \begin{lstClean}[label={lst:mtask_itasksds},caption={Lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] class liftsds v where liftsds :: ((v (Sds t)) -> In (Shared sds t) (Main (MTask v u))) -> Main (MTask v u) | RWShared sds \end{lstClean} +The compilation of the code and the serialisation of the data throws away all typing information. +\Glspl{SDS} are stored in the compiler state as a map from identifiers to either an initial value or an \cleaninline{MTLens}. +The \cleaninline{MTLens} is a type synonym for a \gls{SDS} that represents the typeless serialised value of the underlying \gls{SDS}. +This is done so that the \cleaninline{withDevice} task can write the received \gls{SDS} updates to the according \gls{SDS} independently. +\Gls{ITASK}'s notification mechanism then takes care of the rest. +Such a \gls{SDS} is created by using the \cleaninline{mapReadWriteError} which, given a pair of read and write functions with error handling, produces a \gls{SDS} with the lens embedded. +The read function transforms, the function that converts a typed value to a typeless serialised value, just applies the serialisation. +The write function, the function that, given the new serialised value and the old typed value, produces a new typed value. +It tries to decode the serialised value, if that succeeds, it is written to the underlying \gls{SDS}, an error is thrown otherwise. +\Cref{lst:mtask_itasksds_lens} provides the implementation for this: + +% VimTeX: SynIgnore on +\begin{lstClean}[label={lst:mtask_itasksds_lens},caption={Lens applied to lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] +lens :: (Shared sds a) -> MTLens | type a & RWShared sds +lens sds = mapReadWriteError + ( \r->Ok (fromString (toByteCode{|*|} r) + , \w r-> ?Just <$> iTasksDecode (toString w) + ) ?None sds +\end{lstClean} +% VimTeX: SynIgnore off + +\Cref{lst:mtask_itasksds_lift} shows the code for the implementation of \cleaninline{liftsds} that uses the \cleaninline{lens} function shown earlier. +First, the \gls{SDS} to be lifted is extracted from the expression by bootstrapping the fixed point with a dummy value. +This is safe because the expression on the right-hand side of the \cleaninline{In} is never used. +Then, using \cleaninline{addSdsIfNotExist}, the identifier for this particular \gls{SDS} is either retrieved from the compiler state or generated freshly. +This identifier is then used to provide a reference to the \cleaninline{def} definition to evaluate the main expression. + +% VimTeX: SynIgnore on +\begin{lstClean}[label={lst:mtask_itasksds_lift},caption={Lens applied to lifted \gls{ITASK} \glspl{SDS} in \gls{MTASK}.}] + liftsds def = {main = + let (t In _) = def (abort "liftsds: expression too strict") + in addSdsIfNotExist (Right $ lens t) + >>= \sdsi->let (_ In e) = def (pure (Sds sdsi)) in e.main + } + where +\end{lstClean} +% VimTeX: SynIgnore off + \section{Conclusion}