elaborate on share usage
authorMart Lubbers <mart@martlubbers.net>
Sun, 2 Jul 2017 11:51:26 +0000 (13:51 +0200)
committerMart Lubbers <mart@martlubbers.net>
Sun, 2 Jul 2017 11:51:26 +0000 (13:51 +0200)
arch.communication.tex
arch.devices.tex
arch.itasks.tex
conclusion.discussion.tex

index 7da8ad1..18bc076 100644 (file)
@@ -2,8 +2,8 @@ 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
 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
+boolean flag in the channel type 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.
 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.
@@ -51,7 +51,6 @@ brevity.
        }
 \end{lstlisting}
 
        }
 \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
 \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
index b43a40f..db56b48 100644 (file)
@@ -182,12 +182,12 @@ void run_task(struct task *t){
                        pc+=2;
                        break;
                // ...
                        pc+=2;
                        break;
                // ...
-               case BCADD: trace("add");
+               case BCADD:
                        stack[sp-2] = stack[sp-2] + stack[sp-1];
                        sp -= 1;
                        break;
                // ...
                        stack[sp-2] = stack[sp-2] + stack[sp-1];
                        sp -= 1;
                        break;
                // ...
-               case BCJMPT: trace("jmpt to %d", program[pc]);
+               case BCJMPT:
                        pc = stack[--sp] ? program[pc]-1 : pc+1;
                        break;
                // ...
                        pc = stack[--sp] ? program[pc]-1 : pc+1;
                        break;
                // ...
index 7a21471..040b287 100644 (file)
@@ -25,16 +25,16 @@ the reference to the device and not the actual device record.
        | SerialDevice TTYSettings
        | ...
 :: MTaskDevice =
        | SerialDevice TTYSettings
        | ...
 :: MTaskDevice =
-               { deviceTask     :: Maybe TaskId
-               , deviceError    :: Maybe String
-               , deviceChannels :: String
-               , deviceName     :: String
-               , deviceState    :: BCState
-               , deviceTasks    :: [MTaskTask]
-               , deviceResource :: MTaskResource
-               , deviceSpec     :: Maybe MTaskDeviceSpec
-               , deviceShares   :: [MTaskShare]
-               }
+       { 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
 
 
 channels :: MTaskDevice -> Shared Channels
 
@@ -44,19 +44,23 @@ class MTaskDuplex a where
 
 The \CI{deviceResource} component of the record must implement the
 \CI{MTaskDuplex} interface that provides a function that launches a \gls{Task}
 
 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}}.
