implemented single mode for operation connection splitter
Stefan Schuermans

Stefan Schuermans commited on 2011-12-29 14:30:18
Showing 5 changed files, with 131 additions and 30 deletions.

... ...
@@ -37,6 +37,19 @@
37 37
       If this file does not exists, the number of simultaneous connections
38 38
       is not limited.
39 39
     </p>
40
+    <h3>Single Mode</h3>
41
+    <p>
42
+      In single mode, the operator connection splitter module makes sure that
43
+      all outgoing connections go to the same target module (.
44
+      The first connection works as usual.
45
+      Additional connections are directly forwarded to the module chosen
46
+      by the first connection.
47
+      If no module has been selected by the first connection, no further
48
+      incoming connections are accepted.
49
+      <br>
50
+      Single mode can be enabled by writing <code>true</code> to the file
51
+      <code>single</code>.
52
+    </p>
40 53
     <h3>Extensions / Phone Numbers</h3>
41 54
     <p>
42 55
       The virtual extensions (i.e. phone numbers) that can be called
... ...
@@ -51,6 +51,22 @@ bool OpMgr::close(const std::string &name)
51 51
   return true;
52 52
 }
53 53
 
54
+/**
55
+ * @brief check if operator interface will accept new connection
56
+ * @param[in] name operator interface to connect to
57
+ * @return if a new connection would be accepted
58
+ */
59
+bool OpMgr::canConnect(const std::string &name)
60
+{
61
+  // find requested interface
62
+  ReqIfMap::iterator itReqIf = m_reqIfs.find(name);
63
+  if (itReqIf == m_reqIfs.end())
64
+    return false; // not open
65
+
66
+  // check with server side if new connection is accepted
67
+  return itReqIf->second->acceptNewOpConn(name);
68
+}
69
+
54 70
 /**
55 71
  * @brief connect to operator interface
56 72
  * @param[in] name operator interface to connect to
... ...
@@ -52,6 +52,13 @@ public:
52 52
    */
53 53
   bool close(const std::string &name);
54 54
 
55
+  /**
56
+   * @brief check if operator interface will accept new connection
57
+   * @param[in] name operator interface to connect to
58
+   * @return if a new connection would be accepted
59
+   */
60
+  bool canConnect(const std::string &name);
61
+
55 62
   /**
56 63
    * @brief connect to operator interface
57 64
    * @param[in] name operator interface to connect to
... ...
@@ -6,6 +6,7 @@
6 6
 #include <map>
7 7
 #include <string>
8 8
 
9
+#include "BoolFile.h"
9 10
 #include "Directory.h"
10 11
 #include "File.h"
11 12
 #include "ListTracker.h"
... ...
@@ -36,6 +37,7 @@ OpSplitter::OpSplitter(const std::string &name, Mgrs &mgrs,
36 37
   Module(name, mgrs, dirBase),
37 38
   m_fileSound(dirBase.getFile("sound")),
38 39
   m_fileMaxConn(dirBase.getFile("maxconn")),
40
+  m_fileSingle(dirBase.getFile("single")),
39 41
   m_extListTracker(*this, dirBase.getSubdir("extensions"))
40 42
 {
41 43
   // load extensions
... ...
@@ -86,6 +88,10 @@ void OpSplitter::updateConfig()
86 88
   if (m_fileMaxConn.checkModified())
87 89
     m_fileMaxConn.update();
88 90
 
91
+  // single mode config file was modified -> re-read single mode config
92
+  if (m_fileSingle.checkModified())
93
+    m_fileSingle.update();
94
+
89 95
   // extensions update
90 96
   m_extListTracker.updateConfig();
91 97
 }
... ...
@@ -93,14 +99,24 @@ void OpSplitter::updateConfig()
93 99
 /// callback when requested time reached
94 100
 void OpSplitter::timeCall()
95 101
 {
102
+  // process pending actions for local connections
103
+  MapLocal::iterator it = m_mapLocal.begin();
104
+  while (it != m_mapLocal.end()) {
105
+    // close connection
106
+    if (it->second.m_close) {
107
+      MapLocal::iterator itDel = it;
108
+      ++it; // we have to increment ourself to make erasing in loop work
109
+      itDel->first->close();
110
+      m_mapLocal.erase(itDel);
111
+      continue; // it already incremented -> skip rest of loop
112
+    }
96 113
     // send sound play request on marked local operator connection
97
-  for (MapLocal::iterator it = m_mapLocal.begin();
98
-       it != m_mapLocal.end(); ++it) {
99 114
     if (it->second.m_sendPlay) {
100 115
       it->second.m_sendPlay = false;
101 116
       if (m_fileSound.m_valid)
102 117
         it->first->sendPlay(m_fileSound.m_obj.m_str);
103 118
     }
119
+    ++it;
104 120
   }
105 121
 }
106 122
 
... ...
@@ -115,7 +131,25 @@ bool OpSplitter::acceptNewOpConn(const std::string &name)
115 131
   if (m_fileMaxConn.m_valid &&
116 132
       m_mapLocal.size() + m_mapInOut.size() >= m_fileMaxConn.m_obj.m_uint)
117 133
     return false; // too many connection
134
+
135
+  // single mode
136
+  if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) {
137
+
138
+    // accept first connection
139
+    if (m_mapLocal.empty() && m_mapInOut.empty())
140
+      return true;
141
+
142
+    // no operator interface selected yet
143
+    if (m_singleOp.empty())
144
+      return false;
145
+
146
+    // additional connections: ask selected operator interface
147
+    return m_mgrs.m_opMgr.canConnect(m_singleOp);
148
+
149
+  } // if m_fileSingle
150
+
118 151
   return true;
152
+
119 153
   (void)name; // unused
120 154
 }
121 155
 
... ...
@@ -128,7 +162,29 @@ bool OpSplitter::acceptNewOpConn(const std::string &name)
128 162
  */
