BlinkenArea - GitList
Repositories
Blog
Wiki
Blinker
Code
Commits
Branches
Tags
Search
Tree:
362c1f4
Branches
Tags
master
Blinker
src
common
OpSplitter.cpp
update copyright header
Stefan Schuermans
commited
362c1f4
at 2019-05-04 17:17:10
OpSplitter.cpp
Blame
History
Raw
/* Blinker Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org> Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html a blinkenarea.org project */ #include <map> #include <string> #include "BoolFile.h" #include "Directory.h" #include "File.h" #include "ListTracker.h" #include "ListTracker_impl.h" #include "Mgrs.h" #include "Module.h" #include "NameFile.h" #include "OpConn.h" #include "OpConnIf.h" #include "OpMgr.h" #include "OpSplitter.h" #include "OpSplitterExtension.h" #include "SettingFile.h" #include "Time.h" #include "TimeCallee.h" #include "UIntFile.h" namespace Blinker { /** * @brief constructor * @param[in] name module name * @param[in] mgrs managers * @param[in] dirBase base directory */ OpSplitter::OpSplitter(const std::string &name, Mgrs &mgrs, const Directory &dirBase): Module(name, mgrs, dirBase), m_fileSound(dirBase.getFile("sound")), m_fileMaxConn(dirBase.getFile("maxconn")), m_fileSingle(dirBase.getFile("single")), m_extListTracker(*this, dirBase.getSubdir("extensions")) { // load extensions m_extListTracker.init(); // open operator connection interface m_mgrs.m_opMgr.open(m_name, this); } /// virtual destructor OpSplitter::~OpSplitter() { // close operator connection interface m_mgrs.m_opMgr.close(m_name); // close all connections while (!m_mapLocal.empty()) { MapLocal::iterator it = m_mapLocal.begin(); it->first->close(); m_mapLocal.erase(it); } while (!m_mapInOut.empty()) { MapInOut::iterator it = m_mapInOut.begin(); it->first->close(); m_mapInOut.erase(it); } while (!m_mapOutIn.empty()) { MapOutIn::iterator it = m_mapOutIn.begin(); it->first->close(); m_mapOutIn.erase(it); } // unload extensions m_extListTracker.clear(); // cancel time callback m_mgrs.m_callMgr.cancelTimeCall(this); } /// check for update of configuration void OpSplitter::updateConfig() { // sound name file was modified -> re-read sound name if (m_fileSound.checkModified()) m_fileSound.update(); // max. number of conn. file was modified -> re-read max. number of conn. if (m_fileMaxConn.checkModified()) m_fileMaxConn.update(); // single mode config file was modified -> re-read single mode config if (m_fileSingle.checkModified()) m_fileSingle.update(); // extensions update m_extListTracker.updateConfig(); } /// callback when requested time reached void OpSplitter::timeCall() { // process pending actions for local connections MapLocal::iterator it = m_mapLocal.begin(); while (it != m_mapLocal.end()) { // close connection if (it->second.m_close) { MapLocal::iterator itDel = it; ++it; // we have to increment ourself to make erasing in loop work itDel->first->close(); m_mapLocal.erase(itDel); continue; // it already incremented -> skip rest of loop } // send sound play request on marked local operator connection if (it->second.m_sendPlay) { it->second.m_sendPlay = false; if (m_fileSound.m_valid) it->first->sendPlay(m_fileSound.m_obj.m_str); } ++it; } } /** * @brief check if accepting new operator connction is possible * @param[in] name operator interface name * @return if accepting new connection is possible */ bool OpSplitter::acceptNewOpConn(const std::string &name) { // if maximum number of connections if limited, check it if (m_fileMaxConn.m_valid && m_mapLocal.size() + m_mapInOut.size() >= m_fileMaxConn.m_obj.m_uint) return false; // too many connection // single mode if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) { // accept first connection if (m_mapLocal.empty() && m_mapInOut.empty()) return true; // no operator interface selected yet if (m_singleOp.empty()) return false; // additional connections: ask selected operator interface return m_mgrs.m_opMgr.canConnect(m_singleOp); } // if m_fileSingle return true; (void)name; // unused } /** * @brief new operator connection * @param[in] name operator interface name * @param[in] pConn operator connection object * * The new connection may not yet be used for sending inside this callback. */ void OpSplitter::newOpConn(const std::string &name, OpConn *pConn) { // this is a new incoming connection // single mode and not first connection if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) { if (!m_mapLocal.empty() || !m_mapInOut.empty()) { // try to open outgoiong operator connection to selected operator intf. OpConn *pOutConn = m_mgrs.m_opMgr.connect(m_singleOp, this); if (!pOutConn) { // module is not ready for connection newLocal(pConn, true); // make conn. a local conn. to be closed soon return; } // establish connection forwarding m_mapInOut[pConn] = pOutConn; m_mapOutIn[pOutConn] = pConn; return; } // if !m_mapLocal.empty() ... } // if m_fileSingle ... // make connection a local connection newLocal(pConn); (void)name; // unused } /** * @brief key command received on operator connection * @param[in] pConn operator connection object * @param[in] key key that was pressed */ void OpSplitter::opConnRecvKey(OpConn *pConn, char key) { // local connection -> process MapLocal::iterator itLocal = m_mapLocal.find(pConn); if (itLocal != m_mapLocal.end()) { localKey(itLocal, key); return; } // incoming to outgoing connection -> forward MapInOut::iterator itInOut = m_mapInOut.find(pConn); if (itInOut != m_mapInOut.end()) { itInOut->second->sendKey(key); return; } // outgoing to incoming connection -> forward MapOutIn::iterator itOutIn = m_mapOutIn.find(pConn); if (itOutIn != m_mapOutIn.end()) { itOutIn->second->sendKey(key); return; } } /** * @brief play command received on operator connection * @param[in] pConn operator connection object * @param[in] sound name of sound to play */ void OpSplitter::opConnRecvPlay(OpConn *pConn, const std::string &sound) { // play command does not make sense for local connections -> ignore // incoming to outgoing connection -> forward MapInOut::iterator itInOut = m_mapInOut.find(pConn); if (itInOut != m_mapInOut.end()) { itInOut->second->sendPlay(sound); return; } // outgoing to incoming connection -> forward MapOutIn::iterator itOutIn = m_mapOutIn.find(pConn); if (itOutIn != m_mapOutIn.end()) { itOutIn->second->sendPlay(sound); return; } } /** * @brief operator connection is closed * @param[in] pConn operator connection object * * The connection may not be used for sending any more in this callback. */ void OpSplitter::opConnClose(OpConn *pConn) { // local connection -> forget this connection MapLocal::iterator itLocal = m_mapLocal.find(pConn); if (itLocal != m_mapLocal.end()) { m_mapLocal.erase(itLocal); return; } // incoming to outgoing connection -> close outgoing connection as well MapInOut::iterator itInOut = m_mapInOut.find(pConn); if (itInOut != m_mapInOut.end()) { itInOut->second->close(); m_mapOutIn.erase(itInOut->second); m_mapInOut.erase(itInOut); if (m_mapInOut.empty()) // last connection closed -> no operator intf. sel. m_singleOp.clear(); return; } // outgoing to incoming connection MapOutIn::iterator itOutIn = m_mapOutIn.find(pConn); if (itOutIn != m_mapOutIn.end()) { OpConn * pInConn = itOutIn->second; m_mapInOut.erase(pInConn); m_mapOutIn.erase(itOutIn); if (m_mapInOut.empty()) // last connection closed -> no operator intf. sel. m_singleOp.clear(); // single mode and not last connection if (m_fileSingle.m_valid && m_fileSingle.m_obj.m_bool) { if (!m_mapLocal.empty() || !m_mapInOut.empty()) { // close incoming connection as well pInConn->close(); return; } } // make incoming connection local newLocal(pInConn); return; } } /** * @brief create new local connection * @param[in] pConn connection to make local * @param[in] close if to close connection as soon as possible */ void OpSplitter::newLocal(OpConn *pConn, bool close /* = false */) { // add connection to local connection map Local &local = m_mapLocal[pConn]; local.m_number.clear(); // no extension dialed so far local.m_sendPlay = !close; // send play command if close not requested local.m_close = close; m_mgrs.m_callMgr.requestTimeCall(this, Time::now()); } /** * @brief a key has been pressed for a local connection * @param[in] itLocal local connection * @param[in] key the key pressed */ void OpSplitter::localKey(MapLocal::iterator itLocal, char key) { switch (key) { // begin new extension number case '*': itLocal->second.m_number.clear(); break; // add digit to extension number case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': itLocal->second.m_number += key; break; // close local connection / call extension case '#': if (itLocal->second.m_number.empty()) { itLocal->first->close(); m_mapLocal.erase(itLocal); } else { callExtension(itLocal); } break; } } /** * @brief call extension dialed over local connection * @param[in] itLocal local connection */ void OpSplitter::callExtension(MapLocal::iterator itLocal) { // lookup extension ExtMap::iterator itExt = m_extMap.find(itLocal->second.m_number); if (itExt == m_extMap.end()) { // unknown extension -> ignore itLocal->second.m_number.clear(); // begin new extension number return; } // save selected operator interface for single mode m_singleOp = itExt->second; // try to open outgoiong operator connection to module OpConn *pOutConn = m_mgrs.m_opMgr.connect(itExt->second, this); if (!pOutConn) { // module is not ready for connection itLocal->second.m_number.clear(); // begin new extension number return; } // establish connection forwarding m_mapInOut[itLocal->first] = pOutConn; m_mapOutIn[pOutConn] = itLocal->first; // connection is no more a local one m_mapLocal.erase(itLocal); } } // namespace Blinker