-The implementation and semantics for the \CI{MTaskMSGRecv} and
-\CI{MTaskMSGSend} types is given in Section~\ref{sec:communication}.
+used for synchronizing the channels. The \CI{deviceChannels} field can be used
+to get the memory \gls{SDS} containing the channels. This field does not
+contain the channels itself because they update often. The field is used to
+get a memory \gls{SDS} containing the actual channel data when calling the
+\CI{channels} function. 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}}. The implementation and
+semantics for the \CI{MTaskMSGRecv} and \CI{MTaskMSGSend} types are given in
+Section~\ref{sec:communication}.
 
 Besides all the communication information, the record also keeps track of the
 \glspl{Task} currently on the device, the compiler state (see
 
 Besides all the communication information, the record also keeps track of the
 \glspl{Task} currently on the device, the compiler state (see
@@ -66,67 +70,97 @@ is given in Listing~\ref{lst:mtaskdevice}. The definitions of the message
 format are explained in the following section.
 
 \subsection{Shares}
 format are explained in the following section.
 
 \subsection{Shares}
-\Glspl{SDS} on the device are synchronized in the server and thus stored both
-on the server and on the client. In this way the communication is kept to the
-bare minimum because no communication with the device is needed for a
-\gls{SDS} read operation. In this case the \gls{Task} requesting can just be
-provided with the synchronized value. As mentioned before, the device has to
-explicitly publish an update. This has the implication that the server and the
-client can get out of sync. However, this is not a problem since it is well
-documented and by design.
+\Glspl{SDS} on the device can be accessed by both the device and the server.
+While it would be possible to only store the \glspl{SDS} on the device, this
+would require a lot of communication because every read operation will then
+result in sending messages to-and-fro the device. Thus, the \gls{Task}
+requesting the shared information can just be provided with the synchronized
+value. As mentioned before, the device has to explicitly publish an update.
+This has the implication that the server and the client can get out of sync.
+However, this is by design and well documented. In the current system, an
+\gls{SDS} can only reside on a single device.
 
 % TODO
 
 % TODO
-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.
+% Single share per share
+There are several possible approaches for storing \glspl{SDS} on the server
+each with their own level of control. A possible way is to --- in the device
+record --- add a list of references to \gls{iTasks}-\glspl{SDS} that represent
+the \gls{SDS} on the device. The problem with this is the fact that an
+\gls{SDS} can become an orphan. The \gls{SDS} is still accessible even when the
+device is long gone. There is no way of knowing whether the \gls{SDS} is
+unreachable because of the device being gone, or the \gls{SDS} itself is gone
+on the device. Accessing the \gls{SDS} happens by calling the \CI{get},
+\CI{set} and \CI{upd} functions directory on the actual \gls{SDS}.
+
+% Single share per device
+Another approach would be to have reference to a \gls{SDS} containing a table
+of \gls{SDS} values per device. This approach poses the same orphan problem as
+before. Accessing a single \gls{SDS} on the device happens by calling the
+\CI{get}, \CI{set} and \CI{upd} functions on the actual table \gls{SDS} with an
+applied \CI{mapReadWrite}. Using parametric lenses can circumvent problem of
+watchers getting notified for other shares that are written. Error handling is
+better than the previously mentioned approach because a \gls{SDS} can know
+whether the \gls{SDS} has really gone because it will not be available anymore
+in the table. It still does not know whether the device is still available.
+
+% One share to rule them all
+Finally, all devices containing all of their \glspl{SDS} in a table could be
+stored in a single big \gls{SDS}. While the \CI{mapReadWrite} functions require
+a bit more logic, they can determine the source of the error and act upon it.
+Also, the parametric lenses must contain more logic. A downside of this
+approach is that updating a single \gls{SDS} requires an update of the entire
+object. In practise, this is not a real issue since almost all information can
+be reused and \gls{SDS} residing on devices are often not updated with a very
+high frequency.
 
 \subsection{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.
+The type for the parametric lens of the \gls{SDS} containing all devices is
+\CI{Maybe (MTaskDevice, Int)}. There are several levels of abstraction that
+have to be introduced. First, the \gls{SDS} responsible for storing the entire
+list of devices is called the global \gls{SDS}. Secondly, a \gls{SDS} can focus
+on a single device, such \glspl{SDS} are called local \glspl{SDS}. Finally, a
+\gls{SDS} can focus on a single \gls{SDS} on a single device. These \glspl{SDS}
+are called share \glspl{SDS}. Using parametric lenses, the notifications can be
+directed to only the watchers interested. Moreover, using parametric lenses,
+the \gls{SDS} can know whether it is updating a single \gls{SDS} on a single
+device and synchronize the value with the actual device. This means that when
+writing to a share \gls{SDS} the update is also transformed to messages that
+are put in the channels of the corresponding device to also notify the device
+of the update. The \gls{SDS} is tailor-made and uses an actual standard
+\gls{SDS} that writes to a file or memory as the storage. The tailor-made read
+and write functions are only used to detect whether it is required to send an
+update to the actual device. 
+
+Listing~\ref{lst:actualdev} shows the implementation of the big \gls{SDS}. From
+this \gls{SDS} all other \glspl{SDS} are derived. The following paragraphs show
+how this is achieved for the global \gls{SDS} local \gls{SDS} and the share
+\gls{SDS}. In the big \gls{SDS}, reading the value is just a matter of reading
+the standard \gls{SDS} that serves as the actual storage of the \gls{SDS}. The
+derived shares will filter the output read accordingly. Writing the share
+requires some extra work because it might be possible that an actual device has
+to be notified. First, the actual storage of the \gls{SDS} is written. If the
+parameter was \CI{Nothing} --- the global \gls{SDS} --- the write operation is
+done. If the parameter was \CI{Just (d, -1)} --- a local \gls{SDS} --- nothing
+has to be done as well. The final case is the special case, when the parameter
+is \CI{Just (d, i)}, this means that the \gls{SDS} was focussed on device
+\CI{d} and \gls{SDS} \CI{i} and thus it needs to write to it. First it locates
+the device in the list, followed by the location of the share to check whether
+is still exists. Finally the actual update messages are added to the device
+channels using the \CI{sendMessagesIW} function.
+
+All of the methods share the same \CI{SDSNotifyPred p} which is a function
+\CI{p -> Bool} and determines for the given \CI{p} whether a notification is
+required. The predicate function has the \CI{p} of the writer curried in and
+can determine whether the second argument --- the reader --- needs to be
+notified. In practice, the reader only needs to be notified when the parameters
+are exactly the same.
 
 \begin{lstlisting}[label={lst:actualdev},%
        caption={Device \gls{SDS}}]
 
 \begin{lstlisting}[label={lst:actualdev},%
        caption={Device \gls{SDS}}]
+($<) a fb = fmap (const a) fb
+
 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
 deviceStore :: RWShared (Maybe (MTaskDevice, Int)) [MTaskDevice] [MTaskDevice]
-deviceStore = SDSSource {SDSSource | name="deviceStore", read = realRead, write= realWrite}
+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
 where
        realRead :: (Maybe (MTaskDevice,Int)) *IWorld -> (MaybeError TaskException [MTaskDevice], *IWorld)
        realRead p iw = read realDeviceStore iw
@@ -134,25 +168,16 @@ where
        realWrite :: (Maybe (MTaskDevice,Int)) [MTaskDevice] *IWorld -> (MaybeError TaskException (SDSNotifyPred (Maybe (MTaskDevice,Int))), *IWorld)
        realWrite mi w iw
        # (merr, iw) = write w 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)
+       | isError merr || isNothing mi = (merr $> gEq{|*|} mi, iw)
        # (Just (dev, ident)) = mi
        # (Just (dev, ident)) = mi
-       | ident == -1 = (merr $> notifyPred mi, iw)
+       | ident == -1 = (merr $> gEq{|*|} 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)
        = 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
+                               (Ok _, iw) = (Ok $ gEq{|*|} mi, iw)
 
        realDeviceStore :: Shared [MTaskDevice]
        realDeviceStore = sharedStore "mTaskDevices" []
 
        realDeviceStore :: Shared [MTaskDevice]
        realDeviceStore = sharedStore "mTaskDevices" []
@@ -160,7 +185,9 @@ where
 
 \subsubsection{Global \glspl{SDS}}
 Accessing the global \gls{SDS} is just a matter of focussing the
 
 \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:
+\CI{deviceStore} to \CI{Nothing}. In this way, \glspl{Task} watching the
+\gls{SDS} will only be notified if a device is added or removed. The actual
+code is as follows:
 
 \begin{lstlisting}[caption={Global \gls{SDS}}]
 deviceStoreNP :: Shared [MTaskDevice]
 
 \begin{lstlisting}[caption={Global \gls{SDS}}]
 deviceStoreNP :: Shared [MTaskDevice]
@@ -168,10 +195,12 @@ deviceStoreNP = sdsFocus Nothing deviceStore
 \end{lstlisting}
 
 \subsubsection{Local \glspl{SDS}}
 \end{lstlisting}
 
 \subsubsection{Local \glspl{SDS}}
-Accessing a single device can be done using the \CI{mapReadWrite} function.
+Accessing a single device can be done using the \CI{deviceShare} 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
 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:
+channels and is unique throughout the system. This type of \gls{SDS} will only
+be notified if the device itself changed. It will not be notified when only a
+single \gls{SDS} on the device changes. The implementation is as follows:
 
 \begin{lstlisting}[caption={Local \gls{SDS}}]
 deviceShare :: MTaskDevice -> Shared MTaskDevice
 
 \begin{lstlisting}[caption={Local \gls{SDS}}]
 deviceShare :: MTaskDevice -> Shared MTaskDevice
@@ -187,9 +216,12 @@ deviceShare d = mapReadWriteError
 
 \subsubsection{Local-\gls{SDS} specific \glspl{SDS}}
 A single \gls{SDS} on a single device can be accessed using the \CI{shareShare}
 
 \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.
+function. This function focusses the global \gls{SDS} on a single \gls{SDS}
+from a single device. It can use old share references in the same fashion as
+the local \gls{SDS} only treating it as references. It uses the
+\CI{mapReadWrite} functions to serve the correct part of the information. When
+a \gls{Task} writes to this \gls{SDS}, the global \gls{SDS} will know this
+through the parameter and propagate the value to the device.
 
 \begin{lstlisting}[caption={Local \gls{SDS}}]
 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
 
 \begin{lstlisting}[caption={Local \gls{SDS}}]
 shareShare :: MTaskDevice MTaskShare -> Shared BCValue
index 3b1657f..2bd77d3 100644 (file)
@@ -91,6 +91,16 @@ 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.
 
 for \CI{BCValue}s. Using \glspl{SDS} for multiple \glspl{Task} within one
 device is solved when the previous point is implemented.
 
+Another way of improving on \gls{SDS} handling is to separate \glspl{SDS} from
+devices. In this implementation, the \gls{SDS} not only needs to know on which
+device it is, but also which internal device \gls{SDS} id it has. A pro of
+this technique is that the \gls{SDS} can be shared between \glspl{Task} that
+are not defined in the same scope because they are separated. A con of this
+implementation is that the mechanisms for implementing \glspl{SDS} have to be
+more complex, they have to keep track of the devices containing or sharing an
+\gls{SDS}. Moreover, when the \gls{SDS} is updated, all attached devices must
+be updated which requires some extra work.
+
 \subsection{Robustness}
 \paragraph{Reconnect with lost devices:}
 The robustness of the system can be greatly improved. Devices that lose
 \subsection{Robustness}
 \paragraph{Reconnect with lost devices:}
 The robustness of the system can be greatly improved. Devices that lose