update abstract and acks
[msc-thesis1617.git] / results.arch.tex
index e33dc47..e276010 100644 (file)
@@ -1,5 +1,38 @@
+\section{Overview \& Terminology}
+The goal of the architecture is to facilitate an ecosystem in which an
+\gls{iTasks}-system can add, change and remove devices at runtime. Moreover,
+the \gls{iTasks}-system can send \glspl{mTask}, compiled at runtime to
+bytecode, to the device. The device runs an interpreter which can execute the
+\gls{Task}'s bytecode. Devices are persistent during reboots of the
+\gls{iTasks}-system. The methods of interacting with \glspl{mTask} is analogous
+to interacting with \gls{iTasks}-\glspl{Task} and programmers can access the
+\glspl{SDS} made for a device in the same way as a regular \glspl{SDS}. The
+following terms will be used throughout the architecture description.
+
+\begin{itemize}
+       \item Device, Client
+
+               This is the actual device connected to the system. This can be a real
+               device such as a microcontroller but also just a program on the same
+               machine as the server.
+       \item Server, \gls{iTasks}-System
+
+               The actual executable serving the \gls{iTasks} interfaces. The system
+               will contain \glspl{Task} taking care of the communication with the
+               clients.
+       \item System
+
+               The complete ecosystem, thus containing both the server and client
+               programs.
+       \item Engine
+
+               The runtime system of the client. This system handles the communication
+               with the server and interprets the \glspl{Task}.
+\end{itemize}
+
+
 \section{Devices}
-The client code for the devices is compiled from one codebase. For a device to
+The engine for the devices is compiled from one codebase. For a device to
 be eligible for \glspl{mTask}, it must be able to compile the shared codebase
 and implement (part of) the device specific interface. The shared codebase only
 uses standard \gls{C} and no special libraries or tricks are used. Therefore
@@ -32,39 +65,56 @@ the device software.
 \end{itemize}
 
 \section{Specification}
-Devices are stored in a record type and all devices in the system are stored in
-a \gls{SDS} containing all devices. From the macro settings in the interface
-file a profile is created for the device that describes the specification. When
-a connection between the server and a client is established the server will
-send a request for specification. The client will serialize his specification
-and send it to the server so that the server knows what the client is capable
-of. The exact specification is listed in Listing~\ref{lst:devicespec}
-
-\begin{lstlisting}[language=Clean,label={lst:devicespec},
+The server stores a description for every device available in a record type
+which are stored in a \gls{SDS}. From the macro settings in
+the interface file, a profile is created for the device that describes the
+specification. When a connection between the server and a client is established
+the server will send a request for specification. The client will serialize his
+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}
+
+\begin{lstlisting}[label={lst:devicespec},
        caption={Device specification for \glspl{mTask}}]
 :: MTaskDeviceSpec =
-       {haveLed     :: Bool
-       ,haveAio     :: Bool
-       ,haveDio     :: Bool
-       ,bytesMemory :: Int
+       { haveLed     :: Bool
+       , haveAio     :: Bool
+       , haveDio     :: Bool
+       , bytesMemory :: Int
        }
 \end{lstlisting}
 
-\section{Communication}
-The communication to and fro a device runs via a single \gls{SDS}. Every
-device has a specific resource that is used to connect to the device. The
-current system supports connecting devices via a serial connection and via a
-\gls{TCP} connection. Every device has the type \CI{MTaskDevice} and which
-is listed in Listing~\ref{lst:mtaskdevice}. When a device is added, a background
-\gls{Task} is started that runs the \CI{synFun}. The \CI{synFun} is the task that
-synchronizes the channel \gls{SDS} with the actual device. For the \gls{TCP}
-device this is a simple \CI{tcpconnect}. The \CI{TaskId} of the background task
-is saved to be able to stop the task in the future. When the task is unable to
-connect it will set the \CI{deviceError} field to notify the user.
-\todo{netter maken}
-
-\begin{lstlisting}[language=Clean,caption={Device type},label={lst:mtaskdevice}]
+\section{Device Storage}
+All devices available in the system are stored in a big \gls{SDS} that contains
+a list of \CI{MTaskDevice}s. The exact specification is defined as in
+Listing~\ref{lst:mtaskdevice} with the accompanying classes and types.
+
+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. If the system wants to send a message to the device, it 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.
+
+\begin{lstlisting}[caption={Device type},label={lst:mtaskdevice}]
+deviceStoreNP :: Shared [MTaskDevice]
+
 :: Channels :== ([MTaskMSGRecv], [MTaskMSGSend], Bool)
+:: BCState = ... // Compiler state, explained in later sections
 :: MTaskResource 
        = TCPDevice TCPSettings
        | SerialDevice TTYSettings
@@ -73,12 +123,145 @@ connect it will set the \CI{deviceError} field to notify the user.
                , deviceError :: Maybe String
                , deviceChannels :: String
                , deviceName :: String
+               , deviceState :: BCState
                , deviceTasks :: [MTaskTask]
                , deviceData :: MTaskResource
                , deviceSpec :: Maybe MTaskDeviceSpec
                , deviceShares :: [MTaskShare]
        }
 
+channels :: MTaskDevice -> Shared Channels
+
 class MTaskDuplex a where
        synFun :: a (Shared Channels) -> Task ()
 \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 specific encoding
+is visible in Appendix~\ref{app:communication-protocol}. The type holding the
+messages in Listing~\ref{lst:avmsg}. Detailed explaination about the message
+types 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}, 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 the device functionality heavily
+depends on the \CI{withDevices} function that applies a function a device in
+the \gls{SDS} when they are equal. Device equality is defined as equality on
+their channels. This allows you to give 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}]
+withDevices :: MTaskDevice (MTaskDevice -> MTaskDevice) -> Task [MTaskDevice]
+
+connectDevice :: (MTaskDevice (Shared Channels) -> Task ()) MTaskDevice -> Task Channels
+connectDevice procFun device = let ch = channels device
+       in appendTopLevelTask 'DM'.newMap True
+               (procFun device ch -||- catchAll (getSynFun d.deviceData ch) errHdl)
+               >>= \tid->withDevices device (\d->{d&deviceTask=Just tid,deviceError=Nothing})
+               >>| upd (\(r,s,ss)->(r,s++[MTSpec],ss)) ch
+       where
+               errHdl e = withDevices device (\d->{d & deviceTask=Nothing, deviceError=Just e}) @! ()
+\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 made during compilation are added to the
+deviceshares that were made during the compilation are merged with the existing
+shares on the device. Furthermore the messages are placed in the channel share
+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}.
+When the device returns an acknowledgement the \gls{Task} is updated
+accordingly.
+
+\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 [MTaskDevice]
+sendTaskToDevice wta mTask (device, timeout)
+# (msgs, newState) = toMessages timeout mTask device.deviceState
+# shares = [makeShare wta sdsi sdsval\\{sdsi,sdsval}<-newState.sdss, (MTSds sdsi` _)<-msgs | sdsi == sdsi`] 
+= updateShares device ((++) shares)
+       >>| sendMessages msgs device
+       >>| makeTask wta -1
+       >>= withDevices device o addTaskUpState newState
+       where
+               addTaskUpState :: BCState MTaskTask MTaskDevice -> MTaskDevice
+               addTaskUpState st task device = {MTaskDevice | device &
+                       deviceState=st, deviceTasks=[task:device.deviceTasks]}
+\end{lstlisting}
+
+\subsection{Miscellaneous Messages}
+There exists one special type of message that 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 his 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.