129 163
 void OpSplitter::newOpConn(const std::string &name, OpConn *pConn)
130 164
 {
131
-  // this is a new incoming connection -> make it a local connection
165
+  // this is a new incoming connection
166
+
167
+  // single mode and not first connection
168
+  if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) {
169
+    if (!m_mapLocal.empty() || !m_mapInOut.empty()) {
170
+
171
+      // try to open outgoiong operator connection to selected operator intf.
172
+      OpConn *pOutConn = m_mgrs.m_opMgr.connect(m_singleOp, this);
173
+      if (!pOutConn) {
174
+        // module is not ready for connection
175
+        newLocal(pConn, true); // make conn. a local conn. to be closed soon
176
+        return;
177
+      }
178
+
179
+      // establish connection forwarding
180
+      m_mapInOut[pConn] = pOutConn;
181
+      m_mapOutIn[pOutConn] = pConn;
182
+
183
+      return;
184
+    } // if !m_mapLocal.empty() ...
185
+  } // if m_fileSingle ...
186
+
187
+  // make connection a local connection
132 188
   newLocal(pConn);
133 189
 
134 190
   (void)name; // unused
... ...
@@ -208,15 +264,29 @@ void OpSplitter::opConnClose(OpConn *pConn)
208 264
     itInOut->second->close();
209 265
     m_mapOutIn.erase(itInOut->second);
210 266
     m_mapInOut.erase(itInOut);
267
+    if (m_mapInOut.empty()) // last connection closed -> no operator intf. sel.
268
+      m_singleOp.clear();
211 269
     return;
212 270
   }
213 271
 
214
-  // outgoing to incoming connection -> make incoming connection local
272
+  // outgoing to incoming connection
215 273
   MapOutIn::iterator itOutIn = m_mapOutIn.find(pConn);
216 274
   if (itOutIn != m_mapOutIn.end()) {
217
-    newLocal(itOutIn->second);
218
-    m_mapInOut.erase(itOutIn->second);
275
+    OpConn * pInConn = itOutIn->second;
276
+    m_mapInOut.erase(pInConn);
219 277
     m_mapOutIn.erase(itOutIn);
278
+    if (m_mapInOut.empty()) // last connection closed -> no operator intf. sel.
279
+      m_singleOp.clear();
280
+    // single mode and not last connection
281
+    if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) {
282
+      if (!m_mapLocal.empty() || !m_mapInOut.empty()) {
283
+        // close incoming connection as well
284
+        pInConn->close();
285
+        return;
286
+      }
287
+    }
288
+    // make incoming connection local
289
+    newLocal(pInConn);
220 290
     return;
221 291
   }
222 292
 }
... ...
@@ -224,13 +294,15 @@ void OpSplitter::opConnClose(OpConn *pConn)
224 294
 /**
225 295
  * @brief create new local connection
226 296
  * @param[in] pConn connection to make local
297
+ * @param[in] close if to close connection as soon as possible
227 298
  */
