From 6edff4bd1d7db321f5d2c38a20c1ef20c52390c0 Mon Sep 17 00:00:00 2001
From: apvrille <ludovic.apvrille@eurecom.fr>
Date: Mon, 19 Jun 2017 17:38:15 +0200
Subject: [PATCH] Update on code generation

---
 .../codegeneration_documentation.tex          | 123 ++++++++++++++++++
 doc/codegeneration/figures/controlpanel.pdf   | Bin 0 -> 7175 bytes
 2 files changed, 123 insertions(+)
 create mode 100644 doc/codegeneration/figures/controlpanel.pdf

diff --git a/doc/codegeneration/codegeneration_documentation.tex b/doc/codegeneration/codegeneration_documentation.tex
index 7ca8d92de6..4b3c7782d5 100644
--- a/doc/codegeneration/codegeneration_documentation.tex
+++ b/doc/codegeneration/codegeneration_documentation.tex
@@ -379,8 +379,54 @@ const char* hostname="localhost";
 const char* portname="8374";
 int fd;
 struct addrinfo* res;
+#define MAX_DGRAM_SIZE  549
 \end{lstlisting}
 
+\item It defines the global constants and variables used to be able to interact from the GUI to MS, and the functions to send datagrams to the MS. For example, when you click on the "start" button, a datagram is sent from GUI to MS. This part is explained in more details in section {\ref{sec:GUIActions}}
+\begin{lstlisting}
+pthread_t thread__Datagram;
+
+// Handling start datagrams
+int start = 0;
+pthread_mutex_t startMutex ;
+pthread_cond_t  noStart;
+
+
+
+void startDatagram() {
+  pthread_mutex_lock(&startMutex);
+  start = 1;
+  pthread_cond_signal(&noStart);
+  pthread_mutex_unlock(&startMutex);
+}
+
+// Assumes fd is valid
+void* receiveDatagram(void *arg) {
+  printf("Thread receive datagram started\n");
+
+  char buffer[MAX_DGRAM_SIZE];
+  struct sockaddr_storage src_addr;
+  socklen_t src_addr_len=sizeof(src_addr);
+
+  while(1) {
+    printf("Waiting for datagram packet\n");
+    ssize_t count=recvfrom(fd,buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len);
+    if (count==-1) {
+      perror("recv failed");
+    } else if (count==sizeof(buffer)) {
+      perror("datagram too large for buffer: truncated");
+    } else {
+      //printf("Datagram size: %d.\n", (int)(count));
+      if (strncmp(buffer, "START", 5) == 0) {
+	//printf("+++++++++++++++++++++++ START\n");
+	startDatagram();
+      }
+    }
+  }
+}
+\end{lstlisting}
+
+
 \item It defines a function to send a datagram.
 \begin{lstlisting}
 void sendDatagram(char * data, int size) {
@@ -418,6 +464,8 @@ void __user_init() {
     exit(-1);
   }
 
+  // Start a thread to receive datagrams
+  pthread_create(&thread__Datagram, NULL, receiveDatagram, NULL);
 }
 \end{lstlisting}
 \end{itemize}
@@ -456,6 +504,8 @@ The corresponding packets are also defined in the GUI application e.g. see \text
 \subsection{GUI animation}
 Generate the C code from TTool (be sure to check the "Include user code" option). Start the GUI from a terminal, and then, start the MS application from TTool (you can also start MS from a terminal). You should see the animations of the GUI while the generated application executes. For example, Figure \ref{fig:animopen} shows the microwave when the door opened, and Figure \ref{fig:animcooking} shows the microwave in heating mode.
 
+
+
 \begin{figure}[htbp]
 \centering
 \includegraphics[width=0.5\textwidth]{figures/animopen}
@@ -468,6 +518,79 @@ Generate the C code from TTool (be sure to check the "Include user code" option)
 \caption{GUI when the microwave is cooking} \label{fig:animcooking}
 \end{figure}
 
