\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.
+ 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
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.
+ Ethernet.
This does not only include \gls{Arduino} compatible boards but also
other boards capable of running \gls{Arduino} code. A port of the
\subsection{Client}
\subsubsection{Engine}
The client is in a constant loop listening for input and waiting to execute
-\gls{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
+\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}[H]
+\begin{algorithm}
\KwData{
\textbf{list} $tasks$,
- \textbf{time} $t$
+ \textbf{time} $tm$
}
\Begin{
receive\_data()\;
}
- $t\leftarrow \text{now}()$\;
+ $tm\leftarrow \text{now}()$\;
\ForEach{$t\leftarrow tasks$}{
\uIf{is\_interrupt$(t)$ \textbf{and} had\_interrupt$(t)$}{
run\_task$(t)$\;
}
- \ElseIf{$t-t.\text{lastrun} > t.\text{interval}$}{
+ \ElseIf{$tm-t.\text{lastrun} > t.\text{interval}$}{
run\_task$(t)$\;
\uIf{$t.\text{interval}==0$}{
delete\_task$(t)$\;
\end{algorithm}
\subsubsection{Storage}
-\glspl{Task} and \glspl{SDS} are stored on the client in one big memory space
-that is reserved at the start of the program. The space could also have been
-dynamically allocated but that would require using the heap which is unwanted
-in small memory environments. \Glspl{Task} grow from the bottom up and
-\glspl{SDS} grow from the top down. When a \gls{Task} or \gls{SDS} is removed,
-all \glspl{Task} residing in higher areas of the memory are relocated in the
-memory space to not leave holes. Both \glspl{Task} and \glspl{SDS} are stored
-as structures that are linked in the memory space, helper functions are
-available to loop through them without having to fiddle in the memory space
-itself. The instances for \glspl{Task} and \glspl{SDS} are shown in
-Listing~\ref{lst:structs} accompanied by the helper functions for \glspl{Task}.
-\Glspl{Task} consists of the length, interval, last run time, id and the
-bytecode. \Glspl{SDS} consist only of an id, value and type. The pointer to the
-bytecode of the \gls{Task} always points to the location in the memory space.
+\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}}]
+ caption={The data type storing the \glspl{Task}},float]
struct task {
uint16_t tasklength;
uint16_t interval;
| SerialDevice TTYSettings
| ...
:: MTaskDevice =
- { deviceTask :: Maybe TaskId
- , deviceError :: Maybe String
+ { deviceTask :: Maybe TaskId
+ , deviceError :: Maybe String
, deviceChannels :: String
- , deviceName :: String
- , deviceState :: BCState
- , deviceTasks :: [MTaskTask]
- , deviceData :: MTaskResource
- , deviceSpec :: Maybe MTaskDeviceSpec
- , deviceShares :: [MTaskShare]
+ , deviceName :: String
+ , deviceState :: BCState
+ , deviceTasks :: [MTaskTask]
+ , deviceResource :: MTaskResource
+ , deviceSpec :: Maybe MTaskDeviceSpec
+ , deviceShares :: [MTaskShare]
}
channels :: MTaskDevice -> Shared Channels
{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}-\glspl{Task}. The function is called with a name, \gls{mTask},
+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.
\end{lstlisting}
\section{Examples}
-\todo{example program (demo)}
+Here comes a description of the demo program.