389 lines
11 KiB
C++
389 lines
11 KiB
C++
/**
|
|
* analogdetect.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Analog data detector
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2004-2006 Null Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <yatephone.h>
|
|
#include <yatemodem.h>
|
|
|
|
using namespace TelEngine;
|
|
namespace { // anonymous
|
|
|
|
class ADConsumer; // Base class for all module's consumers (UART)
|
|
class ETSIConsumer; // Data consumer for an ETSIModem
|
|
class ADModule; // The module
|
|
class ChanAttachHandler; // chan.attach handler
|
|
|
|
// Base class for all module's consumers
|
|
class ADConsumer : public DataConsumer
|
|
{
|
|
friend class ADModule;
|
|
public:
|
|
ADConsumer(const String& id, const char* notify);
|
|
virtual ~ADConsumer()
|
|
{}
|
|
// Process received data
|
|
virtual unsigned long Consume(const DataBlock& data, unsigned long tStamp, unsigned long flags);
|
|
// Remove from module's consumer list
|
|
virtual void destroyed();
|
|
protected:
|
|
// Create a chan.notify message
|
|
Message* chanNotify(const char* operation, const char* param = 0, const char* value = 0);
|
|
// Consume data. Return false to stop processing
|
|
virtual bool process(const DataBlock& data)
|
|
{ return false; }
|
|
// Get termination reason from descendents
|
|
virtual const char* getTerminateReason()
|
|
{ return 0; }
|
|
|
|
String m_id; // Consumer's id
|
|
String m_targetid; // The target for chan.notify
|
|
private:
|
|
bool m_terminated; // Stop processing data
|
|
UART* m_uart; // UART descendant class
|
|
};
|
|
|
|
// Data consumer for call setup info (bit collector)
|
|
class ETSIConsumer : public ADConsumer, public ETSIModem
|
|
{
|
|
public:
|
|
ETSIConsumer(const String& id, const char* notify, const NamedList& params);
|
|
virtual ~ETSIConsumer()
|
|
{}
|
|
// Notification from modem that the FSK start was detected
|
|
// Return false to stop feeding data
|
|
virtual bool fskStarted() {
|
|
Engine::enqueue(chanNotify("start"));
|
|
return true;
|
|
}
|
|
protected:
|
|
// Consume data. Return false to stop processing
|
|
virtual bool process(const DataBlock& data)
|
|
{ return demodulate(data); }
|
|
// Process a list of received message parameters
|
|
// Return false to stop processing data
|
|
virtual bool recvParams(MsgType msg, const NamedList& params);
|
|
virtual const char* getTerminateReason()
|
|
{ return lookup(UART::error(),UART::s_errors); }
|
|
};
|
|
|
|
// The module
|
|
class ADModule : public Module
|
|
{
|
|
public:
|
|
ADModule();
|
|
~ADModule();
|
|
inline const String& prefix()
|
|
{ return m_prefix; }
|
|
virtual void initialize();
|
|
// chan.attach handler
|
|
bool chanAttach(Message& msg);
|
|
// Get next consumer's id
|
|
inline unsigned int nextId() {
|
|
Lock lock(this);
|
|
return m_id++;
|
|
}
|
|
protected:
|
|
virtual void statusParams(String& str);
|
|
// Process a request to attach an ETSI detector (src is a valid pointer) or generator (src is 0)
|
|
bool attachETSI(Message& msg, DataSource* src, String& type, const char* notify);
|
|
private:
|
|
unsigned int m_id; // Next consumer's id
|
|
bool m_init; // Already initialized flag
|
|
String m_prefix; // Module's prefix
|
|
};
|
|
|
|
/**
|
|
* Module's data
|
|
*/
|
|
static ADModule plugin;
|
|
static ObjList s_consumers; // Consumers list
|
|
static unsigned int s_count = 0; // The number of active consumers
|
|
|
|
|
|
// chan.attach handler
|
|
class ChanAttachHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline ChanAttachHandler()
|
|
: MessageHandler("chan.attach",100,plugin.name())
|
|
{ }
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
|
|
/**
|
|
* ADConsumer
|
|
*/
|
|
ADConsumer::ADConsumer(const String& id, const char* notify)
|
|
: DataConsumer("slin"),
|
|
m_id(id),
|
|
m_targetid(notify),
|
|
m_terminated(false)
|
|
{
|
|
DDebug(&plugin,DebugAll,"Created %s targetid=%s [%p]",
|
|
m_id.c_str(),m_targetid.c_str(),this);
|
|
Lock lock(plugin);
|
|
s_consumers.append(this)->setDelete(false);
|
|
s_count++;
|
|
}
|
|
|
|
// Process received data
|
|
unsigned long ADConsumer::Consume(const DataBlock& data, unsigned long tStamp, unsigned long flags)
|
|
{
|
|
if (m_terminated)
|
|
return 0;
|
|
m_terminated = !process(data);
|
|
if (!m_terminated)
|
|
return invalidStamp();
|
|
DDebug(&plugin,DebugAll,"Terminated %s targetid=%s [%p]",
|
|
m_id.c_str(),m_targetid.c_str(),this);
|
|
Engine::enqueue(chanNotify("terminate","reason",getTerminateReason()));
|
|
return invalidStamp();
|
|
}
|
|
|
|
// Remove from module's consumer list
|
|
void ADConsumer::destroyed()
|
|
{
|
|
Lock lock(plugin);
|
|
s_consumers.remove(this,false);
|
|
s_count--;
|
|
DDebug(&plugin,DebugAll,"Destroyed %s targetid=%s [%p]",
|
|
m_id.c_str(),m_targetid.c_str(),this);
|
|
}
|
|
|
|
// Create a chan.notify message
|
|
Message* ADConsumer::chanNotify(const char* operation, const char* param, const char* value)
|
|
{
|
|
Message* m = new Message("chan.notify");
|
|
m->addParam("module",plugin.debugName());
|
|
m->addParam("id",m_id);
|
|
m->addParam("targetid",m_targetid);
|
|
m->addParam("operation",operation);
|
|
if (param)
|
|
m->addParam(param,value);
|
|
m->userData(this);
|
|
return m;
|
|
}
|
|
|
|
|
|
/**
|
|
* ETSIConsumer
|
|
*/
|
|
ETSIConsumer::ETSIConsumer(const String& id, const char* notify, const NamedList& params)
|
|
: ADConsumer(id,notify),
|
|
ETSIModem(params,m_id)
|
|
{
|
|
debugChain(&plugin);
|
|
}
|
|
|
|
// Process a list of received message parameters
|
|
// Return false to stop processing data
|
|
bool ETSIConsumer::recvParams(MsgType msg, const NamedList& params)
|
|
{
|
|
Message* m = 0;
|
|
switch (msg) {
|
|
case ETSIModem::MsgCallSetup:
|
|
m = chanNotify("setup");
|
|
break;
|
|
case ETSIModem::MsgMWI:
|
|
m = chanNotify("message-summary");
|
|
break;
|
|
case ETSIModem::MsgCharge:
|
|
m = chanNotify("charge");
|
|
break;
|
|
case ETSIModem::MsgSMS:
|
|
m = chanNotify("sms");
|
|
break;
|
|
default:
|
|
Debug(this,DebugStub,"Can't process message %s [%p]",
|
|
lookup(msg,ETSIModem::s_msg),this);
|
|
return false;
|
|
}
|
|
|
|
DDebug(this,DebugAll,"recvParams(%s) operation=%s [%p]",
|
|
lookup(msg,ETSIModem::s_msg),m->getValue("operation"),this);
|
|
|
|
unsigned int count = params.count();
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
NamedString* param = params.getParam(i);
|
|
if (param)
|
|
m->addParam(param->name(),*param);
|
|
}
|
|
Engine::dispatch(*m);
|
|
TelEngine::destruct(m);
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* ADModule
|
|
*/
|
|
ADModule::ADModule()
|
|
: Module("analogdetect","misc",true),
|
|
m_id(1),
|
|
m_init(false)
|
|
{
|
|
Output("Loaded module Analog Detector");
|
|
m_prefix << debugName() << "/";
|
|
}
|
|
|
|
ADModule::~ADModule()
|
|
{
|
|
Output("Unloading module Analog Detector");
|
|
}
|
|
|
|
void ADModule::initialize()
|
|
{
|
|
Output("Initializing module Analog Detector");
|
|
|
|
if (!m_init) {
|
|
setup();
|
|
installRelay(Command);
|
|
installRelay(Halt);
|
|
Engine::install(new ChanAttachHandler);
|
|
}
|
|
m_init = true;
|
|
}
|
|
|
|
// chan.attach handler
|
|
bool ADModule::chanAttach(Message& msg)
|
|
{
|
|
int detect = -1;
|
|
DataSource* src= 0;
|
|
RefObject* sender = msg.userData();
|
|
|
|
// Check if requested a detector or a generator
|
|
String type = msg.getValue("consumer");
|
|
if (type.startSkip(plugin.prefix(),false)) {
|
|
if (sender)
|
|
src = static_cast<DataSource*>(sender->getObject(YATOM("DataSource")));
|
|
if (src)
|
|
detect = 1;
|
|
else
|
|
msg.setParam("reason","nodata");
|
|
}
|
|
else {
|
|
type = msg.getValue("source");
|
|
if (type.startSkip(plugin.prefix(),false))
|
|
detect = 0;
|
|
}
|
|
if (detect == -1)
|
|
return false;
|
|
|
|
const char* notify = msg.getValue("notify");
|
|
const char* defModem = lookup(FSKModem::ETSI,FSKModem::s_typeName);
|
|
const char* modemType = msg.getValue("modemtype",defModem);
|
|
int mType = lookup(modemType,FSKModem::s_typeName);
|
|
XDebug(this,DebugAll,"Request to create '%s' %s for '%s' modemtype=%s",
|
|
type.c_str(),detect?"detector":"generator",notify,modemType);
|
|
|
|
if (mType == FSKModem::ETSI)
|
|
return attachETSI(msg,src,type,notify);
|
|
|
|
msg.setParam("reason","unknown-modem-type");
|
|
return false;
|
|
}
|
|
|
|
void ADModule::statusParams(String& str)
|
|
{
|
|
Module::statusParams(str);
|
|
Lock lock(this);
|
|
str << "count=" << s_count;
|
|
for (ObjList* o = s_consumers.skipNull(); o; o = o->skipNext()) {
|
|
ADConsumer* c = static_cast<ADConsumer*>(o->get());
|
|
str << "," << c->m_id << "=" << c->m_targetid;
|
|
}
|
|
}
|
|
|
|
// Process a request to attach an ETSI detector (src is a valid pointer) or generator (src is 0)
|
|
bool ADModule::attachETSI(Message& msg, DataSource* src, String& type, const char* notify)
|
|
{
|
|
int t = 0xffffffff;
|
|
|
|
// Check type
|
|
if (type == "callsetup")
|
|
t = ETSIModem::MsgCallSetup;
|
|
|
|
if (t == (int)0xffffffff) {
|
|
if (src)
|
|
msg.setParam("reason","unknown-detector-type");
|
|
else
|
|
msg.setParam("reason","unknown-generator-type");
|
|
return false;
|
|
}
|
|
|
|
String id = prefix();
|
|
|
|
// Detector
|
|
if (src) {
|
|
id << nextId();
|
|
DataConsumer* cons = new ETSIConsumer(id,notify,msg);;
|
|
DataTranslator::attachChain(src,cons);
|
|
bool ok = (cons->getConnSource() != 0);
|
|
if (ok)
|
|
msg.userData(cons);
|
|
else
|
|
msg.setParam("reason","attach-failure");
|
|
TelEngine::destruct(cons);
|
|
return ok;
|
|
}
|
|
|
|
// Generator
|
|
id << "callsetup/" << notify;
|
|
ETSIModem modem(msg,id);
|
|
modem.debugChain(this);
|
|
NamedList params(lookup(t,ETSIModem::s_msg));
|
|
for (int i = 0; ETSIModem::s_msgParams[i].token; i++) {
|
|
NamedString* p = msg.getParam(ETSIModem::s_msgParams[i].token);
|
|
if (p)
|
|
params.addParam(p->name(),*p);
|
|
}
|
|
|
|
DataBlock* buffer = new DataBlock;
|
|
if (!modem.modulate(*buffer,params)) {
|
|
TelEngine::destruct(buffer);
|
|
msg.setParam("reason",params.getValue("error","invalid-message"));
|
|
return false;
|
|
}
|
|
|
|
Message send("chan.attach");
|
|
send.userData(msg.userData());
|
|
send.addParam("override","tone/rawdata");
|
|
send.addParam("single",String::boolText(true));
|
|
send.addParam(new NamedPointer("rawdata",buffer));
|
|
return Engine::dispatch(send);
|
|
}
|
|
|
|
|
|
/**
|
|
* ChanAttachHandler
|
|
*/
|
|
bool ChanAttachHandler::received(Message& msg)
|
|
{
|
|
return plugin.chanAttach(msg);
|
|
}
|
|
|
|
}; // anonymous namespace
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|