+\subsection{GUI actions}\label{sec:GUIActions}
+\subsubsection{GUI side}
+The GUI contains a "start" button.  When the user clicks on this button, the GUI sends a "START" datagram packet to the MS. The GUI is indeed programmed as follows:
+\begin{itemize}
+\item In MainMicrowave.java:
+\begin{lstlisting}
+public void mouseClicked(MouseEvent e){
+        int x = e.getX();
+        int y = e.getY();
+
+        System.out.println("Mouse clicked!!!");
+
+        // START?
+        if ((x>630)&&(x<720)&&(y>335)&&(y<365)) {
+            System.out.println("Mouse clicked on start");
+            if (ds != null) {
+                ds.sendDatagramTo("START");
+            }
+            System.out.println("Action on start sent");
+        }
+}
+\end{lstlisting}
+\textit{ds.sendDatagram(..)} calls a DatagramServer object that sends a datagram to the destination from which it got its first packet.
+\end{itemize}
+\subsubsection{MS side}
+On MS side, the global code starts in  \textit{user\_init()} a thread that handles datagram receiving :
+\begin{lstlisting}
+pthread_create(&thread__Datagram, NULL, receiveDatagram, NULL);
+\end{lstlisting}
+\textit{receiveDatagram()} waits for datagram packets. When it gets a packet, it checks if it contains the "START" string. If so, it calls \textit{startDatagram()}.  This function works as follows:
+\begin{enumerate}
+\item A lock is put on a mutex ("startMutex")
+\item The "start" variable is set to 1
+\item A call is made on the condition variable "noStart"
+\item The mutex is unlock
+\end{enumerate}
+The \textit{ControlPanel} block defines a \textit{start()} method called before it sends the "startButton" signal, see Figure \ref{fig:controlpanel}.
+
+\begin{figure}[htbp]
+\centering
+\includegraphics[width=0.25\textwidth]{figures/controlpanel}
+\caption{Window of the Graphical User Interface} \label{fig:controlpanel}
+\end{figure}
+
+Thus, when calling start(), the MS wants to wait for the "START" datagram. To do so, the \textit{ControlPanel} block implements "start()" as follows. First, it refers to externally defined elements: the start variable ("start"), the mutex (startMutex) and the condition variable ("startMutex").
+\begin{lstlisting}
+extern int start;
+extern pthread_mutex_t startMutex ;
+extern pthread_cond_t  noStart;
+\end{lstlisting}
+The method itself works as follows:
+\begin{enumerate}
+\item It puts a lock on the mutex.
+\item It waits untils "start" is equal to at least 1. Meanwhile, it waits on the "noStart" condition variable.
+\item When "start" is finally equal to 1 or more, it sets "start" to 0.
+\item It unlocks the mutex
+\end{enumerate}
+
+Then , the execution of the \textit{ControlPanel} block can continue with the sending of the "startButton" signal. Note that  this is thanks to the mutex facility that the datagram receinving facility and the \textit{ControlPanel} block cannot modify "start" at the same time.\\
+
+\begin{lstlisting}
+void _userImplemented_ControlPanel__start() {
+  pthread_mutex_lock(&startMutex);
+  printf("Waiting for next start");
+  while(start < 1) {
+    pthread_cond_wait(&noStart, &startMutex);
+  }
+  start = 0;
+  pthread_mutex_unlock(&startMutex);
+  printf("****** MW can start cooking\n");
+}
+\end{lstlisting}
+
 
 \newpage
 \section{Customizing the code generator}\label{sec:customgenerator}
diff --git a/doc/codegeneration/figures/controlpanel.pdf b/doc/codegeneration/figures/controlpanel.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..0874fd64670188cf7047231c5bd0abe110dc5e99
GIT binary patch
literal 7175
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
GH!c7yjuF%V

literal 0
HcmV?d00001

-- 
GitLab