Added analog detector module.

git-svn-id: http://yate.null.ro/svn/yate/trunk@1525 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2007-12-10 15:56:11 +00:00
parent 03e9405f6d
commit 1ca7784b72
1 changed files with 445 additions and 0 deletions

View File

@ -0,0 +1,445 @@
/**
* 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 void Consume(const DataBlock& data, unsigned long tStamp);
// 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();
// Process a request to attach a data consumer
bool attachConsumer(Message& msg, DataSource* src, String& type, const char* notify);
// Process a request to attach a data source
bool attachSource(Message& msg, DataConsumer* cons, String& type, const char* notify);
protected:
virtual bool received(Message& msg, int id);
virtual void statusParams(String& str);
private:
unsigned int m_id; // Next consumer's id
bool m_init; // Already initialized flag
String m_prefix; // Module's prefix
};
// chan.attach handler
class ChanAttachHandler : public MessageHandler
{
public:
inline ChanAttachHandler()
: MessageHandler("chan.attach",100)
{}
virtual bool received(Message& msg);
};
/**
* Module's data
*/
static ADModule plugin;
static ObjList s_consumers; // Consumers list
static unsigned int s_count = 0; // The number of active consumers
static Mutex s_mutex(true); // Lock module
/**
* 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(s_mutex);
s_consumers.append(this);
s_count++;
}
// Process received data
void ADConsumer::Consume(const DataBlock& data, unsigned long tStamp)
{
if (m_terminated)
return;
m_terminated = !process(data);
if (!m_terminated)
return;
DDebug(&plugin,DebugAll,"Terminated %s targetid=%s [%p]",
m_id.c_str(),m_targetid.c_str(),this);
Engine::enqueue(chanNotify("terminate","reason",getTerminateReason()));
}
// Remove from module's consumer list
void ADConsumer::destroyed()
{
Lock lock(s_mutex);
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"),
m_id(1),
m_init(false)
{
Output("Loaded module Analog Detector");
m_prefix << debugName() << "/";
}
ADModule::~ADModule()
{
Output("Unloading module Analog Detector");
}
//TODO: remove after test
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void ADModule::initialize()
{
Output("Initializing module Analog Detector");
Configuration cfg(Engine::configFile("analogdetect"));
cfg.load();
if (!m_init) {
setup();
installRelay(Command);
installRelay(Halt);
Engine::install(new ChanAttachHandler);
}
m_init = true;
if (!cfg.getBoolValue("general","testing",false))
return;
String filename = "//home/marian/callerid.raw.";
String type[3] = {"etsi","bell","etsi-ets"};
unsigned int len[3] = {27200,22400,49280};
debugLevel(10);
unsigned int test = cfg.getIntValue("general","test",0);
if (test > 2)
test = 2;
unsigned int chunk = cfg.getIntValue("general","chunk",0);
if (!chunk)
chunk = 160;
filename << type[test];
int handler = open(filename,O_RDONLY);
if (handler == -1) {
Debug(this,DebugWarn,"Error opening %s",filename.c_str());
return;
}
NamedList p("");
String id;
id << "test/" << type[test];
ETSIConsumer* cons = new ETSIConsumer(id,"TEST",p);
unsigned char buf[len[test]];
read(handler,buf,len[test]);
unsigned int n = len[test]/chunk;
for (unsigned char* p = buf; n; n--, p += chunk) {
DataBlock tmp(p,chunk,false);
cons->demodulate(tmp);
tmp.clear(false);
}
cons->deref();
}
bool ADModule::received(Message& msg, int id)
{
if (id == Halt) {
s_mutex.lock();
s_consumers.clear();
s_mutex.unlock();
return Module::received(msg,id);
}
return Module::received(msg,id);
}
// Process a request to attach data consumer
bool ADModule::attachConsumer(Message& msg, DataSource* src, String& type, const char* notify)
{
XDebug(this,DebugAll,"Attaching consumer '%s' for '%s'",type.c_str(),notify);
String id;
DataConsumer* cons = 0;
if (type == "callsetup") {
s_mutex.lock();
id << m_prefix << m_id++;
s_mutex.unlock();
cons = new ETSIConsumer(id,notify,msg);
}
else {
msg.setParam("reason","unknown-detector-type");
return false;
}
DataTranslator::attachChain(src,cons);
bool ok = cons->getConnSource();
if (ok)
msg.userData(cons);
else
msg.setParam("reason","attach-failure");
TelEngine::destruct(cons);
return ok;
}
// Process a request to attach data source
bool ADModule::attachSource(Message& msg, DataConsumer* cons, String& type, const char* notify)
{
// TODO: remove it after testing
msg.setParam("reason","not-implemented");
return false;
DDebug(this,DebugAll,"Attaching source '%s' for '%s'",type.c_str(),notify);
if (type != "callsetup") {
msg.setParam("reason","unknown-message-type");
return false;
}
String id = prefix();
id << "callsetup/" << notify;
ETSIModem* modem = new ETSIModem(msg,id);
modem->debugChain(this);
NamedList params(lookup(ETSIModem::MsgCallSetup,ETSIModem::s_msg));
params.copyParams(msg,"datetime,caller,callername");
DataBlock buffer;
bool ok = modem->modulate(buffer,params);
delete modem;
if (!ok) {
msg.setParam("reason",params.getValue("error","invalid-message"));
return false;
}
DDebug(this,DebugAll,"Sending %u buffer for %s",buffer.length(),notify);
DataSource* src = new DataSource("slin");
ok = src->attach(cons);
if (ok)
src->Forward(buffer);
else
msg.setParam("reason","attach-failure");
TelEngine::destruct(src);
return ok;
}
void ADModule::statusParams(String& str)
{
Module::statusParams(str);
Lock lock(s_mutex);
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;
}
}
/**
* ChanAttachHandler
*/
bool ChanAttachHandler::received(Message& msg)
{
DataSource* src = 0;
DataConsumer* cons = 0;
String type = msg.getValue("consumer");
if (!type.startSkip(plugin.prefix(),false)) {
type = msg.getValue("source");
if (!type.startSkip(plugin.prefix(),false))
return false;
if (msg.userData())
cons = static_cast<DataConsumer*>(msg.userData()->getObject("DataConsumer"));
}
else if (msg.userData())
src = static_cast<DataSource*>(msg.userData()->getObject("DataSource"));
if (!(src || cons)) {
msg.setParam("reason","nodata");
return false;
}
const char* modemtype = msg.getValue("modemtype");
if (modemtype && *modemtype) {
int mtype = lookup(modemtype,FSKModem::s_typeName);
switch (mtype) {
case FSKModem::ETSI:
break;
default:
msg.setParam("reason","unknown-modem-type");
return false;
}
}
const char* notify = msg.getValue("notify");
if (src)
return plugin.attachConsumer(msg,src,type,notify);
return plugin.attachSource(msg,cons,type,notify);
}
}; // anonymous namespace
/* vi: set ts=8 sw=4 sts=4 noet: */