clarified operator connection usage in callbacks added sound file play request to loveletter module added exit code "*#" to loveletter module
Stefan Schuermans

Stefan Schuermans commited on 2011-12-23 10:59:56
Showing 10 changed files, with 170 additions and 16 deletions.

... ...
@@ -9,13 +9,24 @@
9 9
       An operator connection is used to submit a movie number
10 10
       (prefixed with a star and followed by ahash, e.g. <code>*123#</code>).
11 11
       The movie with this number will then be played until another
12
-      number is requested or the connection is closed.
12
+      number is requested or the connection is closed (either by the
13
+      remote side or using <code>*#</code>).
13 14
     </p>
14 15
     <h2>Configuration</h2>
15 16
     <p>
16 17
       The configuration of the loveletter module with name <code>NAME</code>
17 18
       is located in the <code>loveletters/NAME</code> subdirectory.
18 19
     </p>
20
+    <h3>Sound File</h3>
21
+    <p>
22
+      While an operator is connected to the loveletter module (e.g. via
23
+      a phone connection), a sound file can be requested to be played.
24
+      (In case of the phone connection, this file is located on the
25
+      phone server.)
26
+      The name of the sound to request is contained in the file
27
+      <code>sound</code>.
28
+      If this file does not exists, no sound is requested to be played.
29
+    </p>
19 30
     <h3>Movies</h3>
20 31
     <p>
21 32
       The movies available for on-demand play are located in the subdirectory
... ...
@@ -18,6 +18,7 @@
18 18
 #include "LoveletterMovie.h"
19 19
 #include "Mgrs.h"
20 20
 #include "Module.h"
21
+#include "NameFile.h"
21 22
 #include "OutStreamFile.h"
22 23
 #include "StreamRecv.h"
23 24
 #include "Time.h"
... ...
@@ -34,6 +35,7 @@ namespace Blinker {
34 35
 Loveletter::Loveletter(const std::string &name, Mgrs &mgrs,
35 36
                const Directory &dirBase):
36 37
   Module(name, mgrs, dirBase),
38
+  m_fileSound(dirBase.getFile("sound")),
37 39
   m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
38 40
   m_fileHaltStream(dirBase.getFile("haltstream"), mgrs.m_streamMgr),
39 41
   m_movieTracker(*this, dirBase.getSubdir("movies")),
... ...
@@ -42,7 +44,8 @@ Loveletter::Loveletter(const std::string &name, Mgrs &mgrs,
42 44
   m_curFrame(0),
43 45
   m_curChange(false),
44 46
   m_halted(false),
45
-  m_pOpConn(NULL)
47
+  m_pOpConn(NULL),
48
+  m_sendPlay(false)
46 49
 {
47 50
   // load movies
48 51
   m_fileHaltStream.setStreamRecv(this);
... ...
@@ -71,6 +74,10 @@ Loveletter::~Loveletter()
71 74
 /// check for update of configuration
72 75
 void Loveletter::updateConfig()
73 76
 {
77
+  // sound name file was modified -> re-read sound name
78
+  if (m_fileSound.checkModified())
79
+    m_fileSound.update();
80
+
74 81
   // output stream name file was modified -> re-get output stream
75 82
   if (m_fileOutStream.checkModified()) {
76 83
     m_fileOutStream.update();
... ...
@@ -126,6 +133,13 @@ void Loveletter::setFrame(const std::string &stream, stBlinkenFrame *pFrame)
126 133
 /// callback when requested time reached
127 134
 void Loveletter::timeCall()
128 135
 {
136
+  // send sound play request on operator connection
137
+  if (m_sendPlay) {
138
+    m_sendPlay = false;
139
+    if (m_pOpConn && m_fileSound.m_valid)
140
+      m_pOpConn->sendPlay(m_fileSound.m_obj.m_str);
141
+  }
142
+
129 143
   // leave if halted
130 144
   if (m_halted)
131 145
     return;
... ...
@@ -160,11 +174,15 @@ bool Loveletter::acceptNewOpConn(const std::string &name)
160 174
  * @brief new operator connection
161 175
  * @param[in] name operator interface name
162 176
  * @param[in] pConn operator connection object
177
+ *
178
+ * The new connection may not yet be used for sending inside this callback.
163 179
  */
164 180
 void Loveletter::newOpConn(const std::string &name, OpConn *pConn)
165 181
 {
166 182
   closeOpConn(); // close old connection (to be on the safe side)
167 183
   m_pOpConn = pConn; // remember new connection
184
+  m_sendPlay = true; // schedule sending play request
185
+  m_mgrs.m_callMgr.requestTimeCall(this, Time::now());
168 186
   (void)name; // unused
169 187
 }
170 188
 
... ...
@@ -193,8 +211,11 @@ void Loveletter::opConnRecvKey(OpConn *pConn, char key)
193 211
     case '9':
194 212
       m_movieNumber += key;
195 213
       break;
196
-    // play movie
214
+    // exit / play movie
197 215
     case '#':
216
+      if (m_movieNumber.empty())
217
+        closeOpConn();
218
+      else
198 219
         startPlaying();
199 220
       break;
200 221
   }
... ...
@@ -216,6 +237,8 @@ void Loveletter::opConnRecvPlay(OpConn *pConn, const std::string &sound)
216 237
 /**
217 238
  * @brief operator connection is closed
218 239
  * @param[in] pConn operator connection object
240
+ *
241
+ * The connection may not be used for sending any more in this callback.
219 242
  */
220 243
 void Loveletter::opConnClose(OpConn *pConn)
221 244
 {
... ...
@@ -18,6 +18,7 @@
18 18
 #include "ListTracker.h"
19 19
 #include "Mgrs.h"
20 20
 #include "Module.h"
21
+#include "NameFile.h"
21 22
 #include "OpConn.h"
22 23
 #include "OpConnIf.h"
23 24
 #include "OpReqIf.h"
... ...
@@ -89,6 +90,8 @@ public:
89 90
    * @brief new operator connection
90 91
    * @param[in] name operator interface name
91 92
    * @param[in] pConn operator connection object
93
+   *
94
+   * The new connection may not yet be used for sending inside this callback.
92 95
    */
93 96
   virtual void newOpConn(const std::string &name, OpConn *pConn);
94 97
 
... ...
@@ -109,6 +112,8 @@ public:
109 112
   /**
110 113
    * @brief operator connection is closed
111 114
    * @param[in] pConn operator connection object
115
+   *
116
+   * The connection may not be used for sending any more in this callback.
112 117
    */
113 118
   virtual void opConnClose(OpConn *pConn);
114 119
 
... ...
@@ -132,6 +137,7 @@ protected:
132 137
   void stopPlaying();
133 138
 
134 139
 protected:
140
+  NameFile      m_fileSound;      ///< file containing sound name
135 141
   OutStreamFile m_fileOutStream;  ///< output stream name file
136 142
   InStreamFile  m_fileHaltStream; /**< halt stream name file
137 143
                                        (playing halts if stream active) */
... ...
@@ -147,6 +153,7 @@ protected:
147 153
   Time          m_nextTime;       /**< when to show next frame
148 154
                                        (valid if m_curValid && !m_halted) */
149 155
   OpConn        *m_pOpConn;       ///< current operator connection or NULL
156
+  bool          m_sendPlay;       ///< if sending play request needed
150 157
   std::string   m_movieNumber;    ///< number of movie to play
151 158
 }; // class Loveletter
152 159
 
... ...
@@ -40,6 +40,8 @@ public:
40 40
   /**
41 41
    * @brief operator connection is closed
42 42
    * @param[in] pConn operator connection object
43
+   *
44
+   * The connection may not be used for sending any more in this callback.
43 45
    */
44 46
   virtual void opConnClose(OpConn *pConn) = 0;
45 47
 }; // class OpConnIf
... ...
@@ -67,6 +67,8 @@ bool OpPrinter::acceptNewOpConn(const std::string &name)
67 67
  * @brief new operator connection
68 68
  * @param[in] name operator interface name
69 69
  * @param[in] pConn operator connection object
70
+ *
71
+ * The new connection may not yet be used for sending inside this callback.
70 72
  */
71 73
 void OpPrinter::newOpConn(const std::string &name, OpConn *pConn)
72 74
 {
... ...
@@ -106,6 +108,8 @@ void OpPrinter::opConnRecvPlay(OpConn *pConn, const std::string &sound)
106 108
 /**
107 109
  * @brief operator connection is closed
108 110
  * @param[in] pConn operator connection object
111
+ *
112
+ * The connection may not be used for sending any more in this callback.
109 113
  */
110 114
 void OpPrinter::opConnClose(OpConn *pConn)
111 115
 {
... ...
@@ -60,6 +60,8 @@ public:
60 60
    * @brief new operator connection
61 61
    * @param[in] name operator interface name
62 62
    * @param[in] pConn operator connection object
63
+   *
64
+   * The new connection may not yet be used for sending inside this callback.
63 65
    */
64 66
   virtual void newOpConn(const std::string &name, OpConn *pConn);
65 67
 
... ...
@@ -80,6 +82,8 @@ public:
80 82
   /**
81 83
    * @brief operator connection is closed
82 84
    * @param[in] pConn operator connection object
85
+   *
86
+   * The connection may not be used for sending any more in this callback.
83 87
    */
84 88
   virtual void opConnClose(OpConn *pConn);
85 89
 
... ...
@@ -36,6 +36,8 @@ public:
36 36
    * @brief new operator connection
37 37
    * @param[in] name operator interface name
38 38
    * @param[in] pConn operator connection object
39
+   *
40
+   * The new connection may not yet be used for sending inside this callback.
39 41
    */
40 42
   virtual void newOpConn(const std::string &name, OpConn *pConn) = 0;
41 43
 }; // class OpReqIf
... ...
@@ -8,6 +8,7 @@
8 8
 
9 9
 #include <map>
10 10
 #include <string>
11
+#include <vector>
11 12
 
12 13
 #include "Directory.h"
13 14
 #include "File.h"
... ...
@@ -46,6 +47,12 @@ protected:
46 47
   /// map from connection to line number
47 48
   typedef std::map<OpConn *, unsigned int> ConnLineMap;
48 49
 
50
+  /// type for command buffer (command sent before line connected)
51
+  typedef std::vector<std::string> CmdBuf;
52
+
53
+  /// type for command buffer map (line to command buffer)
54
+  typedef std::map<unsigned int, CmdBuf> CmdBufMap;
55
+
49 56
 public:
50 57
   /**
51 58
    * @brief constructor
... ...
@@ -101,6 +108,8 @@ public:
101 108
   /**
102 109
    * @brief operator connection is closed
103 110
    * @param[in] pConn operator connection object
111
+   *
112
+   * The connection may not be used for sending any more in this callback.
104 113
    */
105 114
   virtual void opConnClose(OpConn *pConn);
106 115
 
... ...
@@ -140,10 +149,24 @@ protected:
140 149
   void sendHangup(unsigned int line);
141 150
 
142 151
   /**
143
-   * @brief send message to server
152
+   * @brief send message buffered to server
153
+   * @param[in] line number of line to hangup
154
+   * @param[in] msg message to send
155
+   */
156
+  void sendBuf(unsigned int line, const std::string &msg);
157
+
158
+  /**
159
+   * @brief send message to server now
160
+   * @param[in] line number of line to hangup
161
+   * @param[in] msg message to send
162
+   */
163
+  void sendNow(unsigned int line, const std::string &msg);
164
+
165
+  /**
166
+   * @brief send message to socket
144 167
    * @param[in] msg message to send
145 168
    */
146
-  void send(const std::string &msg);
169
+  void sendSock(const std::string &msg);
147 170
 
148 171
   /// receive data from socket
149 172
   void receiveFromSock();
... ...
@@ -161,6 +184,12 @@ protected:
161 184
    */
162 185
   void incomingCall(unsigned int line, const std::string &extension);
163 186
 
187
+  /**
188
+   * @brief phone line connected
189
+   * @param[in] line number of phone line
190
+   */
191
+  void connected(unsigned int line);
192
+
164 193
   /**
165 194
    * @brief hangup on phone line
166 195
    * @param[in] line number of phone line
... ...
@@ -207,6 +236,7 @@ protected:
207 236
   ExtMap         m_extMap;         ///< map of extensions to call
208 237
   LineConnMap    m_lineConnMap;    ///< map from line number to connection
209 238
   ConnLineMap    m_connLineMap;    ///< map from connection to line number
239
+  CmdBufMap      m_cmdBufMap;      ///< commands to send after line connected
210 240
 }; // class Phone
211 241
 
212 242
 } // namespace Blinker
... ...
@@ -6,8 +6,10 @@
6 6
 #ifndef BLINKER_PHONE_IMPL_H
7 7
 #define BLINKER_PHONE_IMPL_H
8 8
 
9
+#include <map>
9 10
 #include <sstream>
10 11
 #include <string>
12
+#include <vector>
11 13
 
12 14
 #include "Directory.h"
13 15
 #include "File.h"
... ...
@@ -164,6 +166,8 @@ void Phone<ADDR, SOCK>::opConnRecvPlay(OpConn *pConn, const std::string &sound)
164 166
 /**
165 167
  * @brief operator connection is closed
166 168
  * @param[in] pConn operator connection object
169
+ *
170
+ * The connection may not be used for sending any more in this callback.
167 171
  */
168 172
 template<typename ADDR, typename SOCK>
169 173
 void Phone<ADDR, SOCK>::opConnClose(OpConn *pConn)
... ...
@@ -179,6 +183,9 @@ void Phone<ADDR, SOCK>::opConnClose(OpConn *pConn)
179 183
   // remove connection from maps
180 184
   m_connLineMap.erase(pConn);
181 185
   m_lineConnMap.erase(line);
186
+
187
+  // remove command buffer for this line
188
+  m_cmdBufMap.erase(line);
182 189
 }
183 190
 
184 191
 /// (re-)read server address
... ...
@@ -242,7 +249,7 @@ void Phone<ADDR, SOCK>::destroySock()
242 249
 template<typename ADDR, typename SOCK>
243 250
 void Phone<ADDR, SOCK>::sendRegister()
244 251
 {
245
-  send("0:register");
252
+  sendNow(0, "register");
246 253
 
247 254
   // set time for next register message and next heartbeat
248 255
   m_timeRegister = Time::now() + m_serverTimeout;
... ...
@@ -254,7 +261,7 @@ void Phone<ADDR, SOCK>::sendRegister()
254 261
 template<typename ADDR, typename SOCK>
255 262
 void Phone<ADDR, SOCK>::sendHeartbeat()
256 263
 {
257
-  send("0:heartbeat");
264
+  sendNow(0, "heartbeat");
258 265
 
259 266
   // set time for next heartbeat
260 267
   m_timeHeartbeat = Time::now() + m_heartbeatInterval;
... ...
@@ -270,8 +277,8 @@ template<typename ADDR, typename SOCK>
270 277
 void Phone<ADDR, SOCK>::sendPlay(unsigned int line, const std::string &sound)
271 278
 {
272 279
   std::stringstream strm;
273
-  strm << line << ":playbackground:" << sound;
274
-  send(strm.str());
280
+  strm << "playbackground:" << sound;
281
+  sendBuf(line, strm.str());
275 282
 }
276 283
 
277 284
 /**
... ...
@@ -281,9 +288,7 @@ void Phone<ADDR, SOCK>::sendPlay(unsigned int line, const std::string &sound)
281 288
 template<typename ADDR, typename SOCK>
282 289
 void Phone<ADDR, SOCK>::sendHangup(unsigned int line)
283 290
 {
284
-  std::stringstream strm;
285
-  strm << line << ":hangup";
286
-  send(strm.str());
291
+  sendNow(line, "hangup");
287 292
 }
288 293
 
289 294
 /**
... ...
@@ -292,18 +297,47 @@ void Phone<ADDR, SOCK>::sendHangup(unsigned int line)
292 297
  */
293 298
 template<typename ADDR, typename SOCK>
294 299
 void Phone<ADDR, SOCK>::sendAccept(unsigned int line)
300
+{
301
+  sendNow(line, "accept");
302
+}
303
+
304
+/**
305
+ * @brief send message buffered to server
306
+ * @param[in] line number of line to hangup
307
+ * @param[in] msg message to send
308
+ */
309
+template<typename ADDR, typename SOCK>
310
+void Phone<ADDR, SOCK>::sendBuf(unsigned int line, const std::string &msg)
311
+{
312
+  CmdBufMap::iterator itCmdBuf = m_cmdBufMap.find(line);
313
+  // line not yet connected -> buffer command
314
+  if (itCmdBuf != m_cmdBufMap.end())
315
+    // buffer command
316
+    itCmdBuf->second.push_back(msg);
317
+  // line is connected -> send now
318
+  else
319
+    sendNow(line, msg);
320
+}
321
+
322
+/**
323
+ * @brief send message to server now
324
+ * @param[in] line number of line to hangup
325
+ * @param[in] msg message to send
326
+ */
327
+template<typename ADDR, typename SOCK>
328
+void Phone<ADDR, SOCK>::sendNow(unsigned int line, const std::string &msg)
295 329
 {
296 330
   std::stringstream strm;
297
-  strm << line << ":accept";
298
-  send(strm.str());
331
+  strm << line << ":" << msg;
332
+  sendSock(strm.str());
299 333
 }
300 334
 
301 335
 /**
302
- * @brief send message to server
336
+ * @brief send message to socket
303 337
  * @param[in] msg message to send
304 338
  */
305 339
 template<typename ADDR, typename SOCK>
306
-void Phone<ADDR, SOCK>::send(const std::string &msg)
340
+void Phone<ADDR, SOCK>::sendSock(const std::string &msg)
307 341
 {
308 342
   // check that there is a socket and a server address
309 343
   if (!m_pSock || !m_fileServer.m_valid)
... ...
@@ -354,6 +388,8 @@ void Phone<ADDR, SOCK>::serverMsg(const std::string &msg)
354 388
   if (!parser.uintNo(line) || !parser.fixChr(':') ||
355 389
       !parser.untilDelim(":", false, cmd))
356 390
     return;
391
+  if (line < 1) // we are only interested in real lines here
392
+    return;
357 393
 
358 394
   // incoming call
359 395
   if (cmd == "setup") {
... ...
@@ -364,6 +400,11 @@ void Phone<ADDR, SOCK>::serverMsg(const std::string &msg)
364 400
     incomingCall(line, extension);
365 401
   }
366 402
 
403
+  // call connected
404
+  else if (cmd == "connected") {
405
+    connected(line);
406
+  }
407
+
367 408
   // hangup
368 409
   else if (cmd == "onhook") {
369 410
     hangup(line);
... ...
@@ -390,6 +431,10 @@ void Phone<ADDR, SOCK>::incomingCall(unsigned int line,
390 431
   // hangup old call on this line (to be on the safe side)
391 432
   hangup(line);
392 433
 
434
+  // buffer further commands to be sent to this line until it is connected
435
+  // -> create command buffer for this line
436
+  m_cmdBufMap[line];
437
+
393 438
   // lookup extension
394 439
   ExtMap::iterator itExt = m_extMap.find(extension);
395 440
   if (itExt == m_extMap.end())
... ...
@@ -408,6 +453,28 @@ void Phone<ADDR, SOCK>::incomingCall(unsigned int line,
408 453
   sendAccept(line);
409 454
 }
410 455
 
456
+/**
457
+ * @brief phone line connected
458
+ * @param[in] line number of phone line
459
+ */
460
+template<typename ADDR, typename SOCK>
461
+void Phone<ADDR, SOCK>::connected(unsigned int line)
462
+{
463
+  // get buffered commands for this line
464
+  CmdBuf &cmdBuf = m_cmdBufMap[line];
465
+
466
+  // send buffered commands now
467
+  for (CmdBuf::const_iterator cmd = cmdBuf.begin();
468
+       cmd != cmdBuf.end(); ++cmd) {
469
+    std::stringstream strm;
470
+    strm << line << ":" << *cmd;
471
+    sendSock(strm.str());
472
+  }
473
+
474
+  // delete command buffer for this line
475
+  m_cmdBufMap.erase(line);
476
+}
477
+
411 478
 /**
412 479
  * @brief hangup on phone line
413 480
  * @param[in] line number of phone line
... ...
@@ -426,6 +493,9 @@ void Phone<ADDR, SOCK>::hangup(unsigned int line)
426 493
   // remove connection from maps
427 494
   m_lineConnMap.erase(line);
428 495
   m_connLineMap.erase(pConn);
496
+
497
+  // remove command buffer for this line
498
+  m_cmdBufMap.erase(line);
429 499
 }
430 500
 
431 501
 /**
432 502