228
-void OpSplitter::newLocal(OpConn *pConn)
299
+void OpSplitter::newLocal(OpConn *pConn, bool close /* = false */)
229 300
 {
230 301
   // add connection to local connection map
231 302
   Local &local = m_mapLocal[pConn];
232 303
   local.m_number.clear(); // no extension dialed so far
233
-  local.m_sendPlay = true; // send play command
304
+  local.m_sendPlay = !close; // send play command if close not requested
305
+  local.m_close = close;
234 306
   m_mgrs.m_callMgr.requestTimeCall(this, Time::now());
235 307
 }
236 308
 
... ...
@@ -261,22 +333,14 @@ void OpSplitter::localKey(MapLocal::iterator itLocal, char key)
261 333
       break;
262 334
     // close local connection / call extension
263 335
     case '#':
264
-      if (itLocal->second.m_number.empty())
265
-        localClose(itLocal);
266
-      else
336
+      if (itLocal->second.m_number.empty()) {
337
+        itLocal->first->close();
338
+        m_mapLocal.erase(itLocal);
339
+      } else {
267 340
         callExtension(itLocal);
268
-      break;
269 341
       }
342
+      break;
270 343
   }
271
-
272
-/**
273
- * @brief close local connection
274
- * @param[in] itLocal local connection
275
- */
276
-void OpSplitter::localClose(MapLocal::iterator itLocal)
277
-{
278
-  itLocal->first->close();
279
-  m_mapLocal.erase(itLocal);
280 344
 }
281 345
 
282 346
 /**
... ...
@@ -293,10 +357,12 @@ void OpSplitter::callExtension(MapLocal::iterator itLocal)
293 357
     return;
294 358
   }
295 359
 
360
+  // save selected operator interface for single mode
361
+  m_singleOp = itExt->second;
362
+
296 363
   // try to open outgoiong operator connection to module
297 364
   OpConn *pOutConn = m_mgrs.m_opMgr.connect(itExt->second, this);
298
-  if (!pOutConn)
299
-  {
365
+  if (!pOutConn) {
300 366
     // module is not ready for connection
301 367
     itLocal->second.m_number.clear(); // begin new extension number
302 368
     return;
... ...
@@ -9,6 +9,7 @@
9 9
 #include <map>
10 10
 #include <string>
11 11
 
12
+#include "BoolFile.h"
12 13
 #include "Directory.h"
13 14
 #include "File.h"
14 15
 #include "ListTracker.h"
... ...
@@ -42,6 +43,7 @@ protected:
42 43
   struct Local {
43 44
     std::string m_number;   ///< extension number dialed so far
44 45
     bool        m_sendPlay; ///< if to send a play request
46
+    bool        m_close;    ///< if to close connection as soon as possible
45 47
   };
46 48
 
47 49
   /**
... ...
@@ -129,8 +131,9 @@ protected:
129 131
   /**
130 132
    * @brief create new local connection
131 133
    * @param[in] pConn connection to make local
134
+   * @param[in] close if to close connection as soon as possible
132 135
    */
133
-  void newLocal(OpConn *pConn);
136
+  void newLocal(OpConn *pConn, bool close = false);
134 137
 
135 138
   /**
136 139
    * @brief a key has been pressed for a local connection
... ...
@@ -139,12 +142,6 @@ protected:
139 142
    */
140 143
   void localKey(MapLocal::iterator itLocal, char key);
141 144
 
142
-  /**
143
-   * @brief close local connection
144
-   * @param[in] itLocal local connection
145
-   */
146
-  void localClose(MapLocal::iterator itLocal);
147
-
148 145
   /**
149 146
    * @brief call extension dialed over local connection
150 147
    * @param[in] itLocal local connection
... ...
@@ -154,6 +151,8 @@ protected:
154 151
 protected:
155 152
   NameFile       m_fileSound;      ///< file containing sound name
156 153
   UIntFile       m_fileMaxConn;    ///< file containing max. number of conn.
154
+  BoolFile       m_fileSingle;     ///< file containing single mode config
155
+  std::string    m_singleOp;       ///< operator intf. to call in single mode
157 156
   ExtListTracker m_extListTracker; ///< extension tracker
158 157
   ExtMap         m_extMap;         ///< map of extensions to call
159 158
   MapLocal       m_mapLocal;       ///< localy handled connections
160 159