[ previous ] [ next ] [ threads ]
 To :  yate@v...
 From :  Maciek Kaminski <maciejka@t...>
 Subject :  glue module
 Date :  Mon, 04 Jul 2005 11:11:40 +0200
Rather experimental(two days old) pseudo channel that glues variuos data 
sources into one channel. For example:
glue/wave/play/./subject.gsm+tts/Very complicated subject
Would play subject.gsm from wave module and utterance from tts module.



/**
 * glue.cpp
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Glue module
 *
 * Copyright (C) 2005 Maciek Kaminski 
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2005 Maciek Kaminski (maciejka_at_tiger.com.pl)
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace TelEngine;

static Configuration s_cfg;
static HashTable waiting;
static Mutex s_mutex;

class GlueSource;

class BackCallEndpoint : public CallEndpoint {
public:
  BackCallEndpoint(const char* id) : CallEndpoint (id) {}
};

class GlueSource : public ThreadedSource
{
public:
  GlueSource(CallEndpoint* frontCallEndpoint, const String &text, const char* format);
  ~GlueSource();
  virtual void run();
  inline void setNotify(const String& notify) { m_notify = notify; }
private:
  CallEndpoint* m_frontCallEndpoint;
  BackCallEndpoint* m_backCallEndpointA;
  BackCallEndpoint* m_backCallEndpointB;
  const String m_text;
  String m_notify;
  Mutex m_notify_mutex;
};

class GlueConsumer : public DataConsumer {
public:
  GlueConsumer(GlueSource* source, const char* format) : DataConsumer(format), m_source(source) {};
  virtual void Consume(const DataBlock& data, unsigned long timeDelta) {
    m_source->Forward(data, timeDelta);
  };
private:
  GlueSource* m_source;
};

class GlueChan : public Channel
{
public:
  GlueChan(const String& text);
  ~GlueChan();
};

void GlueSource::run()
{
  Debug("GlueModule", DebugAll,"Started [%p] for '%s'", this, m_text.c_str());

  if(m_frontCallEndpoint) {
    Message* m = new Message("call.answered");
    m->addParam("driver","glue");
    m->addParam("targetid", m_frontCallEndpoint->getPeer()->id());
    Engine::dispatch(m);
  }

  ObjList *l = m_text.split('+');
  for (; l; l = l->next()) {
    String &src = *static_cast(l->get());
    src = src.trimBlanks();
    m_notify_mutex.lock();
    Debug("GlueModule", DebugAll,"Attaching source: '%s'", src.c_str());
    Message m("chan.attach");
    m.addParam("source", src);
    m.addParam("notify", m_backCallEndpointB->id());
    m.userData(m_backCallEndpointB);
    s_mutex.lock();
    waiting.put(m_backCallEndpointB->id(), &m_notify_mutex);
    s_mutex.unlock();
    if(Engine::dispatch(m)) {
      m_notify_mutex.lock();              
      m_notify_mutex.unlock();              
    }
    s_mutex.lock();
    waiting.del(m_backCallEndpointB->id());
    s_mutex.unlock();
  }
  Debug("GlueModule", DebugAll,"End of GlueSource::run");
  if(m_notify) {
    Message *m = new Message("chan.notify");
    m->addParam("targetid", m_notify);
    m->addParam("notifier","source");
    Engine::enqueue(m);
  }
  if(m_frontCallEndpoint)
        m_frontCallEndpoint->disconnect();
}

GlueSource::~GlueSource()
{
  Debug("GlueModule", DebugAll,"~GlueSource() [%p]",this);
  m_backCallEndpointA->disconnect();
  m_backCallEndpointA->deref();
}

class AttachHandler : public MessageHandler
{
public:
    AttachHandler() : MessageHandler("chan.attach") { }
    virtual bool received(Message &msg);
};

class NotifyHandler : public MessageHandler
{
public:
    NotifyHandler() : MessageHandler("chan.notify") { }
    virtual bool received(Message &msg);
};


class GlueDriver : public Driver
{
public:
    GlueDriver();
    ~GlueDriver();
    virtual void initialize();
    virtual bool msgExecute(Message& msg, String& dest);
private:
    AttachHandler *m_attach_handler;
};

INIT_PLUGIN(GlueDriver);

GlueSource::GlueSource(CallEndpoint* frontCallEndpoint, const String &text, const char* format)
  : ThreadedSource(format), m_frontCallEndpoint(frontCallEndpoint), m_text(text) 
{
  Debug("GlueModule", DebugAll,"Started '%s'", m_text.c_str());
  unsigned int id = __plugin.nextid();
  String aid = __plugin.prefix();
  String bid = __plugin.prefix();
  m_backCallEndpointA = new BackCallEndpoint(aid << "A/" <<  id);
  GlueConsumer *c = new GlueConsumer(this, "slin");
  m_backCallEndpointA->setConsumer(c);
  m_backCallEndpointB = new BackCallEndpoint(bid << "B/" <<  id);
  m_backCallEndpointA->connect(m_backCallEndpointB);
  m_backCallEndpointB->deref();
  c->deref();
}


GlueChan::GlueChan(const String& text) : Channel(__plugin) {
  GlueSource *s = new GlueSource(this, text, "slin");
  setSource(s);
  s->deref();
  s->start();
}

GlueChan::~GlueChan()
{
    Debug("GlueModule", DebugAll,"~GlueChan() %s [%p]",id().c_str(),this);
}

bool GlueDriver::msgExecute(Message& msg, String& dest)
{
    Debug("GlueModule", DebugAll,"Text to glue: %s", dest.c_str());
    CallEndpoint *dd = static_cast(msg.userData());
    if (dd) {
	GlueChan *tc = new GlueChan(dest);
	if (dd->connect(tc)) {
	    msg.setParam("targetid", tc->id());
	    tc->deref();
	    return true;
	} else {
	    tc->destruct();
	    return false;
	}
    } else {
	const char *targ = msg.getValue("target");
	if (!targ) {
	    Debug("GlueModule", DebugWarn, "Glue outgoing call with no target!");
	    return false;
	}
	Message m("call.route");
	m.addParam("id",dest);
	m.addParam("caller",dest);
	m.addParam("called",targ);
	if (Engine::dispatch(m)) {
	    m = "call.execute";
	    m.addParam("callto",m.retValue());
	    m.retValue() = 0;
	    GlueChan *tc = new GlueChan(dest);
	    m.setParam("id",tc->id());
	    m.userData(tc);
	    if (Engine::dispatch(m)) {
		tc->deref();
		return true;
	    }
	    Debug("GlueModule", DebugWarn,"Glue outgoing call not accepted!");
	    tc->destruct();
	}
	else
	    Debug("GlueModule", DebugWarn,"Glue outgoing call but no route!");
	return false;
    }
    return true;
}

bool AttachHandler::received(Message &msg)
{
    String dest(msg.getValue("source"));
    if (dest.null())
	return false;

    Regexp r("^glue/\(.*\)$");
    if (!dest.matches(r))
      return false;

    dest = dest.matchString(1);

    CallEndpoint *dd = static_cast(msg.userData());
    if (dd) {
      GlueSource *s = new GlueSource(0, dest, "slin");
      s->setNotify(msg.getValue("notify"));
      dd->setSource(s);
      s->deref();
      s->start();
      return true;
    }
    else {
       Debug("GlueModule", DebugWarn,"Glue '%s' attach request with no data channel!",dest.c_str());
    }
    return false;
}

bool NotifyHandler::received(Message &msg) {

  String targetid(msg.getValue("targetid"));

  s_mutex.lock();
  Mutex *m = static_cast(waiting.get(targetid));
  s_mutex.unlock();

  if(m) {
    m->unlock();
    return true;
  }
  return false;
}


GlueDriver::GlueDriver()
    : Driver("glue", "misc"), m_attach_handler(0)
{
    Output("Loaded module Glue");
}

GlueDriver::~GlueDriver()
{
  Output("Unloading module Glue");
}

void GlueDriver::initialize()
{
    Output("Initializing module Glue");
    setup();
    s_cfg = Engine::configFile("glue");
    s_cfg.load();
    Engine::install(new AttachHandler);
    Engine::install(new NotifyHandler);
}

/* vi: set ts=8 sw=4 sts=4 noet: */