BlinkenArea - GitList
Repositories
Blog
Wiki
Blinker
Code
Commits
Branches
Tags
Search
Tree:
f670ca0
Branches
Tags
master
Blinker
src
common
Phone_impl.h
rename noarch (misnormer) to common
Stefan Schuermans
commited
f670ca0
at 2014-01-03 12:06:24
Phone_impl.h
Blame
History
Raw
/* Blinker Copyright 2011-2014 Stefan Schuermans <stefan@blinkenarea.org> Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html a blinkenarea.org project */ #ifndef BLINKER_PHONE_IMPL_H #define BLINKER_PHONE_IMPL_H #include <map> #include <sstream> #include <string> #include <vector> #include "Directory.h" #include "File.h" #include "IoCallee.h" #include "ListTracker.h" #include "ListTracker_impl.h" #include "Mgrs.h" #include "Module.h" #include "OpConn.h" #include "OpConnIf.h" #include "OpMgr.h" #include "Phone.h" #include "PhoneExtension.h" #include "PhoneExtension_impl.h" #include "SettingFile.h" #include "StringParser.h" #include "Time.h" #include "TimeCallee.h" namespace Blinker { /** * @brief constructor * @param[in] name module name * @param[in] mgrs managers * @param[in] dirBase base directory */ template<typename ADDR, typename SOCK> Phone<ADDR, SOCK>::Phone(const std::string &name, Mgrs &mgrs, const Directory &dirBase): Module(name, mgrs, dirBase), m_fileBind(dirBase.getFile("bind")), m_fileServer(dirBase.getFile("server")), m_extListTracker(*this, dirBase.getSubdir("extensions")), m_pSock(NULL) { // read server address readServer(); // create and bind socket createSock(); // load extensions m_extListTracker.init(); } /// virtual destructor template<typename ADDR, typename SOCK> Phone<ADDR, SOCK>::~Phone() { // unload extensions m_extListTracker.clear(); // close all connections while (!m_connLineMap.empty()) { ConnLineMap::iterator it = m_connLineMap.begin(); it->first->close(); sendHangup(it->second); m_connLineMap.erase(it); } // destroy socket destroySock(); // cancel time callback m_mgrs.m_callMgr.cancelTimeCall(this); } /// check for update of configuration template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::updateConfig() { // bind address file was modified -> re-create socket if (m_fileBind.checkModified()) { destroySock(); createSock(); } // server address file was modified -> re-read server address if (m_fileServer.checkModified()) readServer(); // extensions update m_extListTracker.updateConfig(); } /// callback when requested time reached template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::timeCall() { Time now = Time::now(); // server timeout -> re-register if (m_timeRegister <= now) sendRegister(); // send heartbeat if (m_timeHeartbeat <= now) sendHeartbeat(); } /** * @brief callback when I/O object is readable * @param[in] io I/O object that is readable */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::ioReadCall(Io *io) { // reception on socket if (io == m_pSock) receiveFromSock(); } /** * @brief callback when I/O object is writable * @param[in] io I/O object that is writable */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::ioWriteCall(Io *io) { (void)io; // unused } /** * @brief key command received on operator connection * @param[in] pConn operator connection object * @param[in] key key that was pressed */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::opConnRecvKey(OpConn *pConn, char key) { // the key command does not make sense in this direction, ignore it (void)pConn; // unused (void)key; // unused } /** * @brief play command received on operator connection * @param[in] pConn operator connection object * @param[in] sound name of sound to play */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::opConnRecvPlay(OpConn *pConn, const std::string &sound) { // get phone line of connection unsigned int line; if (!conn2line(pConn, line)) return; // send play message sendPlay(line, sound); } /** * @brief operator connection is closed * @param[in] pConn operator connection object * * The connection may not be used for sending any more in this callback. */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::opConnClose(OpConn *pConn) { // get phone line of connection unsigned int line; if (!conn2line(pConn, line)) return; // send hangup message sendHangup(line); // remove connection from maps m_connLineMap.erase(pConn); m_lineConnMap.erase(line); // remove command buffer for this line m_cmdBufMap.erase(line); } /// (re-)read server address template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::readServer() { m_fileServer.update(); // register with new server sendRegister(); } /// create socket and bind it template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::createSock() { // create socket if (!m_pSock) { m_pSock = new SOCK(); if (!m_pSock) return; } // get bind address from bind address setting file m_fileBind.update(); if (!m_fileBind.m_valid) { delete m_pSock; m_pSock = NULL; return; } // bind socket if (!m_pSock->bind(m_fileBind.m_obj)) { delete m_pSock; m_pSock = NULL; return; } // request callback on recepetion m_mgrs.m_callMgr.requestIoReadCall(this, m_pSock); // register with server sendRegister(); } /// destroy socket template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::destroySock() { // cancel callback request m_mgrs.m_callMgr.cancelIoReadCall(this, m_pSock); // destroy socket if (m_pSock) { delete m_pSock; m_pSock = NULL; } } /// register with server template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendRegister() { ADDR addr; if (m_pSock) m_pSock->getAddr(addr); std::stringstream strm; strm << "register:" << addr.getPort(); sendNow(0, strm.str()); // set time for next register message and next heartbeat Time now = Time::now(); m_timeRegister = now + m_serverTimeout; m_timeHeartbeat = now + m_heartbeatInterval; updateTimeCallback(); } /// send heartbeat to server template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendHeartbeat() { sendNow(0, "heartbeat"); // set time for next heartbeat m_timeHeartbeat = Time::now() + m_heartbeatInterval; updateTimeCallback(); } /** * @brief send play message * @param[in] line number of line to request play on * @param[in] sound name of sound to request */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendPlay(unsigned int line, const std::string &sound) { std::stringstream strm; strm << "playbackground:" << sound; sendBuf(line, strm.str()); } /** * @brief send hangup message * @param[in] line number of line to hangup */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendHangup(unsigned int line) { sendNow(line, "hangup:18"); // nobody knows what the 18 meanas } /** * @brief send accept message * @param[in] line number of line accept on */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendAccept(unsigned int line) { sendNow(line, "accept"); } /** * @brief send message buffered to server * @param[in] line number of line to hangup * @param[in] msg message to send */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendBuf(unsigned int line, const std::string &msg) { CmdBufMap::iterator itCmdBuf = m_cmdBufMap.find(line); // line not yet connected -> buffer command if (itCmdBuf != m_cmdBufMap.end()) // buffer command itCmdBuf->second.push_back(msg); // line is connected -> send now else sendNow(line, msg); } /** * @brief send message to server now * @param[in] line number of line to hangup * @param[in] msg message to send */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendNow(unsigned int line, const std::string &msg) { std::stringstream strm; strm << line << ":" << msg; sendSock(strm.str()); } /** * @brief send message to socket * @param[in] msg message to send */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::sendSock(const std::string &msg) { // check that there is a socket and a server address if (!m_pSock || !m_fileServer.m_valid) return; // send message m_pSock->send(msg, m_fileServer.m_obj); } /// receive data from socket template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::receiveFromSock() { std::string msg; ADDR addr; // make sure socket exists if (!m_pSock) return; // receive (leave if no reception) if (!m_pSock->recv(msg, addr)) return; // check that packet came from server address (but ignore port) if (!m_fileServer.m_valid) return; // no server configured addr.setPort(m_fileServer.m_obj.getPort()); if (addr != m_fileServer.m_obj) return; // mismatch // reset server timeout m_timeRegister = Time::now() + m_serverTimeout; updateTimeCallback(); // process message serverMsg(msg); } /** * @brief process message from server * @param[in] msg message from server */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::serverMsg(const std::string &msg) { // get line number and command StringParser parser(msg); unsigned int line; std::string cmd; if (!parser.uintNo(line) || !parser.fixChr(':') || !parser.untilDelim(":", false, cmd)) return; if (line < 1) // we are only interested in real lines here return; // incoming call if (cmd == "setup") { std::string caller, extension; if (!parser.fixChr(':') || !parser.untilDelim(":", true, caller) || !parser.fixChr(':') || !parser.untilDelim(":", false, extension)) return; incomingCall(line, extension); } // call connected else if (cmd == "connected") { connected(line); } // hangup else if (cmd == "onhook") { hangup(line); } // DTMF symbol (i.e. key pressed) else if (cmd == "dtmf") { char key; if (!parser.fixChr(':') || !parser.oneChrOf("0123456789*#", key)) return; keyPressed(line, key); } } /** * @brief process incoming call * @param[in] line number of phone line * @param[in] extension extenstion called (i.e. phone number) */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::incomingCall(unsigned int line, const std::string &extension) { // hangup old call on this line (to be on the safe side) hangup(line); // buffer further commands to be sent to this line until it is connected // -> create command buffer for this line m_cmdBufMap[line]; // lookup extension ExtMap::iterator itExt = m_extMap.find(extension); if (itExt == m_extMap.end()) return; // unknown extension -> ignore // try to open operator connection to module OpConn *pConn = m_mgrs.m_opMgr.connect(itExt->second, this); if (!pConn) // module is not ready for connection return; // add connection to maps m_lineConnMap[line] = pConn; m_connLineMap[pConn] = line; // send accept message sendAccept(line); } /** * @brief phone line connected * @param[in] line number of phone line */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::connected(unsigned int line) { // get buffered commands for this line CmdBuf &cmdBuf = m_cmdBufMap[line]; // send buffered commands now for (CmdBuf::const_iterator cmd = cmdBuf.begin(); cmd != cmdBuf.end(); ++cmd) { std::stringstream strm; strm << line << ":" << *cmd; sendSock(strm.str()); } // delete command buffer for this line m_cmdBufMap.erase(line); } /** * @brief hangup on phone line * @param[in] line number of phone line */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::hangup(unsigned int line) { // get connection OpConn *pConn; if (!line2conn(line, pConn)) return; // no connection on this line // close connection pConn->close(); // remove connection from maps m_lineConnMap.erase(line); m_connLineMap.erase(pConn); // remove command buffer for this line m_cmdBufMap.erase(line); } /** * @brief key pressed on phone line * @param[in] line number of phone line * @param[in] key key that has been pressed */ template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::keyPressed(unsigned int line, char key) { // get connection OpConn *pConn; if (!line2conn(line, pConn)) return; // no connection on this line // send key press event over connection pConn->sendKey(key); } /** * @brief get phone line number from operator connection * @param[in] pConn operator connection object * @param[out] line phone line number * @return if phone line was found */ template<typename ADDR, typename SOCK> bool Phone<ADDR, SOCK>::conn2line(OpConn *pConn, unsigned int &line) const { ConnLineMap::const_iterator itConn = m_connLineMap.find(pConn); if (itConn == m_connLineMap.end()) return false; line = itConn->second; return true; } /** * @brief get operator connection from phone line number * @param[in] line phone line number * @param[out] pConn operator connection object * @return if phone line was found */ template<typename ADDR, typename SOCK> bool Phone<ADDR, SOCK>::line2conn(unsigned int line, OpConn *&pConn) const { LineConnMap::const_iterator itLine = m_lineConnMap.find(line); if (itLine == m_lineConnMap.end()) return false; pConn = itLine->second; return true; } /// update time callback template<typename ADDR, typename SOCK> void Phone<ADDR, SOCK>::updateTimeCallback() { m_mgrs.m_callMgr.requestTimeCall(this, m_timeRegister < m_timeHeartbeat ? m_timeRegister : m_timeHeartbeat); } template<typename ADDR, typename SOCK> const Time Phone<ADDR, SOCK>::m_serverTimeout(60); template<typename ADDR, typename SOCK> const Time Phone<ADDR, SOCK>::m_heartbeatInterval(20); } // namespace Blinker #endif // #ifndef BLINKER_PHONE_IMPL_H