# Source: libs/ysig/yatesig.h |
Annotated List
Files
Globals
Hierarchy
Index

    /**
     * yatesig.h
     * This file is part of the YATE Project http://YATE.null.ro
     *
     * Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
     *
     * Yet Another Telephony Engine - a fully featured software PBX and IVR
     * Copyright (C) 2004-2014 Null Team
     *
     * This software is distributed under multiple licenses;
     * see the COPYING file in the main directory for licensing
     * information for this specific distribution.
     *
     * This use of this software may be subject to additional restrictions.
     * See the LEGAL file in the main directory for details.
     *
     * 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.
     */

    #ifndef __YATESIG_H
    #define __YATESIG_H

    #include

    #ifdef _WINDOWS

    #ifdef LIBYSIG_EXPORTS
    #define YSIG_API __declspec(dllexport)
    #else
    #ifndef LIBYSIG_STATIC
    #define YSIG_API __declspec(dllimport)
    #endif
    #endif

    #endif /* _WINDOWS */

    #ifndef YSIG_API
    #define YSIG_API
    #endif

    /**
     * Holds all Telephony Engine related classes.
     */
    namespace TelEngine {

    // Signalling classes
    class SignallingDumper;                  // A generic data dumper
    class SignallingDumpable;                // A component that can dump data
    class SignallingNotifier;                // A signalling notifier
    class SignallingTimer;                   // A signalling timer
    class SignallingCounter;                 // A signalling counter
    class SignallingFactory;                 // A signalling component factory
    class SignallingComponent;               // Abstract signalling component that can be managed by the engine
    class SignallingEngine;                  // Main signalling component holder
    class SignallingThreadPrivate;           // Engine private thread
    class SignallingMessage;                 // Abstract signalling message
    class SignallingCallControl;             // Abstract phone call signalling
    class SignallingCall;                    // Abstract single phone call
    class SignallingEvent;                   // A single signalling related event
    class SignallingCircuitEvent;            // A single signalling circuit related event
    class SignallingCircuit;                 // Abstract data circuit used by signalling
    class SignallingCircuitRange;            // A circuit range (set of circuits)
    class SignallingCircuitGroup;            // Group of data circuits used by signalling
    class SignallingCircuitSpan;             // A span in a circuit group
    class SignallingInterface;               // Abstract digital signalling interface (hardware access)
    class SignallingReceiver;                // Abstract Layer 2 packet data receiver
    struct SignallingFlags;                  // Description of parameter flags
    class SignallingUtils;                   // Library wide services and data provider
    class SignallingMessageTimer;            // A pending signalling message
    class SignallingMessageTimerList;        // A pending signalling message list
    // Analog lines
    class AnalogLine;                        // An analog line
    class AnalogLineEvent;                   // A single analog line related event
    class AnalogLineGroup;                   // A group of analog lines
    // SS7
    class SS7PointCode;                      // SS7 Code Point
    class SS7Label;                          // SS7 Routing Label
    class SS7MSU;                            // A block of data that holds a Message Signal Unit
    class SIGTRAN;                           // Abstract SIGTRAN component
    class SIGTransport;                      // Abstract transport for SIGTRAN
    class SIGAdaptation;                     // Abstract Adaptation style SIGTRAN
    class SIGAdaptClient;                    // Client side of adaptation (ASP)
    class SIGAdaptServer;                    // Server side of adaptation (SG)
    class SIGAdaptUser;                      // Abstract Adaptation User SIGTRAN
    class ASPUser;                           // Abstract SS7 ASP user interface
    class SCCP;                              // Abstract SS7 SCCP interface
    class GTT;                               // Abstract SCCP Global Title Translation interface
    class SCCPManagement;                    // Abstract SCCP Management interface
    class SCCPUser;                          // Abstract SS7 SCCP user interface
    class TCAPUser;                          // Abstract SS7 TCAP user interface
    class SS7L2User;                         // Abstract user of SS7 layer 2 (data link) message transfer part
    class SS7Layer2;                         // Abstract SS7 layer 2 (data link) message transfer part
    class SS7L3User;                         // Abstract user of SS7 layer 3 (network) message transfer part
    class SS7Layer3;                         // Abstract SS7 layer 3 (network) message transfer part
    class SS7Layer4;                         // Abstract SS7 layer 4 (application) protocol
    class SS7Route;                          // A SS7 MSU route
    class SS7Router;                         // Main router for SS7 message transfer and applications
    class SS7M2PA;                           // SIGTRAN MTP2 User Peer-to-Peer Adaptation Layer
    class SS7M2UA;                           // SIGTRAN MTP2 User Adaptation Layer
    class SS7M3UA;                           // SIGTRAN MTP3 User Adaptation Layer
    class SS7MTP2;                           // SS7 Layer 2 implementation on top of a hardware interface
    class SS7MTP3;                           // SS7 Layer 3 implementation on top of Layer 2
    class SS7MsgSNM;                         // SNM signalling message
    class SS7MsgMTN;                         // MTN signalling message
    class SS7MsgISUP;                        // ISUP signalling message
    class SS7MsgSCCP;                        // SCCP signalling message
    class SS7Management;                     // SS7 SNM implementation
    class SS7ISUPCall;                       // A SS7 ISUP call
    class SS7ISUP;                           // SS7 ISUP implementation
    class SS7BICC;                           // SS7 BICC implementation
    class SS7TUP;                            // SS7 TUP implementation
    class SS7SCCP;                           // SS7 SCCP implementation
    class SccpSubsystem;                     // Helper class to keep a SCCP subsystem
    class SccpLocalSubsystem;                // Helper class to keep s sccp local subsystem informations
    class SccpRemote;                        // Helper class to keep a remote sccp
    class SS7AnsiSccpManagement;             // SS7 SCCP management implementation for ANSI
    class SS7ItuSccpManagement;              // SS7 SCCP management implementation for ITU
    class SS7SUA;                            // SIGTRAN SCCP User Adaptation Layer
    class SS7ASP;                            // SS7 ASP implementation
    class SS7TCAPMessage;                    // SS7 TCAP message wrapper
    class SS7TCAPError;                      // SS7 TCAP errors
    class SS7TCAP;                           // SS7 TCAP implementation
    class SS7TCAPTransaction;                // SS7 TCAP transaction base class
    class SS7TCAPComponent;                  // SS7 TCAP component
    class SS7TCAPANSI;                       // SS7 ANSI TCAP implementation
    class SS7TCAPTransactionANSI;            // SS7 TCAP ANSI Transaction
    class SS7TCAPITU;                        // SS7 ITU TCAP implementation
    class SS7TCAPTransactionITU;             // SS7 TCAP ITU Transaction
    // ISDN
    class ISDNLayer2;                        // Abstract ISDN layer 2 (Q.921) message transport
    class ISDNLayer3;                        // Abstract ISDN layer 3 (Q.931) message transport
    class ISDNFrame;                         // An ISDN Q.921 frame
    class ISDNQ921;                          // ISDN Q.921 implementation on top of a hardware interface
    class ISDNQ921Passive;                   // Stateless ISDN Q.921 implementation on top of a hardware interface
    class ISDNQ921Management;                // ISDN Layer 2 BRI TEI management or PRI with D-channel(s) backup
    class ISDNIUA;                           // SIGTRAN ISDN Q.921 User Adaptation Layer
    class ISDNQ931IE;                        // A Q.931 ISDN Layer 3 message Information Element
    class ISDNQ931Message;                   // A Q.931 ISDN Layer 3 message
    class ISDNQ931IEData;                    // A Q.931 message IE data processor
    class ISDNQ931State;                     // Q.931 ISDN call and call controller state
    class ISDNQ931Call;                      // A Q.931 ISDN call
    class ISDNQ931CallMonitor;               // A Q.931 ISDN call monitor
    class ISDNQ931ParserData;                // Q.931 message parser data
    class ISDNQ931;                          // ISDN Q.931 implementation on top of Q.921
    class ISDNQ931Monitor;                   // ISDN Q.931 implementation on top of Q.921 of call controller monitor

    // Macro to create a factory that builds a component by class name
    #define YSIGFACTORY(clas) \
    class clas ## Factory : public SignallingFactory \
    { \
    protected: \
    virtual SignallingComponent* create(const String& type, NamedList& name) \
        { return (type == #clas) ? new clas : 0; } \
    }; \
    static clas ## Factory s_ ## clas ## Factory

    // Macro to create a factory that calls a component's static create method
    #define YSIGFACTORY2(clas) \
    class clas ## Factory : public SignallingFactory \
    { \
    protected: \
    virtual SignallingComponent* create(const String& type, NamedList& name) \
        { return clas::create(type,name); } \
    }; \
    static clas ## Factory s_ ## clas ## Factory

    // Macro to call the factory creation method and return the created component
    #define YSIGCREATE(type,name) (static_cast(SignallingFactory::buildInternal(#type,name)))

    /**
     * This class is a generic data dumper with libpcap compatibility
     * @short A generic data dumper
     */
    class YSIG_API SignallingDumper
    {
    public:
        /**
         * Type of dumper output
         */
        enum Type {
    	Raw,
    	Hexa,
    	Hdlc,
    	Q921,
    	Q931,
    	Mtp2,
    	Mtp3,
    	Sccp,
        };

        /**
         * Constructor
         * @param type Type of the output desired
         * @param network True if we are the network side of the link
         */
        SignallingDumper(Type type = Hexa, bool network = false);

        /**
         * Destructor, closes the output
         */
        ~SignallingDumper();

        /**
         * Get the type of the dumper
         * @return Type of the dumper object
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the network side flag
         * @return True if we are the network side
         */
        inline bool network() const
    	{ return m_network; }

        /**
         * Check if the dumper is active
         * @return True if the object will actually send data to something
         */
        bool active() const;

        /**
         * Terminate the dump session, close the output
         */
        void terminate();

        /**
         * Set a new output stream
         * @param stream New stream for output, NULL to terminate
         * @param writeHeader True to write the header (if any) at start of stream
         */
        void setStream(Stream* stream = 0, bool writeHeader = true);

        /**
         * Dump the provided data
         * @param buf Pointer to buffer to dump
         * @param len Length of the data
         * @param sent True if data is being sent, false if is being received
         * @param link Link number (relevant to MTP2 only)
         * @return True if the data was dumped successfully
         */
        bool dump(void* buf, unsigned int len, bool sent = false, int link = 0);

        /**
         * Dump the provided data
         * @param data Buffer to dump
         * @param sent True if data is being sent, false if is being received
         * @param link Link number (relevant to MTP2 only)
         * @return True if the data was dumped successfully
         */
        inline bool dump(const DataBlock& data, bool sent = false, int link = 0)
    	{ return dump(data.data(),data.length(),sent,link); }

        /**
         * Create a file to dump data in it. The file is opened/created in write only, binary mode
         * @param dbg DebugEnabler requesting the operation (used for debug message on failure)
         * @param filename The file name to use
         * @param type The dumper type
         * @param network True to create a network side dumper
         * @param create True to create the file if doesn't exist
         * @param append Append to an existing file. If false and the file already exists, it will be truncated
         * @return SignallingDumper pointer on success, 0 on failure
         */
        static SignallingDumper* create(DebugEnabler* dbg, const char* filename, Type type,
    	bool network = false, bool create = true, bool append = false);

        /**
         * Create a dumper from an already existing stream
         * @param stream Stream to use for output, will be owned by dumper
         * @param type The dumper type
         * @param network True to create a network side dumper
         * @param writeHeader True to write the header (if any) at start of stream
         * @return SignallingDumper pointer on success, 0 on failure
         */
        static SignallingDumper* create(Stream* stream, Type type, bool network = false, bool writeHeader = true);

    private:
        void head();
        Type m_type;
        bool m_network;
        Stream* m_output;
    };

    /**
     * A generic base class for components capable of creating data dumps
     * @short A data dumping capable component
     */
    class YSIG_API SignallingDumpable
    {
    public:
        /**
         * Destructor - destroys the data dumper
         */
        inline ~SignallingDumpable()
    	{ setDumper(); }

    protected:
        /**
         * Constructor
         * @param type Default type of the data dumper
         * @param network True if we are the network side of the link
         */
        inline SignallingDumpable(SignallingDumper::Type type, bool network = false)
    	: m_type(type), m_dumpNet(network), m_dumper(0)
    	{ }

        /**
         * Dump the provided data if the dumper is valid
         * @param buf Pointer to buffer to dump
         * @param len Length of the data
         * @param sent True if data is being sent, false if is being received
         * @param link Link number (relevant to MTP2 only)
         * @return True if the data was dumped successfully
         */
        inline bool dump(void* buf, unsigned int len, bool sent = false, int link = 0)
    	{ return (m_dumper && m_dumper->dump(buf,len,sent,link)); }

        /**
         * Dump data if the dumper is valid
         * @param data Buffer to dump
         * @param sent True if data is being sent, false if is being received
         * @param link Link number (relevant to MTP2 only)
         * @return True if the data was dumped successfully
         */
        inline bool dump(const DataBlock& data, bool sent = false, int link = 0)
    	{ return dump(data.data(),data.length(),sent,link); }

        /**
         * Set the dump network side flag
         * @param network True to dump as network side, false othervise
         */
        inline void setDumpNetwork(bool network)
    	{ m_dumpNet = network; }

        /**
         * Set or remove the data dumper
         * @param dumper Pointer to the data dumper object, 0 to remove
         */
        void setDumper(SignallingDumper* dumper = 0);

        /**
         * Set or remove a file data dumper
         * @param name Name of the file to dump to, empty to remove dumper
         * @param create True to create the file if doesn't exist
         * @param append Append to an existing file. If false and the file already exists, it will be truncated
         * @return True if the file dumper was created or removed
         */
        bool setDumper(const String& name, bool create = true, bool append = false);

        /**
         * Handle dumper related control on behalf of the owning component
         * @param params Control parameters to handle
         * @param owner Optional owning component
         * @return True if control operation was applied
         */
        bool control(NamedList& params, SignallingComponent* owner = 0);

    private:
        SignallingDumper::Type m_type;
        bool m_dumpNet;
        SignallingDumper* m_dumper;
    };

    /**
     * Notifying class. Used to handle notifications.
     * @short Notifier class
     */

    class YSIG_API SignallingNotifier
    {
    public:
        /**
         * Destructor.
         */
        virtual inline ~SignallingNotifier()
    	{ cleanup(); }
        /**
         * Handle the received notifications
         * @param notifs Received notifications
         */
        virtual void notify(NamedList& notifs);
        /**
         * Handle necessary clean up
         */
        virtual void cleanup();
    };

    /**
     * Timer management class. Used to manage timeouts. The time is kept in miliseconds
     * @short A signalling timer
     */
    class YSIG_API SignallingTimer
    {
    public:
        /**
         * Constructor
         * @param interval The timeout interval. Set to 0 to disable
         * @param time Optional timeout value. If non 0, the timer is started
         */
        inline SignallingTimer(u_int64_t interval, u_int64_t time = 0)
    	: m_interval(interval), m_timeout(0)
    	{ if (time) start(time); }

        /**
         * Set the timeout interval
         * @param value The new timeout value
         */
        inline void interval(u_int64_t value)
    	{ m_interval = value; }

        /**
         * Set the timeout interval from a list of parameters. The interval value is
         *  checked to be at least minVal or 0 if allowDisable is true
         * @param params The list of parameters
         * @param param The name of the parameter containing the timer interval value
         * @param minVal Minimum value allowed for the timer interval
         * @param defVal Default value if it fails to get one from the given parameter
         * @param allowDisable True to allow 0 for the timer interval
         * @param sec True if the interval value if given in seconds
         */
        inline void interval(const NamedList& params, const char* param,
    	unsigned int minVal, unsigned int defVal, bool allowDisable, bool sec = false) {
    	    m_interval = getInterval(params,param,minVal,defVal,0,allowDisable);
    	    if (sec)
    		m_interval *= 1000;
    	}

        /**
         * Get the timeout interval
         * @return The timeout interval
         */
        inline u_int64_t interval() const
    	{ return m_interval; }

        /**
         * Get the time this timer will fire (timeout)
         * @return The timeout (fire) time
         */
        inline u_int64_t fireTime() const
    	{ return m_timeout; }

        /**
         * Start the timer if enabled (interval is positive)
         * @param time Time to be added to the interval to set the timeout point
         */
        inline void start(u_int64_t time = Time::msecNow())
    	{ if (m_interval) m_timeout = time + m_interval; }

        /**
         * Fire the timer at a specific absolute time
         * @param time Absolute time (in msec) when the timer will fire
         */
        inline void fire(u_int64_t time = Time::msecNow())
    	{ m_timeout = time; }

        /**
         * Stop the timer
         */
        inline void stop()
    	{ m_timeout = 0; }

        /**
         * Check if the timer is started
         * @return True if the timer is started
         */
        inline bool started() const
    	{ return m_timeout > 0; }

        /**
         * Check if the timer is started and timed out
         * @param time The time to compare with
         * @return True if the timer timed out
         */
        inline bool timeout(u_int64_t time = Time::msecNow()) const
    	{ return started() && (m_timeout < time); }

        /**
         * Retrieve a timer interval from a list of parameters.
         * @param params The list of parameters
         * @param param The name of the parameter containing the timer interval value
         * @param minVal Minimum value allowed for the timer interval
         * @param defVal Default value if it fails to get one from the given parameter
         * @param maxVal Optional interval maximum value
         * @param allowDisable True to allow 0 for the timer interval
         * @return The interval value
         */
        static unsigned int getInterval(const NamedList& params, const char* param,
    	unsigned int minVal, unsigned int defVal, unsigned int maxVal = 0,
    	bool allowDisable = false);

    private:
        u_int64_t m_interval;                // Timer interval
        u_int64_t m_timeout;                 // Timeout value
    };

    /**
     * Counter management class. Keep a value between 0 and a given maximum one
     * @short A counter class
     */
    class YSIG_API SignallingCounter
    {
    public:
        /**
         * Constructor
         * @param maxVal The maximum value for the counter
         */
        inline SignallingCounter(u_int32_t maxVal)
    	: m_max(maxVal), m_count(0)
    	{ }

        /**
         * Set the maximum value for the counter
         * @param value The new maximum value for the counter
         */
        inline void maxVal(u_int32_t value)
    	{ m_max = value; }

        /**
         * Get the maximum value for the counter
         * @return The maximum value for the counter
         */
        inline u_int32_t maxVal() const
    	{ return m_max; }

        /**
         * Get the current value of the counter
         * @return The current value of the counter
         */
        inline u_int32_t count() const
    	{ return m_count; }

        /**
         * Reset the counter's value
         * @param down True to reset to 0, false to reset to maxVal()
         */
        inline void reset(bool down = true)
    	{ m_count = down ? 0 : m_max; }

        /**
         * Increment the counter's value if it can
         * @return False if the counter is full (reached the maximum value)
         */
        inline bool inc()
        {
    	if (full())
    	    return false;
    	m_count++;
    	return true;
        }

        /**
         * Decrement the counter's value if it can
         * @return False if the counter is empty (reached 0)
         */
        inline bool dec()
        {
    	if (empty())
    	    return false;
    	m_count--;
    	return true;
        }

        /**
         * Check if the counter is empty (the value is 0)
         * @return True if the counter is empty
         */
        inline bool empty() const
    	{ return m_count == 0; }

        /**
         * Check if the counter is full (the value reached the maximum)
         * @return True if the counter is full
         */
        inline bool full() const
    	{ return m_count == maxVal(); }

    private:
        u_int32_t m_max;                     // Maximum counter value
        u_int32_t m_count;                   // Current counter value
    };

    /**
     * A factory that constructs various elements by name
     * @short A signalling component factory
     */
    class YSIG_API SignallingFactory : public GenObject
    {
    public:
        /**
         * Constructor, adds the factory to the global list
         * @param fallback True to add this factory at the end of the priority list
         */
        SignallingFactory(bool fallback = false);

        /**
         * Destructor, removes the factory from list
         */
        virtual ~SignallingFactory();

        /**
         * Builds a component given its name and arbitrary parameters
         * @param type The type of the component that should be returned
         * @param name Name of the requested component and additional parameters
         * @return Pointer to the created component, NULL on failure
         */
        static SignallingComponent* build(const String& type, NamedList* name = 0);

        /**
         * This method is for internal use only and must not be called directly
         * @param type The name of the interface that should be returned
         * @param name Name of the requested component and additional parameters
         * @return Raw pointer to the requested interface of the component, NULL on failure
         */
        static void* buildInternal(const String& type, NamedList* name);

    protected:
        /**
         * Creates a component given its name and arbitrary parameters
         * @param type The name of the interface that should be returned
         * @param name Name of the requested component and additional parameters
         * @return Pointer to the created component
         */
        virtual SignallingComponent* create(const String& type, NamedList& name) = 0;
    };

    /**
     * Interface to an abstract signalling component that is managed by an engine.
     * The engine will periodically poll each component to keep them alive.
     * @short Abstract signalling component that can be managed by the engine
     */
    class YSIG_API SignallingComponent : public RefObject, public DebugEnabler
    {
        YCLASS(SignallingComponent,RefObject)
        friend class SignallingEngine;
    public:
        /**
         * Destructor, detaches the engine and other components
         */
        virtual ~SignallingComponent();

        /**
         * Get the component's name so it can be used for list searches
         * @return A reference to the name by which the component is known to engine
         */
        virtual const String& toString() const;

        /**
         * Configure and initialize the component and any subcomponents it may have
         * @param config Optional configuration parameters override
         * @return True if the component was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Choose parameters that should be used for object initialization
         * @param cmpName The name of the parameter holding the component name
         * @param params The list of parameters used to initialize the component
         * @param config The received list of parameters
         * @return True if the config was resolved
         */
        static bool resolveConfig(const String& cmpName, NamedList& params, const NamedList* config);

        /**
         * Query or modify component's settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Create a parameter list adequate to control this component
         * @param oper Optional name of the operation to execute
         * @return A new parameter list or descendant object, NULL if not supported
         */
        virtual NamedList* controlCreate(const char* oper = 0);

        /**
         * Execute or postpone a control command
         * @param params Parameter list describing the command, will be destroyed
         * @return True if the command was accepted (but not necessarily executed)
         */
        virtual bool controlExecute(NamedList* params);

        /**
         * Set the @ref TelEngine::SignallingEngine that manages this component
         *  and any subcomponent of it
         * @param eng Pointer to the engine that will manage this component
         */
        virtual void engine(SignallingEngine* eng);

        /**
         * Get the @ref TelEngine::SignallingEngine that manages this component
         * @return Pointer to engine or NULL if not managed by an engine
         */
        inline SignallingEngine* engine() const
    	{ return m_engine; }

        /**
         * Conditionally set the debug level of the component
         * @param level Desired debug level, negative for no change
         * @return Current debug level
         */
        inline int debugLevel(int level)
    	{ return (level >= 0) ? DebugEnabler::debugLevel(level) : DebugEnabler::debugLevel(); }

        /**
         * Return the type of this component
         * @return A string version of the component type
         */
        inline const String& componentType() const
    	{ return m_compType; }

    protected:
        /**
         * Constructor with a default empty component name
         * @param name Name of this component
         * @param params Optional pointer to creation parameters
         * @param type Default component type string
         */
        SignallingComponent(const char* name = 0, const NamedList* params = 0, const char* type = "unknown");

        /**
         * This method is called to clean up and destroy the object after the
         *  reference counter becomes zero
         */
        virtual void destroyed();

        /**
         * Insert another component in the same engine as this one.
         * This method should be called for every component we attach.
         * @param component Pointer to component to insert in engine
         */
        void insert(SignallingComponent* component);

        /**
         * Detach this component from all its links - components and engine.
         * Reimplement this method in all components that keep pointers to
         *  other components.
         * The default implementation detaches from the engine.
         */
        virtual void detach();

        /**
         * Method called periodically by the engine to keep everything alive
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Change the name of the component after it was constructed
         * @param name Name of this component
         */
        void setName(const char* name);

        /**
         * Change the type of the component after it was constructed
         * @param type Type of this component
         */
        inline void setCompType(const char* type)
    	{ m_compType = type; }

        /**
         * Adjust (decrease only) the desired maximum time until next tick.
         * Can be called only from within timerTick()
         * @param usec Desired time until next engine's timerTick() call in usec
         * @return Timer sleep in usec after applying the current change
         */
        unsigned long tickSleep(unsigned long usec = 1000000) const;

    private:
        SignallingEngine* m_engine;
        String m_name;
        String m_compType;
    };

    /**
     * The engine is the center of all SS7 or ISDN applications.
     * It is used as a base to build the protocol stack from components.
     * @short Main signalling component holder
     */
    class YSIG_API SignallingEngine : public DebugEnabler, public Mutex
    {
        friend class SignallingComponent;
        friend class SignallingThreadPrivate;
    public:
        /**
         * Constructor of an empty engine
         * @param name The debug name of this engine
         */
        SignallingEngine(const char* name = "signalling");

        /**
         * Destructor, removes all components
         */
        virtual ~SignallingEngine();

        /**
         * Get a pointer to the first (and usually only) instance created
         * @param create Create the instance if it doesn't exist
         * @return Pointer to first engine, NULL if not created
         */
        static SignallingEngine* self(bool create = false);

        /**
         * Insert a component in the engine, lock the list while doing so
         * @param component Pointer to component to insert in engine
         */
        void insert(SignallingComponent* component);

        /**
         * Remove a component from the engine, lock the list while doing so
         * @param component Pointer to component to remove from engine
         */
        void remove(SignallingComponent* component);

        /**
         * Remove and destroy a component from the engine by name
         * @param name Name of component to remove from engine
         * @return True if a component was found and destroyed
         */
        bool remove(const String& name);

        /**
         * Retrieve a component by name, lock the list while searching for it
         * @param name Name of the component to find
         * @return Pointer to component found or NULL
         */
        SignallingComponent* find(const String& name);

        /**
         * Retrieve a component by name and class, lock the list while searching for it
         * @param name Name of the component to find, empty to find any of the type
         * @param type Class or base class of the component to find, empty to match any
         * @param start Component to start searching from, search all list if NULL
         * @return Pointer to component found or NULL
         */
        SignallingComponent* find(const String& name, const String& type, const SignallingComponent* start = 0);

        /**
         * Retrieve and reference an existing component, create by factory if not present
         * @param type Class or base class of the component to find or create
         * @param params Name of component to find or create and creation parameters
         * @param init Set to true to initialize a newly created component
         * @param ref True to add a reference when returning existing component
         * @return Pointer to component found or created, NULL on failure
         */
        SignallingComponent* build(const String& type, NamedList& params, bool init = false, bool ref = true);

        /**
         * Apply a control operation to all components in the engine
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed by at least one component
         */
        bool control(NamedList& params);

        /**
         * Check if a component is in the engine's list
         * @param component Pointer to component to check
         * @return True if the component is in the engine's list
         */
        bool find(const SignallingComponent* component);

        /**
         * Handle notifications from a SignallingComponent
         * @param component The SignallingComponent from which the notifications were received
         * @param notifs The notifications sent by this SignallingComponent
         */
        void notify(SignallingComponent* component, NamedList notifs);

        /**
         * Starts the worker thread that keeps components alive
         * @param name Static name of the thread
         * @param prio Thread's priority
         * @param usec How long to sleep between iterations in usec, 0 to use library default
         * @return True if (already) started, false if an error occured
         */
        bool start(const char* name = "Sig Engine", Thread::Priority prio = Thread::Normal, unsigned long usec = 0);

        /**
         * Stops and destroys the worker thread if running
         */
        void stop();

        /**
         * Add to this engine a notifier object
         * @param notifier The SignallingNotifier object to be added to the engine
         */
        inline void setNotifier(SignallingNotifier* notifier)
    	{ m_notifier = notifier; }

        /**
         * Remove from this engine a notifier object
         * @param notifier The SignallingNotifier object to be removed from the engine
         */
        inline void removeNotifier(SignallingNotifier* notifier)
        {
    	if (m_notifier == notifier)
    	    m_notifier = 0;
        }

        /**
         * Return a pointer to the worker thread
         * @return Pointer to running worker thread or NULL
         */
        Thread* thread() const;

        /**
         * Adjust (decrease only) the desired maximum time until next tick.
         * Can be called only from within timerTick()
         * @param usec Desired time until next timerTick() call in usec
         * @return Timer sleep in usec after applying the current change
         */
        unsigned long tickSleep(unsigned long usec = 1000000);

        /**
         * Get the default engine tick sleep time in microseconds
         * @return Default timer sleep in usec
         */
        inline unsigned long tickDefault() const
    	{ return m_usecSleep; }

        /**
         * Get the maximum time we should spend acquiring a non-critical Mutex
         * @return Maximum non-critical lock wait in usec, -1 to wait forever
         */
        inline static long maxLockWait()
    	{ return s_maxLockWait; }

        /**
         * Set the maximum time we should spend acquiring a non-critical Mutex
         * @param maxWait New maximum non-critical lock wait in usec, negative to wait forever
         */
        static void maxLockWait(long maxWait);

        /**
         * Helper template used to remove a component descendant from its engine,
         *  destroy it and set the received pointer to 0
         * @param obj Reference to pointer (lvalue) to the object to remove and destroy
         */
        template  static inline void destruct(Obj*& obj)
        {
    	if (!obj)
    	    return;
    	if (obj->engine())
    	    obj->engine()->remove(obj);
    	TelEngine::destruct(obj);
        }

    protected:
        /**
         * Method called periodically by the worker thread to keep everything alive
         * @param when Time to use as computing base for events and timeouts
         * @return Desired sleep (in usec) until thread's next tick interval
         */
        virtual unsigned long timerTick(const Time& when);

        /**
         * The list of components managed by this engine
         */
        ObjList m_components;

    private:
        SignallingThreadPrivate* m_thread;
        SignallingNotifier* m_notifier;
        unsigned long m_usecSleep;
        unsigned long m_tickSleep;
        static long s_maxLockWait;
    };

    /**
     * Interface of protocol independent signalling message
     * @short Abstract signalling message
     */
    class YSIG_API SignallingMessage : public RefObject
    {
        YCLASS(SignallingMessage,RefObject)
    public:
        /**
         * Constructor
         * @param name Named list's name
         */
        inline SignallingMessage(const char* name = 0)
    	: m_params(name)
    	{ }

        /**
         * Get the name of the message
         * @return The name of the message
         */
        inline const char* name() const
    	{ return m_params.c_str(); }

        /**
         * Get this message's parameter list
         * @return This message's parameter list
         */
        inline NamedList& params()
    	{ return m_params; }

        /**
         * Get this message's parameter list - const version
         * @return This message's parameter list
         */
        inline const NamedList& params() const
    	{ return m_params; }

    protected:
        /**
         * Message parameter list
         */
        NamedList m_params;
    };

    /**
     * Interface of protocol independent signalling for phone calls
     * @short Abstract phone call signalling
     */
    class YSIG_API SignallingCallControl : public Mutex
    {
        friend class SignallingCall;
        friend class SS7ISUPCall;
        friend class ISDNQ931Call;
        friend class ISDNQ931CallMonitor;
    public:

        /**
         * When is media absolutely required during the call
         */
        enum MediaRequired {
    	MediaNever,
    	MediaAnswered,
    	MediaRinging,
    	MediaAlways
        };

        /**
         * Constructor
         * @param params Call controller's parameters
         * @param msgPrefix Optional prefix to be added before a decoded message's
         *  parameters or retrieve message parameters from a list
         */
        SignallingCallControl(const NamedList& params, const char* msgPrefix = 0);

        /**
         * Destructor
         */
        virtual ~SignallingCallControl();

        /**
         * Retrieve Q.850 cause location
         * @return Controller location
         */
        inline const String& location() const
    	{ return m_location; }

        /**
         * Set exiting flag
         */
        inline void setExiting()
    	{ m_exiting = true; }

        /**
         * Get exiting flag
         * @return The exiting flag
         */
        inline bool exiting() const
    	{ return m_exiting; }

        /**
         * Check the verify event flag. Reset it if true is returned
         * @return True if the verify event flag is set
         */
        inline bool verify()
        {
    	Lock lock(this);
    	if (!m_verifyEvent)
    	    return false;
    	m_verifyEvent = false;
    	return true;
        }

        /**
         * Get the Media Required flag
         * @return Configured media requirement
         */
        inline MediaRequired mediaRequired() const
    	{ return m_mediaRequired; }

        /**
         * Get the prefix used by this call controller when decoding message parameters or
         *  retrieve message parameters from a list
         * @return Message parameters prefix used by this call controller
         */
        inline const String& msgPrefix() const
    	{ return m_msgPrefix; }

        /**
         * Get the circuit group attached to this call controller
         * @return The circuit group attached to this call controller
         */
        inline SignallingCircuitGroup* circuits() const
    	{ return m_circuits; }

        /**
         * Get the list of calls currently known by this call controller
         * @return Reference to the list of calls
         */
        inline const ObjList& calls() const
    	{ return m_calls; }

        /**
         * Get the controller's status as text
         * @return Controller status name
         */
        virtual const char* statusName() const = 0;

        /**
         * Attach/detach a circuit group to this call controller. Set group's allocation strategy.
         * Set locked flags for all circuits belonging to the attached circuit group.
         * Cleanup controller before detaching the group or attaching a new one
         * This method is thread safe
         * @param circuits Pointer to the SignallingCircuitGroup to attach. 0 to detach and force a cleanup
         * @return Pointer to the old group that was detached, NULL if none or no change
         */
        SignallingCircuitGroup* attach(SignallingCircuitGroup* circuits);

        /**
         * Reserve a circuit for later use. If the circuit list is 0, try to reserve a circuit from
         *  the group using its strategy. Release the given circuit before trying to reserve it.
         *  Set cic to 0 on failure.
         * This method is thread safe
         * @param cic Destination circuit
         * @param checkLock Lock flags to check. If the given lock flags are set, reservation will fail
         * @param range Optional range name to restrict circuit reservation within attached circuit group
         * @param list Comma separated list of circuits
         * @param mandatory The list is mandatory. If false and none of the circuits in
         *  the list are available, try to reserve a free one. Ignored if list is 0
         * @param reverseRestrict Used when failed to reserve circuit from list. If true and the circuit allocation
         *  strategy includes any restriction (odd or even) use the opposite restriction to reserve a circuit.
         *  Ignored if mandatory is true
         * @return False if the operation failed
         */
        bool reserveCircuit(SignallingCircuit*& cic, const char* range = 0, int checkLock = -1,
    	const String* list = 0,	bool mandatory = true, bool reverseRestrict = false);

        /**
         * Initiate a release of a circuit. Set cic to 0.
         * This method is thread safe
         * @param cic The circuit to release
         * @param sync Synchronous release requested
         * @return True if the circuit release was initiated
         */
        bool releaseCircuit(SignallingCircuit*& cic, bool sync = false);

        /**
         * Initiate a release of a circuit from the attached group
         * This method is thread safe
         * @param code The circuit's code
         * @param sync Synchronous release requested
         * @return True if the circuit release was initiated
         */
        bool releaseCircuit(unsigned int code, bool sync = false);

        /**
         * Cleanup
         * @param reason Cleanup reason
         */
        virtual void cleanup(const char* reason = "net-out-of-order")
    	{ }

        /**
         * Iterate through the call list to get an event
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events
         */
        virtual SignallingEvent* getEvent(const Time& when);

        /**
         * Create an outgoing call. Send a NewCall event with the given msg parameter
         * @param msg Call parameters
         * @param reason Failure reason if any
         * @return Referenced SignallingCall pointer on success or 0 on failure
         */
        virtual SignallingCall* call(SignallingMessage* msg, String& reason)
    	{ reason = "not-implemented"; return 0; }

        /**
         * Build the parameters of a Verify event
         * @param params The list of parameters to fill
         */
        virtual void buildVerifyEvent(NamedList& params)
    	{ }

    protected:
        /**
         * Get the strategy used by the attached circuit group to allocate circuits
         * @return The strategy used by the attached circuit group to allocate circuits
         */
        inline int strategy() const
    	{ return m_strategy; }

        /**
         * Process an event received from a call. This will give to derived classes an opportunity
         *  to intercept events generated by their calls
         * @param event The event
         * @return True if the event was processed by the controller.
         *  False to deliver the event to the requestor
         */
        virtual bool processEvent(SignallingEvent* event)
    	{ return false; }

        /**
         * Process an event received from a non-reserved circuit
         * @param event The event, will be consumed and zeroed
         * @param call Optional signalling call whose circuit generated the event
         * @return Signalling event pointer or 0
         */
        virtual SignallingEvent* processCircuitEvent(SignallingCircuitEvent*& event,
    	SignallingCall* call = 0)
    	{ TelEngine::destruct(event); return 0; }

        /**
         * Clear call list
         */
        void clearCalls();

        /**
         * Remove a call from list
         * @param call The call to remove
         * @param del True to delete it. False to remove without destruct
         */
        void removeCall(SignallingCall* call, bool del = false);

        /**
         * Set the verify event flag. Restart/fire verify timer
         * @param restartTimer True to restart/fire the timer
         * @param fireNow True to fire the verify timer. Ignored if restartTimer is false
         * @param time Optional time to use for timer restart
         */
        void setVerify(bool restartTimer = false, bool fireNow = false, const Time* time = 0);

        /**
         * List of active calls
         */
        ObjList m_calls;

        /**
         * Prefix to be added to decoded message parameters or
         *  retrieve message parameters from a list
         */
        String m_msgPrefix;

        /**
         * Media required flag, call should drop if requirement not satisfied
         */
        MediaRequired m_mediaRequired;

        /**
         * Draw attention to call controller's user that something changed by
         *  raising a Verify event
         */
        bool m_verifyEvent;

        /**
         * Timer used to raise verify events
         */
        SignallingTimer m_verifyTimer;

        /**
         * Controller location used when encoding Q.850 cause
         */
        String m_location;

        /**
         * Media required keywords
         */
        static const TokenDict s_mediaRequired[];

    private:
        SignallingCircuitGroup* m_circuits;  // Circuit group
        int m_strategy;                      // Strategy to allocate circuits for outgoing calls
        bool m_exiting;                      // Call control is terminating. Generate a Disable event when no more calls
    };

    /**
     * Interface of protocol independent phone call
     * @short Abstract single phone call
     */
    class YSIG_API SignallingCall : public RefObject, public Mutex
    {
    public:
        /**
         * Constructor
         * @param controller The call controller owning this call
         * @param outgoing Call direction (true for outgoing)
         * @param signalOnly Just signalling (no voice) flag
         */
        SignallingCall(SignallingCallControl* controller, bool outgoing, bool signalOnly = false);

        /**
         * Destructor, notifies the controller
         */
        virtual ~SignallingCall();

        /**
         * Check if this is an outgoing call
         * @return True if it's an outgoing call
         */
        inline bool outgoing() const
    	{ return m_outgoing; }

        /**
         * Retreive the controller of this call
         */
        inline SignallingCallControl* controller() const
    	{ return m_controller; }

        /**
         * Set this call's private user data
         * @param data New user data
         */
        inline void userdata(void* data)
    	{ m_private = data; }

        /**
         * Retreive the private user data of this call
         * @return User data
         */
        inline void* userdata() const
    	{ return m_private; }

        /**
         * Check if this call is just a signalling (no voice) one
         * @return True if no audio data can be negotiated for this call
         */
        inline bool signalOnly() const
    	{ return m_signalOnly; }

        /**
         * Check if this call is in overlapped send/recv state
         * @return True if this call is expecting more digits to be sent/received
         */
        inline bool overlapDialing() const
    	{ return m_overlap; }

        /**
         * Send an event to this call
         * @param event The event to send
         * @return True if the operation succedded
         */
        virtual bool sendEvent(SignallingEvent* event)
    	{ return false; }

        /**
         * Get an event from this call if not already got one
         * This method is thread safe
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events or this call has a not terminated event
         */
        virtual SignallingEvent* getEvent(const Time& when) = 0;

        /**
         * Event terminated notification. No event will be generated until
         *  the current event is terminated
         * This method is thread safe
         * @param event The terminated event
         */
        virtual void eventTerminated(SignallingEvent* event);

    protected:
        /**
         * Enqueue a received message.
         * This method is thread safe
         * @param msg The received message
         */
        void enqueue(SignallingMessage* msg);

        /**
         * Dequeue a received message. Just return it if remove is false
         * This method is thread safe
         * @param remove True to remove the message from queue
         * @return SignallingMessage pointer or 0 if no more messages
         */
        SignallingMessage* dequeue(bool remove = true);

        /**
         * Clear incoming messages queue
         */
        void clearQueue()
        {
    	Lock lock(m_inMsgMutex);
    	m_inMsg.clear();
        }

        /**
         * Last event generated by this call. Used to serialize events
         */
        SignallingEvent* m_lastEvent;

        /**
         * Call is in overlapped send/recv state
         */
        bool m_overlap;

    private:
        SignallingCallControl* m_controller; // Call controller this call belongs to
        bool m_outgoing;                     // Call direction
        bool m_signalOnly;                   // Just signalling flag
        ObjList m_inMsg;                     // Incoming messages queue
        Mutex m_inMsgMutex;                  // Lock incoming messages queue
        void* m_private;                     // Private user data
    };

    /**
     * An object holding a signalling event and related references
     * @short A single signalling related event
     */
    class YSIG_API SignallingEvent
    {
    public:
        /**
         * Type of the event
         */
        enum Type {
    	Unknown = 0,
    	Generic,
    	// Call related
    	NewCall,
    	Accept,
    	Connect,
    	Complete,
    	Progress,
    	Ringing,
    	Answer,
    	Transfer,
    	Suspend,
    	Resume,
    	Release,
    	Info,
    	Charge,
    	// Non-call related
    	Message,
    	Facility,
    	Circuit,
    	// Controller related
    	Enable,
    	Disable,
    	Reset,
    	Verify,
        };

        /**
         * Constructor for a call related event
         * @param type Type of the event
         * @param message Message carried by the event
         * @param call Call this event refers to
         */
        SignallingEvent(Type type, SignallingMessage* message, SignallingCall* call);

        /**
         * Constructor for a controller related event
         * @param type Type of the event
         * @param message Message carried by the event
         * @param controller Controller this event refers to
         */
        SignallingEvent(Type type, SignallingMessage* message, SignallingCallControl* controller = 0);

        /**
         * Constructor for a signalling circuit related event
         * @param event The event signaled by the circuit, will be consumed and zeroed
         * @param call Call this event refers to
         */
        SignallingEvent(SignallingCircuitEvent*& event, SignallingCall* call);

        /**
         * Destructor, dereferences any resources, notify the signalling call of termination
         */
        virtual ~SignallingEvent();

        /**
         * Get the string associated with this event's type
         * @return The string associated with this event's type, if any
         */
        inline const char* name() const
    	{ return typeName(type()); }

        /**
         * Get the type of the event
         * @return Type of event, may be unknown
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the call that generated this event, may be NULL
         */
        inline SignallingCall* call() const
    	{ return m_call; }

        /**
         * Get the message that generated this event, may be NULL
         */
        inline SignallingMessage* message() const
    	{ return m_message; }

        /**
         * Retrieve the controller of the call
         */
        inline SignallingCallControl* controller() const
    	{ return m_controller; }

        /**
         * Retrieve the circuit event
         */
        inline SignallingCircuitEvent* cicEvent() const
    	{ return m_cicEvent; }

        /**
         * Get the text associated with a given event type for debug purposes
         * @param t The requested type
         * @return The text associated with the given type
         */
        static inline const char* typeName(Type t)
    	{ return lookup(t,s_types,0); }

        /**
         * Send this event through the call that generated it
         * @return True if there was a call and the operation succedded
         */
        bool sendEvent();

    private:
        Type m_type;
        SignallingMessage* m_message;
        SignallingCall* m_call;
        SignallingCallControl* m_controller;
        SignallingCircuitEvent* m_cicEvent;
        static const TokenDict s_types[];
    };

    /**
     * An object holding a signalling circuit event and related references
     * @short A single signalling circuit related event
     */
    class YSIG_API SignallingCircuitEvent : public NamedList
    {
    public:
        /**
         * Type of the event
         */
        enum Type {
    	Unknown      = 0,
    	Dtmf         = 1,                // Transfer tones: param: tone
    	GenericTone  = 2,                // Play or detect tones: param: tone
    	// Analog line events
    	Timeout      = 10,               //
    	Polarity     = 11,               // Line's polarity changed
    	StartLine    = 15,               // Initialize FXO line
    	LineStarted  = 16,               // FXO line initialized: send number
    	DialComplete = 17,               // FXO line completed dialing the number
    	OnHook       = 20,               // The hook is down
    	OffHook      = 21,               // The hook is up
    	RingBegin    = 22,               // Start ringing
    	RingEnd      = 23,               // Stop ringing
    	RingerOn     = 30,               // An FXS started the FXO's ringer
    	RingerOff    = 31,               // An FXS stopped the FXO's ringer
    	Wink         = 32,               // On hook momentarily
    	Flash        = 33,               // Off hook momentarily
    	PulseStart   = 40,               // Pulse dialing start
    	PulseDigit   = 41,               // Transfer pulse digits: param: pulse
    	// Remote circuit events
    	Connect      = 50,               // Request connect
    	Disconnect   = 51,               // Request disconnect
    	Connected    = 52,               // Connected notification
    	Disconnected = 53,               // Disconnected notification
    	// Errors
    	Alarm        = 100,              // Param: alarms (comma separated list of strings)
    	NoAlarm      = 101,              // No more alarms
        };

        /**
         * Constructor for a circuit related event
         * @param cic The circuit that generated this event
         * @param type Event type as enumeration
         * @param name Optional name for the named list
         */
        SignallingCircuitEvent(SignallingCircuit* cic, Type type, const char* name = 0);

        /**
         * Destructor, dereferences any resources
         */
        virtual ~SignallingCircuitEvent();

        /**
         * Get the type of this event
         * @return The type of this event
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the circuit that generated this event
         * @return The circuit that generated this event
         */
        inline SignallingCircuit* circuit()
    	{ return m_circuit; }

        /**
         * Send this event through the circuit. Release (delete) the event
         * @return True if the operation succeded
         */
        bool sendEvent();

    private:
        SignallingCircuit* m_circuit;
        Type m_type;
    };

    /**
     * Interface to an abstract voice/data circuit referenced by signalling
     * @short Abstract data circuit used by signalling
     */
    class YSIG_API SignallingCircuit : public RefObject
    {
        YCLASS(SignallingCircuit,RefObject)
        friend class SignallingCircuitGroup;
        friend class SignallingCircuitEvent;
    public:
        /**
         * Type of the circuit hardware or transport
         */
        enum Type {
    	Unknown = 0,
    	Local, // not really a circuit
    	TDM,
    	RTP,
    	IAX,
        };

        /**
         * Status of the circuit
         */
        enum Status {
    	Missing = 0,
    	Disabled,
    	Idle,
    	Reserved,
    	Starting,
    	Stopping,
    	Special,
    	Connected,
        };

        /**
         * Lock circuit flags
         */
        enum LockFlags {
    	LockLocalHWFail       = 0x0001,  // Local side of the circuit is locked due to HW failure
    	LockLocalMaint        = 0x0002,  // Local side of the circuit is locked for maintenance
    	LockingHWFail         = 0x0004,  // Circuit (un)lock due to HW failure in progress
    	LockingMaint          = 0x0008,  // Circuit (un)lock due to maintenance in progress
    	LockLocalHWFailChg    = 0x0010,  // Local HW failure flag changed
    	LockLocalMaintChg     = 0x0020,  // Local maintenance flag changed
    	Resetting             = 0x0040,  // Circuit is beeing reset
    	LockRemoteHWFail      = 0x0100,  // Remote side of the circuit is locked due to HW failure
    	LockRemoteMaint       = 0x0200,  // Remote side of the circuit is locked for maintenance
    	LockRemoteHWFailChg   = 0x1000,  // Remote HW failure flag changed
    	LockRemoteMaintChg    = 0x2000,  // Remote maintenance flag changed
    	// Masks used to test lock conditions
    	LockLocal             = LockLocalHWFail | LockLocalMaint,
    	LockRemote            = LockRemoteHWFail | LockRemoteMaint,
    	LockLocked            = LockLocal | LockRemote,
    	LockBusy              = LockingHWFail | LockingMaint | Resetting,
    	LockLockedBusy        = LockLocked | LockBusy,
    	LockLocalChg          = LockLocalHWFailChg | LockLocalMaintChg,
    	LockRemoteChg         = LockRemoteHWFailChg | LockRemoteMaintChg,
    	LockChanged           = LockLocalChg | LockRemoteChg,
        };

        /**
         * Destructor. Clear event list
         */
        virtual ~SignallingCircuit();

        /**
         * Initiate a status transition
         * @param newStat Desired new status
         * @param sync Synchronous status change requested
         * @return True if status change has been initiated
         */
        virtual bool status(Status newStat, bool sync = false)
    	{ m_status = newStat; return true; }

        /**
         * Get the type of this circuit
         * @return Enumerated type of circuit
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the status of this circuit
         * @return Enumerated status of circuit
         */
        inline Status status() const
    	{ return m_status; }

        /**
         * Check if the given lock flags are set
         * @param flags The lock flags to check. -1 to check all flags
         * @return The lock flags of this circuit masked by the given flags
         */
        inline int locked(int flags = -1) const
    	{ return (m_lock & flags); }

        /**
         * Set the given lock flags of this circuit
         * @param flags The lock flags to set
         */
        inline void setLock(int flags)
    	{ m_lock |= flags; }

        /**
         * Reset the given lock flags of this circuit
         * @param flags The lock flags to reset
         */
        inline void resetLock(int flags)
    	{ m_lock &= ~flags; }

        /**
         * Set the format of the data transported through this circuit
         * @param format The new data format
         * @param direction The direction to be updated. -1 means to the lower layer, 1 from the lower layer, 0 both directions
         * @return True if the operation succeedded (format changed)
         */
        virtual bool updateFormat(const char* format, int direction)
    	{ return false; }

        /**
         * Set circuit data or trigger some action
         * @param param The data to update or the action to trigger
         * @param value The data value or action parameter
         * @return True on success
         */
        virtual bool setParam(const String& param, const String& value)
    	{ return false; }

        /**
         * Set circuit data from a list of parameters
         * @param params Parameter list to set in the circuit
         * @return True if all parameters were successfully set
         */
        virtual bool setParams(const NamedList& params);

        /**
         * Get circuit parameter
         * @param param The parameter to get
         * @param value The value of the parameter
         * @return True on success. False if the parameter doesn't exist
         */
        virtual bool getParam(const String& param, String& value) const
    	{ return false; }

        /**
         * Get boolean circuit parameter
         * @param param The parameter to get
         * @param defValue The default returned value
         * @return Value from circuit, devValue if the parameter doesn't exist
         */
        virtual bool getBoolParam(const String& param, bool defValue = false) const
    	{ return defValue; }

        /**
         * Get integer circuit parameter
         * @param param The parameter to get
         * @param defValue The default returned value
         * @return Value from circuit, devValue if the parameter doesn't exist
         */
        virtual int getIntParam(const String& param, int defValue = 0) const
    	{ return defValue; }

        /**
         * Get circuit parameters
         * @param params Parameter list to fill from the circuit
         * @param category Optional category used to select desired parameters
         * @return True if handled
         */
        virtual bool getParams(NamedList& params, const String& category = String::empty())
    	{ return false; }

        /**
         * Get the group of circuits this one belongs to
         * @return Pointer to circuit group
         */
        inline SignallingCircuitGroup* group()
    	{ return m_group; }

        /**
         * Get the circuit span this one belongs to
         * @return Pointer to circuit span
         */
        inline SignallingCircuitSpan* span()
    	{ return m_span; }

        /**
         * Get the group of circuits this one belongs to - const version
         * @return Pointer to const circuit group
         */
        inline const SignallingCircuitGroup* group() const
    	{ return m_group; }

        /**
         * Get the circuit span this one belongs to - const version
         * @return Pointer to const circuit span
         */
        inline const SignallingCircuitSpan* span() const
    	{ return m_span; }

        /**
         * Get the group-local code of this circuit
         * @return Identification code within group
         */
        inline unsigned int code() const
    	{ return m_code; }

        /**
         * Get the available status of the circuit
         * @return True if the circuit is available for use
         */
        inline bool available() const
    	{ return m_status == Idle; }

        /**
         * Get the connected status of the circuit
         * @return True if the circuit is connected (in use)
         */
        inline bool connected() const
    	{ return m_status == Connected; }

        /**
         * Reserve this circuit for later use
         * @return True if the circuit was changed from Idle to Reserved
         */
        inline bool reserve()
    	{ return available() && status(Reserved,true); }

        /**
         * Connect this circuit
         * @param format Optional data format to update for both directions
         * @return True if the circuit state was changed to Connected
         */
        inline bool connect(const char* format = 0)
    	{ updateFormat(format,0); return status(Connected,true); }

        /**
         * Disconnect (set state to Reserved) this circuit if connected
         * @return True if the circuit was changed from Connected to Reserved
         */
        inline bool disconnect()
    	{ return status() == Connected && status(Reserved,true); }

        /**
         * Disable this circuit for maintenance
         * @return True if the circuit was changed from Idle to Reserved
         */
        inline bool disable()
    	{ return status(Disabled,true); }

        /**
         * Set/reset HW failure lock flag
         * @param set True to set, false to reset the flag
         * @param remote True to use remote side of the circuit, false to use the local one
         * @param changed Set/reset changed flag. If false the changed flag won't be affected
         * @param setChanged The value of the changed flag. gnored if changed is false
         * @return True if the flag's state changed
         */
        bool hwLock(bool set, bool remote, bool changed = false, bool setChanged = false);

        /**
         * Set/reset maintenance lock flag
         * @param set True to set, false to reset the flag
         * @param remote True to use remote side of the circuit, false to use the local one
         * @param changed Set/reset changed flag. If false the changed flag won't be affected
         * @param setChanged The value of the changed flag. gnored if changed is false
         * @return True if the flag's state changed
         */
        bool maintLock(bool set, bool remote, bool changed = false, bool setChanged = false);

        /**
         * Add an event to the queue
         * This method is thread safe
         * @param event The event to enqueue
         */
        void addEvent(SignallingCircuitEvent* event);

        /**
         * Get an event from queue
         * This method is thread safe
         * @param when The current time
         * @return SignallingCircuitEvent pointer or 0 if no events
         */
        SignallingCircuitEvent* getEvent(const Time& when);

        /**
         * Send an event through this circuit
         * @param type The type of the event to send
         * @param params Optional event parameters
         * @return True on success
         */
        virtual bool sendEvent(SignallingCircuitEvent::Type type, NamedList* params = 0);

        /**
         * Get the text associated with a circuit type
         * @param type Circuit type used to retrieve the text
         * @return Pointer to the string associated with the given circuit type
         */
        static const char* lookupType(int type);

        /**
         * Get the text associated with a circuit status
         * @param status Circuit status used to retrieve the text
         * @return Pointer to the string associated with the given circuit status
         */
        static const char* lookupStatus(int status);

        /**
         * Keep the lock flags names
         */
        static const TokenDict s_lockNames[];

    protected:
        /**
         * Constructor
         */
        SignallingCircuit(Type type, unsigned int code, SignallingCircuitGroup* group = 0,
    	SignallingCircuitSpan* span = 0);

        /**
         * Constructor
         */
        SignallingCircuit(Type type, unsigned int code, Status status,
    	SignallingCircuitGroup* group = 0, SignallingCircuitSpan* span = 0);

        /**
         * Clear event queue
         * This method is thread safe
         */
        virtual void clearEvents();

        /**
         * Event termination notification
         * @param event The terminated event
         */
        void eventTerminated(SignallingCircuitEvent* event);

        /**
         * Circuit operations mutex
         */
        Mutex m_mutex;

    private:
        SignallingCircuitGroup* m_group;     // The group owning this circuit
        SignallingCircuitSpan* m_span;       // The span this circuit belongs to
        unsigned int m_code;                 // Circuit id
        Type m_type;                         // Circuit type (see enumeration)
        Status m_status;                     // Circuit local status
        int m_lock;                          // Circuit lock flags
        ObjList m_events;                    // In-band events
        SignallingCircuitEvent* m_lastEvent; // The last generated event
        bool m_noEvents;                     // No events pending, exit quickly
    };

    /**
     * Keeps a range (set) of circuits. The circuit codes contained within a range may
     *  not be contiguous.
     * See @ref SignallingUtils::parseUIntArray() for the format of the string ranges
     *  this object can be built from
     * @short A circuit range (set of circuits)
     */
    class YSIG_API SignallingCircuitRange : public String
    {
        YCLASS(SignallingCircuitRange,String)
        friend class SignallingCircuitGroup;
    public:
        /**
         * Constructor
         * @param rangeStr String used to build this range
         * @param name Range name
         * @param strategy Strategy used to allocate circuits from this range
         */
        SignallingCircuitRange(const String& rangeStr, const char* name = 0,
    	int strategy = -1);

        /**
         * Destructor
         */
        virtual ~SignallingCircuitRange()
    	{ clear(); }

        /**
         * Get the number of circuits contained by this range
         * @return The number of circuits contained by this range
         */
        inline unsigned int count() const
    	{ return m_count; }

        /**
         * Get the pointer to the circuit codes array
         * @return Pointer to the circuit codes array or 0
         */
        inline const unsigned int* range() const
    	{ return (const unsigned int*)m_range.data(); }

        /**
         * Allocate and return an array containing range circuits
         * @param count Address of variable to be filled with circuit count
         * @return Pointer to allocated buffer, 0 if there is no circuit.
         *  The caller will own the returned buffer
         */
        unsigned int* copyRange(unsigned int& count) const;

        /**
         * Get the pointer to the circuit codes array
         * @return Pointer to the circuit codes array or 0
         */
        inline void clear()
    	{ m_range.clear(); m_count = 0; }

        /**
         * Indexing operator
         * @param index The index in the array to retreive
         * @return The code at the given index
         */
        inline unsigned int operator[](unsigned int index)
    	{ return range()[index]; }

        /**
         * Set this range from a string
         * @param rangeStr String used to (re)build this range
         * @return False if the string has invalid format
         */
        inline bool set(const String& rangeStr)
        {
    	clear();
    	return add(rangeStr);
        }

        /**
         * Add codes to this range from a string
         * @param rangeStr String containing the codes to be added to this range
         * @return False if the string has invalid format
         */
        bool add(const String& rangeStr);

        /**
         * Add an array of circuit codes to this range
         * @param codes The array to add
         * @param len The array's length
         */
        void add(unsigned int* codes, unsigned int len);

        /**
         * Add a circuit code to this range
         * @param code The circuit code to add
         */
        inline void add(unsigned int code)
    	{ add(&code,1); }

        /**
         * Add a compact range of circuit codes to this range
         * @param first The first circuit code to add
         * @param last Number of last circuit code
         */
        void add(unsigned int first, unsigned int last);

        /**
         * Remove a circuit code from this range
         * @param code The circuit code to remove
         */
        void remove(unsigned int code);

        /**
         * Check if a circuit code is within this range
         * @param code The circuit code to find
         * @return True if found
         */
        bool find(unsigned int code);

        /**
         * Release memory
         */
        virtual void destruct()
        {
    	clear();
    	String::destruct();
        }

    protected:
        void updateLast();                   // Update last circuit code

        DataBlock m_range;                   // Array containing the circuit codes
        unsigned int m_count;                // The number of elements in the array
        unsigned int m_last;                 // Last (the greater) not used circuit code within this range
        int m_strategy;                      // Keep the strategy used to allocate circuits from this range
        unsigned int m_used;                 // Last used circuit code
    };

    /**
     * Interface to a stateful group of voice/data circuits
     * @short Group of data circuits used by signalling
     */
    class YSIG_API SignallingCircuitGroup : public SignallingComponent, public Mutex
    {
        YCLASS(SignallingCircuitGroup,SignallingComponent)
        friend class SignallingCircuit;
        friend class SignallingCallControl;
        friend class SS7ISUP;
        friend class ISDNQ931;
    public:
        /**
         * Circuit allocation strategy
         */
        enum Strategy {
    	Other     = 0,
    	// basic strategies
    	Increment = 0x0001, // round-robin, up
    	Decrement = 0x0002, // round-robin, down
    	Lowest    = 0x0003, // pick first available
    	Highest   = 0x0004, // pick last available
    	Random    = 0x0005, // pick random circuit
    	// even/odd strict select (glare avoidance)
    	OnlyEven  = 0x1000,
    	OnlyOdd   = 0x2000,
    	// glare avoidance with fallback (to be able to use all circuits)
    	Fallback  = 0x4000,
        };

        /**
         * Constructor, creates a group with a specific base code
         * @param base Base of identification codes for this group
         * @param strategy Default strategy used for circuit allocation
         * @param name Name of this component
         */
        SignallingCircuitGroup(unsigned int base = 0, int strategy = Increment,
    	const char* name = "circgroup");

        /**
         * Destructor
         */
        virtual ~SignallingCircuitGroup();

        /**
         * Get the number of circuits in this group
         * @return The number of circuits owned by this group
         */
        inline unsigned int count() const
    	{ return m_circuits.count(); }

        /**
         * Get the base of identification codes for this group
         * @return Base of identification codes for this group
         */
        inline unsigned int base() const
    	{ return m_base; }

        /**
         * Get the maximum of identification codes for this group
         * @return The maximum of identification codes for this group
         */
        inline unsigned int last() const
    	{ return m_range.m_last; }

        /**
         * Get the circuit allocation strategy
         * @return Strategy flags ORed together
         */
        inline int strategy() const
    	{ return m_range.m_strategy; }

        /**
         * Set the circuit allocation strategy
         * @param strategy The new circuit allocation strategy
         */
        inline void setStrategy(int strategy)
    	{ Lock lock(this); m_range.m_strategy = strategy; }

        /**
         * Get the circuit list
         */
        inline ObjList& circuits()
    	{ return m_circuits; }

        /**
         * Create a comma separated list with this group's circuits
         * @param dest The destination string
         */
        void getCicList(String& dest);

        /**
         * Insert a circuit in the group
         * @param circuit Pointer to the circuit to insert
         * @return False if a circuit with the same code already exists
         */
        bool insert(SignallingCircuit* circuit);

        /**
         * Remove a circuit from the group
         * @param circuit Pointer to the circuit to remove
         */
        void remove(SignallingCircuit* circuit);

        /**
         * Create a circuit span using the factory
         * @param name Name of the span to create
         * @param start Desired start of circuit codes in span
         * @param params Optional parameters for creation of span and circuits
         * @return Pointer to new circuit span or NULL on failure
         */
        SignallingCircuitSpan* buildSpan(const String& name, unsigned int start = 0, NamedList* params = 0);

        /**
         * Insert a circuit span in the group
         * @param span Pointer to the circuit span to insert
         * @return False on failure
         */
        bool insertSpan(SignallingCircuitSpan* span);

        /**
         * Build and insert a range from circuits belonging to a given span
         * @param span Span to find
         * @param name Range name or 0 to use span's id
         * @param strategy Strategy used to allocate circuits from the new range,
         *  -1 to use group's strategy
         */
        void insertRange(SignallingCircuitSpan* span, const char* name,
    	int strategy = -1);

        /**
         * Build and insert a range contained in a string.
         * See @ref SignallingUtils::parseUIntArray() for the format of the string range
         * @param range String used to build the range
         * @param name Range name
         * @param strategy Strategy used to allocate circuits from the new range,
         *  -1 to use group's strategy
         */
        void insertRange(const String& range, const char* name,
    	int strategy = -1);

        /**
         * Remove a circuit span from the group
         * @param span Pointer to the circuit span to remove
         * @param delCics True to delete signalling circuits associated to the span
         * @param delSpan True to delete the span
         */
        void removeSpan(SignallingCircuitSpan* span, bool delCics = true, bool delSpan = false);

        /**
         * Remove signalling circuits associated to the given span
         * @param span Pointer to the circuit span whose circuits will be removed
         */
        void removeSpanCircuits(SignallingCircuitSpan* span);

        /**
         * Find a specific circuit by its identification code
         * @param cic Circuit Identification Code
         * @param local Interpret the cic parameter as group-local code
         * @return Pointer to circuit or NULL if not found
         */
        SignallingCircuit* find(unsigned int cic, bool local = false);

        /**
         * Find a range of circuits owned by this group
         * @param name The range name to find
         * @return Pointer to circuit range or 0 if not found
         */
        SignallingCircuitRange* findRange(const char* name);

        /**
         * Get the status of a circuit
         * @param cic Circuit Identification Code
         * @return Enumerated status of circuit
         */
        SignallingCircuit::Status status(unsigned int cic);

        /**
         * Initiate a circuit status transition
         * @param cic Circuit Identification Code
         * @param newStat Desired new status
         * @param sync Synchronous status change requested
         * @return True if status change has been initiated
         */
        bool status(unsigned int cic, SignallingCircuit::Status newStat, bool sync = false);

        /**
         * Reserve a circuit for later use
         * @param checkLock Lock flags to check. If the given lock flags are set, reservation will fail
         * @param strategy Strategy used for allocation, use group default if negative
         * @param range Range of circuits to allocate from. 0 to use group default
         * @return Referenced pointer to a reserved circuit or 0 on failure
         */
        SignallingCircuit* reserve(int checkLock = -1, int strategy = -1,
    	SignallingCircuitRange* range = 0);

        /**
         * Reserve a circuit for later use
         * @param list Comma separated list of circuits
         * @param mandatory The list is mandatory. If false and none of the circuits in
         *  the list are available, try to reserve a free one
         * @param checkLock Lock flags to check. If the given lock flags are set, reservation will fail
         * @param strategy Strategy used for allocation if failed to allocate one from
         *  the list, use group default if negative
         * @param range Range of circuits to allocate from. 0 to use group default
         * @return Referenced pointer to a reserved circuit or 0 on failure
         */
        SignallingCircuit* reserve(const String& list, bool mandatory,
    	int checkLock = -1, int strategy = -1, SignallingCircuitRange* range = 0);

        /**
         * Initiate a release of a circuit
         * @param cic Circuit to release
         * @param sync Synchronous release requested
         * @return True if the circuit release was initiated
         */
        inline bool release(SignallingCircuit* cic, bool sync = false)
    	{ return cic && cic->status(SignallingCircuit::Idle,sync); }

        /**
         * Get the strategy value associated with a given name
         * @param name Strategy name whose value we want to obtain
         * @param def Value to return if not found
         * @return The requested strategy value or the default one
         */
        static int str2strategy(const char* name, int def = Increment)
    	{ return lookup(name,s_strategy,def); }

        /**
         * Keep the strategy names
         */
        static const TokenDict s_strategy[];

    protected:
        /**
         * Remove all spans and circuits. Release object
         */
        virtual void destroyed()
        {
    	clearAll();
    	SignallingComponent::destroyed();
        }

    private:
        unsigned int advance(unsigned int n, int strategy, SignallingCircuitRange& range);
        void clearAll();

        ObjList m_circuits;                  // The circuits belonging to this group
        ObjList m_spans;                     // The spans belonging to this group
        ObjList m_ranges;                    // Additional circuit ranges
        SignallingCircuitRange m_range;      // Range containing all circuits belonging to this group
        unsigned int m_base;
    };

    /**
     * An interface to a span belonging to a circuit group
     * @short A span in a circuit group
     */
    class YSIG_API SignallingCircuitSpan : public SignallingComponent
    {
        YCLASS(SignallingCircuitSpan,SignallingComponent)
    public:
        /**
         * Destructor. Remove from group's queue
         */
        virtual ~SignallingCircuitSpan();

        /**
         * Get the owner of this span
         * @return SignallingCircuitGroup pointer or 0
         */
        inline SignallingCircuitGroup* group() const
    	{ return m_group; }

        /**
          * Get this span's id
          * @return The id of this span
          */
        inline const String& id() const
    	{ return m_id; }

        /**
         * Get the increment in circuit numbers caused by this span
         * @return Circuit number increment for this span
         */
        inline unsigned int increment() const
    	{ return m_increment; }

    protected:
        /**
         * Constructor
         * @param id Optional span id
         * @param group Optional circuit group owning the span's circuits
         */
        SignallingCircuitSpan(const char* id = 0, SignallingCircuitGroup* group = 0);

        /**
         * The owner of this span
         */
        SignallingCircuitGroup* m_group;

        /**
         * The increment in channel code caused by this span
         */
        unsigned int m_increment;

    private:
        String m_id;                         // Span's id
    };

    /**
     * An interface to an abstraction of a Layer 1 (hardware HDLC) interface
     * @short Abstract digital signalling interface (hardware access)
     */
    class YSIG_API SignallingInterface : virtual public SignallingComponent
    {
        YCLASS(SignallingInterface,SignallingComponent)
        friend class SignallingReceiver;
    public:
        /**
         * Interface control operations
         */
        enum Operation {
    	Specific  = 0,
    	EnableTx  = 0x01,
    	EnableRx  = 0x02,
    	Enable    = 0x03,
    	DisableTx = 0x04,
    	DisableRx = 0x08,
    	Disable   = 0x0c,
    	FlushTx   = 0x10,
    	FlushRx   = 0x20,
    	Flush     = 0x30,
    	QueryTx   = 0x40,
    	QueryRx   = 0x80,
    	Query     = 0xc0
        };

        /**
         * Interface generated notifications
         */
        enum Notification {
    	LinkUp = 0,
    	LinkDown,
    	HardwareError,
    	TxClockError,
    	RxClockError,
    	AlignError,
    	CksumError,
    	TxOversize,
    	RxOversize,
    	TxOverflow,
    	RxOverflow,
    	TxUnderrun,
    	RxUnderrun,
        };

        /**
         * Packet types
         */
        enum PacketType {
    	Unknown = 0,
    	SS7Fisu,
    	SS7Lssu,
    	SS7Msu,
    	Q921
        };

        /**
         * Constructor
         */
        inline SignallingInterface()
    	: m_recvMutex(true,"SignallingInterface::recv"), m_receiver(0)
    	{ }

        /**
         * Destructor, stops and detaches the interface
         */
        virtual ~SignallingInterface();

        /**
         * Attach a receiver to the interface. Detach from the old one if valid
         * @param receiver Pointer to receiver to attach
         */
        virtual void attach(SignallingReceiver* receiver);

        /**
         * Retrieve the signalling receiver attached to this interface
         * @return Pointer to attached receiver, NULL if none
         */
        inline SignallingReceiver* receiver() const
    	{ return m_receiver; }

        /**
         * Execute a control operation. Operations can enable, disable or flush
         *  the transmitter, receiver or both. The status (enabled/disabled) can
         *  be queried and also interface-specific operations can be executed.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the interface is enabled and operational
         */
        virtual bool control(Operation oper, NamedList* params = 0);

        /**
         * Keeps the names associated with the notifications
         */
        static const TokenDict s_notifName[];

    protected:
        /**
         * Transmit a packet over the hardware interface
         * @param packet Packet data to send
         * @param repeat Continuously send a copy of the packet while no other
         *  data is available for transmission
         * @param type Type of the packet to send
         * @return True if the interface accepted the packet
         */
        virtual bool transmitPacket(const DataBlock& packet, bool repeat, PacketType type) = 0;

        /**
         * Push a valid received Signalling Packet up the protocol stack.
         * The starting and ending flags and any CRC are not part of the data.
         * @return True if packet was successfully delivered to the receiver
         */
        bool receivedPacket(const DataBlock& packet);

        /**
         * Generate a notification event to the attached receiver
         * @param event Notification event to be reported
         * @return True if notification was accepted by the receiver
         */
        bool notify(Notification event);

    private:
        Mutex m_recvMutex;                   // Lock receiver pointer operations
        SignallingReceiver* m_receiver;
    };

    /**
     * An interface to an abstraction of a Layer 2 packet data receiver
     * @short Abstract Layer 2 packet data receiver
     */
    class YSIG_API SignallingReceiver : virtual public SignallingComponent
    {
        YCLASS(SignallingReceiver,SignallingComponent)
        friend class SignallingInterface;
    public:
        /**
         * Constructor
         * @param name Name of the component to create
         */
        SignallingReceiver(const char* name = 0);

        /**
         * Destructor, stops the interface and detaches from it
         */
        virtual ~SignallingReceiver();

        /**
         * Attach a hardware interface to the data link. Detach from the old one if valid
         * @param iface Pointer to interface to attach
         * @return Pointer to old attached interface or NULL
         */
        virtual SignallingInterface* attach(SignallingInterface* iface);

        /**
         * Retrieve the interface used by this receiver
         * @return Pointer to the attached interface or NULL
         */
        inline SignallingInterface* iface() const
    	{ return m_interface; }

        /**
         * Execute a control operation on the attached interface.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the interface is enabled and operational
         */
        bool control(SignallingInterface::Operation oper, NamedList* params = 0);

    protected:
        /**
         * Send a packet to the attached interface for transmission
         * @param packet Packet data to send
         * @param repeat Continuously send a copy of the packet while no other
         *  data is available for transmission
         * @param type Type of the packet to send
         * @return True if the interface accepted the packet
         */
        bool transmitPacket(const DataBlock& packet, bool repeat,
    	SignallingInterface::PacketType type = SignallingInterface::Unknown);

        /**
         * Process a Signalling Packet received by the interface
         * @return True if message was successfully processed
         */
        virtual bool receivedPacket(const DataBlock& packet) = 0;

        /**
         * Process a notification generated by the attached interface
         * @param event Notification event reported by the interface
         * @return True if notification was processed
         */
        virtual bool notify(SignallingInterface::Notification event);

    private:
        Mutex m_ifaceMutex;                  // Lock interface pointer operations
        SignallingInterface* m_interface;
    };

    /**
     * This class keeps a description of a parameter flag used to encode/decode flags
     * @short Description of parameter flags
     */
    struct SignallingFlags
    {
        /**
         * Mask to separate the relevant bits
         */
        unsigned int mask;

        /**
         * Actual value to match
         */
        unsigned int value;

        /**
         * Name of the flag
         */
        const char* name;
    };

    /**
     * Provides data and services for SS7 and ISDN
     * @short Library wide services and data provider
     */
    class YSIG_API SignallingUtils
    {
    public:
        /**
         * Retreive the dictionary keeping the coding standard flags of ISUP and ISDN parameters as defined in Q.850
         * @return Pointer to the coding standards dictionary
         */
        static const TokenDict* codings();

        /**
         * Retreive the dictionary keeping the location flags of ISUP and ISDN parameters as defined in Q.850
         * @return Pointer to the locations dictionary
         */
        static const TokenDict* locations();

        /**
         * Retreive a dictionary given by index and coding standard for ISUP and ISDN parameters
         * @param index The desired disctionary:
         *  0: The release causes of ISUP and ISDN calls as defined in Q.850.
         *  1: The formats negotiated in ISDN and ISUP parameters as defined in Q.931/Q.763.
         *  2: The transfer capability negotiated in ISDN and ISUP parameters as defined in Q.931/Q.763.
         *  3: The transfer mode negotiated in ISDN and ISUP parameters as defined in Q.931/Q.763.
         *  4: The transfer rate negotiated in ISDN and ISUP parameters as defined in Q.931/Q.763.
         * @param coding Optional coding standard. Defaults to CCITT if 0
         * @return Pointer to the requested dictionary or 0
         */
        static inline const TokenDict* dict(unsigned int index, unsigned char coding = 0)
        {
    	if (index > 4)
    	    return 0;
    	return (!coding ? s_dictCCITT[index] : 0);
        }

        /**
         * Check if a comma separated list of flags has a given flag
         * @param flags The list of flags
         * @param flag The flag to check
         * @return True if the given flag is found
         */
        static bool hasFlag(const String& flags, const char* flag);

        /**
         * Append a flag to a comma separated list of flags if it doesn't exist
         * @param flags The list of flags
         * @param flag The flag to add
         * @return True if the given flag was not found but added
         */
        static bool appendFlag(String& flags, const char* flag);

        /**
         * Remove a flag from a comma separated list of flags
         * @param flags The list of flags
         * @param flag The flag to remove
         * @return True if the given flag was found and removed
         */
        static bool removeFlag(String& flags, const char* flag);

        /**
         * Check if a list's parameter (comma separated list of flags) has a given flag
         * @param list The parameter list
         * @param param The parameter to check
         * @param flag The flag to check
         * @return True if the given flag is found
         */
        static bool hasFlag(const NamedList& list, const char* param, const char* flag);

        /**
         * Append a flag to a list parameter (comma separated list), craete parameter if missing
         * @param list The parameter list
         * @param param The parameter to append to
         * @param flag The flag to add
         * @return True if the given flag was not found but added
         */
        static bool appendFlag(NamedList& list, const char* param, const char* flag);

        /**
         * Add string (keyword) if found in a dictionary or integer parameter to a named list
         * @param list Destination list
         * @param param Parameter to add to the list
         * @param tokens The dictionary used to find the given value
         * @param val The value to find/add to the list
         */
        static void addKeyword(NamedList& list, const char* param,
    	const TokenDict* tokens, unsigned int val);

        /**
         * Dump a buffer to a list of parameters
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param list The destination list
         * @param param Parameter to add to the list
         * @param buf The buffer containing the data to dump
         * @param len Buffer's length
         * @param sep The separator between elements
         */
        static void dumpData(const SignallingComponent* comp, NamedList& list, const char* param,
    	const unsigned char* buf, unsigned int len, char sep = ' ');

        /**
         * Dump data from a buffer to a list of parameters. The buffer is parsed until (and including)
         *  the first byte with the extension bit (the most significant one) set
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param list The destination list
         * @param param Parameter to add to the list
         * @param buf The buffer containing the data to dump
         * @param len Buffer's length
         * @param sep The separator between elements
         * @return The number of bytes processed. 0 if the end of the buffer was reached without finding
         *  a byte with the extension bit set
         */
        static unsigned int dumpDataExt(const SignallingComponent* comp, NamedList& list, const char* param,
    	const unsigned char* buf, unsigned int len, char sep = ' ');

        /**
         * Decode a received buffer to a comma separated list of flags and add it to a list of parameters
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param list The destination list
         * @param param The parameter to add to the list
         * @param flags The flags description to use
         * @param buf The buffer containing the data to parse
         * @param len Buffer's length
         * @return False if the flags description or the buffer is missing or the buffer's length exceeds
         *  the length of the 'unsigned int' data type
         */
        static bool decodeFlags(const SignallingComponent* comp, NamedList& list, const char* param,
    	const SignallingFlags* flags, const unsigned char* buf, unsigned int len);

        /**
         * Decode cause parameters as defined in Q.850
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param list The destination list
         * @param buf The buffer containing the data to parse
         * @param len Buffer's length
         * @param prefix The prefix to add to the fields before adding to the destination list
         * @param isup True if the requestor is ISUP, false for ISDN requestor
         * @return True if successfully parsed
         */
        static bool decodeCause(const SignallingComponent* comp, NamedList& list, const unsigned char* buf,
    	unsigned int len, const char* prefix, bool isup);

        /**
         * Decode bearer capabilities as defined in Q.931 (Bearer Capabilities) and Q.763 (User Service Information)
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param list The destination list
         * @param buf The buffer containing the data to parse
         * @param len Buffer's length
         * @param prefix The prefix to add to the fields before adding to the destination list
         * @param isup True if the requestor is ISUP, false for ISDN requestor
         * @return True if successfully parsed
         */
        static bool decodeCaps(const SignallingComponent* comp, NamedList& list, const unsigned char* buf,
    	unsigned int len, const char* prefix, bool isup);

        /**
         * Encode a comma separated list of flags. Flags can be prefixed with the '-'
         *  character to be reset if previously set
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param dest Destination flak mask
         * @param flags The flag list
         * @param dict Dictionary used to retrieve the flag names and values
         * @return The OR'd value of found flags
         */
        static void encodeFlags(const SignallingComponent* comp, int& dest, const String& flags,
    	const TokenDict* dict);

        /**
         * Encode a comma separated list of signalling flags
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param flags The flag list
         * @param dict Signalling flags used to retrieve the flag names and values
         * @param paramName Optional flags parameter name used for debug purposes
         * @return The OR'd value of found flags
         */
        static unsigned int encodeFlags(const SignallingComponent* comp, const String& flags,
    	const SignallingFlags* dict, const char* paramName = 0);

        /**
         * Encode cause parameters as defined in Q.850. Create with normal clearing value if parameter is missing.
         * Don't encode diagnostic if total length exceeds 32 bytes for Q.931 requestor
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param buf The destination buffer
         * @param params The list with the parameters
         * @param prefix The prefix of the fields obtained from parameter list
         * @param isup True if the requestor is ISUP, false for ISDN requestor
         * @param fail Fail if the buffer is too long. Ignored if isup is true
         * @return False if the requestor is Q.931, fail is true and the length exceeds 32 bytes
         */
        static bool encodeCause(const SignallingComponent* comp, DataBlock& buf, const NamedList& params,
    	const char* prefix, bool isup, bool fail = false);

        /**
         * Encode bearer capabilities as defined in Q.931 (Bearer Capabilities) and Q.763 (User Service Information)
         * @param comp Signalling component requesting the service. Used to print debug messages
         * @param buf The destination buffer
         * @param params The list with the parameters
         * @param prefix The prefix of the fields obtained from parameter list
         * @param isup True if the requestor is ISUP, false for ISDN requestor
         * @return True
         */
        static bool encodeCaps(const SignallingComponent* comp, DataBlock& buf, const NamedList& params,
    	const char* prefix, bool isup);

        /**
         * Parse a list of unsigned integers or unsigned integer intervals. Source elements
         *  must be separated by a '.' or ',' character. Interval margins must be separated
         *  by a '-' character. Empty elements are ignored
         * @param source The string to parse
         * @param minVal The minimum value for each element in the array
         * @param maxVal The maximum value for each element in the array
         * @param count On exit will contain the length of the returned array (0 on failure)
         * @param discardDup True to discard duplicate values
         * @return Pointer to an array of unsigned integers on success (the caller must delete it after use).
         *  0 on failure (source is empty or has invalid format or an invalid value was found)
         */
        static unsigned int* parseUIntArray(const String& source, unsigned int minVal, unsigned int maxVal,
    	unsigned int& count, bool discardDup);

    private:
        static const TokenDict* s_dictCCITT[5];
    };

    /**
     * This class holds a signalling message along with timeout value(s)
     * @short A pending signalling message
     */
    class YSIG_API SignallingMessageTimer : public GenObject, public SignallingTimer
    {
    public:
        /**
         * Constructor
         * @param interval Operation timeout interval
         * @param global Operation global timeout interval
         */
        inline SignallingMessageTimer(u_int64_t interval, u_int64_t global = 0)
    	: SignallingTimer(interval),
    	  m_globalTimer(global), m_msg(0)
    	{ }

        /**
         * Destructor. Release data
         */
        virtual ~SignallingMessageTimer()
    	{ TelEngine::destruct(m_msg); }

        /**
         * Retrieve stored signaling message
         * @return Pointer to the stored message
         */
        inline SignallingMessage* message() const
    	{ return m_msg; }

        /**
         * Set a new message
         * @param msg Message to store in the timer
         */
        inline void message(SignallingMessage* msg)
    	{ m_msg = msg; }

        /**
         * Get access to the global timer
         * @return A reference to the global timer
         */
        inline SignallingTimer& global()
    	{ return m_globalTimer; }

        /**
         * Get const access to the global timer
         * @return A const reference to the global timer
         */
        inline const SignallingTimer& global() const
    	{ return m_globalTimer; }

        /**
         * Get the time this message timer will timeout
         * @return The time this message timer will timeout
         */
        inline u_int64_t fireTime() const {
    	if (!m_globalTimer.started() || m_globalTimer.fireTime() > SignallingTimer::fireTime())
    	    return SignallingTimer::fireTime();
    	return m_globalTimer.fireTime();
        }

    protected:
        SignallingTimer m_globalTimer;
        SignallingMessage* m_msg;
    };

    /**
     * This class holds pending signalling messages.
     * The list will keep objects in timeout ascending order
     * @short A pending signalling message list
     */
    class YSIG_API SignallingMessageTimerList : public ObjList
    {
    public:
        /**
         * Constructor
         */
        inline SignallingMessageTimerList()
    	{ }

        /**
         * Add a pending operation to the list. Start its timer
         * @param interval Operation timeout interval
         * @param when Current time
         * @return Added operation or 0 on failure
         */
        inline SignallingMessageTimer* add(u_int64_t interval, const Time& when = Time())
    	{ return interval ? add(new SignallingMessageTimer(interval),when) : 0; }

        /**
         * Add a pending operation to the list. Start its timer
         * @param m The Message Timer to add to the pending list
         * @param when Current time
         * @return Added message
         */
        SignallingMessageTimer* add(SignallingMessageTimer* m, const Time& when = Time());

        /**
         * Check if the first operation timed out. Remove it from list before returning it
         * @param when Current time
         * @return SignallingMessageTimer pointer or 0 if no timeout occured
         */
        SignallingMessageTimer* timeout(const Time& when = Time());
    };

    /**
     * This class is used to manage an analog line and keep data associated with it.
     * Also it can be used to monitor a pair of FXS/FXO analog lines
     * @short An analog line
     */
    class YSIG_API AnalogLine : public RefObject, public Mutex
    {
        YCLASS(AnalogLine,RefObject)
        friend class AnalogLineGroup;        // Reset group if destroyed before the line
    public:
        /**
         * Line type enumerator
         */
        enum Type {
    	FXO,                             // Telephone linked to an exchange
    	FXS,                             // Telephone exchange linked to a telephone
    	Recorder,                        // Passive FXO recording a 2 wire line
    	Monitor,                         // Monitor (a pair of FXS/FXO lines)
    	Unknown
        };

        /**
         * Line state enumeration
         */
        enum State {
    	OutOfService   = -1,             // Line is out of service
    	Idle           = 0,              // Line is idle (on hook)
    	Dialing        = 1,              // FXS line is waiting for the FXO to dial the number
    	DialComplete   = 2,              // FXS line: got enough digits from the FXO to reach a destination
    	Ringing        = 3,              // Line is ringing
    	Answered       = 4,              // Line is answered
    	CallEnded      = 5,              // FXS line: notify the FXO on call termination
    	OutOfOrder     = 6,              // FXS line: notify the FXO that the hook is off after call ended notification
        };

        /**
         * Call setup (such as Caller ID) management (send and detect)
         */
        enum CallSetupInfo {
    	After,                           // Send/detect call setup after the first ring
    	Before,                          // Send/detect call setup before the first ring
    	NoCallSetup                      // No call setup detect or send
        };

        /**
         * Constructor. Reserve the line's circuit. Connect it if requested. Creation will fail if no group,
         *  circuit, caller or the circuit is already allocated for another line in the group
         * @param grp The group owning this analog line
         * @param cic The code of the signalling circuit used this line
         * @param params The line's parameters
         */
        AnalogLine(AnalogLineGroup* grp, unsigned int cic, const NamedList& params);

        /**
         * Destructor
         */
        virtual ~AnalogLine();

        /**
         * Get this line's type
         * @return The line type as enumeration
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the line state
         * @return The line state as enumeration
         */
        inline State state() const
    	{ return m_state; }

        /**
         * Get the group owning this line
         * @return The group owning this line
         */
        inline AnalogLineGroup* group()
    	{ return m_group; }

        /**
         * Get this line's peer if belongs to a pair of monitored lines
         * @return This line's peer if belongs to a pair of monitored lines
         */
        inline AnalogLine* getPeer()
    	{ return m_peer; }

        /**
         * Remove old peer's peer. Set this line's peer
         * @param line This line's peer
         * @param sync True to synchronize (set/reset) with the old peer
         */
        void setPeer(AnalogLine* line = 0, bool sync = true);

        /**
         * Get the line's circuit
         * @return SignallingCircuit pointer or 0 if no circuit was attached to this line
         */
        inline SignallingCircuit* circuit()
    	{ return m_circuit; }

        /**
         * Get the line address: group_name/circuit_number
         * @return The line address
         */
        inline const char* address() const
    	{ return m_address; }

        /**
         * Check if allowed to send outband DTMFs (DTMF events)
         * @return True if allowed to send outband DTMFs
         */
        inline bool outbandDtmf() const
    	{ return !m_inband; }

        /**
         * Check if the line should be answered on polarity change
         * @return True if the line should be answered on polarity change
         */
        inline bool answerOnPolarity() const
    	{ return m_answerOnPolarity; }

        /**
         * Check if the line should be hanged up on polarity change
         * @return True if the line should be hanged up on polarity change
         */
        inline bool hangupOnPolarity() const
    	{ return m_hangupOnPolarity; }

        /**
         * Check if the line polarity change should be used
         * @return True if the line polarity change should be used
         */
        inline bool polarityControl() const
    	{ return m_polarityControl; }

        /**
         * Check if the line is processing (send/receive) the setup info (such as caller id) and when it does it
         * @return Call setup info processing as enumeration
         */
        inline CallSetupInfo callSetup() const
    	{ return m_callSetup; }

        /**
         * Get the time allowed to ellapse between the call setup data and the first ring
         * @return The time allowed to ellapse between the call setup data and the first ring
         */
        inline u_int64_t callSetupTimeout() const
    	{ return m_callSetupTimeout; }

        /**
         * Get the time allowed to ellapse without receiving a ring on incoming calls
         * @return The time allowed to ellapse without receiving a ring on incoming calls
         */
        inline u_int64_t noRingTimeout() const
    	{ return m_noRingTimeout; }

        /**
         * Get the time allowed to stay in alarm. This option can be used by the clients to terminate an active call
         * @return The time allowed to stay in alarm
         */
        inline u_int64_t alarmTimeout() const
    	{ return m_alarmTimeout; }

        /**
         * Get the time delay of dialing the called number
         * @return The time delay of dialing the called number
         */
        inline u_int64_t delayDial() const
    	{ return m_delayDial; }

        /**
         * Set/reset accept pulse digits flag
         * @param ok True to accept incoming pulse digits, false to ignore them
         */
        inline void acceptPulseDigit(bool ok)
    	{ m_acceptPulseDigit = ok; }

        /**
         * Get the private user data of this line
         * @return The private user data of this line
         */
        inline void* userdata() const
    	{ return m_private; }

        /**
         * Set the private user data of this line and its peer if any
         * @param data The new private user data value of this line
         * @param sync True to synchronize (set data) with the peer
         */
        inline void userdata(void* data, bool sync = true)
        {
    	Lock lock(this);
    	m_private = data;
    	if (sync && m_peer)
    	    m_peer->userdata(data,false);
        }

        /**
         * Get this line's address
         * @return This line's address
         */
        virtual const String& toString() const
    	{ return m_address; }

        /**
         * Reset the line circuit's echo canceller to line default echo canceller state
         * @param train Start echo canceller training if enabled
         */
        void resetEcho(bool train);

        /**
         * Reset the line's circuit (change its state to Reserved)
         * @return True if the line's circuit state was changed to Reserved
         */
        inline bool resetCircuit()
    	{ return state() != OutOfService && m_circuit && m_circuit->reserve(); }

        /**
         * Set a parameter of this line's circuit
         * @param param Parameter name
         * @param value Optional parameter value
         * @return True if the line's circuit parameter was set
         */
        inline bool setCircuitParam(const char* param, const char* value = 0)
    	{ return m_circuit && m_circuit->setParam(param,value); }

        /**
         * Connect the line's circuit. Reset line echo canceller
         * @param sync True to synchronize (connect) the peer
         * @return True if the line's circuit state was changed to Connected
         */
        bool connect(bool sync);

        /**
         * Disconnect the line's circuit. Reset line echo canceller
         * @param sync True to synchronize (disconnect) the peer
         * @return True if the line's circuit was disconnected (changed state from Connected to Reserved)
         */
        bool disconnect(bool sync);

        /**
         * Send an event through this line if not out of service
         * @param type The type of the event to send
         * @param params Optional event parameters
         * @return True on success
         */
        bool sendEvent(SignallingCircuitEvent::Type type, NamedList* params = 0);

        /**
         * Send an event through this line if not out of service and change its state on success
         * @param type The type of the event to send
         * @param newState The new state of the line if the event was sent
         * @param params Optional event parameters
         * @return True on success
         */
        inline bool sendEvent(SignallingCircuitEvent::Type type, State newState,
    	NamedList* params = 0)
        {
    	if (!sendEvent(type,params))
    	    return false;
    	changeState(newState,false);
    	return true;
        }

        /**
         * Get events from the line's circuit if not out of service. Check timeouts
         * @param when The current time
         * @return AnalogLineEvent pointer or 0 if no events
         */
        virtual AnalogLineEvent* getEvent(const Time& when);

        /**
         * Alternate get events from this line or peer
         * @param when The current time
         * @return AnalogLineEvent pointer or 0 if no events
         */
        virtual AnalogLineEvent* getMonitorEvent(const Time& when);

        /**
         * Check timeouts if the line is not out of service and no event was generated by the circuit
         * @param when Time to use as computing base for timeouts
         */
        virtual void checkTimeouts(const Time& when)
    	{ }

        /**
         * Change the line state if neither current or new state are OutOfService
         * @param newState The new state of the line
         * @param sync True to synchronize (change state) the peer
         * @return True if line state changed
         */
        bool changeState(State newState, bool sync = false);

        /**
         * Enable/disable line. Change circuit's state to Disabled/Reserved when
         *  entering/exiting the OutOfService state
         * @param ok Enable (change state to Idle) or disable (change state to OutOfService) the line
         * @param sync True to synchronize (enable/disable) the peer
         * @param connectNow Connect the line if enabled. Ignored if the line will be disabled
         * @return True if line state changed
         */
        bool enable(bool ok, bool sync, bool connectNow = true);

        /**
         * Line type names dictionary
         */
        static const TokenDict* typeNames();

        /**
         * Line state names dictionary
         */
        static const TokenDict* stateNames();

        /**
         * Call setup info names
         */
        static const TokenDict* csNames();

    protected:
        /**
         * Deref the circuit. Remove itself from group
         */
        virtual void destroyed();

    private:
        Type m_type;                               // Line type
        State m_state;                             // Line state
        bool m_inband;                             // Refuse to send DTMFs if they should be sent in band
        int m_echocancel;                          // Default echo canceller state (0: managed by the circuit, -1: off, 1: on)
        bool m_acceptPulseDigit;                   // Accept incoming pulse digits
        bool m_answerOnPolarity;                   // Answer on line polarity change
        bool m_hangupOnPolarity;                   // Hangup on line polarity change
        bool m_polarityControl;                    // Set line polarity flag
        CallSetupInfo m_callSetup;                 // Call setup management
        u_int64_t m_callSetupTimeout;              // FXO: timeout period for received call setup data before first ring
        u_int64_t m_noRingTimeout;                 // FXO: timeout period with no ring received on incoming calls
        u_int64_t m_alarmTimeout;                  // Timeout period to stay in alarms
        u_int64_t m_delayDial;                     // FXO: Time to delay sending number
        AnalogLineGroup* m_group;                  // The group owning this line
        SignallingCircuit* m_circuit;              // The circuit managed by this line
        String m_address;                          // Line address: group and circuit
        void* m_private;                           // Private data used by this line's user
        // Monitor data
        AnalogLine* m_peer;                        // This line's peer if any
        bool m_getPeerEvent;                       // Flag used to get events from peer
    };

    /**
     * An object holding an event generated by an analog line and related references
     * @short A single analog line related event
     */
    class YSIG_API AnalogLineEvent : public GenObject
    {
    public:
        /**
         * Constructor
         * @param line The analog line that generated this event
         * @param event The signalling circuit event
         */
        AnalogLineEvent(AnalogLine* line, SignallingCircuitEvent* event)
    	: m_line(0), m_event(event)
    	{ if (line && line->ref()) m_line = line; }

        /**
         * Destructor, dereferences any resources
         */
        virtual ~AnalogLineEvent()
        {
    	TelEngine::destruct(m_line);
    	TelEngine::destruct(m_event);
        }

        /**
         * Get the analog line that generated this event
         * @return The analog line that generated this event
         */
        inline AnalogLine* line()
    	{ return m_line; }

        /**
         * Get the signalling circuit event carried by this analog line event
         * @return The signalling circuit event carried by this analog line event
         */
        inline SignallingCircuitEvent* event()
    	{ return m_event; }

        /**
         * Disposes the memory
         */
        virtual void destruct()
        {
    	TelEngine::destruct(m_line);
    	TelEngine::destruct(m_event);
    	GenObject::destruct();
        }

    private:
        AnalogLine* m_line;
        SignallingCircuitEvent* m_event;
    };

    /**
     * This class is an analog line container.
     * It may contain another group when used to monitor analog lines
     * @short A group of analog lines
     */
    class YSIG_API AnalogLineGroup : public SignallingCircuitGroup
    {
        YCLASS(AnalogLineGroup,SignallingCircuitGroup)
    public:
        /**
         * Constructor. Construct an analog line group owning single lines
         * @param type Line type as enumeration
         * @param name Name of this component
         * @param slave True if this is an FXO group owned by an FXS one. Ignored if type is not FXO
         */
        AnalogLineGroup(AnalogLine::Type type, const char* name, bool slave = false);

        /**
         * Constructor. Construct an FXS analog line group owning another group of FXO analog lines.
         * The fxo group is owned by this component and will be destructed if invalid (not FXO type)
         * @param name Name of this component
         * @param fxo The FXO group
         */
        AnalogLineGroup(const char* name, AnalogLineGroup* fxo);

        /**
         * Destructor
         */
        virtual ~AnalogLineGroup();

        /**
         * Get this group's type
         * @return The group's type
         */
        inline AnalogLine::Type type() const
    	{ return m_type; }

        /**
         * Get the analog lines belonging to this group
         * @return The group's lines list
         */
        inline ObjList& lines()
    	{ return m_lines; }

        /**
         * Get the group holding the FXO lines if present
         * @return The group holding the FXO lines or 0
         */
        inline AnalogLineGroup* fxo()
    	{ return m_fxo; }

        /**
         * Check if this is an FXO group owned by an FXS one
         * @return True if this is an FXO group owned by an FXS one
         */
        inline bool slave()
    	{ return m_slave; }

        /**
         * Append a line to this group. Line must have the same type as this group and must be owned by this group
         * @param line The line to append
         * @param destructOnFail Destroy line if failed to append. Defaults to true
         * @return True on success
         */
        bool appendLine(AnalogLine* line, bool destructOnFail = true);

        /**
         * Remove a line from the list and destruct it
         * @param cic The signalling circuit's code used by the line
         */
        void removeLine(unsigned int cic);

        /**
         * Remove a line from the list without destroying it
         * @param line The line to be removed
         */
        void removeLine(AnalogLine* line);

        /**
         * Find a line by its circuit
         * @param cic The signalling circuit's code used by the line
         * @return AnalogLine pointer or 0 if not found
         */
        AnalogLine* findLine(unsigned int cic);

        /**
         * Find a line by its address
         * @param address The address of the line
         * @return AnalogLine pointer or 0 if not found
         */
        AnalogLine* findLine(const String& address);

        /**
         * Iterate through the line list to get an event
         * @param when The current time
         * @return AnalogLineEvent pointer or 0 if no events
         */
        virtual AnalogLineEvent* getEvent(const Time& when);

    protected:
        /**
         * Remove all lines. Release object
         */
        virtual void destroyed();

        /**
         * The analog lines belonging to this group
         */
        ObjList m_lines;

    private:
        AnalogLine::Type m_type;             // Line type
        AnalogLineGroup* m_fxo;              // The group containing the FXO lines if this is a monitor
        bool m_slave;                        // True if this is an FXO group owned by an FXS one
    };

    /**
     * An universal SS7 Layer 3 routing Code Point
     * @short SS7 Code Point
     */
    class YSIG_API SS7PointCode : public GenObject
    {
        YCLASS(SS7PointCode,GenObject)
    public:
        /**
         * Different incompatible types of points codes
         */
        enum Type {
    	Other  = 0,
    	ITU    = 1, // ITU-T Q.704
    	ANSI   = 2, // ANSI T1.111.4
    	ANSI8  = 3, // 8-bit SLS
    	China  = 4, // GF 001-9001
    	Japan  = 5, // JT-Q704, NTT-Q704
    	Japan5 = 6, // 5-bit SLS
    	// Do not change the next line, must be past last defined type
    	DefinedTypes
        };

        /**
         * Constructor from components
         * @param network ANSI Network Identifier / ITU-T Zone Identification
         * @param cluster ANSI Network Cluster / ITU-T Area/Network Identification
         * @param member ANSI Cluster Member / ITU-T Signalling Point Identification
         */
        inline SS7PointCode(unsigned char network = 0, unsigned char cluster = 0, unsigned char member = 0)
    	: m_network(network), m_cluster(cluster), m_member(member)
    	{ }

        /**
         * Constructor from unpacked format
         * @param type Type of the unpacking desired
         * @param packed Packed format of the point code
         */
        inline SS7PointCode(Type type, unsigned int packed)
    	: m_network(0), m_cluster(0), m_member(0)
    	{ unpack(type,packed); }

        /**
         * Copy constructor
         * @param original Code point to be copied
         */
        inline SS7PointCode(const SS7PointCode& original)
    	: m_network(original.network()), m_cluster(original.cluster()), m_member(original.member())
    	{ }

        /**
         * Destructor
         */
        inline ~SS7PointCode()
    	{ }

        /**
         * Retrieve the Network / Zone component of the Code Point
         * @return ANSI Network Identifier / ITU-T Zone Identification
         */
        inline unsigned char network() const
    	{ return m_network; }

        /**
         * Retrieve the Cluster / Area component of the Code Point
         * @return ANSI Network Cluster / ITU-T Area/Network Identification
         */
        inline unsigned char cluster() const
    	{ return m_cluster; }

        /**
         * Retrieve the Cluster / Point component of the Code Point
         * @return ANSI Cluster Member / ITU-T Signalling Point Identification
         */
        inline unsigned char member() const
    	{ return m_member; }

        /**
         * Assignment from components
         * @param network ANSI Network Identifier / ITU-T Zone Identification
         * @param cluster ANSI Network Cluster / ITU-T Area/Network Identification
         * @param member ANSI Cluster Member / ITU-T Signalling Point Identification
         */
        inline void assign(unsigned char network, unsigned char cluster, unsigned char member)
    	{ m_network = network; m_cluster = cluster; m_member = member; }

        /**
         * Assign data members from a given string of form 'network-cluster-member'
         * @param src Source string
         * @param type Type of the point code if numeric (packed) representation is used
         * @return False if the string has incorrect format or individual elements are not in the range 0..255
         */
        bool assign(const String& src, Type type = Other);

        /**
         * Assign data members from a packed memory block
         * @param type Type of the point code in memory
         * @param src Pointer to packed point code in memory
         * @param len Length of data, negative to not check validity
         * @param spare Pointer to variable to save spare bits, NULL to ignore them
         * @return True if success, false if invalid type or memory area
         */
        bool assign(Type type, const unsigned char* src, int len = -1, unsigned char* spare = 0);

        /**
         * Assignment operator
         * @param original Code point to be copied
         */
        inline SS7PointCode& operator=(const SS7PointCode& original)
    	{ assign(original.network(),original.cluster(),original.member()); return *this; }

        /**
         * Equality operator
         * @param original Code point to be compared with
         */
        inline bool operator==(const SS7PointCode& original) const
    	{ return m_network == original.network() && m_cluster == original.cluster() && m_member == original.member(); }

        /**
         * Inequality operator
         * @param original Code point to be compared with
         */
        inline bool operator!=(const SS7PointCode& original) const
    	{ return m_network != original.network() || m_cluster != original.cluster() || m_member != original.member(); }

        /**
         * Check if the point code is compatible with a packing type
         * @return True if the Network and Member fit in the packing format
         */
        bool compatible(Type type) const;

        /**
         * Pack the code point into a single integer number.
         * @param type Type of the packing desired
         * @return Compact code point as integer or zero if the packing type is not supported
         */
        unsigned int pack(Type type) const;

        /**
         * Unpack an integer number into a point code
         * @param type Type of the unpacking desired
         * @param packed Packed format of the point code
         * @return True if the unpacking succeeded and the point code was updated
         */
        bool unpack(Type type, unsigned int packed);

        /**
         * Store the point code in a memory area
         * @param type Type of the packing desired
         * @param dest Location to store the label info, must be at least length() long
         * @param spare Spare bits to store after the point code if applicable (ITU)
         * @return True if the unpacking succeeded and the memory was updated
         */
        bool store(Type type, unsigned char* dest, unsigned char spare = 0) const;

        /**
         * Get the size (in bits) of a packed code point according to its type
         * @param type Type of the packing
         * @return Number of bits required to represent the code point, zero if unknown
         */
        static unsigned char size(Type type);

        /**
         * Get the length (in octets) of a packed code point according to its type
         * @param type Type of the packing
         * @return Number of octets required to represent the code point, zero if unknown
         */
        static unsigned char length(Type type);

        /**
         * Get a point type associated to a given text
         * @param text Text to find
         * @return Point code type as enumeration
         */
        static Type lookup(const char* text)
    	{ return (Type)TelEngine::lookup(text,s_names,(int)Other); }

        /**
         * Get the text associated to a point type
         * @param type Type to find
         * @return The requested text or 0 if not found
         */
        static const char* lookup(Type type)
    	{ return TelEngine::lookup((int)type,s_names); }

    private:
        static const TokenDict s_names[];    // Keep the strigns associated with point code type
        unsigned char m_network;
        unsigned char m_cluster;
        unsigned char m_member;
    };

    // The number of valid point code types
    #define YSS7_PCTYPE_COUNT (SS7PointCode::DefinedTypes-1)

    /**
     * Operator to write a point code to a string
     * @param str String to append to
     * @param cp Point code to append to the string
     */
    YSIG_API String& operator<<(String& str, const SS7PointCode& cp);

    /**
     * A SS7 Layer 3 routing label, both ANSI and ITU capable
     * @short SS7 Routing Label
     */
    class YSIG_API SS7Label
    {
    public:
        /**
         * Constructor of an empty, invalid label
         */
        SS7Label();

        /**
         * Copy constructor
         * @param original Label to copy
         */
        SS7Label(const SS7Label& original);

        /**
         * Swapping constructor, puts SPC into DPC and the other way around
         * @param original Label to swap
         * @param sls Signalling Link Selection
         * @param spare Spare bits
         */
        SS7Label(const SS7Label& original, unsigned char sls, unsigned char spare = 0);

        /**
         * Constructor from label components
         * @param type Type of point code used to pack the label
         * @param dpc Destination Point Code
         * @param opc Originating Point Code
         * @param sls Signalling Link Selection
         * @param spare Spare bits
         */
        SS7Label(SS7PointCode::Type type, const SS7PointCode& dpc,
    	const SS7PointCode& opc, unsigned char sls, unsigned char spare = 0);

        /**
         * Constructor from packed label components
         * @param type Type of point code used to pack the label
         * @param dpc Destination Point Code
         * @param opc Originating Point Code
         * @param sls Signalling Link Selection
         * @param spare Spare bits
         */
        SS7Label(SS7PointCode::Type type, unsigned int dpc,
    	unsigned int opc, unsigned char sls, unsigned char spare = 0);

        /**
         * Constructor from type and received MSU
         * @param type Type of point code to use to decode the MSU
         * @param msu A received MSU to be parsed
         */
        SS7Label(SS7PointCode::Type type, const SS7MSU& msu);

        /**
         * Assignment from label components
         * @param type Type of point code used to pack the label
         * @param dpc Destination Point Code
         * @param opc Originating Point Code
         * @param sls Signalling Link Selection
         * @param spare Spare bits
         */
        void assign(SS7PointCode::Type type, const SS7PointCode& dpc,
    	const SS7PointCode& opc, unsigned char sls, unsigned char spare = 0);

        /**
         * Assignment from packed label components
         * @param type Type of point code used to pack the label
         * @param dpc Destination Point Code
         * @param opc Originating Point Code
         * @param sls Signalling Link Selection
         * @param spare Spare bits
         */
        void assign(SS7PointCode::Type type, unsigned int dpc,
    	unsigned int opc, unsigned char sls, unsigned char spare = 0);

        /**
         * Assignment from type and received MSU
         * @param type Type of point code to use to decode the MSU
         * @param msu A received MSU to be parsed
         * @return True if the assignment succeeded
         */
        bool assign(SS7PointCode::Type type, const SS7MSU& msu);

        /**
         * Assignment from a packed memory block
         * @param type Type of the point codes in memory block
         * @param src Pointer to packed label in memory
         * @param len Length of data, negative to not check validity
         * @return True if success, false if invalid type or memory area
         */
        bool assign(SS7PointCode::Type type, const unsigned char* src, int len = -1);

        /**
         * Pack and store the label in a memory location
         * @param dest Location to store the label info, must be at least length() long
         * @return True on success, false if type is invalid
         */
        bool store(unsigned char* dest) const;

        /**
         * Check if the label is compatible with another packing type
         * @return True if the DLC, SLC and SLS fit in the new packing format
         */
        bool compatible(SS7PointCode::Type type) const;

        /**
         * Get the type (SS7 dialect) of the routing label
         * @return Dialect of the routing label as enumeration
         */
        inline SS7PointCode::Type type() const
    	{ return m_type; }

        /**
         * Get the Destination Code Point inside the label
         * @return Reference of the destination code point
         */
        inline const SS7PointCode& dpc() const
    	{ return m_dpc; }

        /**
         * Get a writable reference to the Destination Code Point inside the label
         * @return Reference of the destination code point
         */
        inline SS7PointCode& dpc()
    	{ return m_dpc; }

        /**
         * Get the Originating Code Point inside the label
         * @return Reference of the source code point
         */
        inline const SS7PointCode& opc() const
    	{ return m_opc; }

        /**
         * Get a writable reference to the Originating Code Point inside the label
         * @return Reference of the originating code point
         */
        inline SS7PointCode& opc()
    	{ return m_opc; }

        /**
         * Get the Signalling Link Selection inside the label
         * @return Value of the SLS field
         */
        inline unsigned char sls() const
    	{ return m_sls; }

        /**
         * Set the Signalling Link Selection inside the label
         * @param sls New value of the SLS/SLC field
         */
        inline void setSls(unsigned char sls)
    	{ m_sls = sls; }

        /**
         * Get the spare bits inside the label
         * @return Value of the bits not included in DPC, OPC, or SLS
         */
        inline unsigned char spare() const
    	{ return m_spare; }

        /**
         * Set the spare bits inside the label
         * @param spare New value of the spare bits
         */
        inline void setSpare(unsigned char spare)
    	{ m_spare = spare; }

        /**
         * Get the length (in bytes) of this routing label
         * @return Number of bytes required to represent the label, zero if unknown
         */
        inline unsigned int length() const
    	{ return length(m_type); }

        /**
         * Get the length (in bytes) of a packed routing label according to its type
         * @param type Type of the packing
         * @return Number of bytes required to represent the label, zero if unknown
         */
        static unsigned int length(SS7PointCode::Type type);

        /**
         * Get the size (in bits) of this routing label except the spare bits
         * @return Number of bits required to represent this label, zero if unknown
         */
        inline unsigned char size() const
    	{ return size(m_type); }

        /**
         * Get the size (in bits) of a packed routing label according to its type
         * @param type Type of the packing
         * @return Number of bits required to represent the label, zero if unknown
         */
        static unsigned char size(SS7PointCode::Type type);

    private:
        SS7PointCode::Type m_type;
        SS7PointCode m_dpc;
        SS7PointCode m_opc;
        unsigned char m_sls;
        unsigned char m_spare;
    };

    /**
     * Operator to write a routing label to a string
     * @param str String to append to
     * @param label Label to append to the string
     */
    YSIG_API String& operator<<(String& str, const SS7Label& label);

    /**
     * A raw data block with a little more understanding about MSU format
     * @short A block of data that holds a Message Signal Unit
     */
    class YSIG_API SS7MSU : public DataBlock
    {
        YCLASS(SS7MSU,DataBlock)
    public:
        /**
         * Service indicator values
         */
        enum Services {
    	// Signalling Network Management
    	SNM   =  0,
    	// Maintenance
    	MTN   =  1,
    	// Maintenance special
    	MTNS  =  2,
    	// Signalling Connection Control Part
    	SCCP  =  3,
    	// Telephone User Part
    	TUP   =  4,
    	// ISDN User Part
    	ISUP  =  5,
    	// Data User Part - call and circuit related
    	DUP_C =  6,
    	// Data User Part - facility messages
    	DUP_F =  7,
    	// MTP Testing User Part (reserved)
    	MTP_T =  8,
    	// Broadband ISDN User Part
    	BISUP =  9,
    	// Satellite ISDN User Part
    	SISUP = 10,
    	// AAL type2 Signaling
    	AAL2  = 12,
    	// Bearer Independent Call Control
    	BICC  = 13,
    	// Gateway Control Protocol
    	GCP   = 14,
        };

        /**
         * Priority values
         */
        enum Priority {
    	Regular  = 0x00,
    	Special  = 0x10,
    	Circuit  = 0x20,
    	Facility = 0x30
        };

        /**
         * Subservice types
         */
        enum NetIndicator {
    	International      = 0x00,
    	SpareInternational = 0x40,
    	National           = 0x80,
    	ReservedNational   = 0xc0
        };

        /**
         * Empty MSU constructor
         */
        inline SS7MSU()
    	{ }

        /**
         * Copy constructor
         * @param value Original MSU
         */
        inline SS7MSU(const SS7MSU& value)
    	: DataBlock(value)
    	{ }

        /**
         * Constructor from data block
         * @param value Raw data block to copy
         */
        inline SS7MSU(const DataBlock& value)
    	: DataBlock(value)
    	{ }

        /**
         * Constructor of an initialized MSU
         * @param value Data to assign, may be NULL to fill with zeros
         * @param len Length of data, may be zero (then value is ignored)
         * @param copyData True to make a copy of the data, false to use the pointer
         */
        inline SS7MSU(void* value, unsigned int len, bool copyData = true)
    	: DataBlock(value,len,copyData)
    	{ }

        /**
         * Constructor from routing label and raw data
         * @param sio Service Information Octet
         * @param label Routing label
         * @param len Length of data, may be zero (then value is ignored)
         * @param value Data to assign, may be NULL to fill with zeros
         */
        SS7MSU(unsigned char sio, const SS7Label label, void* value = 0, unsigned int len = 0);

        /**
         * Constructor from routing label and raw data
         * @param sif Service Information Field
         * @param ssf Subservice Field
         * @param label Routing label
         * @param len Length of data, may be zero (then value is ignored)
         * @param value Data to assign, may be NULL to fill with zeros
         */
        SS7MSU(unsigned char sif, unsigned char ssf, const SS7Label label, void* value = 0, unsigned int len = 0);

        /**
         * Destructor
         */
        virtual ~SS7MSU();

        /**
         * Assignment operator
         * @param value Original MSU
         * @return A reference to this MSU
         */
        inline SS7MSU& operator=(const SS7MSU& value)
    	{ DataBlock::operator=(value); return *this; }

        /**
         * Assignment operator from data block
         * @param value Data block to assign
         * @return A reference to this MSU
         */
        inline SS7MSU& operator=(const DataBlock& value)
    	{ DataBlock::operator=(value); return *this; }

        /**
         * Check if the MSU length appears valid
         * @return True if the MSU length is valid
         */
        bool valid() const;

        /**
         * Get a pointer to raw data
         * @param offs Offset in the MSU
         * @param len Minimum length of data requested
         * @return Pointer to data or NULL if invalid offset or length
         */
        inline unsigned char* getData(unsigned int offs, unsigned int len = 1)
    	{ return (offs+len <= length()) ? offs + (unsigned char*)data() : 0; }

        /**
         * Get a const pointer to raw data
         * @param offs Offset in the MSU
         * @param len Minimum length of data requested
         * @return Pointer to data or NULL if invalid offset or length
         */
        inline const unsigned char* getData(unsigned int offs, unsigned int len = 1) const
    	{ return (offs+len <= length()) ? offs + (const unsigned char*)data() : 0; }

        /**
         * Get a pointer to raw user part data after a routing label
         * @param label Routing label of the MSU
         * @param len Minimum length of data requested
         * @return Pointer to data or NULL if invalid offset or length
         */
        inline unsigned char* getData(const SS7Label& label, unsigned int len = 1)
    	{ return getData(label.length()+1,len); }

        /**
         * Get a const pointer to raw user part data after a routing label
         * @param label Routing label of the MSU
         * @param len Minimum length of data requested
         * @return Pointer to data or NULL if invalid offset or length
         */
        inline const unsigned char* getData(const SS7Label& label, unsigned int len = 1) const
    	{ return getData(label.length()+1,len); }

        /**
         * Retrieve the Service Information Octet
         * @return Value of the SIO or -1 if the MSU is empty
         */
        inline int getSIO() const
    	{ return null() ? -1 : *(const unsigned char*)data(); }

        /**
         * Retrieve the Service Information Field
         * @return Value of the SIF or -1 if the MSU is empty
         */
        inline int getSIF() const
    	{ return null() ? -1 : 0x0f & *(const unsigned char*)data(); }

        /**
         * Retrieve the Subservice Field (SSF)
         * @return Value of the subservice or -1 if the MSU is empty
         */
        inline int getSSF() const
    	{ return null() ? -1 : 0xf0 & *(const unsigned char*)data(); }

        /**
         * Retrieve the Priority Field
         * @return Value of the priority or -1 if the MSU is empty
         */
        inline int getPrio() const
    	{ return null() ? -1 : 0x30 & *(const unsigned char*)data(); }

        /**
         * Retrieve the Network Indicator (NI)
         * @return Value of the subservice or -1 if the MSU is empty
         */
        inline int getNI() const
    	{ return null() ? -1 : 0xc0 & *(const unsigned char*)data(); }

        /**
         * Retrieve the name of the Service as decoded from the SIF
         * @return Name of the service, NULL if unknown or invalid MSU
         */
        const char* getServiceName() const;

        /**
         * Retrieve the name of the Priority as decoded from the SIF
         * @return Name of the priority, NULL if unknown or invalid MSU
         */
        const char* getPriorityName() const;

        /**
         * Retrieve the name of the Network Indicator as decoded from the SIF
         * @return Name of the network indicator, NULL if unknown or invalid MSU
         */
        const char* getIndicatorName() const;

        /**
         * Get the priority associated with a given name
         * @param name Priority name to find
         * @param defVal Default value to return if not found
         * @return The priority value or the given default one if not exists
         */
        static unsigned char getPriority(const char* name, unsigned char defVal = Regular);

        /**
         * Get the network indicator associated with a given name
         * @param name Network indicator name to find
         * @param defVal Default value to return if not found
         * @return The network indicator value or the given default one if not exists
         */
        static unsigned char getNetIndicator(const char* name, unsigned char defVal = National);
    };

    /**
     * Simple inline class used to know if a MSU was handled and if not why
     * @short MSU handling result codes (Q.704 15.17.5 and more)
     */
    class HandledMSU
    {
    public:
        enum Result {
    	Rejected     =  0,
    	Unequipped   =  1,
    	Inaccessible =  2,
    	// private used (non Q.704)
    	Accepted     = 16,
    	Failure      = 17,
    	NoAddress    = 18,
    	NoCircuit    = 19,
        };

        /**
         * Regular constructor
         * @param result MSU handling result
         */
        inline HandledMSU(Result result = Rejected)
    	{ m_result = result; }

        /**
         * Constructor from boolean success
         * @param success True signifies Accepted, false for Failure
         */
        inline HandledMSU(bool success)
    	{ m_result = success ? Accepted : Failure; }

        /**
         * Copy constructor
         * @param original Result to copy
         */
        inline HandledMSU(const HandledMSU& original)
    	{ m_result = original; }

        /**
         * Assignment from Result enumeration
         * @param result MSU handling result
         */
        inline HandledMSU& operator=(Result result)
    	{ m_result = result; return *this; }

        /**
         * Assignment operator
         * @param original Result to assign from
         */
        inline HandledMSU& operator=(const HandledMSU& original)
    	{ m_result = original; return *this; }

        /**
         * Equality operator
         * @param result Handling result value to compare to
         */
        inline bool operator==(Result result)
    	{ return m_result == result; }

        /**
         * Equality operator
         * @param result Handling result value to compare to
         */
        inline bool operator==(const HandledMSU& result)
    	{ return m_result == result; }

        /**
         * Inequality operator
         * @param result Handling result value to compare to
         */
        inline bool operator!=(Result result)
    	{ return m_result != result; }

        /**
         * Inequality operator
         * @param result Handling result value to compare to
         */
        inline bool operator!=(const HandledMSU& result)
    	{ return m_result != result; }

        /**
         * Result retrieval operator
         * @return Handling result enumeration
         */
        inline operator Result() const
    	{ return m_result; }

        /**
         * Success checking
         * @return True if MSU was handled, false for any other result
         */
        inline bool ok() const
    	{ return Accepted == m_result; }

        /**
         * Retrieve Q.704 15.17.5 UPU cause code
         * @return UPU cause code, 0 (Unknown) for all private causes
         */
        inline unsigned char upu() const
    	{ return (Accepted > m_result) ? m_result : Rejected; }

    private:
        Result m_result;
    };

    /**
     * A an abstraction offering connectivity to a SIGTRAN transport
     * @short An abstract SIGTRAN transport layer
     */
    class YSIG_API SIGTransport : public SignallingComponent
    {
        YCLASS(SIGTransport,SignallingComponent)
        friend class SIGTRAN;
    public:
        /**
         * Type of transport used
         */
        enum Transport {
    	None = 0,
    	Sctp,
    	// All the following transports are not standard
    	Tcp,
    	Udp,
    	Unix,
        };

        /**
         * Get the SIGTRAN component attached to this transport
         * @return Pointer to adaptation layer or NULL
         */
         inline SIGTRAN* sigtran() const
    	{ return m_sigtran; }

        /**
         * Get the default SCTP/TCP/UDP port used
         * @return Default protocol port, 0 if unknown, not set or no SIGTRAN attached
         */
        u_int32_t defPort() const;

        /**
         * Check if transport layer is reliable
         * @return true if transport is reliable
         */
        virtual bool reliable() const = 0;

        /**
         * Notify the SIGTRAN layer about transport status changes
         * @param status Status to notify
         */
        void notifyLayer(SignallingInterface::Notification status);

        /**
         * Configure and initialize the component and any subcomponents it may have
         * @param config Optional configuration parameters override
         * @return True if the component was initialized properly
         */
        virtual bool initialize(const NamedList* config)
    	{ return false;}

        /**
         * Check if the network transport layer is connected
         * @param streamId Identifier of the stream to check if applicable
         * @return True if the transport (and stream if applicable) is connected
         */
        virtual bool connected(int streamId) const = 0;

        /**
         * Attach an user adaptation layer
         * @param sigtran SIGTRAN component to attach, can be NULL
         */
        void attach(SIGTRAN* sigtran);

        /**
         * Send a complete message to the adaptation layer for processing
         * @param msgVersion Version of the protocol
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        bool processMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId) const;

        /**
         * Force the underlaying transport to reconnect
         * @param force True to force transport socket reconnection
         */
        virtual void reconnect(bool force = false)
    	{ }

        /**
         * Get sctp socket parameters.
         * @param params List of parameters to obtain
         * @param result List of parameters to fill
         * @return True if operation was successful, false if an error occurred
         */
        virtual bool getSocketParams(const String& params, NamedList& result)
    	{ return false; }

        /**
         * Notification that a new incomming connection has been made
         * NOTE newTransport needs to be destroyed if will not be used
         * @param newTransport The new created transport
         * @param addr The newly created transport socket address
         * @return True if the newTransport will be used.
         */
        virtual bool transportNotify(SIGTransport* newTransport, const SocketAddr& addr);

        /**
         * Check if the transport thread is still running
         * @return True if the thread is still running.
         */
        virtual bool hasThread()
    	{ return false; }

        /**
         * Stop the transport thread
         */
        virtual void stopThread()
    	{ }
    protected:
        /**
         * Constructor
         * @param name Default empty component name
         */
        inline explicit SIGTransport(const char* name = 0)
    	: SignallingComponent(name), m_sigtran(0)
    	{ }

        /**
         * Notification if the attached state changed
         * @param hasUAL True if an User Adaptation Layer is now attached
         */
        virtual void attached(bool hasUAL) = 0;

        /**
         * Transmit a message to the network
         * @param msgVersion Version of the protocol
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream to send the data over
         * @return True if the message was transmitted to network
         */
        virtual bool transmitMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId = 0);

        /**
         * Transmit a prepared message to the network
         * @param header Message header, typically 8 octets
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream to send the data over
         * @return True if the message was transmitted to network
         */
        virtual bool transmitMSG(const DataBlock& header, const DataBlock& msg, int streamId = 0) = 0;

    private:
        SIGTRAN* m_sigtran;
    };

    /**
     * An interface to a Signalling Transport user adaptation component
     * @short Abstract SIGTRAN user adaptation component
     */
    class YSIG_API SIGTRAN
    {
        friend class SIGTransport;
    public:
        /**
         * Message classes
         */
        enum MsgClass {
    	// Management (IUA/M2UA/M3UA/SUA)
    	MGMT  =  0,
    	// Transfer (M3UA)
    	TRAN  =  1,
    	// SS7 Signalling Network Management (M3UA/SUA)
    	SSNM  =  2,
    	// ASP State Maintenance (IUA/M2UA/M3UA/SUA)
    	ASPSM =  3,
    	// ASP Traffic Maintenance (IUA/M2UA/M3UA/SUA)
    	ASPTM =  4,
    	// Q.921/Q.931 Boundary Primitives Transport (IUA)
    	QPTM  =  5,
    	// MTP2 User Adaptation (M2UA)
    	MAUP  =  6,
    	// Connectionless Messages (SUA)
    	CLMSG =  7,
    	// Connection-Oriented Messages (SUA)
    	COMSG =  8,
    	// Routing Key Management (M3UA/SUA)
    	RKM   =  9,
    	// Interface Identifier Management (M2UA)
    	IIM   = 10,
    	// M2PA Messages (M2PA)
    	M2PA  = 11,
        };

        /**
         * Management messages
         */
        enum MsgMGMT {
    	MgmtERR  =  0,
    	MgmtNTFY =  1,
        };

        /**
         * Signalling Network Management messages
         */
        enum MsgSSNM {
    	SsnmDUNA =  1, // Destination Unavailable
    	SsnmDAVA =  2, // Destination Available
    	SsnmDAUD =  3, // Destination State Audit
    	SsnmSCON =  4, // Signalling Congestion
    	SsnmDUPU =  5, // Destination User Part Unavailable
    	SsnmDRST =  6, // Destination Restricted
        };

        /**
         * ASP State Maintenance messages
         */
        enum MsgASPSM {
    	AspsmUP       =  1,
    	AspsmDOWN     =  2,
    	AspsmBEAT     =  3,
    	AspsmUP_ACK   =  4,
    	AspsmDOWN_ACK =  5,
    	AspsmBEAT_ACK =  6,
        };

        /**
         * ASP Traffic Maintenance messages
         */
        enum MsgASPTM {
    	AsptmACTIVE       =  1,
    	AsptmINACTIVE     =  2,
    	AsptmACTIVE_ACK   =  3,
    	AsptmINACTIVE_ACK =  4,
        };

        /**
         * Routing Key Management messages
         */
        enum MsgRKM {
    	RkmREG_REQ    =  1,
    	RkmREG_RSP    =  2,
    	RkmDEREG_REQ  =  3,
    	RkmDEREG_RSP  =  4,
        };

        /**
         * Interface Identifier Management messages
         */
        enum MsgIIM {
    	IimREG_REQ    =  1,
    	IimREG_RSP    =  2,
    	IimDEREG_REQ  =  3,
    	IimDEREG_RSP  =  4,
        };

        /**
         * Constructs an uninitialized signalling transport
         * @param payload SCTP payload code, ignored for other transports
         * @param port SCTP/TCP/UDP default port used for transport
         */
        explicit SIGTRAN(u_int32_t payload = 0, u_int16_t port = 0);

        /**
         * Destructor, terminates transport layer
         */
        virtual ~SIGTRAN();

        /**
         * Attach a transport (connectivity provider)
         * @param trans Transport to attach to this component
         */
        virtual void attach(SIGTransport* trans);

        /**
         * Get the transport of this user adaptation component
         * @return Pointer to the transport layer or NULL
         */
        inline SIGTransport* transport() const
    	{ return m_trans; }

        /**
         * Get the SCTP payload of this user adaptation component
         * @return SCTP payload code
         */
        inline u_int32_t payload() const
    	{ return m_payload; }

        /**
         * Get the default SCTP/TCP/UDP port used for transport
         * @return Default protocol port, 0 if unknown or not set
         */
        inline u_int16_t defPort() const
    	{ return m_defPort; }

        /**
         * Check if the network transport layer is connected
         * @param streamId Identifier of the stream to check if applicable
         * @return True if the transport (and stream if applicable) is connected
         */
        bool connected(int streamId = 0) const;

        virtual void notifyLayer(SignallingInterface::Notification status)
    	{ }

        /**
         * Message class names dictionary
         * @return Pointer to dictionary of message classes
         */
        static const TokenDict* classNames();

        /**
         * Message types name lookup
         * @param msgClass Class of the message to look up
         * @param msgType Type of the message, depends on the class
         * @param defValue Value to return if lookup fails
         * @return Pointer to message type name
         */
        static const char* typeName(unsigned char msgClass, unsigned char msgType,
    	const char* defValue = 0);

        /**
         * Transmit a message to the network transport layer
         * @param msgVersion Version of the protocol
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream to send the data over
         * @return True if the message was transmitted to network
         */
        bool transmitMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId = 0) const;

        /**
         * Transmit a message with default version to the network transport layer
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream to send the data over
         * @return True if the message was transmitted to network
         */
        inline bool transmitMSG(unsigned char msgClass, unsigned char msgType,
    	const DataBlock& msg, int streamId = 0) const
    	{ return transmitMSG(1,msgClass,msgType,msg,streamId); }

        /**
         * Restart the underlaying transport
         * @param force True to hard restart, false to force restart if transport is down
         * @return True if the transport was notified that it needs to restart
         */
        bool restart(bool force);

        /**
         * Get sctp socket parameters.
         * @param params List of parameters to obtain
         * @param result List of parameters to fill
         * @return True if operation was successful, false if an error occurred
         */
        bool getSocketParams(const String& params, NamedList& result);

        /**
         * Notification that a new incomming connection has been made
         * @param newTransport The new created transport
         * @param addr The newly created transport socket address
         * @return True if the newTransport will be used.
         */
        virtual bool transportNotify(SIGTransport* newTransport, const SocketAddr& addr)
    	{ TelEngine::destruct(newTransport); return false; }

        /**
         * Check if the transport thread is running
         * @return true if the transport thread is running
         */
        bool hasTransportThread();

        /**
         * Stop the transport thread
         */
        void stopTransportThread();

    protected:
        /**
         * Process a complete message
         * @param msgVersion Version of the protocol
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId) = 0;

    private:
        SIGTransport* m_trans;
        u_int32_t m_payload;
        u_int16_t m_defPort;
        mutable Mutex m_transMutex;
    };

    /**
     * An interface to a Signalling Transport User Adaptation component
     * @short Abstract SIGTRAN User Adaptation component
     */
    class YSIG_API SIGAdaptation : public SignallingComponent, public SIGTRAN, public Mutex
    {
        YCLASS(SIGAdaptation,SignallingComponent)
    public:
        /**
         * Traffic modes
         */
        enum TrafficMode {
    	TrafficUnused    = 0,
    	TrafficOverride  = 1,
    	TrafficLoadShare = 2,
    	TrafficBroadcast = 3,
        };

        enum HeartbeatState {
    	HeartbeatDisabled     = 0,
    	HeartbeatEnabled      = 1,
    	HeartbeatWaitResponse = 2,
        };

        enum Errors {
    	InvalidVersion            = 0x01,
    	InvalidIID                = 0x02,
    	UnsupportedMessageClass   = 0x03,
    	UnsupportedMessageType    = 0x04,
    	UnsupportedTrafficMode    = 0x05,
    	UnexpectedMessage         = 0x06,
    	ProtocolError             = 0x07,
    	UnsupportedIIDType        = 0x08,
    	InvalidStreamIdentifier   = 0x09,
    	UnassignedTEI             = 0x0a,
    	UnrecognizedSAPI          = 0x0b,
    	InvalidTEISAPI            = 0x0c,
    	ManagementBlocking        = 0x0d,
    	ASPIDRequired             = 0x0e,
    	InvalidASPID              = 0x0f,
    	ASPActiveIID              = 0x10,
    	InvalidParameterValue     = 0x11,
    	ParameterFieldError       = 0x12,
    	UnexpectedParameter       = 0x13,
    	DestinationStatusUnknown  = 0x14,
    	InvalidNetworkAppearance  = 0x15,
    	MissingParameter          = 0x16,
    	InvalidRoutingContext     = 0x19,
    	NotConfiguredAS           = 0x1a,
    	SubsystemStatusUnknown    = 0x1b,
    	InvalidLoadsharingLabel   = 0x1c
        };

        /**
         * Destructor
         */
        virtual ~SIGAdaptation();

        /**
         * Transport initialization
         * @param config Configuration section for the adaptation
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Advance to next tag in a Type-Length-Value set of parameters
         * @param data Block of data containing TLV parameters
         * @param offset Offset of current parameter in block, initialize to negative for first tag
         * @param tag Type tag of returned parameter
         * @param length Unpadded length of returned parameter in octets
         * @return True if the current parameter was valid
         */
        static bool nextTag(const DataBlock& data, int& offset, uint16_t& tag, uint16_t& length);

        /**
         * Find a specific tag in a Type-Length-Value set of parameters
         * @param data Block of data containing TLV parameters
         * @param offset Offset of current parameter in block, gets updated
         * @param tag Type tag of searched parameter
         * @param length Unpadded length of returned parameter in octets
         * @return True if the requested parameter was found
         */
        static bool findTag(const DataBlock& data, int& offset, uint16_t tag, uint16_t& length);

        /**
         * Get the value of a 32 bit integer parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of searched parameter
         * @param value Variable to store the decoded parameter if found
         * @return True if the requested parameter was found and decoded
         */
        static bool getTag(const DataBlock& data, uint16_t tag, uint32_t& value);

        /**
         * Get the value of a String parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of searched parameter
         * @param value Variable to store the decoded parameter if found
         * @return True if the requested parameter was found and decoded
         */
        static bool getTag(const DataBlock& data, uint16_t tag, String& value);

        /**
         * Get the value of a raw binary parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of searched parameter
         * @param value Variable to store the decoded parameter if found
         * @return True if the requested parameter was found and decoded
         */
        static bool getTag(const DataBlock& data, uint16_t tag, DataBlock& value);

        /**
         * Add a 32 bit integer parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of parameter to add
         * @param value Value of parameter to add
         */
        static void addTag(DataBlock& data, uint16_t tag, uint32_t value);

        /**
         * Add a String parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of parameter to add
         * @param value Value of parameter to add
         */
        static void addTag(DataBlock& data, uint16_t tag, const String& value);

        /**
         * Add a raw binary parameter
         * @param data Block of data containing TLV parameters
         * @param tag Type tag of parameter to add
         * @param value Value of parameter to add
         */
        static void addTag(DataBlock& data, uint16_t tag, const DataBlock& value);

        /**
         * Method called when the transport status has been changed
         * @param status Status of the transport causing the notification
         */
        void notifyLayer(SignallingInterface::Notification status);

    protected:
        /**
         * Constructs an uninitialized User Adaptation component
         * @param name Name of this component
         * @param params Optional pointer to creation parameters
         * @param payload SCTP payload code, ignored for other transports
         * @param port SCTP/TCP/UDP default port used for transport
         */
        explicit SIGAdaptation(const char* name = 0, const NamedList* params = 0,
    	u_int32_t payload = 0, u_int16_t port = 0);

        /**
         * Processing of common management messages
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processCommonMSG(unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Abstract processing of Management messages
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processMgmtMSG(unsigned char msgType, const DataBlock& msg, int streamId) = 0;

        /**
         * Abstract processing of ASP State Maintenance messages
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAspsmMSG(unsigned char msgType, const DataBlock& msg, int streamId) = 0;

        /**
         * Abstract processing of ASP Traffic Maintenance messages
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAsptmMSG(unsigned char msgType, const DataBlock& msg, int streamId) = 0;

        /**
         * Method called periodically by the engine to keep everything alive
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process the heartbeat messages
         * @param msgType The message type
         * @param msg Message data
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        bool processHeartbeat(unsigned char msgType, const DataBlock& msg,
    	int streamId);

        /**
         * Reset heartbeat for all streams
         */
        inline void resetHeartbeat()
        {
    	Lock myLock(this);
    	for (int i = 0;i < 32;i++)
    	    m_streamsHB[i] = HeartbeatDisabled;
        }

        /**
         * Enable heartbeat for the specifyed steam id
         * @param streamId The stream id
         */
        inline void enableHeartbeat(unsigned char streamId) {
    	if (streamId > 31)
    	    return;
    	m_streamsHB[streamId] = HeartbeatEnabled;
        }
    private:
        unsigned int m_maxRetransmit;
        SignallingTimer m_sendHeartbeat;
        SignallingTimer m_waitHeartbeatAck;
        unsigned char m_streamsHB[32];
    };

    /**
     * Generic client side (ASP) Signalling Transport User Adaptation component
     * @short Client side SIGTRAN User Adaptation component
     */
    class YSIG_API SIGAdaptClient : public SIGAdaptation
    {
        friend class SIGAdaptUser;
        YCLASS(SIGAdaptClient,SIGAdaptation)
    public:
        /**
         * ASP Client states
         */
        enum AspState {
    	AspDown = 0,
    	AspUpRq,
    	AspUp,
    	AspActRq,
    	AspActive
        };

        /**
         * Method called when the transport status has been changed
         * @param status Status of the transport causing the notification
         */
        virtual void notifyLayer(SignallingInterface::Notification status);

    protected:
        /**
         * Constructs an uninitialized User Adaptation client component
         * @param name Name of this component
         * @param params Optional pointer to creation parameters
         * @param payload SCTP payload code, ignored for other transports
         * @param port SCTP/TCP/UDP default port used for transport
         */
        explicit SIGAdaptClient(const char* name = 0, const NamedList* params = 0,
    	u_int32_t payload = 0, u_int16_t port = 0);

        /**
         * Process Management messages as ASP
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processMgmtMSG(unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Process ASP State Maintenance messages as ASP
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAspsmMSG(unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Process ASP Traffic Maintenance messages as ASP
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAsptmMSG(unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Traffic activity state change notification
         * @param active True if the ASP is active and traffic is allowed
         */
        virtual void activeChange(bool active);

        /**
         * Check if the ASP is Up
         * @return True if the ASPSM is in UP state
         */
        inline bool aspUp() const
    	{ return m_state >= AspUp; }

        /**
         * Check if the ASP is Active
         * @return True if the ASPTM is in ACTIVE state
         */
        inline bool aspActive() const
    	{ return m_state >= AspActive; }

        /**
         * Request activation of the ASP
         * @return True if ASP activation started, false on failure
         */
        bool activate();

        /**
         * Set the state of the ASP, notify user components of changes
         * @param state New state of the ASP
         * @param notify True to notify user layers, false if the changes are internal
         */
        void setState(AspState state, bool notify = true);

        /**
         * Get access to the list of Adaptation Users of this component
         * @return Reference to the list of Adaptation Users
         */
        inline ObjList& users()
    	{ return m_users; }

        /**
         * ASP Identifier for ASPSM UP messages
         */
        int32_t m_aspId;

        /**
         * Traffic mode for ASPTM ACTIVE messages
         */
        TrafficMode m_traffic;

    private:
        void attach(SIGAdaptUser* user);
        void detach(SIGAdaptUser* user);
        ObjList m_users;
        AspState m_state;
    };

    /**
     * Generic server side (SG) Signalling Transport User Adaptation component
     * @short Server side SIGTRAN User Adaptation component
     */
    class YSIG_API SIGAdaptServer : public SIGAdaptation
    {
        YCLASS(SIGAdaptServer,SIGAdaptation)
    protected:
        /**
         * Constructs an uninitialized User Adaptation server component
         * @param name Name of this component
         * @param params Optional pointer to creation parameters
         * @param payload SCTP payload code, ignored for other transports
         * @param port SCTP/TCP/UDP default port used for transport
         */
        inline explicit SIGAdaptServer(const char* name = 0, const NamedList* params = 0,
    	u_int32_t payload = 0, u_int16_t port = 0)
    	: SIGAdaptation(name,params,payload,port)
    	{ }

        /**
         * Process Management messages as SG
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processMgmtMSG(unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Process ASP State Maintenance messages as SG
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAspsmMSG(unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Process ASP Traffic Maintenance messages as SG
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processAsptmMSG(unsigned char msgType, const DataBlock& msg, int streamId);
    };

    /**
     * An interface to a Signalling Transport Adaptation user
     * @short Abstract SIGTRAN Adaptation user
     */
    class YSIG_API SIGAdaptUser
    {
        friend class SIGAdaptClient;
    public:
        /**
         * Destructor
         */
        virtual ~SIGAdaptUser();

    protected:
        /**
         * Default constructor
         */
        inline SIGAdaptUser()
    	: m_autostart(false), m_streamId(1), m_adaptation(0)
    	{ }

        /**
         * Get the User Adaptation to which this component belongs
         * @return Pointer to the User Adaptation layer
         */
        inline SIGAdaptClient* adaptation() const
    	{ return m_adaptation; }

        /**
         * Get the transport of the user adaptation component
         * @return Pointer to the transport layer or NULL
         */
        inline SIGTransport* transport() const
    	{ return m_adaptation ? m_adaptation->transport() : 0; }

        /**
         * Set the User Adaptation to which this component belongs
         * @param adapt Pointer to the new User Adaptation layer
         */
        void adaptation(SIGAdaptClient* adapt);

        /**
         * Traffic activity state change notification
         * @param active True if the ASP is active and traffic is allowed
         */
        virtual void activeChange(bool active) = 0;

        /**
         * Request activation of the ASP
         * @return True if ASP activation started, false on failure
         */
        inline bool activate()
    	{ return m_adaptation && m_adaptation->activate(); }

        /**
         * Check if the ASP is Up
         * @return True if the ASPSM is in UP state
         */
        inline bool aspUp() const
    	{ return m_adaptation && m_adaptation->aspUp(); }

        /**
         * Check if the ASP is Active
         * @return True if the ASPTM is in ACTIVE state
         */
        inline bool aspActive() const
    	{ return m_adaptation && m_adaptation->aspActive(); }

        /**
         * Obtain the stream id to use for data messages
         * @return The stream id
         */
        inline unsigned char getStreamId()
    	{ return m_streamId; }

        /**
         * Automatically start on init flag
         */
        bool m_autostart;

        /**
         * The SCTP streamId
         */
        unsigned char m_streamId;

    private:
        SIGAdaptClient* m_adaptation;
    };

    /**
     * An interface to a SS7 Application Signalling Part user
     * @short Abstract SS7 ASP user interface
     */
    class YSIG_API ASPUser
    {
    };

    /**
     * An interface to a SS7 SCCP Global Title Translation
     * @short Abstract SS7 SCCP GTT interface
     */
    class YSIG_API GTT : virtual public SignallingComponent
    {
    public:
        /**
         * Constructor
         */
        GTT(const NamedList& config);

        /**
         * Destructor
         */
        virtual ~GTT();

        /**
         * Route a SCCP message based on Global Title
         * @param gt The original global title used for message routing
         * @param prefix Optional prefix for gt params
         * @param nextPrefix Optional prefix for gt params
         * @return A new SCCP called party address or null if no route was found.
         */
        virtual NamedList* routeGT(const NamedList& gt, const String& prefix, const String& nextPrefix);

        /**
         * Initialize this GTT
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Attach a SCCP to us
         * @param sccp Pointer to the SCCP to use
         */
        virtual void attach(SCCP* sccp);

        /**
         * Request to update Translations tables.
         * Called when a remote pointcode or ssn has become reachable / unreachable
         * @param params List of parameters that fired this request
         */
        virtual void updateTables(const NamedList& params)
    	{ }

        /**
         * Retrieve the SCCP attached to this translator
         * @return Pointer to the attached SCCP
         */
        inline SCCP* sccp() const
    	{ return m_sccp; }

    protected:
        virtual void destroyed();

    private:
        SCCP* m_sccp;

    };

    /**
     * An interface to a SS7 Signalling Connection Control Part
     * @short Abstract SS7 SCCP interface
     */
    class YSIG_API SCCP : virtual public SignallingComponent
    {
        YCLASS(SCCP,SignallingComponent)
        friend class SCCPManagement;
    public:

        enum Type {                     // used in management status method                                 Flow
             CoordinateRequest         = 0, // Request that a subsystem to go oos (out of service)              (User->SCCP)
             CoordinateConfirm         = 1, // Confirmation that a subsystem can go oos                         (SCCP->User)
             CoordinateIndication      = 2, // Indication that a subsystem requires to go oos                   (SCCP->User)
             CoordinateResponse        = 3, // Response to a subsystem requires to go oos                       (User->SCCP)
             StatusIndication          = 4, // Indication form SCCP that a subsystem status has been changed    (SCCP->User)
             StatusRequest             = 5, // Indication from a user that a subsystem status has been changed  (User->SCCP)
             PointCodeStatusIndication = 6, // Indication to User that a point code is available / unavailable  (SCCP->User)
             TraficIndication          = 7, // ANSI only! Indication that a traffic mix has been detected       (SCCP->User).
             SubsystemStatus           = 8, // Request from sccp t users to find the status of a local subsystem
         };

        /**
         * Constructor
         */
        SCCP();

        /**
         * Destructor
         */
        virtual ~SCCP();

        /**
         * Send a message
         * @param data Data to be transported trough SCCP protocol
         * @param params SCCP parameters
         * SCCP parameters :
         * MessageReturn : boolean / integer True or 0x08 to return message on error. NOTE int values should me below 0x0f
         * sequenceControl : boolean. True to send messages in sequence
         * LocalPC : integer. Local pointcode
         * RemotePC : integer. Remote pointcode
         * Address Parameter:
         * 	Address parameter starts with : CallingPartyAddress or CalledPartyAddress followed by:
         *	    .ssn             : integer (0-255) Subsequence number
         *	    .pointcode       : integer Packed pointcode
         *	    .gt              : string The digits of the global title
         *	    .gt.plan         : integer GT numbering plan
         *	    .gt.encoding     : integer GT encoding scheme
         *	    .gt.translation  : integer GT Translation type
         *	    .gt.nature       : integer Gt nature of address indicator (ITU only)
         * Importance : integer (0-7) Importance of the message! (ITU only)
         */
        virtual int sendMessage(DataBlock& data, const NamedList& params);

        /**
          * Receive management information from attached users.
          * @param type The type of management message
          * @param params List of parameters (Affected subsystem [M])
          * @return True if the notification was processed
          */
         virtual bool managementStatus(Type type, NamedList& params);

        /**
         * Attach an user to this SS7 SCCP
         * @param user Pointer to the SCCP user
         */
        virtual void attach(SCCPUser* user);

        /**
         * Detach an user from this SS7 SCCP
         * @param user Pointer to the SCCP user
         */
        virtual void detach(SCCPUser* user);

        /**
         * Attach an Global Title Translator to this SS7 SCCP
         * @param gtt Pointer to the Global Title Translator
         */
        virtual void attachGTT(GTT* gtt);

        /**
         * Obtain the dictionary for notifications types
         * @return Pointer to the notification types dictionary
         */
        static const TokenDict* notifTypes();

        virtual void updateTables(const NamedList& params)
    	{
    	    Lock lock(m_translatorLocker);
    	    if (m_translator)
    		m_translator->updateTables(params);
    	}
    protected:
        /**
         * Translate a Global Title
         * @param params The Global Title content
         * @param prefix The prefix of the global title content parameters
         * @param nextPrefix Other prefix of the global title content parameters
         * @return a new SCCP route or 0 is no route was found
         */
        NamedList* translateGT(const NamedList& params, const String& prefix,
    	    const String& nextPrefix);

        /**
         * Send a SCCP message to users list for processing
         * @param data The message data
         * @param params The list of parameters
         * @param ssn The ssn of the SCCP user
         * @return HandledMSU enum value
         */
        HandledMSU pushMessage(DataBlock& data, NamedList& params, int ssn);

        /**
         * Notify the users that a message failed to be delivered to destination
         * @param data The message data
         * @param params The list of parameters
         * @param ssn The ssn of the SCCP user
         * @return HandledMSU enum value
         */
        HandledMSU notifyMessage(DataBlock& data, NamedList& params, int ssn);

        /**
         * Broadcast a management message to all attached users
         * @param type The type of notification
         * @param params The list of parameters
         * @return True if at least one user processed the message
         */
        bool managementMessage(Type type, NamedList& params);

        /**
         * Check if this sccp is an endpoint
         * @return False
         */
        virtual bool isEndpoint()
    	{ return false; }

        /**
         * Copy the parameters returned by Global Title Translator in the SCCP Message
         * @param msg The SCCP message
         * @param gtParams The parameters returned by GTT
         */
        void resolveGTParams(SS7MsgSCCP* msg, const NamedList* gtParams);
    private:
        ObjList m_users;
        Mutex m_translatorLocker;
        Mutex m_usersLocker;
        GTT* m_translator;
    };

    class YSIG_API SubsystemStatusTest : public RefObject
    {
        YCLASS(SubsystemStatusTest,RefObject)
    public:
        /**
         * Constructor
         * @param interval The time interval in milliseconds representing the test duration
         */
        inline SubsystemStatusTest(u_int32_t interval)
    	: m_interval(interval), m_statusInfo(interval), m_remoteSccp(0), m_remoteSubsystem(0),
    	m_markAllowed(false)
    	{ }

        /**
         * Destructor
         */
        virtual ~SubsystemStatusTest();

        /**
         * Start this subsystem status test
         * @param remoteSccp The remote sccp where the subsystem is located
         * @param rSubsystem The remote subsystem for witch the test is performed
         * @return True if the test has successfully started
         */
        bool startTest(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem);

        /**
         * Obtain the remote SCCP of this status test
         * @return Remote Sccp
         */
        inline SccpRemote* getRemote()
    	{ return m_remoteSccp; };

        /**
         * Helper method to check for timeouts
         * @return True if timed out
         */
        inline bool timeout()
    	{ return m_statusInfo.started() && m_statusInfo.timeout(); }
        /**
         * Get the subsystem who caused this test
         * @return The subsystem for who this test was initiated
         */
        inline SccpSubsystem* getSubsystem()
    	{ return m_remoteSubsystem; }

        /**
         * Restart subsystem status test with exponential backoff
         */
        void restartTimer();

        /**
         * Helper method used to find if we should mark the remote subsystem as allowed at the end of the test
         */
        inline bool markAllowed()
    	{ return m_markAllowed; }

        /**
         * Mark the tested subsystem as allowed at the end of the test
         * @param allowed True to set the subsystem allowed at the end of the test
         */
        inline void setAllowed(bool allowed)
    	{ m_markAllowed = allowed; }
    private:
        u_int32_t m_interval;
        SignallingTimer m_statusInfo;
        SccpRemote* m_remoteSccp;
        SccpSubsystem* m_remoteSubsystem;
        bool m_markAllowed;
    };

    /**
     * An interface to a SS7 Signalling Connection Control Part user
     * @short Abstract SS7 SCCP user interface
     */
    class YSIG_API SCCPUser : virtual public SignallingComponent
    {
        YCLASS(SCCPUser,SignallingComponent)
    public:
        /**
         * Constructor
         */
        SCCPUser(const NamedList& params);

        /**
         * Destructor, detaches from the SCCP implementation
         */
        virtual ~SCCPUser();

        /**
         * Configure and initialize the component and any subcomponents it may have
         * @param config Optional configuration parameters override
         * @return True if the component was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Send a message to SCCP for transport
         * @param data User data
         * @param params SCCP parameters
         * Note! If the request is made with return option set there is no warranty that a notification
         * will be received in case that the message failed to be delivered
         */
        virtual bool sendData(DataBlock& data, NamedList& params);

        /**
         * Send a request / notification from users to sccp regarding a subsystem status
         *
          Type : CoordinateRequest -> Request from a user to sccp to go OOS
               params: "ssn" The ssn to go OOS
         	           "smi" Subsystem multiplicity indicator
         		   "backups" The number of backup subsystems
         		   "backup.N.ssn" The ssn of the backup subsystem number N
         		   "backup.N.pointcode" The ssn of the backup subsystem number N
          Type : CoordinateResponse -> Indication from a user to sccp in response to CoordinateIndication<
               params: "ssn" The subsystem number that approved the indication
                       "smi" 0
          Type : StatusRequest -> Request from user to sccp to update a subsystem status
           	   params: "ssn" The affected subsystem number
           		   "smi" 0;
          		   "subsystem-status": The requested status: UserOutOfService, UserInService

         * @param type The type of request / notification
         * @param params List of parameters
         * @return True if sccp management has processed the request / notification.
         */
        virtual bool sccpNotify(SCCP::Type type, NamedList& params);

        /**
         * Notification from SCCP that a message has arrived
         * @param data Received user data
         * @param params SCCP parameters
         * @return True if this user has processed the message, false otherwise
         */
         virtual HandledMSU receivedData(DataBlock& data, NamedList& params);

        /**
         * Notification from SCCP that a message failed to arrive to it's destination
         * @param data User data send.
         * @param params SCCP parameters
         * Note! The data may not contain the full message block previously sent (in case of SCCP segmentation),
         * but it must always must contain the first segment
         */
         virtual HandledMSU notifyData(DataBlock& data, NamedList& params);

         /**
          * Notification from SCCP management to a sccp user about pointcodes status, OOS responses/indications, subsystems status
          *
          Type: CoordinateIndication -> Indication from a remote SCCP User that requires to go OOS
              params: "ssn" -> The subsystem number of the remote SCCP User
          	          "smi" -> Subsystem multiplicity indicator
          	          "pointcode" -> The pointcode of the remote SCCP User
          Type: SubsystemStatus -> Request from SCCP Management to SCCP Users to query the specified subsystem status.
                                   This happens when a SubsystemStatusTest(SST) message is received
          	  params: "ssn" -> The requested subsystem
              returned params:
          	          "subsystem-status" -> The status of the subsystem: UserOutOfService, UserInService
          	        		        Missing = UserOutOfService

          * @param type The type of notification
          * @param params List of parameters
          * @return False on error
          */
         virtual bool managementNotify(SCCP::Type type, NamedList& params);

        /**
         * Attach as user to a SCCP
         * @param sccp Pointer to the SCCP to use
         * NOTE: This method will deref the pointer if is the same with the one that we already have!!
         * When this method is called the sccp pointer reference counter must be incremented for this
         * SCCPUser.
         */
        virtual void attach(SCCP* sccp);

        /**
         * Retrieve the SCCP to which this component is attached
         * @return Pointer to the attached SCCP or NULL
         */
        inline SCCP* sccp() const
    	{ return m_sccp; }

    protected:
        virtual void destroyed();

    private:
        SCCP* m_sccp;
        Mutex m_sccpMutex;
        int m_sls;
    };

    /**
     * An interface to a SS7 Transactional Capabilities Application Part user
     * @short Abstract SS7 TCAP user interface
     */
    class YSIG_API TCAPUser : public SignallingComponent
    {
        YCLASS(TCAPUser,SignallingComponent)
        friend class SS7TCAP;
    public:
        TCAPUser(const char* name, const NamedList* params = 0)
          : SignallingComponent(name,params),
    	m_tcap(0)
    	{}
        /**
         * Destructor, detaches from the TCAP implementation
         */
        virtual ~TCAPUser();

        /**
         * Attach as user to a SS7 TCAP
         * @param tcap Pointer to the TCAP to use
         */
        virtual void attach(SS7TCAP* tcap);

        /**
         * Receive a TCAP message from TCAP layer
         * @param params The message in NamedList form
         * @return True or false if the message was processed by this user
         */
        virtual bool tcapIndication(NamedList& params);

        /**
         * Retrieve the TCAP to which this user is attached
         * @return Pointer to a SS7 TCAP interface or NULL
         */
        inline SS7TCAP* tcap() const
    	{ return m_tcap; }

        /**
         * Received a management notification from SCCP layer
         * @param type SCCP management notification type
         * @param params Management notification params
         * @return True or false if the notification was handled bu this user
         */
        virtual bool managementNotify(SCCP::Type type, NamedList& params);

        /**
         * Get TCAP user management state
         * @return The state of the user
         */
        virtual int managementState();

        /**
         * This method is called to clean up and destroy the object after the
         *  reference counter becomes zero
         */
        virtual void  destroyed();

    protected:
        inline void setTCAP(SS7TCAP* tcap)
        {
    	Lock l(m_tcapMtx);
    	m_tcap = tcap;
        }

    private:
        SS7TCAP* m_tcap;
        Mutex m_tcapMtx;
    };

    /**
     * An user of a Layer 2 (data link) SS7 message transfer part
     * @short Abstract user of SS7 layer 2 (data link) message transfer part
     */
    class YSIG_API SS7L2User : virtual public SignallingComponent
    {
        YCLASS(SS7L2User,SignallingComponent)
        friend class SS7Layer2;
    public:
        /**
         * Attach a SS7 Layer 2 (data link) to the user component
         * @param link Pointer to data link to attach
         */
        virtual void attach(SS7Layer2* link) = 0;

        /**
         * Detach a SS7 Layer 2 (data link) from the user component
         * @param link Pointer to data link to detach
         */
        virtual void detach(SS7Layer2* link) = 0;

    protected:
        /**
         * Process a MSU received from the Layer 2 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param link Data link that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the MSU was processed
         */
        virtual bool receivedMSU(const SS7MSU& msu, SS7Layer2* link, int sls) = 0;

        /**
         * Process a MSU recovered from the Layer 2 component after failure
         * @param msu Message data, starting with Service Indicator Octet
         * @param link Data link from where the MSU was recovered
         * @param sls Signalling Link the MSU was recovered from
         * @return True if the MSU was processed
         */
        virtual bool recoveredMSU(const SS7MSU& msu, SS7Layer2* link, int sls) = 0;

        /**
         * Process a notification generated by the attached data link
         * @param link Data link that generated the notification
         */
        virtual void notify(SS7Layer2* link) = 0;
    };

    /**
     * An interface to a Layer 2 (data link) SS7 message transfer part
     * @short Abstract SS7 layer 2 (data link) message transfer part
     */
    class YSIG_API SS7Layer2 : virtual public SignallingComponent
    {
        YCLASS(SS7Layer2,SignallingComponent)
        friend class SS7MTP3;
    public:
        /**
         * LSSU Status Indications
         */
        enum LinkStatus {
    	OutOfAlignment = 0,
    	NormalAlignment = 1,
    	EmergencyAlignment = 2,
    	OutOfService = 3,
    	ProcessorOutage = 4,
    	Busy = 5,
    	// short versions as defined by RFC
    	O = OutOfAlignment,
    	N = NormalAlignment,
    	E = EmergencyAlignment,
    	OS = OutOfService,
    	PO = ProcessorOutage,
    	B = Busy,
        };

        /**
         * Control primitives
         */
        enum Operation {
    	// take link out of service
    	Pause  = 0x100,
    	// start link operation, align if it needs to
    	Resume = 0x200,
    	// start link, force realignment
    	Align  = 0x300,
    	// get operational status
    	Status = 0x400,
        };

        /**
         * Link inhibition reason bits
         */
        enum Inhibitions {
    	// basic maintenance checks not performed
    	Unchecked = 0x01,
    	// management inactivation
    	Inactive  = 0x02,
    	// locally inhibited
    	Local     = 0x04,
    	// remotely inhibited
    	Remote    = 0x08,
        };

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @return True if message was successfully queued
         */
        virtual bool transmitMSU(const SS7MSU& msu) = 0;

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sequence)
    	{ }

        /**
         * Retrieve the current link status indications
         * @return Link status indication bits
         */
        virtual unsigned int status() const;

        /**
         * Get the name of a Layer 2 status
         * @param status Status indication value
         * @param brief Request to return the short status name
         * @return String describing the status
         */
        virtual const char* statusName(unsigned int status, bool brief) const;

        /**
         * Get the name of the current local Layer 2 status
         * @param brief Request to return the short status name
         * @return String describing the status
         */
        inline const char* statusName(bool brief = false) const
    	{ return statusName(status(),brief); }

        /**
         * Check if the link is fully operational
         * @return True if the link is aligned and operational
         */
        virtual bool operational() const = 0;

        /**
         * Get the uptime of the link
         * @return Time since link got up in seconds
         */
        unsigned int upTime() const
    	{ return m_lastUp ? (Time::secNow() - m_lastUp) : 0; }

        /**
         * Attach a Layer 2 user component to the data link. Detach from the old one if valid
         * @param l2user Pointer to Layer 2 user component to attach
         */
        void attach(SS7L2User* l2user);

        /**
         * Get the Layer 2 user component that works with this data link
         * @return Pointer to the user component to which the messages are sent
         */
        inline SS7L2User* user() const
    	{ return m_l2user; }

        /**
         * Get the Signalling Link Selection number allocated to this link
         * @return SLS value assigned by the upper layer
         */
        inline int sls() const
    	{ return m_sls; }

        /**
         * Assign a new Signalling Link Selection number
         * @param linkSel New SLS to assign to this link
         */
        inline void sls(int linkSel)
    	{ if ((m_sls < 0) || !m_l2user) m_sls = linkSel; }

        /**
         * Retrieve the inhibition flags set by MTP3 Management
         * @return Inhibition flags ORed together, zero if not inhibited
         */
        inline int inhibited() const
    	{ return m_inhibited; }

        /**
         * Check some of the inhibition flags set by MTP3 Management
         * @param flags Flags to check for, ORed together
         * @return True if any of the specified inhibition flags is active
         */
        inline bool inhibited(int flags) const
    	{ return (m_inhibited & flags) != 0; }

        /**
         * Get the current congestion level of the link
         * @return Congestion level, 0 if not congested, 3 if maximum congestion
         */
        virtual unsigned int congestion()
    	{ return m_congestion; }

        /**
         * Get the sequence number of the last MSU received
         * @return Last FSN received, negative if not available
         */
        virtual int getSequence()
    	{ return m_lastSeqRx; }

        /**
         * Execute a control operation. Operations can change the link status or
         *  can query the aligned status.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the data link is aligned and operational
         */
        virtual bool control(Operation oper, NamedList* params = 0);

        /**
         * Query or modify layer's settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

    protected:
        /**
         * Constructor
         */
        inline SS7Layer2()
    	: m_autoEmergency(true), m_lastSeqRx(-1), m_congestion(0),
    	  m_l2userMutex(true,"SS7Layer2::l2user"), m_l2user(0), m_sls(-1),
    	  m_checkTime(0), m_checkFail(0), m_inhibited(Unchecked), m_lastUp(0),
    	  m_notify(false)
    	{ }

        /**
         * Method called periodically by the engine to keep everything alive
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Push a received Message Signal Unit up the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @return True if message was successfully delivered to the user component
         */
        inline bool receivedMSU(const SS7MSU& msu)
        {
    	m_l2userMutex.lock();
    	RefPointer tmp = m_l2user;
    	m_l2userMutex.unlock();
    	return tmp && tmp->receivedMSU(msu,this,m_sls);
        }

        /**
         * Push a recovered Message Signal Unit back up the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @return True if message was successfully delivered to the user component
         */
        inline bool recoveredMSU(const SS7MSU& msu)
        {
    	m_l2userMutex.lock();
    	RefPointer tmp = m_l2user;
    	m_l2userMutex.unlock();
    	return tmp && tmp->recoveredMSU(msu,this,m_sls);
        }

        /**
         * Set the notify flag.
         * The user part will be notified on timer tick about status change
         */
        void notify();

        /**
         * Set and clear inhibition flags, method used by MTP3
         * @param setFlags Flag bits to set ORed together
         * @param clrFlags Flag bits to clear  ORed together (optional)
         * @return True if inhibition flags were set
         */
        bool inhibit(int setFlags, int clrFlags = 0);

        /**
         * Get a best guess of the emergency alignment requirement
         * @param params Optional parameters whose "emergency" is used
         * @param emg Default emergency state
         * @return True if emergency alignment should be used
         */
        bool getEmergency(NamedList* params = 0, bool emg = false) const;

        /**
         * Flag to automatically perform emergency alignment when linkset is down
         */
        bool m_autoEmergency;

        /**
         * Last received MSU sequence number, -1 if unknown, bit 24 set if long FSN
         */
        int m_lastSeqRx;

        /**
         * Current congestion level
         */
        unsigned int m_congestion;

    private:
        Mutex m_l2userMutex;
        SS7L2User* m_l2user;
        int m_sls;
        u_int64_t m_checkTime;
        int m_checkFail;
        int m_inhibited;
        u_int32_t m_lastUp;
        bool m_notify;                       // Notify on timer tick
    };

    /**
     * Keeps a packed destination point code, a network priority or a list of networks used
     *  to route to the enclosed destination point code
     * @short A SS7 MSU route
     */
    class YSIG_API SS7Route : public RefObject, public Mutex
    {
        friend class SS7Layer3;
        friend class SS7Router;
    public:
        /**
         * Route state
         */
        enum State {
    	Unknown       = 0x80,
    	// Standard route states
    	Prohibited    = 0x01,
    	Restricted    = 0x02,
    	Congestion    = 0x04,
    	Allowed       = 0x08,
    	// Masks for typical combinations
    	NotAllowed    = 0x77,
    	NotCongested  = 0x78,
    	NotRestricted = 0x7c,
    	NotProhibited = 0x7e,
    	KnownState    = 0x7f,
    	AnyState      = 0xff
        };

        /**
         * Constructor
         * @param packed The packed value of the destination point code
         * @param type The destination point code type
         * @param priority Optional value of the network priority
         * @param shift SLS right shift to apply for balancing between linksets
         * @param maxDataLength The maximum data that can be transported on this
         *        route
         */
        inline SS7Route(unsigned int packed, SS7PointCode::Type type,
    	    unsigned int priority = 0, unsigned int shift = 0,
    	    unsigned int maxDataLength = 272)
    	: Mutex(true,"SS7Route"), m_packed(packed), m_type(type),
    	m_priority(priority), m_shift(shift),m_maxDataLength(maxDataLength),
    	m_state(Unknown),m_buffering(0), m_congCount(0),m_congBytes(0)
    	{ m_networks.setDelete(false); }

        /**
         * Copy constructor
         * @param original The original route
         */
        inline SS7Route(const SS7Route& original)
    	: Mutex(true,"SS7Route"), m_packed(original.packed()),
    	  m_type(original.m_type), m_priority(original.priority()),
    	  m_shift(original.shift()), m_maxDataLength(original.getMaxDataLength()),
    	  m_state(original.state()), m_buffering(0), m_congCount(0), m_congBytes(0)
    	{ m_networks.setDelete(false); }

        /**
         * Destructor
         */
        virtual ~SS7Route()
    	{ }

        /**
         * Retrieve the current state of the route
         * @return Current route state
         */
        State state() const
    	{ return m_state; }

        /**
         * Retrieve the state names token table
         * @return Pointer to the token table mapping states to names
         */
        static const TokenDict* stateNames();

        /**
         * Retrieve the name of the current state
         * @return Name of the state, NULL if invalid
         */
        const char* stateName() const
    	{ return lookup(m_state,stateNames()); }

        /**
         * Retrieve the name of an arbitrary state
         * @param state Route state whose name to return
         * @return Name of the state, NULL if invalid
         */
        static const char* stateName(State state)
    	{ return lookup(state,stateNames()); }

        /**
         * Get the priority of this route
         * @return Route priority, zero = highest (adjacent)
         */
        unsigned int priority() const
    	{ return m_priority; }

        /**
         * Get the maximum data length that can be transported on this route
         * @return The maximum data length
         */
        unsigned int getMaxDataLength() const
    	{ return m_maxDataLength; }

        /**
         * Get the packed Point Code of this route
         * @return Packed Point Code of the route's destination
         */
        unsigned int packed() const
    	{ return m_packed; }

        /**
         * Get the SLS right shift for this route
         * @return How many bits of SLS to shift off right when selecting linkset
         */
        unsigned int shift() const
    	{ return m_shift; }

        /**
         * Attach a network to use for this destination or change its priority.
         * This method is thread safe
         * @param network The network to attach or change priority
         * @param type The point code type used to get the priority from the given network or the networks already in the list
         */
        void attach(SS7Layer3* network, SS7PointCode::Type type);

        /**
         * Remove a network from the list without deleting it.
         * This method is thread safe
         * @param network The network to remove
         * @return False if the list of networks is empty
         */
        bool detach(SS7Layer3* network);

        /**
         * Check if this route goes to a specific network
         * @param network Pointer to the network to search
         * @return True if the network was found in the route's list
         */
        bool hasNetwork(const SS7Layer3* network);

        /**
         * Check if this route goes to a specific network
         * @param network Pointer to the network to search
         * @return True if the network was found in the route's list
         */
        bool hasNetwork(const SS7Layer3* network) const;

        /**
         * Check if the at least one network/linkset is fully operational
         * @param sls Signalling Link to check, negative to check if any is operational
         * @return True if the route has at least one linkset operational
         */
        bool operational(int sls = -1);

        /**
         * Try to transmit a MSU through one of the attached networks.
         * This method is thread safe
         * @param router The router requesting the operation (used for debug)
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the MSU
         * @param sls Signalling Link Selection, negative to choose best
         * @param source Avoided network where the packet was received from
         * @param states The states a network can have to be a transmission candidate
         * @return Link the message was successfully queued to, negative for error
         */
        int transmitMSU(const SS7Router* router, const SS7MSU& msu,
    	const SS7Label& label, int sls, State states, const SS7Layer3* source = 0);

        /**
         * Check the current congestion status according to Q.704 11.2.3.1
         * @return True if a TFC should be sent
         */
        bool congested();

        /**
         * Initiate controlled rerouting procedure, buffer user part messages for T6
         */
        void reroute();

    private:
        int transmitInternal(const SS7Router* router, const SS7MSU& msu,
    	const SS7Label& label, int sls, State states, const SS7Layer3* source);
        void rerouteCheck(u_int64_t when);
        void rerouteFlush();
        unsigned int m_packed;               // Packed destination point code
        SS7PointCode::Type m_type;           // The point code type
        unsigned int m_priority;             // Network priority for the given destination (used by SS7Layer3)
        unsigned int m_shift;                // SLS right shift when selecting linkset
        unsigned int m_maxDataLength;        // The maximum data length that can be transported on this route
        ObjList m_networks;                  // List of networks used to route to the given destination (used by SS7Router)
        State m_state;                       // State of the route
        u_int64_t m_buffering;               // Time when controlled rerouting ends
        ObjList m_reroute;                   // Controlled rerouting buffer
        unsigned int m_congCount;            // Congestion event count
        unsigned int m_congBytes;            // Congestion MSU bytes count
    };

    /**
     * An user of a Layer 3 (data link) SS7 message transfer part
     * @short Abstract user of SS7 layer 3 (network) message transfer part
     */
    class YSIG_API SS7L3User : virtual public SignallingComponent
    {
        friend class SS7Layer3;
        friend class SS7Router;
    public:
        /**
         * Attach a SS7 Layer 3 (network) to the user component
         * @param network Pointer to network component to attach
         */
        virtual void attach(SS7Layer3* network) = 0;

    protected:
        /**
         * Process a MSU received from the Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls) = 0;

        /**
         * Reroute a recovered Message Signal Unit
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the recovered MSU
         * @param network Network layer that recovered the MSU
         * @param sls Signalling Link the MSU was recovered from
         * @return True if the MSU was successfully rerouted
         */
        virtual bool recoveredMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls)
    	{ return false; }

        /**
         * Notification for receiving User Part Unavailable
         * @param type Type of Point Code
         * @param node Node on which the User Part is unavailable
         * @param part User Part (service) reported unavailable
         * @param cause Unavailability cause - Q.704 15.17.5
         * @param label Routing label of the UPU message
         * @param sls Signaling link the UPU was received on
         */
        virtual void receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
    	SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls)
    	{ }

        /**
         * Process a notification generated by the attached network layer
         * @param link Network or linkset that generated the notification
         * @param sls Signalling Link that generated the notification, negative if none
         */
        virtual void notify(SS7Layer3* link, int sls);

        /**
         * Process route status changed notifications
         * @param type Type of Point Code
         * @param node Destination node witch state has changed
         * @param state The new route state
         */
        virtual void routeStatusChanged(SS7PointCode::Type type, const SS7PointCode& node, SS7Route::State state)
    	{ }

        /**
         * Retrieve the route table of a network for a specific Point Code type
         * @param network Network layer to retrieve routes from
         * @param type Point Code type of the desired table
         * @return Pointer to the list of SS7Route or NULL if no such route
         */
        static ObjList* getNetRoutes(SS7Layer3* network, SS7PointCode::Type type);

        /**
         * Retrieve the route table of a network for a specific Point Code type
         * @param network Network layer to retrieve routes from
         * @param type Point Code type of the desired table
         * @return Pointer to the list of SS7Route or NULL if no such route
         */
        static const ObjList* getNetRoutes(const SS7Layer3* network, SS7PointCode::Type type);
    };

    /**
     * An interface to a Layer 3 (network) SS7 message transfer part
     * @short Abstract SS7 layer 3 (network) message transfer part
     */
    class YSIG_API SS7Layer3 : virtual public SignallingComponent
    {
        YCLASS(SS7Layer3,SignallingComponent)
        friend class SS7L3User;
        friend class SS7Router;              // Access the data members to build the routing table
        friend class SS7Route;
    public:
        /**
         * Destructor
         */
        virtual ~SS7Layer3()
    	 { attach(0); }

        /**
         * Initialize the network layer, connect it to the SS7 router
         * @param config Optional configuration parameters override
         * @return True if the network was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the MSU to use in routing
         * @param sls Signalling Link Selection, negative to choose best
         * @return Link the message was successfully queued to, negative for error
         */
        virtual int transmitMSU(const SS7MSU& msu, const SS7Label& label, int sls = -1) = 0;

        /**
         * Check if the network/linkset is fully operational
         * @param sls Signalling Link to check, negative to check if any is operational
         * @return True if the linkset is enabled and operational
         */
        virtual bool operational(int sls = -1) const = 0;

        /**
         * Retrieve inhibition flags of a specific link
         * @param sls Signalling Link to check
         * @return Inhibitions of the specified link, zero if not inhibited
         */
        virtual int inhibited(int sls) const
    	{ return 0; }

        /**
         * Check some of the inhibition flags of a specific link
         * @param sls Signalling Link to check
         * @param flags Flags to check for, ORed together
         * @return True if any of the specified inhibition flags is active
         */
        inline bool inhibited(int sls, int flags) const
    	{ return (inhibited(sls) & flags) != 0; }

        /**
         * Set and clear inhibition flags on the links
         * @param sls Signalling Link to modify
         * @param setFlags Flag bits to set ORed together
         * @param clrFlags Flag bits to clear ORed together (optional)
         * @return True if inhibition flags were set
         */
        virtual bool inhibit(int sls, int setFlags, int clrFlags = 0)
    	{ return false; }

        /**
         * Check if a link is operational and not inhibited
         * @param sls Signalling Link to check
         * @param ignore Inhibition flags to ignore, ORed together
         * @return True if the link is operational and not inhibited
         */
        inline bool inService(int sls, int ignore = 0)
    	{ return operational(sls) && !inhibited(sls,~ignore); }

        /**
         * Get the current congestion level of a link
         * @param sls Signalling Link to check for congestion, -1 for maximum
         * @return Congestion level, 0 if not congested, 3 if maximum congestion
         */
        virtual unsigned int congestion(int sls)
    	{ return 0; }

        /**
         * Get the sequence number of the last MSU received on a link
         * @param sls Signalling Link to retrieve MSU number from
         * @return Last FSN received, negative if not available
         */
        virtual int getSequence(int sls) const
    	{ return -1; }

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sls Signalling Link to recover MSUs from
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sls, int sequence)
    	{ }

        /**
         * Initiate a MTP restart procedure if supported by the network layer
         * @return True if a restart was initiated
         */
        virtual bool restart()
    	{ return false; }

        /**
         * Attach a Layer 3 user component to this network. Detach the old user if valid.
         * Attach itself to the given user
         * @param l3user Pointer to Layer 3 user component to attach
         */
        void attach(SS7L3User* l3user);

        /**
         * Retrieve the Layer 3 user component to which this network is attached
         * @return Pointer to the Layer 3 user this network is attached to
         */
        inline SS7L3User* user() const
    	{ return m_l3user; }

        /**
         * Retrieve the point code type of this Layer 3 component for a MSU type
         * @param netType Type of the network like coded in the MSU NI field
         * @return The type of codepoint this component will use
         */
        SS7PointCode::Type type(unsigned char netType) const;

        /**
         * Set the point code of this Layer 3 component for a network type
         * @param type Point code type to set for the network type
         * @param netType Type of the network like coded in the MSU NI field
         */
        void setType(SS7PointCode::Type type, unsigned char netType);

        /**
         * Set the point code of this Layer 3 component for all network types
         * @param type Point code type to set
         */
        void setType(SS7PointCode::Type type);

        /**
         * Check if the network can possibly handle a Point Code type
         * @param pcType Point code type to check
         * @return True if there is one network type that can handle the Point Code
         */
        bool hasType(SS7PointCode::Type pcType) const;

        /**
         * Get the Network Indicator bits that would match a Point Code type
         * @param pcType Point Code type to search for
         * @param defNI Default Network Indicator bits to use
         * @return Network Indicator bits matching the Point Code type
         */
        virtual unsigned char getNI(SS7PointCode::Type pcType, unsigned char defNI) const;

        /**
         * Get the Network Indicator bits that would match a Point Code type
         * @param pcType Point Code type to search for
         * @return Network Indicator bits matching the Point Code type
         */
        inline unsigned char getNI(SS7PointCode::Type pcType) const
    	{ return getNI(pcType,m_defNI); }

        /**
         * Get the default Network Indicator bits
         * @return Default Network Indicator bits for this layer
         */
        inline unsigned char getNI() const
    	{ return m_defNI; }

        /**
         * Set the default Network Indicator bits
         * @param defNI Network Indicator bits to set as defaults
         */
        void setNI(unsigned char defNI);

        /**
         * Build the list of outgoing routes serviced by this network. Clear the list before re-building it.
         * This method is thread safe
         * @param params The parameter list
         * @return False if no route available
         */
        bool buildRoutes(const NamedList& params);

        /**
         * Get the maximum data length of a route by packed Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param packedPC The packed point code
         * @return The maximum data length that can be transported on the route. Maximum msu size (272) if no route to the given point code
         */
        unsigned int getRouteMaxLength(SS7PointCode::Type type, unsigned int packedPC);

        /**
         * Get the priority of a route by packed Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param packedPC The packed point code
         * @return The priority of the route. -1 if no route to the given point code
         */
        unsigned int getRoutePriority(SS7PointCode::Type type, unsigned int packedPC);

        /**
         * Get the priority of a route by unpacked Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param dest The destination point code
         * @return The priority of the route. -1 if no route to the given point code
         */
        inline unsigned int getRoutePriority(SS7PointCode::Type type, const SS7PointCode& dest)
    	{ return getRoutePriority(type,dest.pack(type)); }

        /**
         * Get the current state of a route by packed Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param packedPC The packed point code
         * @param checkAdjacent True to take into account the adjacent STP
         * @return The state of the route, SS7Route::Unknown if no route to the given point code
         */
        SS7Route::State getRouteState(SS7PointCode::Type type, unsigned int packedPC, bool checkAdjacent = false);

        /**
         * Get the current state of a route by unpacked Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param dest The destination point code
         * @param checkAdjacent True to take into account the adjacent STP
         * @return The state of the route, SS7Route::Unknown if no route to the given point code
         */
        inline SS7Route::State getRouteState(SS7PointCode::Type type, const SS7PointCode& dest, bool checkAdjacent = false)
    	{ return getRouteState(type,dest.pack(type),checkAdjacent); }

        /**
         * Check if access to a specific Point Code is allowed from this network
         * @param type Destination point code type
         * @param packedPC The destination point code
         * @return True if access to the specified Point Code is allowed
         */
        virtual bool allowedTo(SS7PointCode::Type type, unsigned int packedPC) const
    	{ return true; }

        /**
         * Print the destinations or routing table to output
         */
        void printRoutes();

        /**
         * Retrieve the local Point Code for a specific Point Code type
         * @param type Desired Point Code type
         * @return Packed local Point Code, zero if not set
         */
        inline unsigned int getLocal(SS7PointCode::Type type) const
    	{ return (type < SS7PointCode::DefinedTypes) ? m_local[type-1] : 0; }

        /**
         * Retrieve the default local Point Code for a specific Point Code type
         * @param type Desired Point Code type
         * @return Packed local Point Code, zero if not set
         */
        virtual unsigned int getDefaultLocal(SS7PointCode::Type type) const
    	{ return getLocal(type); }

    protected:
        /**
         * Constructor
         * @param type Default point code type
         */
        SS7Layer3(SS7PointCode::Type type = SS7PointCode::Other);

        /**
         * Push a received Message Signal Unit up the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing by user part
         */
        inline HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, int sls)
        {
    	m_l3userMutex.lock();
    	RefPointer tmp = m_l3user;
    	m_l3userMutex.unlock();
    	return tmp ? tmp->receivedMSU(msu,label,this,sls) : HandledMSU(HandledMSU::Unequipped);
        }

        /**
         * Push a recovered Message Signal Unit back up the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the recovered MSU
         * @param sls Signalling Link the MSU was recovered from
         * @return True if the MSU was successfully rerouted
         */
        inline bool recoveredMSU(const SS7MSU& msu, const SS7Label& label, int sls)
        {
    	m_l3userMutex.lock();
    	RefPointer tmp = m_l3user;
    	m_l3userMutex.unlock();
    	return tmp && tmp->recoveredMSU(msu,label,this,sls);
        }

        /**
         * Notify out user part about a status change
         * @param sls Link that generated the notification, -1 if none
         */
        inline void notify(int sls = -1)
        {
    	m_l3userMutex.lock();
    	RefPointer tmp = m_l3user;
    	m_l3userMutex.unlock();
    	if (tmp)
    	    tmp->notify(this,sls);
        }

        /**
         * Callback called from maintenance when valid SLTA or SLTM are received
         * @param sls Link that was checked by maintenance
         * @param remote True if remote checked the link, false if local success
         */
        virtual void linkChecked(int sls, bool remote)
    	{ }

        /**
         * Default processing of a MTN (Maintenance MSU)
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the MSU was processed
         */
        virtual bool maintenance(const SS7MSU& msu, const SS7Label& label, int sls);

        /**
         * Default processing of a SNM (Management MSU)
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the MSU was processed
         */
        virtual bool management(const SS7MSU& msu, const SS7Label& label, int sls);

        /**
         * Default processing of an unknown MSU - emit an User Part Unavailable
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param sls Signalling Link the MSU was received from
         * @param cause Unavailability cause code (Q.704 15.17.5)
         * @return True if the MSU was processed
         */
        virtual bool unavailable(const SS7MSU& msu, const SS7Label& label, int sls, unsigned char cause = 0);

        /**
         * Send a Transfer Prohibited if an unexpected MSU is received in STP mode
         * @param ssf Subservice Field of received MSU
         * @param label Routing label of the received MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the TFP was sent
         */
        virtual bool prohibited(unsigned char ssf, const SS7Label& label, int sls);

        /**
         * Check if we should answer with SLTA to received SLTM in maintenance()
         * @return True to send a SLTA for each good received SLTM
         */
        virtual bool responder() const
    	{ return true; }

        /**
         * Find a route having the specified point code type and packed point code.
         * This method is thread safe
         * @param type The point code type used to choose the list of packed point codes
         * @param packed The packed point code to find in the list
         * @return SS7Route pointer or 0 if type is invalid or the given packed point code was not found
         */
        SS7Route* findRoute(SS7PointCode::Type type, unsigned int packed);

        /**
         * Retrieve the route table for a specific Point Code type
         * @param type Point Code type of the desired table
         * @return Pointer to the list of SS7Route or NULL if no such route
         */
        inline ObjList* getRoutes(SS7PointCode::Type type)
    	{ return (type < SS7PointCode::DefinedTypes) ? &m_route[type-1] : 0; }

        /**
         * Retrieve the route table for a specific Point Code type
         * @param type Point Code type of the desired table
         * @return Pointer to the list of SS7Route or NULL if no such route
         */
        inline const ObjList* getRoutes(SS7PointCode::Type type) const
    	{ return (type < SS7PointCode::DefinedTypes) ? &m_route[type-1] : 0; }

        /** Mutex to lock routing list operations */
        Mutex m_routeMutex;

        /** Outgoing point codes serviced by a network (for each point code type) */
        ObjList m_route[YSS7_PCTYPE_COUNT];

    private:
        Mutex m_l3userMutex;                 // Mutex to lock L3 user pointer
        SS7L3User* m_l3user;
        SS7PointCode::Type m_cpType[4];      // Map incoming MSUs net indicators to point code type
                                             // or the routing table of a message router
        unsigned int m_local[YSS7_PCTYPE_COUNT];
        unsigned char m_defNI;               // Default Network Indicator
    };

    /**
     * An interface to a Layer 4 (application) SS7 protocol
     * @short Abstract SS7 layer 4 (application) protocol
     */
    class YSIG_API SS7Layer4 : public SS7L3User
    {
    public:
        /**
         * This method is called to clean up and destroy the object after the
         *  reference counter becomes zero
         */
        virtual void destroyed();

        /**
         * Initialize the application layer, connect it to the SS7 router
         * @param config Optional configuration parameters override
         * @return True if the application was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Attach a SS7 network or router to this service. Detach itself from the old one if valid
         * @param network Pointer to network or router to attach
         */
        virtual void attach(SS7Layer3* network);

        /**
         * Retrieve the SS7 network or router to which this service is attached
         * @return Pointer to the network or router this service is attached to
         */
        inline SS7Layer3* network() const
    	{ return m_layer3; }

        /**
         * Get the default sending Service Information Octet for this protocol
         * @return SIO value
         */
        inline unsigned char sio() const
    	{ return m_sio; }

        /**
         * Get the Service Information Field (SS7 protocol number)
         * @return SIF value used in matching and sending MSUs
         */
        inline unsigned char sif() const
    	{ return m_sio & 0x0f; }

        /**
         * Get the default sending Service Switching Function bits for this protocol
         * @return Combined Priority and Network Indicator bits
         */
        inline unsigned char ssf() const
    	{ return m_sio & 0xf0; }

        /**
         * Get the default sending Priority bits for this protocol
         * @return Priority bits
         */
        inline unsigned char prio() const
    	{ return m_sio & 0x30; }

        /**
         * Get the default sending Network Indicator bits for this protocol
         * @return Network Indicator bits
         */
        inline unsigned char ni() const
    	{ return m_sio & 0xc0; }

        /**
         * Get a SIO value from a parameters list
         * @param params Parameter list to retrieve "service", "priority" and "netindicator"
         * @param sif Default Service Information Field to apply parameters to
         * @param prio Default Priority Field to apply parameters to
         * @param ni Default Network Indicator Field to apply parameters to
         * @return Adjusted SIO value
         */
        static unsigned char getSIO(const NamedList& params, unsigned char sif, unsigned char prio, unsigned char ni);

        /**
         * Get a SIO value from a parameters list
         * @param params Parameter list to retrieve "service", "priority" and "netindicator"
         * @param sif Default Service Information Field to apply parameters to
         * @param ssf Default Subservice Field to apply parameters to
         * @return Adjusted SIO value
         */
        static inline unsigned char getSIO(const NamedList& params, unsigned char sif, unsigned char ssf)
    	{ return getSIO(params,sif,ssf & 0x30,ssf & 0xc0); }

        /**
         * Get a SIO value from a parameters list
         * @param params Parameter list to retrieve "service", "priority" and "netindicator"
         * @param sio Default SIO to apply parameters to
         * @return Adjusted SIO value
         */
        static inline unsigned char getSIO(const NamedList& params, unsigned char sio)
    	{ return getSIO(params,sio & 0x0f,sio & 0x30,sio & 0xc0); }

        /**
         * Get a SIO value from a parameters list
         * @param params Parameter list to retrieve "service", "priority" and "netindicator"
         * @return Adjusted SIO value
         */
        inline unsigned char getSIO(const NamedList& params) const
    	{ return getSIO(params,m_sio); }

    protected:
        /**
         * Constructor
         * @param sio Default value of Service Information Octet
         * @param params Optional parameters to alter the value of SIO
         */
        SS7Layer4(unsigned char sio = SS7MSU::National, const NamedList* params = 0);

        /**
         * Ask the Layer 3 to push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the MSU to use in routing
         * @param sls Signalling Link Selection, negative to choose best
         * @return Link the message was successfully queued to, negative for error
         */
        inline int transmitMSU(const SS7MSU& msu, const SS7Label& label, int sls = -1)
        {
    	m_l3Mutex.lock();
    	RefPointer tmp = m_layer3;
    	m_l3Mutex.unlock();
    	return tmp ? tmp->transmitMSU(msu,label,sls) : -1;
        }

        /**
         * Service Information Octet (SIO) for this protocol
         */
        unsigned char m_sio;

    private:
        Mutex m_l3Mutex;                     // Lock pointer use operations
        SS7Layer3* m_layer3;
    };

    /**
     * A message router between Transfer and Application layers.
     *  Messages are distributed according to the service type.
     * @short Main router for SS7 message transfer and applications
     */
    class YSIG_API SS7Router : public SS7L3User, public SS7Layer3, public Mutex
    {
        YCLASS2(SS7Router,SS7L3User,SS7Layer3)
    public:
        /**
         * Control primitives
         */
        enum Operation {
    	// stop MTP operation, disable the router
    	Pause     = 0x100,
    	// start router, restart MTP if needed
    	Resume    = 0x200,
    	// forcibly execute MTP restart
    	Restart   = 0x300,
    	// get operational status
    	Status    = 0x400,
    	// forcibly advertise availability to adjacent routes
    	Traffic   = 0x500,
    	// forcibly advertise available routes
    	Advertise = 0x600,
        };

        /**
         * Default constructor
         * @param params The list with the parameters
         */
        SS7Router(const NamedList& params);

        /**
         * Destructor
         */
        virtual ~SS7Router();

        /**
         * Configure and initialize the router, maintenance and management
         * @param config Optional configuration parameters override
         * @return True if the router was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the MSU to use in routing
         * @param sls Signalling Link Selection, negative to choose best
         * @return Link the message was successfully queued to, negative for error
         */
        virtual int transmitMSU(const SS7MSU& msu, const SS7Label& label, int sls = -1);

        /**
         * Check if the router is fully operational
         * @param sls Signalling Link to check, negative to check if any is operational
         * @return True if the router is enabled and operational
         */
        virtual bool operational(int sls = -1) const;

        /**
         * Initiate a MTP restart procedure
         * @return True if a restart was initiated
         */
        virtual bool restart();

        /**
         * Attach a SS7 Layer 3 (network) to the router. Attach the router to the given network
         * @param network Pointer to network to attach
         */
        virtual void attach(SS7Layer3* network);

        /**
         * Detach a SS7 Layer 3 (network) from the router. Detach the router from the given network
         * @param network Pointer to network to detach
         */
        virtual void detach(SS7Layer3* network);

        /**
         * Attach a SS7 Layer 4 (service) to the router. Attach itself to the service
         * @param service Pointer to service to attach
         */
        void attach(SS7Layer4* service);

        /**
         * Detach a SS7 Layer 4 (service) from the router. Detach itself from the service
         * @param service Pointer to service to detach
         */
        void detach(SS7Layer4* service);

        /**
         * Management request uninhibiting a signaling link
         * @param network SS7 Layer 3 owning the link to uninhibit
         * @param sls Signalink Link Selection
         * @param remote True to uninhibit the remote side of the link
         * @return True if an uninhibition request was sent
         */
        bool uninhibit(SS7Layer3* network, int sls, bool remote);

        /**
         * Set and clear inhibition flags on a link of an attached network
         * @param link Signalling Link to modify identified by a routing label
         * @param setFlags Flag bits to set ORed together
         * @param clrFlags Flag bits to clear  ORed together (optional)
         * @param notLast Do not apply inhibition to the last usable link
         * @return True if inhibition flags were set
         */
        bool inhibit(const SS7Label& link, int setFlags, int clrFlags = 0, bool notLast = false);

        /**
         * Check inhibition flags on a link of a router attached network
         * @param link Signalling Link to check identified by a routing label
         * @param flags Flag bits to check ORed together
         * @return True if any of the specified inhibition flags are set
         */
        bool inhibited(const SS7Label& link, int flags);

        /**
         * Get the sequence number of the last MSU received on a link
         * @param link Routing label identifying the link to retrieve the sequence from
         * @return Last FSN received, negative if not available
         */
        int getSequence(const SS7Label& link);

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param link Routing label identifying the link to recover MSUs
         * @param sequence First sequence number to recover, flush earlier packets
         */
        void recoverMSU(const SS7Label& link, int sequence);

        /**
         * Notification for receiving User Part Unavailable
         * @param type Type of Point Code
         * @param node Node on which the User Part is unavailable
         * @param part User Part (service) reported unavailable
         * @param cause Unavailability cause - Q.704 15.17.5
         * @param label Routing label of the UPU message
         * @param sls Signaling link the UPU was received on
         */
        virtual void receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
    	SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls);

        /**
         * Check if the transfer function and STP management is enabled
         * @return True if acting as a full STP
         */
        inline bool transfer() const
    	{ return m_transfer; }

        /**
         * Check if the messages are transferred even if STP management may be disabled
         * @return True if messages are transferred between networks
         */
        inline bool transferring() const
    	{ return m_transfer || m_transferSilent; }

        /**
         * Check if the MTP is restarting
         * @return True if MTP restart procedure is in progress
         */
        inline bool starting() const
    	{ return !m_started; }

        /**
         * Get access to the Management component if available
         * @return A pointer to the SS7Management or NULL if not created
         */
        inline SS7Management* getManagement() const
    	{ return m_mngmt; }

        /**
         * Get the Network Indicator bits that would match a Point Code type
         * @param pcType Point Code type to search for
         * @param defNI Default Network Indicator bits to use
         * @return Network Indicator bits matching the Point Code type
         */
        virtual unsigned char getNI(SS7PointCode::Type pcType, unsigned char defNI) const;

        /**
         * Retrieve the default local Point Code for a specific Point Code type
         * @param type Desired Point Code type
         * @return Packed local Point Code, zero if not set
         */
        virtual unsigned int getDefaultLocal(SS7PointCode::Type type) const;

    protected:
        /**
         * Reset state of all routes of a network to Unknown
         */
        void clearView(const SS7Layer3* network);

        /**
         * Get the state of a route as seen from another network or adjacent point code
         * @param type Point Code type to search for
         * @param packedPC The packed point code whose state is viewed
         * @param remotePC The point code of an adjacent viewer, its network is skipped
         * @param network The network that will be not included in the view
         * @return State of the route as viewed from the specified point code or network
         */
        SS7Route::State getRouteView(SS7PointCode::Type type, unsigned int packedPC,
            unsigned int remotePC = 0, const SS7Layer3* network = 0);

        /**
         * Set the current state of a route by packed Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param packedPC The packed point code
         * @param state The new state of the route
         * @param remotePC The point code that caused the route change
         * @param network The network that caused the route change
         * @return True if the route was found and its state changed
         */
        bool setRouteState(SS7PointCode::Type type, unsigned int packedPC, SS7Route::State state,
            unsigned int remotePC = 0, const SS7Layer3* network = 0);

        /**
         * Set the current state of a route by unpacked Point Code.
         * This method is thread safe
         * @param type Destination point code type
         * @param dest The destination point code
         * @param state The new state of the route
         * @param remotePC The point code that caused the route change
         * @param network The network that caused the route change
         * @return True if the route was found and its state changed
         */
        inline bool setRouteState(SS7PointCode::Type type, const SS7PointCode& dest, SS7Route::State state,
            unsigned int remotePC = 0, const SS7Layer3* network = 0)
    	{ return setRouteState(type,dest.pack(type),state,remotePC,network); }

        /**
         * Load the default local Point Codes from a list of parameters
         * @param params List of parameters to load "local=" entries from
         */
        void loadLocalPC(const NamedList& params);

        /**
         * Periodical timer tick used to perform state transition and housekeeping
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a MSU received from the Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Add a network to the routing table. Clear all its routes before appending it to the table
         * This method is thread safe
         * @param network The network to add to the routing table
         */
        void updateRoutes(SS7Layer3* network);

        /**
         * Remove the given network from all destinations in the routing table.
         * Remove the entry in the routing table if empty (no more routes to the point code).
         * This method is thread safe
         * @param network The network to remove
         */
        void removeRoutes(SS7Layer3* network);

        /**
         * Trigger the route changed notification for each route that is not Unknown
         * @param states Mask of required states of the route
         * @param onlyPC The only point code that should receive notifications
         */
        void notifyRoutes(SS7Route::State states = SS7Route::AnyState, unsigned int onlyPC = 0);

        /**
         * Trigger the route changed notification for each route that is not Unknown
         * @param states Mask of required states of the route
         * @param network Notify to adjacent nodes of this network
         */
        void notifyRoutes(SS7Route::State states, const SS7Layer3* network);

        /**
         * Notification callback when a route state changed to other than Unknown.
         * This method is called with the route mutex locked
         * @param route Pointer to the route whose state has changed
         * @param type Type of the pointcode of the route
         * @param remotePC The point code that caused the route change
         * @param network The network that caused the route change
         * @param onlyPC If set only advertise to this point code
         * @param forced Notify even if the route view didn't change
         */
        virtual void routeChanged(const SS7Route* route, SS7PointCode::Type type,
            unsigned int remotePC = 0, const SS7Layer3* network = 0,
            unsigned int onlyPC = 0, bool forced = false);

        /**
         * Process a notification generated by the attached network layer
         * @param network Network or linkset that generated the notification
         * @param sls Signallink Link that generated the notification, negative if none
         * @return True if notification was processed
         */
        virtual void notify(SS7Layer3* network, int sls);

        /**
         * Query or modify the management settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Detach management
         */
        virtual void destroyed();

        /** List of L3 (networks) attached to this router */
        ObjList m_layer3;
        /** List of L4 (services) attached to this router */
        ObjList m_layer4;
        /** Counter used to spot changes in the lists of L3 or L4 */
        int m_changes;
        /** Locally unhandled MSUs are to be routed to other networks */
        bool m_transfer;
        /** STP phase 2 of restart procedure in effect */
        bool m_phase2;
        /** MTP restart procedure has completed */
        bool m_started;
        /** MTP overall restart timer T20 */
        SignallingTimer m_restart;
        /** MTP isolation timer T1 */
        SignallingTimer m_isolate;

    private:
        void restart2();
        void disable();
        void sendRestart(const SS7Layer3* network = 0);
        void sendRestart(SS7PointCode::Type type, unsigned int packedPC);
        void silentAllow(const SS7Layer3* network = 0);
        void silentAllow(SS7PointCode::Type type, unsigned int packedPC);
        void checkRoutes(const SS7Layer3* noResume = 0);
        void clearRoutes(SS7Layer3* network, bool ok);
        void reroute(const SS7Layer3* network);
        void rerouteCheck(const Time& when);
        void rerouteFlush();
        bool setRouteSpecificState(SS7PointCode::Type type, unsigned int packedPC,
    	unsigned int srcPC, SS7Route::State state, const SS7Layer3* changer = 0);
        inline bool setRouteSpecificState(SS7PointCode::Type type, const SS7PointCode& dest,
    	const SS7PointCode&src, SS7Route::State state, const SS7Layer3* changer = 0)
    	{ return setRouteSpecificState(type,dest.pack(type),src.pack(type),state,changer); }
        void sendRouteTest();
        int routeMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls, SS7Route::State states);
        void buildView(SS7PointCode::Type type, ObjList& view, SS7Layer3* network);
        void buildViews();
        void printStats();
        Mutex m_statsMutex;
        SignallingTimer m_trafficOk;
        SignallingTimer m_trafficSent;
        SignallingTimer m_routeTest;
        bool m_testRestricted;
        bool m_transferSilent;
        bool m_checkRoutes;
        bool m_autoAllowed;
        bool m_sendUnavail;
        bool m_sendProhibited;
        unsigned long m_rxMsu;
        unsigned long m_txMsu;
        unsigned long m_fwdMsu;
        unsigned long m_failMsu;
        unsigned long m_congestions;
        SS7Management* m_mngmt;
    };

    /**
     * RFC4165 SS7 Layer 2 implementation over SCTP/IP.
     * M2PA is intended to be used as a symmetrical Peer-to-Peer replacement of
     *  a hardware based SS7 data link.
     * @short SIGTRAN MTP2 User Peer-to-Peer Adaptation Layer
     */
    class YSIG_API SS7M2PA : public SS7Layer2, public SIGTRAN
    {
        YCLASS(SS7M2PA,SS7Layer2)
    public:
        enum m2paState {
    	Alignment = 1,
    	ProvingNormal = 2,
    	ProvingEmergency = 3,
    	Ready = 4,
    	ProcessorOutage = 5,
    	ProcessorRecovered = 6,
    	Busy = 7,
    	BusyEnded = 8,
    	OutOfService = 9,
        };

        enum msgType {
    	UserData = 1,
    	LinkStatus = 2
        };

        enum sctpState {
    	Idle,
    	Associating,
    	Established
        };

        enum M2PAOperations {
    	Pause        = SS7Layer2::Pause,
    	// start link operation, align if it needs to
    	Resume       = SS7Layer2::Resume,
    	// start link, force realignment
    	Align        = SS7Layer2::Align,
    	// get operational status
    	Status       = SS7Layer2::Status,
    	// restart transport layer
    	TransRestart = 0x500
        };

        /**
         * Constructor
         */
        SS7M2PA(const NamedList& params);

        /**
         * Destructor
         */
        ~SS7M2PA();

        /**
         * Configure and initialize M2PA and its transport
         * @param config Optional configuration parameters override
         * @return True if M2PA and the transport were initialized properly
         */
        virtual bool initialize(const NamedList* config);

         /**
         * Query or modify layer's settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Execute a control operation. Operations can change the link status or
         *  can query the aligned status.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the data link is aligned and operational
         */
        virtual bool control(M2PAOperations oper, NamedList* params = 0);

        /**
         * Execute a control operation. Operations can change the link status or
         *  can query the aligned status.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the data link is aligned and operational
         */
        virtual bool control(SS7Layer2::Operation oper, NamedList* params = 0)
    	{ return control((M2PAOperations)oper,params); }

        /**
         * Retrieve the current link status indications
         * @return Link status indication bits
         */
        virtual unsigned int status() const;

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu MSU data to transmit
         * @return True if message was successfully queued
         */
        virtual bool transmitMSU(const SS7MSU& msu);

        /**
         * Method called when the transport status has been changed
         * @param status Up or down
         */
        virtual void notifyLayer(SignallingInterface::Notification status);

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sequence);

        /**
         * Decode sequence numbers from message and process them
         * @param data The message
         * @param msgType The message type
         * @return True if sequence numbers ar as we expected to be
         */
        bool decodeSeq(const DataBlock& data, u_int8_t msgType);

        /**
         * Helper method called when an error was detected
         * Change state to OutOfService and notifys upper layer
         * @param info Debuging purpose, Information about detected error
         */
        void abortAlignment(const char* info = 0);

        /**
         * Send link status message to inform the peer about ouer curent state
         * @param streamId The id of the stream who should send the message
         */
        void transmitLS(int streamId = 0);

        /**
         * Create M2PA header (sequence numbers)
         * @param data The data where the header will be stored
         */
        void setHeader(DataBlock& data);

        /**
         * Decode and process link status message
         * @param data The message
         * @param streamId The stream id witch received the message
         * @return True if the message was procesed
         */
        bool processLinkStatus(DataBlock& data, int streamId);

        /**
         * Decode and process link status message in more strict manner
         * @param data The message
         * @param streamId The stream id witch received the message
         * @return True if the message was procesed
         */
        bool processSLinkStatus(DataBlock& data, int streamId);

        /**
         * Helper method used to acknowledge the last received message
         * when no data are to transmit
         */
        void sendAck();

        /**
         * Remove a frame from acknowledgement list
         * @param bsn The sequence number of the frame to be removed
         * @return True if the frame was found and removed.
         */
        bool removeFrame(u_int32_t bsn);

        /**
         * Check if a sequence number may be a valid next BSN
         * @param bsn Backward Sequence Number to check
         * @return True if the provided BSN is in the valid range
         */
        bool nextBsn(u_int32_t bsn) const;

        /**
         * Increment the given sequence number
         * @param nr Reference of the number to increment
         * @return The incremented number
         */
        static inline u_int32_t increment(u_int32_t& nr)
    	{ return (nr == 0xffffff) ? (nr = 0) : nr++; }

        /**
         * Obtain next sequence number
         * @param nr The current sequence number
         * @return The next number in sequence
         */
        static inline u_int32_t getNext(u_int32_t nr)
    	{ return (nr == 0xffffff) ? 0 : nr + 1; }

    protected:

        /**
         * Periodical timer tick used to perform alignment and housekeeping
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Check if the link is aligned.
         * The link may not be operational, the other side may be still proving.
         * @return True if the link is aligned
         */
        virtual bool aligned() const;

        /**
         * Check if the link is aligned and operational
         * @return True if the link is operational
         */
        virtual bool operational() const;

        /**
         * Process a complete message
         * @param msgVersion Version of the protocol
         * @param msgClass Class of the message
         * @param msgType Type of the message, depends on the class
         * @param msg Message data, may be empty
         * @param streamId Identifier of the stream the message was received on
         * @return True if the message was handled
         */
        virtual bool processMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId);

        /**
         * Initiates alignment and proving procedure
         * @param emergency True if emergency alignment is desired
         */
        void startAlignment(bool emergency = false);

        /**
         * Retransmit unacknowledged data
         */
         void retransData();

         virtual void destroyed();
    private:
        void dumpMsg(u_int8_t version, u_int8_t mClass, u_int8_t type,
    	const DataBlock& data, int stream, bool send);
        void setLocalStatus(unsigned int status);
        void setRemoteStatus(unsigned int status);
        u_int32_t m_seqNr;
        u_int32_t m_needToAck;
        u_int32_t m_lastAck;
        u_int32_t m_confCounter;
        u_int32_t m_maxUnack;
        u_int32_t m_maxQueueSize;
        unsigned int m_localStatus;
        unsigned int m_state;
        unsigned int m_remoteStatus;
        unsigned int m_transportState;
        unsigned int m_connFailCounter;
        unsigned int m_connFailThreshold;
        Mutex m_mutex;
        ObjList m_ackList;
        SignallingTimer m_t1;
        SignallingTimer m_t2;
        SignallingTimer m_t3;
        SignallingTimer m_t4;
        SignallingTimer m_ackTimer;
        SignallingTimer m_confTimer;
        SignallingTimer m_oosTimer;
        SignallingTimer m_waitOosTimer;
        SignallingTimer m_connFailTimer;
        bool m_autostart;
        bool m_sequenced;
        bool m_dumpMsg;
    };

    /**
     * The common client side of SIGTRAN SS7 MTP2 User Adaptation (RFC3331)
     * @short Client side of SIGTRAN SS7 MTP2 UA
     */
    class YSIG_API SS7M2UAClient : public SIGAdaptClient
    {
        YCLASS(SS7M2UAClient,SIGAdaptClient)
    public:
        /**
         * Contructor of an empty IUA client
         */
        inline SS7M2UAClient(const NamedList& params)
    	: SIGAdaptClient(params.safe("SS7M2UAClient"),¶ms,2,2904)
    	{ }
        virtual bool processMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId);
    };

    /**
     * RFC3331 SS7 Layer 2 implementation over SCTP/IP.
     * M2UA is intended to be used as a Provider-User where real MTP2 runs on a
     *  Signalling Gateway and MTP3 runs on an Application Server.
     * @short SIGTRAN MTP2 User Adaptation Layer
     */
    class YSIG_API SS7M2UA : public SS7Layer2, public SIGAdaptUser
    {
        friend class SS7M2UAClient;
        YCLASS(SS7M2UA,SS7Layer2)
    public:
        /**
         * Constructor
         * @param params List of construction parameters
         */
        SS7M2UA(const NamedList& params);

        /**
         * Configure and initialize M2UA and its transport
         * @param config Optional configuration parameters override
         * @return True if M2UA and the transport were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Execute a control operation. Operations can change the link status or
         *  can query the aligned status.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the data link is aligned and operational
         */
        virtual bool control(Operation oper, NamedList* params = 0);

        /**
         * Retrieve the current link status indications
         * @return Link status indication bits
         */
        virtual unsigned int status() const;

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @return True if message was successfully queued
         */
        virtual bool transmitMSU(const SS7MSU& msu);

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sequence);

        /**
         * Check if the link is fully operational
         * @return True if the link is aligned and operational
         */
        virtual bool operational() const;

        /**
         * Get the sequence number of the last MSU received, request if not available
         * @return Last FSN received, negative if not available
         */
        virtual int getSequence();

        /**
         * Traffic activity state change notification
         * @param active True if the ASP is active and traffic is allowed
         */
        virtual void activeChange(bool active);

        /**
         * Retrieve the numeric Interface Identifier (if any)
         * @return IID value, -1 if not set
         */
        inline int32_t iid() const
    	{ return m_iid; }

    protected:
        enum LinkState {
    	LinkDown,
    	LinkReq,
    	LinkReqEmg,
    	LinkUp,
    	LinkUpEmg,
        };

        /**
         * Periodical timer tick used to perform alignment and housekeeping
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        SS7M2UAClient* client() const
    	{ return static_cast(adaptation()); }
        virtual bool processMGMT(unsigned char msgType, const DataBlock& msg, int streamId);
        virtual bool processMAUP(unsigned char msgType, const DataBlock& msg, int streamId);
        void postRetrieve();
        SignallingTimer m_retrieve;
        int32_t m_iid;
        int m_linkState;
        bool m_rpo;
        bool m_longSeq;
    };

    /**
     * RFC3332 SS7 Layer 3 implementation over SCTP/IP.
     * M3UA is intended to be used as a Provider-User where real MTP3 runs on a
     *  Signalling Gateway and MTP users are located on an Application Server.
     * @short SIGTRAN MTP3 User Adaptation Layer
     */
    class YSIG_API SS7M3UA : public SS7Layer3, public SIGAdaptUser
    {
        YCLASS(SS7M3UA,SS7Layer3)
    };

    /**
     * Q.703 SS7 Layer 2 (Data Link) implementation on top of a hardware interface
     * @short SS7 Layer 2 implementation on top of a hardware interface
     */
    class YSIG_API SS7MTP2 : public SS7Layer2, public SignallingReceiver, public SignallingDumpable, public Mutex
    {
        YCLASS2(SS7MTP2,SS7Layer2,SignallingReceiver)
    public:
        /**
         * Types of error correction
         */
        enum ErrorCorrection {
    	Basic,       // retransmits only based on sequence numbers
    	Preventive,  // continuously retransmit unacknowledged packets
    	Adaptive,    // switch to using preventive retransmission dynamically
        };

        /**
         * Constructor
         * @param params Layer's parameters
         * @param status Initial status
         */
        SS7MTP2(const NamedList& params, unsigned int status = OutOfService);

        /**
         * Destructor
         */
        virtual ~SS7MTP2();

        /**
         * Configure and initialize MTP2 and its interface
         * @param config Optional configuration parameters override
         * @return True if MTP2 and the interface were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu MSU data to transmit
         * @return True if message was successfully queued
         */
        virtual bool transmitMSU(const SS7MSU& msu);

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sequence);

        /**
         * Retrieve the current link status indications
         * @return Link status indication bits
         */
        virtual unsigned int status() const;

        /**
         * Check if the link is aligned.
         * The link may not be operational, the other side may be still proving.
         * @return True if the link is aligned
         */
        virtual bool aligned() const;

        /**
         * Check if the link is aligned and operational
         * @return True if the link is operational
         */
        virtual bool operational() const;

        /**
         * Execute a control operation. Operations can change the link status or
         *  can query the aligned status.
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the data link is aligned and operational
         */
        virtual bool control(Operation oper, NamedList* params = 0);

        /**
         * Process a notification generated by the attached interface
         * @param event Notification event reported by the interface
         * @return True if notification was processed
         */
        virtual bool notify(SignallingInterface::Notification event);

    protected:
        /**
         * Remove all attachements. Disposes the memory
         */
        virtual void destroyed()
        {
    	SS7Layer2::attach(0);
    	TelEngine::destruct(SignallingReceiver::attach(0));
    	SignallingComponent::destroyed();
        }

        /**
         * Periodical timer tick used to perform alignment and housekeeping
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a Signalling Packet received by the hardware interface
         * @return True if message was successfully processed
         */
        virtual bool receivedPacket(const DataBlock& packet);

        /**
         * Process a received Fill-In Signal Unit
         */
        virtual void processFISU();

        /**
         * Process a received Link Status Signal Unit
         * @param status Link status indications
         */
        virtual void processLSSU(unsigned int status);

        /**
         * Push a Link Status Signal Unit down the protocol stack
         * @param status Link status indications
         * @return True if message was successfully queued
         */
        bool transmitLSSU(unsigned int status);

        /**
         * Push a Link Status Signal Unit with the current status down the protocol stack
         * @return True if message was successfully queued
         */
        inline bool transmitLSSU()
    	{ return transmitLSSU(m_lStatus); }

        /**
         * Push a Fill-In Signal Unit down the protocol stack
         * @return True if message was successfully queued
         */
        bool transmitFISU();

        /**
         * Initiates alignment and proving procedure
         * @param emergency True if emergency alignment is desired
         */
        void startAlignment(bool emergency = false);

        /**
         * Abort an alignment procedure if link errors occur
         * @param retry Keep trying to align
         */
        void abortAlignment(bool retry = true);

        /**
         * Start the link proving period
         * @return True if proving period was started
         */
        bool startProving();

    private:
        virtual bool control(NamedList& params)
    	{ return SignallingDumpable::control(params,this) || SS7Layer2::control(params); }
        void unqueueAck(unsigned char bsn);
        bool txPacket(const DataBlock& packet, bool repeat, SignallingInterface::PacketType type = SignallingInterface::Unknown);
        void setLocalStatus(unsigned int status);
        void setRemoteStatus(unsigned int status);
        // sent but yet unacknowledged packets
        ObjList m_queue;
        // data link status (alignment) - desired, local and remote
        unsigned int m_status, m_lStatus, m_rStatus;
        // various interval period end
        u_int64_t m_interval;
        // time when resending packets
        u_int64_t m_resend;
        // time when aborting resending packets
        u_int64_t m_abort;
        // time when need to transmit next FISU/LSSU
        u_int64_t m_fillTime;
        // remote congestion indicator
        bool m_congestion;
        // backward and forward sequence numbers
        unsigned char m_bsn, m_fsn;
        // backward and forward indicator bits
        bool m_bib, m_fib;
        // last forward sequence number we sent a retransmission request
        unsigned char m_lastFsn;
        // last received backward sequence number
        unsigned char m_lastBsn;
        // last received backward indicator bit
        bool m_lastBib;
        // count of errors
        unsigned int m_errors;
        // maximum accepted errors
        unsigned int m_maxErrors;
        // packet resend interval
        unsigned int m_resendMs;
        // packet resend abort interval
        unsigned int m_abortMs;
        // FISU/LSSU soft resend interval
        unsigned int m_fillIntervalMs;
        // fill link with end-to-end FISU/LSSU
        bool m_fillLink;
        // automatically align on resume
        bool m_autostart;
        // flush MSUs after aligning
        bool m_flushMsus;
    };

    /**
     * Q.704 SS7 Layer 3 (Network) implementation on top of SS7 Layer 2
     * @short SS7 Layer 3 implementation on top of Layer 2
     */
    class YSIG_API SS7MTP3 : public SS7Layer3, public SS7L2User, public SignallingDumpable, public Mutex
    {
        YCLASS(SS7MTP3,SS7Layer3)
    public:
        /**
         * Control primitives
         */
        enum Operation {
    	// take linkset out of service
    	Pause   = 0x100,
    	// start linkset operation
    	Resume  = 0x200,
    	// force a MTP restart procedure
    	Restart = 0x300,
    	// get operational status
    	Status  = 0x400,
        };

        /**
         * Constructor
         * @param params Layer's parameters
         */
        SS7MTP3(const NamedList& params);

        /**
         * Destructor
         */
        virtual ~SS7MTP3();

        /**
         * Configure and initialize the MTP3 and all its links
         * @param config Optional configuration parameters override
         * @return True if MTP3 and at least one link were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Push a Message Signal Unit down the protocol stack
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the MSU used in routing
         * @param sls Signalling Link Selection, negative to choose best
         * @return Link the message was successfully queued to, negative for error
         */
        virtual int transmitMSU(const SS7MSU& msu, const SS7Label& label, int sls = -1);

        /**
         * Check if the network/linkset is fully operational
         * @param sls Signalling Link to check, negative to check if any is operational
         * @return True if the linkset is enabled and operational
         */
        virtual bool operational(int sls = -1) const;

        /**
         * Retrieve inhibition flags of a specific link
         * @param sls Signalling Link to check
         * @return Inhibitions of the specified link, zero if not inhibited
         */
        virtual int inhibited(int sls) const;

        /**
         * Set and clear inhibition flags on the links
         * @param sls Signalling Link to modify
         * @param setFlags Flag bits to set ORed together
         * @param clrFlags Flag bits to clear  ORed together (optional)
         * @return True if inhibition flags were set
         */
        virtual bool inhibit(int sls, int setFlags, int clrFlags = 0);

        /**
         * Get the current congestion level of a link
         * @param sls Signalling Link to check for congestion, -1 for maximum
         * @return Congestion level, 0 if not congested, 3 if maximum congestion
         */
        virtual unsigned int congestion(int sls);

        /**
         * Get the sequence number of the last MSU received on a link
         * @param sls Signalling Link to retrieve MSU number from
         * @return Last FSN received, negative if not available
         */
        virtual int getSequence(int sls) const;

        /**
         * Remove the MSUs waiting in the transmit queue and return them
         * @param sls Signalling Link to recover MSUs from
         * @param sequence First sequence number to recover, flush earlier packets
         */
        virtual void recoverMSU(int sls, int sequence);

        /**
         * Execute a control operation on the linkset
         * @param oper Operation to execute
         * @param params Optional parameters for the operation
         * @return True if the command completed successfully, for query operations
         *  also indicates the linkset is enabled and operational
         */
        virtual bool control(Operation oper, NamedList* params = 0);

        /**
         * Attach a SS7 Layer 2 (data link) to the network transport. Attach itself to the link
         * @param link Pointer to data link to attach
         */
        virtual void attach(SS7Layer2* link);

        /**
         * Detach a SS7 Layer 2 (data link) from the network transport. Remove the link's L2 user
         * @param link Pointer to data link to detach
         */
        virtual void detach(SS7Layer2* link);

        /**
         * Query or modify layer's settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Check if access to a specific Point Code is allowed from this network
         * @param type Destination point code type
         * @param packedPC The destination point code
         * @return True if access to the specified Point Code is allowed
         */
        virtual bool allowedTo(SS7PointCode::Type type, unsigned int packedPC) const;

        /**
         * Get the total number of links attached
         * @return Number of attached data links
         */
        inline unsigned int linksTotal() const
    	{ return m_total; }

        /**
         * Get the number of links that are checked by maintenance
         * @return Number of MTN checked data links
         */
        inline unsigned int linksChecked() const
    	{ return m_checked; }

        /**
         * Get the number of links that are currently operational
         * @return Number of operational data links
         */
        inline unsigned int linksActive() const
    	{ return m_active; }

        /**
         * Get a list of the links held by this linkset
         * @return A list containing the links
         */
        inline const ObjList* links() const
    	{ return &m_links; }

    protected:
        /**
         * Detach all links and user. Destroys the object, disposes the memory
         */
        virtual void destroyed();

        /**
         * Periodical timer tick used to perform housekeeping and link checking
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Callback called from maintenance when valid SLTA or SLTM are received
         * @param sls Link that was checked by maintenance
         * @param remote True if remote checked the link, false if local success
         */
        virtual void linkChecked(int sls, bool remote);

        /**
         * Check if we should answer with SLTA to received SLTM in maintenance()
         * @return True to send a SLTA for each good received SLTM
         */
        virtual bool responder() const
    	{ return !m_inhibit; }

        /**
         * Process a MSU received from the Layer 2 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param link Data link that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the MSU was processed
         */
        virtual bool receivedMSU(const SS7MSU& msu, SS7Layer2* link, int sls);

        /**
         * Process a MSU recovered from the Layer 2 component after failure
         * @param msu Message data, starting with Service Indicator Octet
         * @param link Data link from where the MSU was recovered
         * @param sls Signalling Link the MSU was recovered from
         * @return True if the MSU was processed
         */
        virtual bool recoveredMSU(const SS7MSU& msu, SS7Layer2* link, int sls);

        /**
         * Process a notification generated by the attached data link
         * @param link Data link that generated the notification
         * @return True if notification was processed
         */
        virtual void notify(SS7Layer2* link);

        /**
         * Count the total and active number of links
         * @return Number of active links
         */
        unsigned int countLinks();

    private:
        ObjList m_links;
        // total links in linkset
        unsigned int m_total;
        // checked links in linkset
        unsigned int m_checked;
        // currently active links
        unsigned int m_active;
        // SLS to SLC shift
        bool m_slcShift;
        // inhibited flag
        bool m_inhibit;
        // warn if all links are down
        bool m_warnDown;
        // check the links before placing them in service
        bool m_checklinks;
        // realign links that don't respond to test messages anymore
        bool m_forcealign;
        // maintenance check intervals (Q.707)
        u_int64_t m_checkT1;
        u_int64_t m_checkT2;
        // list of allowed point codes seen from this network
        unsigned int* m_allowed[YSS7_PCTYPE_COUNT];
    };

    /**
     * Decoded Signalling Network Management (SNM) User Part message
     * @short SNM signalling message
     */
    class YSIG_API SS7MsgSNM : public SignallingMessage
    {
    public:
        /**
         * SNM Message type as defined by Q.704 Table 1
         */
        enum Type {
    	Unknown = 0,
    	COO  = 0x11, // Changeover Order signal
    	ECO  = 0x12, // Emergency Changeover Order signal
    	RCT  = 0x13, // Route Set Congestion Test signal
    	TFP  = 0x14, // Transfer Prohibited signal
    	RST  = 0x15, // Route Set Test for prohibited destination
    	RSP  = RST,  // Route Set Test for prohibited destination (ANSI)
    	LIN  = 0x16, // Link Inhibit signal
    	TRA  = 0x17, // Traffic Restart Allowed signal
    	DLC  = 0x18, // Data Link Connection Order signal
    	UPU  = 0x1a, // User Part Unavailable signal
    	COA  = 0x21, // Changeover Acknowledgment signal
    	ECA  = 0x22, // Emergency Changeover Acknowledgment signal
    	TFC  = 0x23, // Transfer Controlled signal
    	TCP  = 0x24, // Transfer Cluster Prohibited
    	TFPA = TCP,  // Transfer Prohibited Acknowledgment (Yellow Book only)
    	RSR  = 0x25, // Route Set Test for prohibited destination (national use)
    	LUN  = 0x26, // Link Uninhibit signal
    	TRW  = 0x27, // Traffic Restart Waiting (ANSI only)
    	CSS  = 0x28, // Connection Successful signal
    	XCO  = 0x31, // Extended Changeover Order signal
    	TFR  = 0x34, // Transfer Restricted signal (national use)
    	RCP  = 0x35, // Route Set Test for cluster-prohibited
    	LIA  = 0x36, // Link Inhibit Acknowledgment signal
    	CNS  = 0x38, // Connection Not Successful signal
    	XCA  = 0x41, // Extended Changeover Acknowledgment signal
    	TCR  = 0x44, // Transfer Cluster Restricted signal (ANSI only)
    	RCR  = 0x45, // Route Set Test for cluster-restricted (ANSI only)
    	LUA  = 0x46, // Link Uninhibit Acknowledgment signal
    	CNP  = 0x48, // Connection Not Possible signal
    	CBD  = 0x51, // Changeback Declaration signal
    	TFA  = 0x54, // Transfer Allowed signal
    	LID  = 0x56, // Link Inhibit Denied signal
    	CBA  = 0x61, // Changeback Acknowledgment signal
    	TCA  = 0x64, // Transfer Cluster Allowed
    	TFAA = TCA,  // Transfer Allowed Acknowledgment (Yellow Book only)
    	LFU  = 0x66, // Link Forced Uninhibit signal
    	LLT  = 0x76, // Link Local Inhibit Test signal
    	LLI  = LLT,  // Link Local Inhibit Test signal (ANSI)
    	LRT  = 0x86, // Link Remote Inhibit Test signal
    	LRI  = LRT,  // Link Remote Inhibit Test signal (ANSI)
        };

        /**
         * SNM Message group (H0) as defined by Q.704 15.3
         */
        enum Group {
    	CHM = 0x01,  // Changeover and changeback
    	ECM = 0x02,  // Emergency changeover
    	FCM = 0x03,  // Transfer controlled and signalling route set congestion
    	TFM = 0x04,  // Transfer prohibited/allowed/restricted
    	RSM = 0x05,  // Signalling route/set/test
    	MIM = 0x06,  // Management inhibit
    	TRM = 0x07,  // Traffic restart allowed
    	DLM = 0x08,  // Signalling data/link/connection
    	UFC = 0x0a,  // User part flow control
        };

        /**
         * Constructor
         * @param type Message type
         */
        SS7MsgSNM(unsigned char type);

        /**
         * Get the type of this message
         * @return The type of this message
         */
        inline unsigned char type() const
    	{ return m_type; }

        /**
         * Get the group this message belongs to
         * @return This message's group
         */
        inline unsigned char group() const
    	{ return m_type & 0x0f; }

        /**
         * Fill a string with this message's parameters for debug purposes
         * @param dest The destination string
         * @param label The routing label
         * @param params True to add parameters
         */
        void toString(String& dest, const SS7Label& label, bool params) const;

        /**
         * Parse a received buffer and build a message from it
         * @param receiver The SS7 management entity that received the MSU
         * @param type Message type
         * @param pcType The point code type contained in received MSU's label
         * @param buf Buffer after message head
         * @param len Buffer length
         * @return Valid message pointer of 0 on failure
         */
        static SS7MsgSNM* parse(SS7Management* receiver, unsigned char type,
    	SS7PointCode::Type pcType,
    	const unsigned char* buf, unsigned int len);

        /**
         * Get the dictionary containing the names of the message type
         */
        static const TokenDict* names();

        /**
         * Convert a SNM message type to a C string
         * @param type Type of SNM message to look up
         * @param defvalue Default string to return
         * @return Name of the SNM message type
         */
        static inline const char* lookup(Type type, const char* defvalue = 0)
    	{ return TelEngine::lookup(type,names(),defvalue); }

        /**
         * Look up a SNM message name
         * @param name String name of the SNM message
         * @param defvalue Default type to return
         * @return Encoded type of the SNM message
         */
        static inline Type lookup(const char* name, Type defvalue = Unknown)
    	{ return static_cast(TelEngine::lookup(name,names(),defvalue)); }

    private:
        unsigned char m_type;
    };

    /**
     * Decoded Maintenance (MTN) User Part message
     * @short MTN signalling message
     */
    class YSIG_API SS7MsgMTN
    {
    public:
        /**
         * MTN Message type as defined by Q.707 5.4
         */
        enum Type {
    	Unknown = 0,
    	SLTM = 0x11, // Signalling Link Test Message
    	SLTA = 0x21, // Signalling Link Test Acknowledgment
        };

        static const TokenDict* names();

        /**
         * Convert a MTN message type to a C string
         * @param type Type of MTN message to look up
         * @param defvalue Default string to return
         * @return Name of the MTN message type
         */
        static inline const char* lookup(Type type, const char* defvalue = 0)
    	{ return TelEngine::lookup(type,names(),defvalue); }

        /**
         * Look up a MTN message name
         * @param name String name of the MTN message
         * @param defvalue Default type to return
         * @return Encoded type of the MTN message
         */
        static inline Type lookup(const char* name, Type defvalue = Unknown)
    	{ return static_cast(TelEngine::lookup(name,names(),defvalue)); }
    };

    /**
     * Decoded ISDN User Part message
     * @short ISUP signalling message
     */
    class YSIG_API SS7MsgISUP : public SignallingMessage
    {
        YCLASS(SS7MsgISUP,SignallingMessage)
        friend class SS7ISUPCall;
    public:
        /**
         * ISUP Message type as defined by Q.762 Table 2 and Q.763 Table 4
         */
        enum Type {
    	Unknown = 0,
    	IAM  = 0x01, // Initial Address Message
    	SAM  = 0x02, // Subsequent Address Message
    	INR  = 0x03, // Information Request (national use)
    	INF  = 0x04, // Information (national use)
    	COT  = 0x05, // Continuity
    	ACM  = 0x06, // Address Complete Message
    	CON  = 0x07, // Connect
    	FOT  = 0x08, // Forward Transfer
    	ANM  = 0x09, // Answer Message
    	REL  = 0x0c, // Release Request
    	SUS  = 0x0d, // Suspend
    	RES  = 0x0e, // Resume
    	RLC  = 0x10, // Release Complete
    	CCR  = 0x11, // Continuity Check Request
    	RSC  = 0x12, // Reset Circuit
    	BLK  = 0x13, // Blocking
    	UBL  = 0x14, // Unblocking
    	BLA  = 0x15, // Blocking Acknowledgement
    	UBA  = 0x16, // Unblocking Acknowledgement
    	GRS  = 0x17, // Circuit Group Reset
    	CGB  = 0x18, // Circuit Group Blocking
    	CGU  = 0x19, // Circuit Group Unblocking
    	CGA  = 0x1a, // Circuit Group Blocking Acknowledgement
    	CGBA = CGA,
    	CUA  = 0x1b, // Circuit Group Unblocking Acknowledgement
    	CMR  = 0x1c, // Call Modification Request (ANSI only)
    	CMC  = 0x1d, // Call Modification Completed (ANSI only)
    	CMRJ = 0x1e, // Call Modification Rejected (ANSI only)
    	FACR = 0x1f, // Facility Request
    	FAA  = 0x20, // Facility Accepted
    	FRJ  = 0x21, // Facility Reject
    	FAD  = 0x22, // Facility Deactivated (ANSI only)
    	FAI  = 0x23, // Facility Information (ANSI only)
    	LPA  = 0x24, // Loopback Acknowledgement (national use)
    	CSVR = 0x25, // CUG Selection and Validation Request (ANSI only)
    	CSVS = 0x26, // CUG Selection and Validation Response (ANSI only)
    	DRS  = 0x27, // Delayed Release (ANSI only)
    	PAM  = 0x28, // Pass Along Message (national use)
    	GRA  = 0x29, // Circuit Group Reset Acknowledgement
    	CQM  = 0x2a, // Circuit Group Query (national use)
    	CQR  = 0x2b, // Circuit Group Query Response (national use)
    	CPR  = 0x2c, // Call Progress
    	CPG  = CPR,
    	USR  = 0x2d, // User-to-User Information
    	UEC  = 0x2e, // Unequipped CIC (national use)
    	UCIC = UEC,
    	CNF  = 0x2f, // Confusion
    	OLM  = 0x30, // Overload Message (national use)
    	CRG  = 0x31, // Charge Information (national use and format, ITU only)
    	NRM  = 0x32, // Network Resource Management
    	FAC  = 0x33, // Facility (national use)
    	UPT  = 0x34, // User Part Test
    	UPA  = 0x35, // User Part Available
    	IDR  = 0x36, // Identification Request (ITU only)
    	IRS  = 0x37, // Identification Response (ITU only)
    	SGM  = 0x38, // Segmentation
    	LOP  = 0x40, // Loop Prevention
    	APM  = 0x41, // Application Transport
    	PRI  = 0x42, // Pre-Release Information
    	SDN  = 0x43, // Subsequent Directory Number (national use)
    	CRA  = 0xe9, // Circuit Reservation Acknowledgement (ANSI only)
    	CRM  = 0xea, // Circuit Reservation (ANSI only)
    	CVR  = 0xeb, // Circuit Validation Response (ANSI only)
    	CVT  = 0xec, // Circuit Validation Test (ANSI only)
    	EXM  = 0xed, // Exit Message (ANSI only)
    	// Dummy, used for various purposes
    	CtrlSave = 256, // control, save circuits
    	CtrlCicEvent,   // control, generate circuit events, debug only
        };

        /**
         * ISUP Message type as defined by Q.763 Table 5
         */
        enum Parameters {
    	EndOfParameters                = 0,
    	CallReference                  = 0x01,
    	TransmissionMediumRequirement  = 0x02,
    	AccessTransport                = 0x03,
    	CalledPartyNumber              = 0x04,
    	SubsequentNumber               = 0x05,
    	NatureOfConnectionIndicators   = 0x06,
    	ForwardCallIndicators          = 0x07,
    	OptionalForwardCallIndicators  = 0x08,
    	CallingPartyCategory           = 0x09,
    	CallingPartyNumber             = 0x0a,
    	RedirectingNumber              = 0x0b,
    	RedirectionNumber              = 0x0c,
    	ConnectionRequest              = 0x0d,
    	InformationRequestIndicators   = 0x0e,
    	InformationIndicators          = 0x0f,
    	ContinuityIndicators           = 0x10,
    	BackwardCallIndicators         = 0x11,
    	CauseIndicators                = 0x12,
    	RedirectionInformation         = 0x13,
    	GroupSupervisionTypeIndicator  = 0x15,
    	RangeAndStatus                 = 0x16,
    	CallModificationIndicators     = 0x17, // ANSI only
    	FacilityIndicator              = 0x18,
    	FacilityInformationIndicators  = 0x19, // ANSI only
    	CUG_InterlockCode              = 0x1a,
    	Index                          = 0x1b, // ANSI only
    	CUG_CheckResponseIndicators    = 0x1c, // ANSI only
    	UserServiceInformation         = 0x1d,
    	SignallingPointCode            = 0x1e,
    	UserToUserInformation          = 0x20,
    	ConnectedNumber                = 0x21,
    	SuspendResumeIndicators        = 0x22,
    	TransitNetworkSelection        = 0x23,
    	EventInformation               = 0x24,
    	CircuitAssignmentMap           = 0x25, // ANSI only
    	CircuitStateIndicator          = 0x26,
    	AutomaticCongestionLevel       = 0x27,
    	OriginalCalledNumber           = 0x28,
    	OptionalBackwardCallIndicators = 0x29,
    	UserToUserIndicators           = 0x2a,
    	OriginationISCPointCode        = 0x2b, // ITU only
    	GenericNotification            = 0x2c, // ITU only
    	CallHistoryInformation         = 0x2d, // ITU only
    	AccessDeliveryInformation      = 0x2e, // ITU only
    	NetworkSpecificFacilities      = 0x2f, // ITU only
    	UserServiceInformationPrime    = 0x30,
    	PropagationDelayCounter        = 0x31, // ITU only
    	RemoteOperations               = 0x32,
    	ServiceActivation              = 0x33,
    	UserTeleserviceInformation     = 0x34, // ITU only
    	TransmissionMediumUsed         = 0x35,
    	CallDiversionInformation       = 0x36, // ITU only
    	EchoControlInformation         = 0x37, // ITU only
    	MessageCompatInformation       = 0x38, // ITU only
    	ParameterCompatInformation     = 0x39, // ITU only
    	MLPP_Precedence                = 0x3a, // ITU name
    	Precedence                     = MLPP_Precedence, // ANSI name
    	MCID_RequestIndicator          = 0x3b, // ITU only
    	MCID_ResponseIndicator         = 0x3c, // ITU only
    	HopCounter                     = 0x3d,
    	TransMediumRequirementPrime    = 0x3e, // ITU only
    	LocationNumber                 = 0x3f, // ITU only
    	RedirectionNumberRestriction   = 0x40, // ITU only
    	FreephoneIndicators            = 0x41, // ITU only
    	GenericReference               = 0x42, // ITU only
    	CCSScallIndication             = 0x4b,
    	ForwardGVNS                    = 0x4c,
    	BackwardGVNS                   = 0x4d,
    	RedirectCapability             = 0x4e, // National use
    	CalledINNumber                 = 0x6f,
    	UID_ActionIndicators           = 0x74,
    	UID_CapabilityIndicators       = 0x75,
    	RedirectCounter                = 0x77, // National use
    	ApplicationTransport           = 0x78,
    	CCNRpossibleIndicator          = 0x7a,
    	PivotRoutingIndicators         = 0x7c,
    	CalledDirectoryNumber          = 0x7d, // National use
    	OriginalCalledINNumber         = 0x7f,
    	CallingGeodeticLocation        = 0x81,
    	HTR_Information                = 0x82,
    	NetworkRoutingNumber           = 0x84, // National use
    	QueryOnReleaseCapability       = 0x85, // Network option
    	PivotStatus                    = 0x86, // National use
    	PivotCounter                   = 0x87,
    	PivotRoutingForwardInformation = 0x88,
    	PivotRoutingBackInformation    = 0x89,
    	RedirectStatus                 = 0x8a, // National use
    	RedirectForwardInformation     = 0x8b, // National use
    	RedirectBackwardInformation    = 0x8c, // National use
    	NumberPortabilityInformation   = 0x8d, // Network option
    	GenericNumber                  = 0xc0, // ITU name
    	GenericAddress                 = GenericNumber, // ANSI name
    	GenericDigits                  = 0xc1,
    	OperatorServicesInformation    = 0xc2, // ANSI only
    	Egress                         = 0xc3, // ANSI only
    	Jurisdiction                   = 0xc4, // ANSI only
    	CarrierIdentification          = 0xc5, // ANSI only
    	BusinessGroup                  = 0xc6, // ANSI only
    	GenericName                    = 0xc7, // ANSI only
    	NotificationIndicator          = 0xe1, // ANSI only
    	TransactionRequest             = 0xe3, // ANSI only
    	CircuitGroupCharactIndicator   = 0xe5, // ANSI only
    	CircuitValidationRespIndicator = 0xe6, // ANSI only
    	OutgoingTrunkGroupNumber       = 0xe7, // ANSI only
    	CircuitIdentificationName      = 0xe8, // ANSI only
    	CommonLanguage                 = 0xe9, // ANSI only
    	OriginatingLineInformation     = 0xea, // ANSI only
    	ChargeNumber                   = 0xeb, // ANSI only
    	ServiceCodeIndicator           = 0xec, // ANSI only
    	SpecialProcessingRequest       = 0xed, // ANSI only
    	CarrierSelectionInformation    = 0xee, // ANSI only
    	NetworkTransport               = 0xef, // ANSI only
    	NationalForwardCallIndicatorsLinkByLink = 0xf4, // UK-ISUP
    	NationalInformationIndicators           = 0xf5, // UK-ISUP
    	NationalInformationRequestIndicators    = 0xf6, // UK-ISUP
    	CalledSubscribersTerminatingFacilMarks  = 0xf7, // UK-ISUP
    	CallingSubscribersOriginatingFacilMarks = 0xf8, // UK-ISUP
    	CallingSubscribersBasicServiceMarks     = 0xf9, // UK-ISUP
    	CalledSubscribersBasicServiceMarks      = 0xfa, // UK-ISUP
    	PartialCLI                              = 0xfb, // UK-ISUP
    	LastDivertingLineIdentity               = 0xfc, // UK-ISUP
    	PresentationNumber                      = 0xfd, // UK-ISUP
    	NationalForwardCallIndicators           = 0xfe, // UK-ISUP
        };

        /**
         * Constructor
         * @param type Type of ISUP message as enumeration
         * @param cic Source/destination Circuit Identification Code
         */
        inline SS7MsgISUP(Type type, unsigned int cic)
    	: SignallingMessage(lookup(type,"Unknown")), m_type(type), m_cic(cic)
    	{ }

        /**
         * Destructor
         */
        virtual ~SS7MsgISUP()
    	{ }

        /**
         * Get the type of this message
         * @return The type of this message as enumeration
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the source/destination Circuit Identification Code of this message
         * @return The source/destination Circuit Identification Code of this message
         */
        inline unsigned int cic() const
    	{ return m_cic; }

        /**
         * Fill a string with this message's parameters for debug purposes
         * @param dest The destination string
         * @param label The routing label
         * @param params True to add parameters
         * @param raw Optional raw message data to be added to destination
         * @param rawLen Raw data length
         */
        void toString(String& dest, const SS7Label& label, bool params,
    	const void* raw = 0, unsigned int rawLen = 0) const;

        /**
         * Get the dictionary with the message names
         * @return Pointer to the dictionary with the message names
         */
        static const TokenDict* names();

        /**
         * Convert an ISUP message type to a C string
         * @param type Type of ISUP message to look up
         * @param defvalue Default string to return
         * @return Name of the ISUP message type
         */
        static inline const char* lookup(Type type, const char* defvalue = 0)
    	{ return TelEngine::lookup(type,names(),defvalue); }

        /**
         * Look up an ISUP message name
         * @param name String name of the ISUP message
         * @param defvalue Default type to return
         * @return Encoded type of the ISUP message
         */
        static inline Type lookup(const char* name, Type defvalue = Unknown)
    	{ return static_cast(TelEngine::lookup(name,names(),defvalue)); }

    private:
        Type m_type;                         // Message type
        unsigned int m_cic;                  // Source/destination Circuit Identification Code
    };

    /**
     * Implementation of SS7 SNM User Part (Management) - Q.704
     * @short SS7 SNM implementation
     */
    class YSIG_API SS7Management : public SS7Layer4, public Mutex
    {
        YCLASS(SS7Management,SS7Layer4)
    public:
        /**
         * Constructor
         */
        SS7Management(const NamedList& params, unsigned char sio = SS7MSU::SNM|SS7MSU::National);

    protected:
        /**
         * Process a MSU received from a Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Set and clear inhibition flags on a link of a router attached network
         * @param link Signalling Link to modify identified by a routing label
         * @param setFlags Flag bits to set ORed together
         * @param clrFlags Flag bits to clear  ORed together (optional)
         * @return True if inhibition flags were set
         */
        bool inhibit(const SS7Label& link, int setFlags, int clrFlags = 0);

        /**
         * Check inhibition flags on a link of a router attached network
         * @param link Signalling Link to check identified by a routing label
         * @param flags Flag bits to check ORed together
         * @return True if any of the specified inhibition flags are set
         */
        bool inhibited(const SS7Label& link, int flags);

        /**
         * Recover MSUs from a link
         * @param link Signalling Link to recover identified by a routing label
         * @param sequence Starting sequence number to recover
         */
        void recover(const SS7Label& link, int sequence);

        /**
         * Process a notification generated by the attached network layer
         * @param link Network or linkset that generated the notification
         * @param sls Signallink Link that generated the notification, negative if none
         * @return True if notification was processed
         */
        virtual void notify(SS7Layer3* link, int sls);

        /**
         * Query or modify the management settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Method called periodically by the engine to retransmit messages
         * @param when Time to use as computing base for timers
         */
        virtual void timerTick(const Time& when);

    private:
        bool postpone(SS7MSU* msu, const SS7Label& label, int txSls,
    	u_int64_t interval, u_int64_t global = 0, bool force = false, const Time& when = Time());
        bool timeout(const SS7MSU& msu, const SS7Label& label, int txSls, bool final);
        bool timeout(SignallingMessageTimer& timer, bool final);
        SignallingMessageTimerList m_pending;
        bool m_changeMsgs;
        bool m_changeSets;
        bool m_neighbours;
    };

    /**
     * Implementation of SS7 MTP Test User Part - Q.782 2.3
     * @short SS7 MTP Test Traffic implementation
     */
    class YSIG_API SS7Testing : public SS7Layer4, public Mutex
    {
        YCLASS(SS7Testing,SS7Layer4)
    public:
        /**
         * Constructor
         */
        inline SS7Testing(const NamedList& params, unsigned char sio = SS7MSU::MTP_T|SS7MSU::National)
    	: SignallingComponent(params.safe("SS7Testing"),¶ms,"ss7-test"),
    	  SS7Layer4(sio,¶ms),
    	  Mutex(true,"SS7Testing"),
    	  m_timer(0), m_exp(0), m_seq(0), m_len(16), m_sharing(false)
    	{ }

        /**
         * Configure and initialize the user part
         * @param config Configuration parameters
         * @return True if the component was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Query or modify the test part settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

    protected:
        /**
         * Process a MSU received from a Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Process a notification generated by the attached network layer
         * @param link Network or linkset that generated the notification
         * @param sls Signallink Link that generated the notification, negative if none
         * @return True if notification was processed
         */
        virtual void notify(SS7Layer3* link, int sls);

        /**
         * Method called periodically by the engine to emit new messages
         * @param when Time to use as computing base for timers
         */
        virtual void timerTick(const Time& when);

    private:
        bool sendTraffic();
        void setParams(const NamedList& params, bool setSeq = false);
        SignallingTimer m_timer;
        SS7Label m_lbl;
        u_int32_t m_exp;
        u_int32_t m_seq;
        u_int16_t m_len;
        bool m_sharing;
    };

    /**
     * A signalling call using SS7 ISUP protocol
     * @short An SS7 ISUP call
     */
    class YSIG_API SS7ISUPCall : public SignallingCall
    {
        friend class SS7ISUP;
    public:
        /**
         * Call state enumerators
         */
        enum State {
    	// NOTE: Keep the order of state values: the code relies on it
    	Null      = 0,                   // No message exchanged
    	Testing   = 1,                   // IAM but waiting for continuity check
    	Setup     = 2,                   // IAM (initial address)
    	Accepted  = 3,                   // ACM (address complete)
    	Ringing   = 4,                   // CPM (call progress)
    	Answered  = 5,                   // ANM (answer)
    	Releasing = 6,                   // REL (release)
    	Released  = 7                    // Call released, no message or events allowed
        };

        /**
         * Destructor.
         * Complete call release. Releas circuit. Remove itself from controller's list
         */
        virtual ~SS7ISUPCall();

        /**
         * Get the call state
         * @return The call state as enumeration
         */
        inline State state() const
    	{ return m_state; }

        /**
         * Check if the call is a not test one in early state
         * @return True if this a non test call in early state
         */
        inline bool earlyState() const
    	{ return m_state <= Setup && !m_testCall; }

        /**
         * Get the call's circuit range
         * @return The call's circuit range
         */
        inline const String& cicRange() const
    	{ return m_cicRange; }

        /**
         * Get the call id (the code of the circuit reserved for this call)
         * @return The call id
         */
        inline unsigned int id() const
    	{ return m_circuit ? m_circuit->code() : 0; }

        /**
         * Get an event from this call
         * This method is thread safe
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events
         */
        virtual SignallingEvent* getEvent(const Time& when);

        /**
         * Send an event to this call
         * @param event The event to send
         * @return True if the operation succedded
         */
        virtual bool sendEvent(SignallingEvent* event);

        /**
         * Set termination flag. Set termination reason if not already set
         * @param gracefully True to send RLC on termination, false to destroy the call without notification
         * @param reason Termination reason
         * @param diagnostic Optional diagnostic data to be sent with termination reason
         * @param location Optional release location
         */
        inline void setTerminate(bool gracefully, const char* reason = 0,
    	const char* diagnostic = 0, const char* location = 0)
        {
    	Lock lock(this);
    	m_terminate = true;
    	m_gracefully = gracefully;
    	setReason(reason,0,diagnostic,location);
        }

        /**
         * Get a pointer to this object or other data
         * @param name Object name
         * @return The requested pointer or 0 if not exists
         */
        virtual void* getObject(const String& name) const;

    protected:
        /**
         * Constructor
         * @param controller The call controller
         * @param cic The reserved circuit
         * @param local The local point code used to create the routing label for sent messages
         * @param remote The remote point code used to create the routing label for sent messages
         * @param outgoing Call direction
         * @param sls Optional link for the routing label
         * @param range Optional range used to re-allocate a circuit for this call if necessary
         * @param testCall True if this is a test call
         */
        SS7ISUPCall(SS7ISUP* controller, SignallingCircuit* cic,
    	const SS7PointCode& local, const SS7PointCode& remote, bool outgoing,
    	int sls = -1, const char* range = 0, bool testCall = false);

        /**
         * Release call. Stop timers. Send a RLC (Release Complete) message if it should terminate gracefully
         * Decrease the object's referrence count and generate a Release event if not final
         * This method is thread safe
         * @param final True if called from destructor
         * @param msg Received message with parameters if any
         * @param reason Optional release reason
         * @param timeout True if this is method is called due to T5 timer expiry
         * @return SignallingEvent pointer or 0
         */
        SignallingEvent* releaseComplete(bool final, SS7MsgISUP* msg = 0, const char* reason = 0,
    	bool timeout = false);

        /**
         * Check if the call's circuit can be replaced at this time
         * @return True if the circuit can be replaced
         */
        bool canReplaceCircuit();

        /**
         * Replace the circuit reserved for this call. Release the already reserved circuit.
         * Retransmit the initial IAM request on success.
         * On failure set the termination flag and release the new circuit if valid.
         * If false is returned, the call is prepared to return a Release event.
         * This method is thread safe
         * @param circuit The new circuit reserved for this call
         * @param msg Optional message to send before IAM (it will be consumed)
         * @return False if the state is greater then Setup, the call is not outgoing or the new circuit is 0
         */
        bool replaceCircuit(SignallingCircuit* circuit, SS7MsgISUP* msg = 0);

        /**
         * Stop waiting for a SGM (Segmentation) message when another message is received by the controller.
         * This method is thread safe
         * @param discard True to discard (destruct) the segment waiting message if any
         */
        void stopWaitSegment(bool discard);

    private:
        // Initialize/set IAM message parameters
        // @param msg Valid ISUP message
        // @param outgoing Message direction: true for outgoing
        // @param sigMsg Valid signalling message with parameters if outgoing
        bool copyParamIAM(SS7MsgISUP* msg, bool outgoing = false, SignallingMessage* sigMsg = 0);
        // If already releasing, set termination flag. Otherwise, send REL (Release) message
        // Set m_lastEvent if event is 0 (the method is called internally)
        // @param event Event with the parameters. 0 if release is started on some timeout
        // @param msg Received message to put in release event
        // @return Generated release event if any
        SignallingEvent* release(SignallingEvent* event = 0, SS7MsgISUP* msg = 0);
        // Set termination reason from message or parameter
        void setReason(const char* reason, SignallingMessage* msg, const char* diagnostic = 0,
    	const char* location = 0);
        // Accept send/receive messages in current state based on call direction
        bool validMsgState(bool send, SS7MsgISUP::Type type, bool hasBkwCallInd = false);
        // Connect the reserved circuit. Return false if it fails. Return true if this call is a signalling only one
        bool connectCircuit(const char* special = 0);
        // Transmit the IAM message. Start IAM timer if not started
        bool transmitIAM();
        // Transmit SAM digits
        bool transmitSAM(const char* extra = 0);
        // (Re)transmit REL. Create and populate message if needed. Remember sls
        bool transmitREL(const NamedList* params = 0);
        // Check if the circuit needs continuity testing
        bool needsTesting(const SS7MsgISUP* msg);
        // Stop waiting for a SGM (Segmentation) message. Copy parameters to the pending segmented message if sgm is valid.
        // Change call state and set m_lastEvent
        // @param sgm Optional received SGM message with parameters to be added to the pending segmented message
        // @param timeout True if waiting timer timed out. Ignored if sgm is non null
        // @return m_lastEvent
        SignallingEvent* processSegmented(SS7MsgISUP* sgm = 0, bool timeout = false);
        // Transmit message. Set routing label's link if not already set
        inline bool transmitMessage(SS7MsgISUP* msg);
        // Get the ISUP call controller
        inline SS7ISUP* isup() const;
        // Set overlapped flag. Output a debug message
        void setOverlapped(bool on, bool numberComplete = true);

        State m_state;                       // Call state
        bool m_testCall;                     // Test only call
        SignallingCircuit* m_circuit;        // Circuit reserved for this call
        String m_cicRange;                   // The range used to re(alloc) a circuit
        SS7Label m_label;                    // The routing label for this call
        bool m_terminate;                    // Termination flag
        bool m_gracefully;                   // Terminate gracefully: send RLC
        bool m_circuitChanged;               // Circuit change flag
        bool m_circuitTesting;               // The circuit is tested for continuity
        bool m_inbandAvailable;              // Inband data is available
        int m_replaceCounter;                // Circuit replace counter
        String m_format;                     // Data format used by the circuit
        String m_reason;                     // Termination reason
        String m_diagnostic;                 // Termination diagnostic
        String m_location;                   // Termination location
        SS7MsgISUP* m_iamMsg;                // Message with the call parameters for outgoing calls
        SS7MsgISUP* m_sgmMsg;                // Pending received message with segmentation flag set
        SS7MsgISUP* m_relMsg;                // Release message preserved for retransmissions
        // Overlapped
        String m_samDigits;                  // SAM digits
        unsigned int m_sentSamDigits;        // The number of sent SAM digits
        // Timers
        SignallingTimer m_relTimer;          // Send release
        SignallingTimer m_iamTimer;          // Send initial address
        SignallingTimer m_sgmRecvTimer;      // Receive segmented message
        SignallingTimer m_contTimer;         // Continuity timer
        SignallingTimer m_anmTimer;          // T9 ACM -> ANM timer
    };

    /**
     * Implementation of SS7 ISDN User Part
     * @short SS7 ISUP implementation
     */
    class YSIG_API SS7ISUP : public SignallingCallControl, public SS7Layer4
    {
        YCLASS(SS7ISUP,SS7Layer4)
        friend class SS7ISUPCall;
    public:
        /**
         * Special SLS values
         */
        enum {
    	SlsAuto    = -1,
    	SlsLatest  = -2,
    	SlsCircuit = -3,
    	SlsDefault = -4
        };

        enum ChargeProcess {
    	Confusion,
    	Ignore,
    	Raw,
    	Parsed
        };

        /**
         * Constructor
         * @param params Call controller's parameters
         * @param sio The default Service Information Octet
         */
        SS7ISUP(const NamedList& params, unsigned char sio = SS7MSU::ISUP|SS7MSU::National);

        /**
         * Destructor
         */
        virtual ~SS7ISUP();

        /**
         * Configure and initialize the call controller and user part
         * @param config Optional configuration parameters override
         * @return True if ISUP was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Get the controller's status as text
         * @return Controller status name
         */
        virtual const char* statusName() const;

        /**
         * Attach a SS7 network or router to this service. Detach itself from the old one
         * @param network Pointer to network or router to attach
         */
        virtual void attach(SS7Layer3* network);

        /**
         * Get the length of the Circuit Identification Code for this user part
         * @return Length of the CIC field in octets
         */
        unsigned int cicLen() const
    	{ return m_cicLen; }

        /**
         * Get the default data format
         * @return The default data format
         */
        const String& format() const
    	{ return m_format; }

        /**
         * Check if the message parser of this controller should ignore unknown digits encoding
         * @return True if unknown digits are ignored
         */
        inline bool ignoreUnknownAddrSignals() const
    	{ return m_ignoreUnkDigits; }

        /**
         * Append a point code to the list of point codes serviced by this controller
         *  if not already there. Set default point code if requested.
         * If the list is empty, the default point code is set to the first point code added
         * @param pc The point code to append
         * @param def True if this point code is the default for outgoing calls
         * @return False if the point code is invalid for this call controller type. If true is returned, don't reuse the pointer
         */
        bool setPointCode(SS7PointCode* pc, bool def);

        /**
         * Append all point codes from a parameter list, use "pointcode" and
         *  "defaultpointcode" parameters
         * @param params List of parameters to take point codes from
         * @return Count of point codes added
         */
        unsigned int setPointCode(const NamedList& params);

        /**
         * Check if the given point code is serviced by this controller
         * @param pc The point code to check
         * @return SS7PointCode pointer or 0 if not found
         */
        SS7PointCode* hasPointCode(const SS7PointCode& pc);

        /**
         * Check if this controller should handle a remote point code
         * @param pc The remote point code to check
         * @return True if pc matches the remote or there is no remote set
         */
        inline bool handlesRemotePC(const SS7PointCode& pc) const
    	{ return !m_remotePoint || (pc == *m_remotePoint); }

        /**
         * Set a routing label to be used for outgoing messages
         * @param label Routing label to set
         * @param opc Originating point code
         * @param dpc Destination point code
         * @param sls Signalling Link Selection
         */
        inline void setLabel(SS7Label& label, const SS7PointCode& opc, const SS7PointCode& dpc,
    	unsigned char sls = 255)
    	{ label.assign(m_type,dpc,opc,sls); }

        /**
         * Set debug data of this call controller
         * @param printMsg Enable/disable message printing on output
         * @param extendedDebug Enable/disable hex data dump if print messages is enabled
         */
        inline void setDebug(bool printMsg, bool extendedDebug)
    	{ m_extendedDebug = ((m_printMsg = printMsg) && extendedDebug); }

        /**
         * Create a new MSU populated with type, routing label and space for fixed part
         * @param type Type of ISUP message
         * @param ssf Subservice Field
         * @param label Routing label for the new MSU
         * @param cic Circuit Identification Code
         * @param params Optional parameter list
         * @return Pointer to the new MSU or NULL if an error occured
         */
        virtual SS7MSU* createMSU(SS7MsgISUP::Type type, unsigned char ssf,
    	const SS7Label& label, unsigned int cic, const NamedList* params = 0) const;

        /**
         * Create an outgoing call. Send a NewCall event with the given msg parameter
         * This method is thread safe
         * @param msg Call parameters
         * @param reason Failure reason if any
         * @return Referenced SignallingCall pointer on success or 0 on failure
         */
        virtual SignallingCall* call(SignallingMessage* msg, String& reason);

        /**
         * Converts an ISUP message to a Message Signal Unit and push it down the protocol stack.
         * The given message is consumed
         * @param msg The message to send
         * @param label The routing label for the message
         * @param recvLbl True if the given label is from a received message. If true, a new routing
         *  label will be created from the received one
         * @param sls Signalling Link to use for the new routing label. Ignored if recvLbl is false
         * @return Link the message was successfully queued to, negative for error
         */
        int transmitMessage(SS7MsgISUP* msg, const SS7Label& label, bool recvLbl, int sls = SlsDefault);

        /**
         * Cleanup calls
         * This method is thread safe
         * @param reason Cleanup reason
         */
        virtual void cleanup(const char* reason = "net-out-of-order");

        /**
         * Query or modify ISUP's settings or operational parameters
         * @param params The list of parameters to query or change
         * @return True if the control operation was executed
         */
        virtual bool control(NamedList& params);

        /**
         * Decode an ISUP message buffer to a list of parameters
         * @param msg Destination list of parameters
         * @param msgType The message type
         * @param pcType The point code type (message version)
         * @param paramPtr Pointer to the Parameter area (just after the message type)
         * @param paramLen Length of the Parameter area
         * @return True if the mesage was successfully parsed
         */
        bool decodeMessage(NamedList& msg, SS7MsgISUP::Type msgType, SS7PointCode::Type pcType,
    	const unsigned char* paramPtr, unsigned int paramLen);

        /**
         * Encode an ISUP list of parameters to a buffer.
         * The input list may contain a 'message-prefix' parameter to override this controller's prefix
         * @param buf Destination buffer
         * @param msgType The message type
         * @param pcType The point code type (message version)
         * @param params Message list of parameters
         * @param cic Optional cic to be added before mesage
         * @return True if the mesage was successfully encoded
         */
        bool encodeMessage(DataBlock& buf, SS7MsgISUP::Type msgType, SS7PointCode::Type pcType,
    	const NamedList& params, unsigned int* cic = 0);

        /**
         * Process parameter compatibility lists.
         * Terminate an existing call if a non emtpy release call parameter(s) list is found.
         * Send CNF if non emtpy cnf parameter(s) list is found
         * @param list Message parameter list
         * @param cic The circuit code
         * @param callReleased Optional pointer to boolean value to be set if a call was released
         * @return True if any parameter compatibility was handled
         */
        bool processParamCompat(const NamedList& list, unsigned int cic, bool* callReleased = 0);

        /**
         * Obtain the way that charge message should be processed
         * @return The way that charge message should be processed
         */
        inline ChargeProcess getChargeProcessType() const
    	{ return m_chargeProcessType; }

    protected:
        /**
         * Remove all links with other layers. Disposes the memory
         */
        virtual void destroyed();

        /**
         * Send CGU if not already done. Check timeouts
         * @param when Time to use as computing base for timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a notification generated by the attached network layer
         * @param link Network or linkset that generated the notification
         * @param sls Signalling Link that generated the notification, negative if none
         */
        virtual void notify(SS7Layer3* link, int sls);

        /**
         * Create a new MSU populated with type, routing label and space for fixed part
         * @param type Type of ISUP message
         * @param sio Service Information Octet
         * @param label Routing label for the new MSU
         * @param cic Circuit Identification Code
         * @param params Parameter list
         * @return Pointer to the new MSU or NULL if an error occured
         */
        SS7MSU* buildMSU(SS7MsgISUP::Type type, unsigned char sio,
    	const SS7Label& label, unsigned int cic, const NamedList* params) const;

        /**
         * Process a MSU received from a Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Process a MSU received from a Layer 3 component
         * @param type Type of ISUP message
         * @param cic Circuit Identification Code
         * @param paramPtr Pointer to the Parameter area
         * @param paramLen Length of the Parameter area
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return True if the MSU was processed
         */
        virtual bool processMSU(SS7MsgISUP::Type type, unsigned int cic,
            const unsigned char* paramPtr, unsigned int paramLen,
    	const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Notification for receiving User Part Unavailable
         * @param type Type of Point Code
         * @param node Node on which the User Part is unavailable
         * @param part User Part (service) reported unavailable
         * @param cause Unavailability cause - Q.704 15.17.5
         * @param label Routing label of the UPU message
         * @param sls Signaling link the UPU was received on
         */
        virtual void receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
    	SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls);

        /**
         * Process an event received from a non-reserved circuit
         * @param event The event, will be consumed and zeroed
         * @param call Optional signalling call whose circuit generated the event
         * @return Signalling event pointer or 0
         */
        virtual SignallingEvent* processCircuitEvent(SignallingCircuitEvent*& event,
    	SignallingCall* call = 0);

        /**
         * Initiate circuit reset. The circuit must be already reserved
         * This method is thread safe
         * @param cic The circuit to reset. Its referrence counter will be decreased and
         *  the pointer will be zeroed
         * @param timer Ellapsed timer
         * @return True if the circuit reset was initiated
         */
        bool startCircuitReset(SignallingCircuit*& cic, const String& timer);

        /**
         * Length of the Circuit Identification Code in octets
         */
        unsigned int m_cicLen;

    private:
        // Process a received message that should be processed by a call
        // @param msg The received message
        // @param label The routing label of the received message
        // @param sls Signalling Link the message was received from
        void processCallMsg(SS7MsgISUP* msg, const SS7Label& label, int sls);
        // Process a received message that should be processed by this call controller
        // @param msg The received message
        // @param label The routing label of the received message
        // @param sls Signalling Link the message was received from
        void processControllerMsg(SS7MsgISUP* msg, const SS7Label& label, int sls);
        // Replace a call's circuit if checkCall is true
        // Clear lock flags of the circuit. Release currently reseting circuit if the code match
        // Return false if the given circuit doesn't exist
        bool resetCircuit(unsigned int cic, bool remote, bool checkCall);
        // Block/unblock a circuit side (local or remote)
        // Return false if the given circuit doesn't exist
        bool blockCircuit(unsigned int cic, bool block, bool remote, bool hwFail,
    	bool changed, bool changedState, bool resetLocking = false);
        // Find a call by its circuit identification code
        // This method is not thread safe
        SS7ISUPCall* findCall(unsigned int cic);
        // Find a call by its circuit identification code
        // This method is thread safe
        inline void findCall(unsigned int cic, RefPointer& call) {
    	    Lock mylock(this);
    	    call = findCall(cic);
    	}
        // Encode a raw message
        SS7MSU* encodeRawMessage(SS7MsgISUP::Type type, unsigned char sio,
    	const SS7Label& label, unsigned int cic, const String& param) const;
        // Send blocking/unblocking messages.
        // Restart the re-check timer if there is any (un)lockable, not sent cic
        // Return false if no request was sent
        bool sendLocalLock(const Time& when = Time());
        // Fill label from local/remote point codes
        // This method is thread safe
        // Return a true if local and remote point codes are valid
        bool setLabel(SS7Label& label, unsigned int cic);
        // Retrieve a pending message
        SignallingMessageTimer* findPendingMessage(SS7MsgISUP::Type type, unsigned int cic,
    	bool remove = false);
        // Retrieve a pending message with given parameter
        SignallingMessageTimer* findPendingMessage(SS7MsgISUP::Type type, unsigned int cic,
    	const String& param, const String& value, bool remove = false);
        // Transmit a list of messages. Return true if at least 1 message was sent
        bool transmitMessages(ObjList& list);
        // Handle circuit(s) (un)block command
        bool handleCicBlockCommand(const NamedList& p, bool block);
        // Handle remote circuit(s) (un)block command
        bool handleCicBlockRemoteCommand(const NamedList& p, unsigned int* cics,
    	unsigned int count, bool block);
        // Handle circuit(s) event generation command
        bool handleCicEventCommand(const NamedList& p);
        // Try to start single circuit (un)blocking. Set a pending operation on success
        // @param force True to ignore resetting/(un)blocking flags of the circuit
        // Return built message to be sent on success
        SS7MsgISUP* buildCicBlock(SignallingCircuit* cic, bool block, bool force = false);
        // Replace circuit for outgoing calls in Setup state
        // Send REL/RSC before repeat attempt
        void replaceCircuit(unsigned int cic, const String& map, bool rel = true);
        // Handle circuit hw-fail block
        // Replace cics for outgoing calls. Terminate incoming
        void cicHwBlocked(unsigned int cic, const String& map);

        SS7PointCode::Type m_type;           // Point code type of this call controller
        ObjList m_pointCodes;                // Point codes serviced by this call controller
        SS7PointCode* m_defPoint;            // Default point code for outgoing calls
        SS7PointCode* m_remotePoint;         // Default remote point code for outgoing calls and maintenance
        unsigned char m_sls;                 // Last known valid SLS
        bool m_earlyAcm;                     // Accept progress/ringing in early ACM
        bool m_inn;                          // Routing to internal network number flag
        int m_defaultSls;                    // Default SLS to use in outbound calls
        unsigned int m_maxCalledDigits;      // Maximum digits allowed in Called Number in IAM
        String m_numPlan;                    // Numbering plan
        String m_numType;                    // Number type
        String m_numPresentation;            // Number presentation
        String m_numScreening;               // Number screening
        String m_callerCat;                  // Caller party category
        String m_format;                     // Default format
        String m_continuity;                 // Continuity test type
        bool m_confirmCCR;                   // Send LPA in response to CCR
        bool m_dropOnUnknown;                // Drop call in early state on unknown message
        bool m_ignoreGRSSingle;              // Ignore (drop) GRS with range 0 (1 circuit affected)
        bool m_ignoreCGBSingle;              // Ignore (drop) CGB with range 0 (1 circuit in map)
        bool m_ignoreCGUSingle;              // Ignore (drop) CGU with range 0 (1 circuit in map)
        bool m_duplicateCGB;                 // Send duplicate CGB messages (ANSI)
        bool m_ignoreUnkDigits;              // Check if the message parser should ignore unknown digits encoding
        bool m_l3LinkUp;                     // Flag indicating the availability of a Layer3 data link
        ChargeProcess m_chargeProcessType;   // Indicates the way that charge message should be processed
        u_int64_t m_t1Interval;              // Q.764 T1 timer interval
        u_int64_t m_t5Interval;              // Q.764 T5 timer interval
        u_int64_t m_t7Interval;              // Q.764 T7 timer interval
        u_int64_t m_t9Interval;              // Q.764 T9 AMM/CON recv timer interval
        u_int64_t m_t12Interval;             // Q.764 T12 BLK timer interval
        u_int64_t m_t13Interval;             // Q.764 T13 BLK global timer interval
        u_int64_t m_t14Interval;             // Q.764 T14 UBL timer interval
        u_int64_t m_t15Interval;             // Q.764 T15 UBL global timer interval
        u_int64_t m_t16Interval;             // Q.764 T16 RSC timer interval
        u_int64_t m_t17Interval;             // Q.764 T17 timer interval
        u_int64_t m_t18Interval;             // Q.764 T18 CGB timer interval
        u_int64_t m_t19Interval;             // Q.764 T19 CGB global timer interval
        u_int64_t m_t20Interval;             // Q.764 T20 CGU timer interval
        u_int64_t m_t21Interval;             // Q.764 T21 CGU global timer interval
        u_int64_t m_t27Interval;             // Q.764 T27 Reset after Cont. Check failure
        u_int64_t m_t34Interval;             // Q.764 T34 Segmentation receive timout
        SignallingMessageTimerList m_pending;// Pending messages (RSC ...)
        // Remote User Part test
        SignallingTimer m_uptTimer;          // Timer for UPT
        bool m_userPartAvail;                // Flag indicating the remote User Part availability
        SS7MsgISUP::Type m_uptMessage;       // Message used, may not be always UPT
        unsigned int m_uptCicCode;           // The circuit code sent with UPT
        int m_cicWarnLevel;                  // Wrong CIC warn level
        int m_replaceCounter;                // Circuit replace counter
        // Circuit reset
        SignallingTimer m_rscTimer;          // RSC message or idle timeout
        SignallingCircuit* m_rscCic;         // Circuit currently beeing reset
        u_int32_t m_rscInterval;             // Saved reset interval
        u_int32_t m_rscSpeedup;              // Circuits left for speedup
        // Blocking/unblocking circuits
        SignallingTimer m_lockTimer;         // Timer used to re-check local lock
        bool m_lockGroup;                    // Allow sending requests for a group
        // Debug flags
        bool m_printMsg;                     // Print messages to output
        bool m_extendedDebug;                // Extended debug flag
    };

    /**
     * Implementation of SS7 Bearer Independent Call Control User Part
     * @short SS7 BICC implementation
     */
    class YSIG_API SS7BICC : public SS7ISUP
    {
        YCLASS(SS7BICC,SS7ISUP)
    public:
        /**
         * Constructor
         * @param params Call controller's parameters
         * @param sio The default Service Information Octet
         */
        SS7BICC(const NamedList& params, unsigned char sio = SS7MSU::BICC|SS7MSU::National);

        /**
         * Destructor
         * Terminate all calls
         */
        virtual ~SS7BICC();

    protected:
        /**
         * Process a MSU received from a Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);
    };

    /**
     * Implementation of SS7 Telephone User Part
     * @short SS7 TUP implementation
     */
    class YSIG_API SS7TUP : public SignallingCallControl, public SS7Layer4
    {
    public:
        /**
         * Constructor
         * @param params Call controller's parameters
         * @param sif The default Service Information Field
         */
        SS7TUP(const NamedList& params, unsigned char sif = SS7MSU::TUP);

        /**
         * Destructor
         * Terminate all calls
         */
        virtual ~SS7TUP();
    };

    /**
     * An interface to a SS7 SCCP Management
     * @short Abstract SS7 SCCP Management
     */

    class YSIG_API SCCPManagement : public SignallingComponent , public Mutex
    {
        friend class SS7SCCP;
        friend class SccpLocalSubsystem; // Local Broadcast
        YCLASS(SCCPManagement,SignallingComponent)
    public:
        enum MsgType {
    	SSA = 0x01,  // Subsystem-allowed
    	SSP = 0x02,  // Subsystem-prohibited
    	SST = 0x03,  // Subsystem-status-test
    	SOR = 0x04,  // Subsystem-out-of-service-request
    	SOG = 0x05,  // Subsystem-out-of-service-grant
    	SSC = 0x06,  // SCCP/Subsystem-congested      (ITU  only)
    	SBR = 0xfd,  // Subsystem-backup-routing      (ANSI only)
    	SNR = 0xfe,  // Subsystem-normal-routing      (ANSI only)
    	SRT = 0xff   // Subsystem-routing-status-test (ANSI only)
        };

        enum LocalBroadcast {
    	UserOutOfService,
    	UserInService,
    	PCInaccessible,         // Signalling Point Inaccessible
    	PCAccessible,           // Signalling Point Accessible
    	SccpRemoteInaccessible,
    	SccpRemoteAccessible,
    	PCCongested,           // Signalling Point Congested
    	SubsystemStatus        // Request send by sccp management to find if a ssn is available
        };

        enum SccpStates {
    	Allowed = SS7Route::Allowed,
    	Prohibited = SS7Route::Prohibited,
    	Unknown = SS7Route::Unknown,
    	WaitForGrant,
    	IgnoreTests
        };

        /**
         * Constructor
         */
        SCCPManagement(const NamedList& params, SS7PointCode::Type type);

        /**
         * Destructor
         */
        virtual ~SCCPManagement();

        /**
         * Initialize this sccp management
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Process a management message received from sccp
         * @param message The message to process
         * @return True if the message was processed successfully
         */
        virtual bool processMessage(SS7MsgSCCP* message);

        /**
         * Attach a ss7 sccp to this management
         * @param sccp The ss7 sccp to attach
         */
        void attach(SS7SCCP* sccp);

        /**
         * Process a notification from MTP about a pointcode status
         * @param link The affected link
         * @param operational True if the layer3 is operational
         */
        virtual void pointcodeStatus(SS7Layer3* link, bool operational);

        /**
         * Process a notification from router about a route state change
         * @param type The Point Code type
         * @param node The remote pointcode
         * @param state The route state
         */
        virtual void routeStatus(SS7PointCode::Type type, const SS7PointCode& node, SS7Route::State state);

        /**
         * Notification from sccp about local subsystems status
         * @param type The type of notification
         * @param params The notification parameters
         */
        virtual void notify(SCCP::Type type, NamedList& params);

        /**
         * Method called by SCCP to inform management that no route was found for the message
         * @param msg The SCCP message that failed to be routed
         */
        virtual void routeFailure(SS7MsgSCCP* msg);

        /**
         * Method called by sccp when a sccp message hasn't been processed by any user
         * @param msg The message
         * @param label The mtp routing label
         */
        virtual void subsystemFailure(SS7MsgSCCP* msg, const SS7Label& label);

        /**
         * Notification from layer3 about a remote sccp unavailability
         * @param pointcode The poincode of the unavailable sccp
         * @param cause Unavailability cause
         */
        virtual void sccpUnavailable(const SS7PointCode& pointcode, unsigned char cause);

        /**
         * Helper method used to obtain a string statistic about the messages received for unknown subsystems
         * @param dest The string where the statistics will be stored
         * @param extended True to print an extended statistic ( the number of messages received for unknown subsystem)
         */
        void subsystemsStatus(String& dest,bool extended = true);

        /**
         * Helper method used to obtain information about the messages that failed to be routed
         * @param dest The destination string
         * @param extended True to print the GTT failures
         */
        void routeStatus(String& dest,bool extended = false);

        /**
         * Helper method used to notify the concerned signalling points about a subsystem status
         * @param msg The message type to broadcast
         * @param ssn Local affected ssn
         * @param smi Local subsystem multiplicity indicator
         */
        virtual void notifyConcerned(MsgType msg, unsigned char ssn, int smi);

        /**
         * Obtain broadcast type dict table
         * @return Pointer to broadcast type dict table
         */
        static const TokenDict* broadcastType();

        /**
         * Helper method used to inform Global Title Translator to update translation tables
         * @param rsccp The remote SCCP witch status has been changed
         * @param ssn The remote SCCP subsystem witch status has been changed
         */
        virtual void updateTables(SccpRemote* rsccp, SccpSubsystem* ssn = 0);

        /**
         * Print a sccp management message
         * @param dest The destination string
         * @param type The sccp management message type
         * @param params List of sccp management message parameters
         */
        virtual void printMessage(String& dest, MsgType type, const NamedList& params);

        /**
         * Obtain a sccp management state name
         * @param state The sccp management enum state
         * @return The state name if found or 0
         */
        static const char* stateName(SCCPManagement::SccpStates state)
    	{ return lookup(state,s_states); }
    protected:

        /**
         * Method called periodically by engine to check for timeouts
         * @param when Time to use as computing base for events and timeouts
         * Reimplemented from SignallingComponent
         */
        virtual void timerTick(const Time& when);

        inline SS7SCCP* sccp()
    	{ return m_sccp; }

        ObjList m_remoteSccp;
        ObjList m_statusTest;
        ObjList m_localSubsystems;
        ObjList m_concerned;
        SS7PointCode::Type m_pcType;

        /**
         * Obtain the subsystem status test time interval
         * @return Subsystem status test duration
         */
        inline u_int32_t getTestTimeout()
    	{ return m_testTimeout; }

        /**
         * Broadcast a management message to local attached sccp users
         * @param type The broadcast type
         * @param params List of parameters
         * @return True if at least one user has processed the message
         */
        bool managementMessage(SCCP::Type type, NamedList& params);

        /**
         * Obtain a local subsystem
         * @param ssn The local subsystem ssn
         * @return Pointer to local subsystem if found, 0 otherwise
         */
        SccpLocalSubsystem* getLocalSubsystem(unsigned char ssn);

        /**
         * Obtain a remote SCCP
         * @param pointcode The remote sccp pointcode
         * @return The remote SCCP with the matching pointcode or 0 if not found
         */
        SccpRemote* getRemoteSccp(int pointcode);

        /**
         * Encode a sccp management message and send it to remote address
         * @param msgType The SCCP management message type
         * @param params List of message parameters
         * @return True if the message was successfully send
         */
        virtual bool sendMessage(SCCPManagement::MsgType msgType, const NamedList& params) = 0;

        /**
         * Stop subsystem status tests for a remote location
         * @param remoteSccp The remote sccp
         * @param rSubsystem The remote subsystem. Can be 0 to stop all tests for the remote sccp
         * @param less Stop all sst except this
         */
        virtual void stopSst(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem = 0, SccpSubsystem* less = 0);

        /**
         * Stop all subsystem status tests
         */
        inline void stopSSTs()
    	{ Lock lock(this); m_statusTest.clear(); }

        /**
         * Start a new subsystem status test
         * @param remoteSccp The remote sccp
         * @param rSubsystem The remote subsystem
         */
        virtual void startSst(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem);

        /**
         * Notification from sccp that mtp has finished restarting
         */
        void mtpEndRestart();

        /**
         * Send a local sccp broadcast
         * @param type The broadcast message type
         * @param pointcode The affected pointcode. -1 if it should not be included
         * @param sps The signalling point status.  -1 if it should not be included
         * @param rss The remote sccp status. -1 if it should not be included
         * @param rl The restriction level. -1 if it should not be included
         * @param ssn The affected ssn. -1 if it should not be included
         * @param ss The subsystem status. -1 if it should not be included
         */
        void localBroadcast(SCCP::Type type, int pointcode, int sps, int rss = -1,
    	    int rl = -1, int ssn = -1, int ss = -1);

        /**
         * Helper method. Send subsystem status test
         * Note: Management mutex must not be locked. Thread safe
         * @param remote The remote sccp
         * @param sub The remote subsystem
         */
        bool sendSST(SccpRemote* remote, SccpSubsystem* sub);

        /**
         * Process a sccp management message
         * @param msgType The sccp management message type
         * @param ssn The affected subsystem
         * @param smi The subsystem multiplicity indicator
         * @param params The message params
         * @return True if the message was handled
         */
        bool handleMessage(int msgType, unsigned char ssn, unsigned char smi, NamedList& params);

        /**
         * Process remote sccp state
         * @param rsccp The remote sccp witch state has changed
         * @param newState The new state of the remote sccp
         */
        virtual void manageSccpRemoteStatus(SccpRemote* rsccp, SS7Route::State newState)
    	{ }

        /**
         * Helper method used to check if we should print sccp management messages
         * @return True if we should print messages
         */
        inline bool printMessagess()
    	{ return m_printMessages; }

        /**
         * Helper method that handles coordinate request
         * @param ssn Local subsystem that wish to go out of service
         * @param smi Subsystem multiplicity indicator
         * @param params List of parameters
         */
        void handleCoordinateChanged(unsigned char ssn, int smi, const NamedList& params);

        /**
         * Handle a subsystem out of service grant message
         * @param ssn Remote subsystem ssn
         * @param pointcode Remote subsystem pointcode
         * Note! Lock management mutex before calling this method
         */
        void handleSog(unsigned char ssn, int pointcode);

        /**
         * Process the status of subsystems
         * @param subsystem The subsystem who's status has changed
         * @param allowed True if the subsystem status is Allowed false for Prohibited
         * @param remote The remote sccp pointcode where the subsystem is located
         * @param smi Subsystem Multiplicity Indicator
         */
        virtual void handleSubsystemStatus(SccpSubsystem* subsystem, bool allowed, SccpRemote* remote, int smi)
    	{ }

        /**
         * Ontain the coordinate changed time interval
         * @return The coordinate time interval in ms
         */
        inline u_int32_t getCoordTimeout()
    	{ return m_coordTimeout; }

        /**
         * Obtain ignore status tests time interval
         * @return ignore status tests time interval in ms
         */
        inline u_int32_t getIgnoreTestsInterval()
    	{ return m_ignoreStatusTestsInterval; }
    private:
        // Helper method to fill broadcast param list
        void putValue(NamedList& params,int val,const char* name, bool dict = false);

        SS7SCCP* m_sccp;
        NamedList m_unknownSubsystems;
        unsigned int m_subsystemFailure;    // Counter used in status to inform about the total number of packets received for a unknown ssn
        unsigned int m_routeFailure;
        u_int32_t m_testTimeout;
        u_int32_t m_coordTimeout;
        u_int32_t m_ignoreStatusTestsInterval;
        bool m_autoAppend;
        bool m_printMessages;
        static const TokenDict s_broadcastType[];
        static const TokenDict s_states[];
    };

    class YSIG_API SS7MsgSCCP : public SignallingMessage
    {
        YCLASS(SS7MsgSCCP,SignallingMessage)
    public:
        /**
         * SCCP Message type
         */
        enum Type {
    	Unknown = 0,
    	CR     = 0x01, // Connection request
    	CC     = 0x02, // Connection confirm
    	CREF   = 0x03, // Connection refused
    	RLSD   = 0x04, // Released
    	RLC    = 0x05, // Release complete
    	DT1    = 0x06, // Data form 1
    	DT2    = 0x07, // Data form 2
    	AK     = 0x08, // Data acknowledgement
    	UDT    = 0x09, // Unitdata
    	UDTS   = 0x0a, // Unitdata service
    	ED     = 0x0b, // Expedited data
    	EA     = 0x0c, // Expedited data acknowledgement
    	RSR    = 0x0d, // Reset request
    	RSC    = 0x0e, // Reset confirmation
    	ERR    = 0x0f, // Protocol data unit error
    	IT     = 0x10, // Inactivity test
    	XUDT   = 0x11, // Extended unitdata
    	XUDTS  = 0x12, // Extended unitdata service
    	LUDT   = 0x13, // Long unitdata
    	LUDTS  = 0x14, // Long unitdata service
        };

        enum Parameters {
    	EndOfParameters                = 0,
    	DestinationLocalReference      = 0x01,
    	SourceLocalReference           = 0x02,
    	CalledPartyAddress             = 0x03,
    	CallingPartyAddress            = 0x04,
    	ProtocolClass                  = 0x05,
    	Segmenting                     = 0x06,
    	ReceiveSequenceNumber          = 0x07,
    	Sequencing                     = 0x08,
    	Credit                         = 0x09,
    	ReleaseCause                   = 0x0a,
    	ReturnCause                    = 0x0b,
    	ResetCause                     = 0x0c,
    	ErrorCause                     = 0x0d,
    	RefusalCause                   = 0x0e,
    	Data                           = 0x0f,
    	Segmentation                   = 0x10,
    	HopCounter                     = 0x11,
    	Importance                     = 0x12, // ITU only
    	LongData                       = 0x13,
    	MessageTypeInterworking        = 0xf8, // ANSI only
    	INS                            = 0xf9, // ANSI only
    	ISNI                           = 0xfa, // ANSI only
        };

        /**
         * Constructor
         * @param type Type of SCCP message as enumeration
         */
        inline SS7MsgSCCP(Type type)
    	: SignallingMessage(lookup(type,"Unknown")), m_type(type), m_data(0)
    	{ }

        /**
         * Destructor
         * NOTE: The SCCP message does not own the data pointer
         * In destructor the data pointer should be valid if data was set from decode message
         * In any other cases the pointer should be 0
         * NOTE: The data is not destroyed!! Only removed from data object and after the data object is destroyed
         */
        virtual ~SS7MsgSCCP();

        /**
         * Get the type of this message
         * @return The type of this message as enumeration
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Helper method to change the message type
         * @param type The new message type
         */
        inline void updateType(Type type)
    	{ m_type = type; params().assign(lookup(type,"Unknown")); }

        /**
         * Utility method to verify if this message is a long unit data
         * @return True if this message is a long unit data
         */
        inline bool isLongDataMessage() const
    	{ return m_type == LUDT || m_type == LUDTS; }

        /**
         * Utility method to verify if this message can be a UDT message
         * A SCCP message can be an UDT message if it not contains HopCounter parameter
         * or other optional parameters
         * @return True if this message can be a UDT message
         */
        inline bool canBeUDT() const
    	{ return !(params().getParam(YSTRING("Importance")) ||
    		    params().getParam(YSTRING("HopCounter"))); }

        /**
         * Fill a string with this message's parameters for debug purposes
         * @param dest The destination string
         * @param label The routing label
         * @param params True to add parameters
         * @param raw Optional raw message data to be added to destination
         * @param rawLen Raw data length
         */
        void toString(String& dest, const SS7Label& label, bool params,
    	const void* raw = 0, unsigned int rawLen = 0) const;

        /**
         * Get the dictionary with the message names
         * @return Pointer to the dictionary with the message names
         */
        static const TokenDict* names();

        /**
         * Convert an SCCP message type to a C string
         * @param type Type of SCCP message to look up
         * @param defvalue Default string to return
         * @return Name of the SCCP message type
         */
        static inline const char* lookup(Type type, const char* defvalue = 0)
    	{ return TelEngine::lookup(type,names(),defvalue); }

        /**
         * Look up an SCCP message name
         * @param name String name of the SCCP message
         * @param defvalue Default type to return
         * @return Encoded type of the SCCP message
         */
        static inline Type lookup(const char* name, Type defvalue = Unknown)
    	{ return static_cast(TelEngine::lookup(name,names(),defvalue)); }

        /**
         * Set data for this message
         * @param data the data
         */
        inline void setData(DataBlock* data)
    	{ m_data = data; }

        /**
         * Remove the data from this message
         */
        inline void removeData()
    	{ m_data = 0; }

        /**
         * Obtain the data associated with this message
         * @return The data
         */
        inline DataBlock* getData()
    	{ return m_data; }

        /**
         * Extract the data associated with this message
         * @return The data
         */
        inline DataBlock* extractData()
    	{
    	    DataBlock* data = m_data;
    	    m_data = 0;
    	    return data;
    	}

    private:
        Type m_type;                         // Message type
        DataBlock* m_data;                   // Message data NOTE: The message never owns the data
    };

    class YSIG_API SS7MsgSccpReassemble : public SS7MsgSCCP
    {
        YCLASS(SS7MsgSccpReassemble,SignallingMessage)
    public:
        enum Return {
    	Rejected,
    	Accepted,
    	Error,
    	Finished,
        };
        /**
         * Constructor
         * @param msg The first message segment
         * @param label The MTP routing label
         * @param timeToLive The time internal in milliseconds that we wait to reassemble the message
         */
        SS7MsgSccpReassemble(SS7MsgSCCP* msg, const SS7Label& label, unsigned int timeToLive);

        /**
         * Destructor
         */
        virtual ~SS7MsgSccpReassemble();

        /**
         * Helper method used to check if the given message is part of this reassembling process
         * @param msg The message to verify
         * @param label The SS7 routing label
         * @return True if msg is a part of this reassembling process
         */
        bool canProcess(const SS7MsgSCCP* msg, const SS7Label& label);

        /**
         * Append a sccp message segment to the main message
         * @param msg The message segment
         * @param label The SS7 routing label
         * @return One of the Return enum options
         */
        Return appendSegment(SS7MsgSCCP* msg, const SS7Label& label);

        /**
         * Check if this reassemble process has expired
         * @return True if this reassemble process has expired
         */
        inline bool timeout()
    	{ return m_timeout > 0 ? Time::msecNow() > m_timeout : false; }

        /**
         * Helper method to verify if all segments have arrived
         * @return True if all segments arrived
         */
        inline bool haveAllSegments()
    	{ return m_remainingSegments == 0; }
    private:
        SS7Label m_label;
        NamedList m_callingPartyAddress;
        u_int32_t m_segmentationLocalReference;
        u_int64_t m_timeout;
        unsigned char m_remainingSegments;
        unsigned int m_firstSgmDataLen;
    };

    class YSIG_API SccpSubsystem : public RefObject
    {
        YCLASS(SccpSubsystem,RefObject);
    public:
        /**
         * Constructor
         * @param ssn The subsystem number alocated to this subsystem
         * @param state The subsystem initial state
         * @param smi The subsystem multiplicity indicator
         */
        inline SccpSubsystem(int ssn, SCCPManagement::SccpStates state = SCCPManagement::Allowed, unsigned char smi = 0)
    	: m_ssn(ssn), m_smi(smi), m_state(state)
    	{ }

        virtual ~SccpSubsystem()
    	{ }

        /**
         * Obtain subsystem number
         * @return The subsystem number
         */
        inline unsigned char getSSN()
    	{ return m_ssn; }

        /**
         * Obtain the state of this subsystem
         * @return This subsystem state
         */
        inline SCCPManagement::SccpStates getState()
    	{ return m_state; }

        /**
         * Set the state of this subsystem
         * @param state The new state
         */
        inline void setState(SCCPManagement::SccpStates state)
    	{ m_state = state; }

        /**
         * Obtain the subsystem multiplicity indicator of this sccp subsystem
         * @return The subsystem multiplicity indicator
         */
        inline unsigned char getSmi()
    	{ return m_smi; }

        /**
         * Dump this sccp subsystem status
         * @param dest Destination string
         */
        void dump(String& dest)
    	{
    	    dest << "Subsystem: " << m_ssn << " , smi: " << m_smi;
    	    dest << ", state: " << SCCPManagement::stateName(m_state) << " ";
    	}
    private:
        unsigned char m_ssn;
        unsigned char m_smi;
        SCCPManagement::SccpStates m_state;
    };

    class YSIG_API RemoteBackupSubsystem : public GenObject
    {
        YCLASS(RemoteBackupSubsystem,GenObject);
    public:

        /**
         * Constructor
         * @param ssn Remote subsystem number
         * @param pointcode Remote pointcode
         * @param wfg True if we are expecting SOG from this remote subsystem
         */
        RemoteBackupSubsystem(unsigned char ssn, int pointcode, bool wfg = false)
    	: m_ssn(ssn), m_pointcode(pointcode), m_waitForGrant(wfg)
    	{ }

        virtual ~RemoteBackupSubsystem()
    	{ }

        /**
         * Helper method used to verify if a remore subsystem match this one
         * @param ssn Remote subsystem number
         * @param pointcode Remote pointcode
         * @return True if pointcode and ssn match
         */
        inline bool equals(unsigned char ssn, int pointcode)
    	{ return m_pointcode == pointcode && m_ssn == ssn; }

        /**
         * Helper method to reset wait for grant flag
         */
        inline void permisionGranted()
    	{ m_waitForGrant = false; }

        /**
         * Check if we are steel waiting to receive SOG
         * @return True if SOG message has arrived from the remote subsystem
         */
        inline bool waitingForGrant()
    	{ return m_waitForGrant; }

    private:
        unsigned char m_ssn;
        int m_pointcode;
        bool m_waitForGrant;
    };

    class YSIG_API SccpLocalSubsystem : public RefObject, public Mutex
    {
        YCLASS(SccpLocalSubsystem,RefObject);
    public:
        /**
         * Constructor
         * @param ssn The subsystem number
         * @param coordInterval The time interval for coordinate changed timer
         * @param istInterval The time interval for ignore status test timer
         * @param smi Subsystem multiplicity indicator
         */
        SccpLocalSubsystem(unsigned char ssn, u_int64_t coordInterval, u_int64_t istInterval, unsigned char smi = 0);

        /**
         * Destructor
         */
        virtual ~SccpLocalSubsystem();

        /**
         * Obtain the subsystem number number of this sccp subsystem
         * @return The ssn associated with this subsystem
         */
        inline unsigned char getSSN()
    	{ return m_ssn; }

        /**
         * Set a new state of this SCCP subsystem
         * @param newState Thew new state to set
         */
        inline void setState(SCCPManagement::SccpStates newState)
    	{ m_state = newState; }

        /**
         * Obtain the state associated with this sccp subsystem
         * @return The state of this SCCP subsystem
         */
        inline SCCPManagement::SccpStates getState()
    	{ return m_state; }

        /**
         * Start coordinate change timer
         */
        inline void startCoord()
    	{ m_coordTimer.start(); }

        /**
         * Check if this subsystem should ignore SST (Subsystem status test)
         */
        inline bool ignoreTests()
    	{ return m_ignoreTestsTimer.started(); }

        /**
         * Inform this subsystem if should ignore subsystem status tests
         * @param ignore True to ignore subsystem status tests
         */
        void setIgnoreTests(bool ignore);

        /**
         * Check if coordinate change timer has timed out
         * @return True if coordinate change timer has timed out
         */
        bool timeout();

        /**
         * Handle coord timer timeout
         * @param mgm Pointer to sccp management who owns this sccp local subsystem
         */
        void manageTimeout(SCCPManagement* mgm);

        /**
         * Stop coordinate change timer
         */
        inline void stopCoordTimer()
    	{ m_coordTimer.stop(); }

        /**
         * Obtain the subsystem multiplicity indicator of this subsystem
         * @return The Subsystem multiplicity indicator
         */
        inline unsigned char getSmi()
    	{ return m_smi; }

        /**
         * Dump this sccp subsystem status
         * @param dest Destination string
         */
        void dump(String& dest);

        /**
         * Process a subsystem out of service grant message
         * @param ssn The remote ssn
         * @param pointcode The remote pointcode
         * @return True if the message was procesed
         */
        bool receivedSOG(unsigned char ssn, int pointcode);

        /**
         * Helper method used to reset timers
         */
        inline void resetTimers()
    	{ m_coordTimer.stop(); m_ignoreTestsTimer.stop(); }
        /**
         * Clear remote backup subsystems
         */
        inline void clearBackups()
    	{
    	    Lock lock(this);
    	    m_backups.clear();
    	}

        /**
         * Append new backup subsystem
         * @param backup The backup subsystem to append
         */
        inline void appendBackup(RemoteBackupSubsystem* backup)
    	{
    	    Lock lock(this);
    	    m_backups.append(backup);
    	}
    private:
        unsigned char m_ssn;
        unsigned char m_smi;
        SCCPManagement::SccpStates m_state;
        SignallingTimer m_coordTimer;
        SignallingTimer m_ignoreTestsTimer;
        ObjList m_backups;
        bool m_receivedAll;
    };

    /**
     * Helper class to keep a remote sccp
     */
    class YSIG_API SccpRemote : public RefObject, public Mutex
    {
        YCLASS(SccpRemote,RefObject);
    public:
        /**
         * Constructor
         * @param pcType The pointcode type
         */
        SccpRemote(SS7PointCode::Type pcType);

        /**
         * Constructor.
         * Construncot an Remote sccp from given pointcode and pointcode type
         * @param pointcode Integer value assigned to remote pointcode.
         * @param pcType Remote pointcode type
         */
        SccpRemote(unsigned int pointcode, SS7PointCode::Type pcType);

        /**
         * Destructor
         */
        virtual ~SccpRemote();

        /**
         * Initialize the pointcode and subsystems list from a string
         * @param params String containing the pointcode and the subsystems list
         * @return False if the pointcode from the string is not valid
         * Usage
         */
        bool initialize(const String& params);

        /**
         * Obtain the state of this remote SCCP
         * @return Remote SCCP state
         */
        inline SCCPManagement::SccpStates getState()
    	{ return m_state; }

        /**
         * Find a subsystem stored in remote subsystems list
         * @param ssn The subsystem number of the remote subsystem
         * @return Pointer to the Subsystem or 0 if it was not found
         */
        SccpSubsystem* getSubsystem(int ssn);

        /**
         * Set remote SCCP state
         * @param state The new state of the remote SCCP
         */
        void setState(SCCPManagement::SccpStates state);

        /**
         * Obtain the PointCode of the remote SCCP
         * @return The pointcode of the remote SCCP
         */
        inline const SS7PointCode& getPointCode()
    	{ return m_pointcode; }

        /**
         * Obtain the pointcode as an integer
         * @return The packed pointcode representation
         */
        inline int getPackedPointcode()
    	{ return m_pointcode.pack(m_pointcodeType); }

        /**
         * Obtain a string representation of the remote pointcode type
         * @return String representation of remote pointcode type
         */
        inline const char* getPointCodeType()
    	{ return SS7PointCode::lookup(m_pointcodeType); }

        /**
         * Dump this sccp status an all it's subsystems
         * @param dest Destination string
         * @param extended True to append the subsystems status
         */
        void dump(String& dest, bool extended = false);

        /**
         * Helper method to change a subsystem state
         * @param ssn The subsystem ssn
         * @param newState The subsystem new state
         * @return False if the subsystem state is the same
         */
        bool changeSubsystemState(int ssn,SCCPManagement::SccpStates newState);

        /**
         * Helper method to obtain remote sccp's subsystems list
         * @return The subsystems list
         */
        inline ObjList& getSubsystems()
    	{ return m_subsystems; }

        /**
         * Helper method to set congestion level
         * @param cl The new congestion level
         */
        inline void setCongestion(unsigned int cl)
    	{ m_congestionLevel = cl; }

        /**
         * Helper method to reset congestion level
         */
        inline void resetCongestion()
    	{ m_congestionLevel = 0; }

        /**
         * Helper method to obtain the congestion level
         * @return The congestion level
         */
        inline unsigned int getCongestion()
    	{ return m_congestionLevel; }
    private:
        SS7PointCode m_pointcode;
        SS7PointCode::Type m_pointcodeType;
        ObjList m_subsystems;
        SCCPManagement::SccpStates m_state;
        unsigned int m_congestionLevel;
    };

    class YSIG_API SS7AnsiSccpManagement : public SCCPManagement
    {
        YCLASS(SS7AnsiSccpManagement,SCCPManagement)
    public:

        /**
         * Constructor
         */
        inline SS7AnsiSccpManagement(const NamedList& params)
    	: SCCPManagement(params,SS7PointCode::ANSI)
    	{ }

        virtual ~SS7AnsiSccpManagement();

        /**
         * Process a management message received from sccp
         * @param message The message to process
         * @return True if the message was processed successfully
         */
        virtual bool processMessage(SS7MsgSCCP* message);

        /**
         * Encode a sccp management message and send it to remote address
         * @param msgType The SCCP management message type
         * @param params List of message parameters
         * @return True if the message was successfully send
         */
        virtual bool sendMessage(SCCPManagement::MsgType msgType, const NamedList& params);

        /**
         * Handle notifications received from remote concerned sccp's
         * @param rsccp Remote SCCP pointcode
         * @param newState The new state of the remote SCCP
         */
        virtual void manageSccpRemoteStatus(SccpRemote* rsccp, SS7Route::State newState);

        /**
         * Process the status of subsystems
         * @param subsystem The subsystem who's status has changed
         * @param allowed True if the subsystem status is Allowed false for Prohibited
         * @param remote The remote sccp pointcode where the subsystem is located
         * @param smi Subsystem Multiplicity Indicator
         */
        virtual void handleSubsystemStatus(SccpSubsystem* subsystem, bool allowed, SccpRemote* remote, int smi);

        /**
         * Handle a SCCP Management message
         * @param msgType The message type
         * @param params List of message parameters
         * @return True if the message was handled
         */
        bool handleMessage(int msgType, NamedList& params);
    };

    class YSIG_API SS7ItuSccpManagement : public SCCPManagement
    {
        YCLASS(SS7ItuSccpManagement,SCCPManagement)
    public:

        /**
         * Constructor
         */
        SS7ItuSccpManagement(const NamedList& params);

        /**
         * Destructor
         */
        virtual ~SS7ItuSccpManagement()
    	{ }

        /**
         * Process a management message received from sccp
         * @param message The message to process
         * @return True if the message was processed successfully
         */
        virtual bool processMessage(SS7MsgSCCP* message);

        /**
         * Encode and send a SCCP ITU management message
         * @param msgType The type of sccp management message
         * @param params List of parameters
         * @return True if the message was successfully sent
         */
        virtual bool sendMessage(MsgType msgType, const NamedList& params);

        /**
         * Handle notifications received from remote concerned sccp's
         * @param rsccp Remote SCCP pointcode
         * @param newState The new state of the remote SCCP
         */
        virtual void manageSccpRemoteStatus(SccpRemote* rsccp, SS7Route::State newState);

        /**
         * Handle a SCCP Management message
         * @param msgType The message type
         * @param params List of message parameters
         * @return True if the message was handled
         */
        bool handleMessage(int msgType, NamedList& params);

        /**
         * Process the status of subsystems
         * @param subsystem The subsystem who's status has changed
         * @param allowed True if the subsystem status is Allowed false for Prohibited
         * @param remote The remote sccp pointcode where the subsystem is located
         * @param smi Subsystem Multiplicity Indicator
         */
        virtual void handleSubsystemStatus(SccpSubsystem* subsystem, bool allowed, SccpRemote* remote, int smi);

    };

    /**
     * Helper class to memorize SCCP data segments
     */
    class SS7SCCPDataSegment : public GenObject
    {
        YCLASS(SS7SCCPDataSegment,GenObject)
    public:
        /**
         * Constructor
         * @param index The index in the original DataBlock where this segment starts
         * @param length The length of this segment
         */
        inline SS7SCCPDataSegment(unsigned int index, unsigned int length)
    	: m_length(length), m_index(index)
    	{}

        /**
         * Destructor
         */
        virtual ~SS7SCCPDataSegment()
    	{}

        /**
         * Assignees to a DataBlock this segment's data
         * @param temp The destination DataBlock segment
         * @param orig The original DataBlock where this segment is located
         */
        inline void fillSegment(DataBlock& temp, const DataBlock& orig)
    	{ temp.assign(orig.data(m_index,m_length),m_length,false); }

    private:
        unsigned int m_length;
        unsigned int m_index;
    };

    /**
     * Implementation of SS7 Signalling Connection Control Part
     * @short SS7 SCCP implementation
     */
    class YSIG_API SS7SCCP : public SS7Layer4, public SCCP, public Mutex
    {
        YCLASS(SS7SCCP,SCCP)
        friend class SCCPManagement;
    public:
        enum ReturnCauses {
    	NoTranslationAddressNature         = 0x00,
    	NoTranslationSpecificAddress       = 0x01,
    	SubsystemCongestion                = 0x02,
    	SubsystemFailure                   = 0x03,
    	UnequippedUser                     = 0x04,
    	MtpFailure                         = 0x05,
    	NetworkCongestion                  = 0x06,
    	Unqualified                        = 0x07,
    	ErrorInMessageTransport            = 0x08,
    	ErrorInLocalProcessing             = 0x09,
    	DestinationCanNotPerformReassembly = 0x0a,
    	SccpFailure                        = 0x0b,
    	HopCounterViolation                = 0x0c,
    	SegmentationNotSupported           = 0x0d,
    	SegmentationFailure                = 0x0e,
    	// ANSI only
    	MessageChangeFailure               = 0xf7,
    	InvalidINSRoutingRequest           = 0xf8,
    	InvalidISNIRoutingRequest          = 0xf9,
    	UnauthorizedMessage                = 0xfa,
    	MessageIncompatibility             = 0xfb,
    	NotSupportedISNIRouting            = 0xfc,
    	RedundantISNIConstrainedRouting    = 0xfd,
    	ISNIIdentificationFailed           = 0xfe,
        };

        enum Control {
    	Status                    = 0x01,
    	FullStatus                = 0x02,
    	EnableExtendedMonitoring  = 0x03,
    	DisableExtendedMonitoring = 0x04,
    	EnablePrintMsg            = 0x05,
    	DisablePrintMsg           = 0x06,
        };
        /**
         * Constructor
         */
         SS7SCCP(const NamedList& config);

        /**
          * Destructor
          */
         ~SS7SCCP();

        /**
         * Configure and initialize the Signalling connection control part
         * @param config Optional configuration parameters override
         * @return True if SCCP was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Attach a SS7 network or router to this service. Detach itself from the old one
         * @param network Pointer to network or router to attach
         */
        virtual void attach(SS7Layer3* network);

        /**
         * Converts an SCCP message to a Message Signal Unit and push it down the protocol stack.
         * The given message is consumed
         * @param msg The message to send
         * @param local True if the message is local initiated
         * @return Link the message was successfully queued to, negative for error
         */
        int transmitMessage(SS7MsgSCCP* msg, bool local = false);

        /**
          * Receive management information from attached users.
          * @param type The type of management message
          * @param params List of parameters (Affected subsystem [M])
          * @return True if the notification was processed
          */
         virtual bool managementStatus(Type type, NamedList& params);

        /**
         * Send a message
         * Reimplemented from SCCP
         * @param data Data to be transported trough SCCP protocol
         * @param params SCCP parameters
         */
        virtual int sendMessage(DataBlock& data, const NamedList& params);

        /**
         * Process a MSU received from the Layer 3 component
         * @param msu Message data, starting with Service Indicator Octet
         * @param label Routing label of the received MSU
         * @param network Network layer that delivered the MSU
         * @param sls Signalling Link the MSU was received from
         * @return Result of MSU processing
         */
        virtual HandledMSU receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls);

        /**
         * Notification for receiving User Part Unavailable
         * @param type Type of Point Code
         * @param node Node on which the User Part is unavailable
         * @param part User Part (service) reported unavailable
         * @param cause Unavailability cause - Q.704 15.17.5
         * @param label Routing label of the UPU message
         * @param sls Signaling link the UPU was received on
         */
        virtual void receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
    	SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls);

        virtual bool control(NamedList& params);

        /**
         * Message changeover procedure for segmentation purpose
         * @param origMsg The original message
         * @param label MTP3 routing label
         * @param local True if the origMsg is local initiated
         * @return Negative value if the message failed to be sent
         */
        int segmentMessage(SS7MsgSCCP* origMsg, const SS7Label& label, bool local);

        /**
         * Helper method to know if we use ITU or ANSI
         */
         inline const bool ITU() const
    	{ return m_type == SS7PointCode::ITU; }

        /**
         * Check if GT digit parser of should ignore unknown digits encoding
         * @return True if unknown digits are ignored
         */
        inline bool ignoreUnknownAddrSignals() const
    	{ return m_ignoreUnkDigits; }

        /**
         * Process a notification generated by the attached network layer
         * @param link Network or linkset that generated the notification
         * @param sls Signalling Link that generated the notification, negative if none
         */
        virtual void notify(SS7Layer3* link, int sls);

        /**
         * Process route status changed notifications
         * @param type Type of Point Code
         * @param node Destination node witch communication status has changed
         * @param state The new route state
         */
        virtual void routeStatusChanged(SS7PointCode::Type type, const SS7PointCode& node, SS7Route::State state);
        /**
         * Obtain the number of messages send by this SCCP instance
         * @return The number of messages send
         */
        inline unsigned int messagesSend()
    	{ return m_totalSent; }

        /**
         * Obtain the number of messages received by this SCCP instance
         * @return The number of messsages received
         */
        inline unsigned int messagesReceived()
    	{ return m_totalReceived; }

        /**
         * Obtain the number of errors found by this SCCP instance
         * @return The number of errors found
         */
        inline unsigned int errors()
    	{ return m_errors; }

        /**
         * Obtain the number of GT translations made by this SCCP instance
         * @return The number of translations made
         */
        inline unsigned int translations()
    	{ return m_totalGTTranslations; }

        /**
         * Obtain the local SCCP point code
         * @return Pointer to local point code or 0 if no pointcode was configured
         */
        inline const SS7PointCode* getLocalPointCode() const
    	{ return m_localPointCode; }

        /**
         * Obtain local pointcode type
         * @return Local pointcode type
         */
         inline SS7PointCode::Type getLocalPointCodeType()
    	{ return m_type; }

        /**
         * Helper method to obtain the packed pointcode
         * @return Packed pointcode or 0 if local pointcode is not set
         */
        inline int getPackedPointCode()
    	{ return m_localPointCode ? m_localPointCode->pack(m_type) : 0; }

        /**
         * Helper method to check if attached layer 3 is up
         * @return True if attached layer3 is up
         */
        inline bool isLayer3Up()
    	{ return m_layer3Up; }
    protected:

        /**
         * This method is called to clean up and destroy the object after the
         *  reference counter becomes zero
         */
        virtual void  destroyed();

        /**
         * Helper method to check if this sccp needs extended monitoring
         * @return True if extended monitoring is needed
         */
        inline bool extendedMonitoring()
    	{ return m_extendedMonitoring; }

        /**
         * Method called periodically to check for timeouts
         * Reimplemented from SignallingComponent
         */
        virtual void timerTick(const Time& when);

        /**
         * Reassemble a message segment
         * @param segment The message segment
         * @param label The MTP routing label
         * @param msg Pointer to fill with the SS7MsgSccpReassemble who processed the message
         * @return SS7MsgSccpReassemble::Return enum value
         */
        SS7MsgSccpReassemble::Return reassembleSegment(SS7MsgSCCP* segment, const SS7Label& label, SS7MsgSCCP*& msg);

        /**
         * Check if this sccp is an endpoint
         * @return True if this sccp is an endpoint
         */
        virtual bool isEndpoint()
    	{ return m_endpoint; }

        /**
         * Route a SCCP Message to other SCCP component.
         * @param msg The sccp message.
         * @return -1 if the message failed to be routed
         */
        int routeLocal(SS7MsgSCCP* msg);
    private:
        // Helper method to calculate sccp address length
        unsigned int getAddressLength(const NamedList& params, const String& prefix);
        // Helper method used to ajust HopCounter and Importance parameters
        void ajustMessageParams(NamedList& params, SS7MsgSCCP::Type type);
        // Obtain maximum data length for a UDT, XUDT and LUDT message
        void getMaxDataLen(const SS7MsgSCCP* msg, const SS7Label& label,
    	    unsigned int& udtLength, unsigned int& xudtLength, unsigned int& ludtLength);
        // Helper method to obtain data segments
        // NOTE The list must be destroyed
        ObjList* getDataSegments(unsigned int dataLength, unsigned int maxSegmentSize);
        // Helper method to print a SCCP message
        void printMessage(const SS7MSU* msu, const SS7MsgSCCP* msg, const SS7Label& label);
        // Helper method used to extract the pointcode from Calling/Called party address.
        // Also will call GT translate if there is no pointcode in called party address
        int getPointCode(SS7MsgSCCP* msg, const String& prefix, const char* pCode, bool translate);
        // Transmit a SCCP message
        int sendSCCPMessage(SS7MsgSCCP* sccpMsg,int dpc,int opc, bool local);
        // Helper method used to verify if the message is a connectionless data message
        inline bool isSCLCMessage(int msgType)
    	{ return msgType == SS7MsgSCCP::UDT || msgType == SS7MsgSCCP::XUDT || msgType == SS7MsgSCCP::LUDT; }
        // Helper method used to verify if the message is a connectionless service message
        inline bool isSCLCSMessage(int msgType)
    	{ return msgType == SS7MsgSCCP::UDTS || msgType == SS7MsgSCCP::XUDTS || msgType == SS7MsgSCCP::LUDTS; }
        bool isSCOCMsg(int msgType);

        bool fillLabelAndReason(String& dest, const SS7Label& label,const SS7MsgSCCP* msg);
        inline bool unknownPointCodeType()
    	{ return m_type != SS7PointCode::ITU && m_type != SS7PointCode::ANSI && m_type != SS7PointCode::ANSI8; }
        // Helper method used to verify if th importance level is in standard range
        int checkImportanceLevel(int msgType,int initialImportance);
        // Helper method used to verify if the optional parameters present in message are declared in standards
        void checkSCLCOptParams(SS7MsgSCCP* msg);
        // Helper method used to monitor service messages
        void archiveMessage(SS7MsgSCCP* msg);
        // Helper method used to dump service messages and error codes status
        void dumpArchive(String& msg, bool extended);

        bool processMSU(SS7MsgSCCP::Type type, const unsigned char* paramPtr,
    	    unsigned int paramLen, const SS7Label& label, SS7Layer3* network, int sls);

        bool decodeMessage(SS7MsgSCCP* msg, SS7PointCode::Type pcType,
    	    const unsigned char* paramPtr, unsigned int paramLen);

        void returnMessage(SS7MsgSCCP* message, int error);

        static void switchAddresses(const NamedList& source, NamedList& dest);
        // Helper method to dump sccp status
        void printStatus(bool extended);
        void setNetworkUp(bool operational);

        SS7MSU* buildMSU(SS7MsgSCCP* msg, const SS7Label& label, bool checkLength = true) const;
        bool routeSCLCMessage(SS7MsgSCCP*& msg, const SS7Label& label);
        // Member data
        SS7PointCode::Type m_type;           // Point code type of this SCCP
        SS7PointCode* m_localPointCode;      // Local point code
        SCCPManagement* m_management;        // SCCP management
        ObjList m_reassembleList;            // List of sccp messages that are waiting to be reassembled
        u_int8_t m_hopCounter;               // Hop counter value
        NamedList m_msgReturnStatus;         // List with message return statistics
        u_int32_t m_segTimeout;              // Time in milliseconds for segmentation timeout
        bool m_ignoreUnkDigits;              // Check if GT digit parser of should ignore unknown digits encoding
        bool m_layer3Up;                     // Flag used to verify if the network is operational
        unsigned int m_maxUdtLength;         // The maximum length of data packet transported in a UDT message
        u_int32_t m_totalSent;               // Counter of the total number of SCCP messages sent
        u_int32_t m_totalReceived;           // The number of incoming sccp messages
        u_int32_t m_errors;                  // Counter of the number of messages that failed to be delivered
        u_int32_t m_totalGTTranslations;     // The number of GT translations made
        u_int32_t m_gttFailed;               // Number of global title that failed to be translated
        bool m_extendedMonitoring;           // Flag used to check if the monitoring is normal or extended
        const char* m_mgmName;               // The name of the sccp management section from ysigchan.conf
        // Debug flags
        bool m_printMsg;                     // Print messages to output
        bool m_extendedDebug;                // Extended debug flag
        bool m_endpoint;                     // Flag used to force message processing if the message have a ssn
    };

    /**
     * RFC3868 SS7 SCCP implementation over SCTP/IP
     * SUA is intended to be used as a Provider-User where real SCCP runs on a
     *  Signalling Gateway and SCCP users are located on an Application Server.
     * @short SIGTRAN SCCP User Adaptation Layer
     */
    class YSIG_API SS7SUA : public SIGAdaptUser, public SCCP
    {
    };

    /**
     * A TCAP message wrapper, encapsulates the data received from SCCP
     * @short TCAP message wrapper
     */
    class YSIG_API SS7TCAPMessage : public GenObject
    {
    public:
        /**
         * Constructor
         * @param params NamedList reference containing information from the SCCP level
         * @param data DataBlock representing the TCAP payload
         * @param notice Flag if this is a notification, true if it is, false if it's a message
         */
        inline SS7TCAPMessage(NamedList& params, DataBlock& data, bool notice = false)
    	: m_msgParams(params), m_msgData(data), m_notice(notice)
    	{}

        /**
         * Get the SCCP parameters
         * @return NamedList reference containing information from the SCCP level
         */
        inline NamedList& msgParams()
    	{ return m_msgParams; }

        /**
         * Get the TCAP message data
         * @return DataBlock representing the encoded TCAP message
         */
        inline DataBlock& msgData()
    	{ return m_msgData; }

        /**
         * Is this message a notice or a normal message?
         * @return True if message is a notice, false otherwise
         */
        inline bool& isNotice()
    	{ return m_notice; }

    private:
        NamedList m_msgParams;
        DataBlock m_msgData;
        bool m_notice;
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part
     * @short SS7 TCAP implementation
     */
    class YSIG_API SS7TCAP : public SCCPUser
    {
        YCLASS(SS7TCAP,SCCPUser)
    public:
        /**
         * TCAP implementation variant
         */
        enum TCAPType {
    	UnknownTCAP,
    	ITUTCAP,
    	ANSITCAP,
        };

        /**
         * Component handling primitives between TCAP and TCAP user (TC-user)
         */
        enum TCAPUserCompActions {
    	TC_Invoke         = 1,        // ITU-T Invoke primitive, ANSI InvokeLast - Request/Indication
    	TC_ResultLast     = 2,        // ITU-T & ANSI ResultLast primitive - Request/Indication
    	TC_U_Error        = 3,        // ITU-T & ANSI ReturnError primitive - Request/Indication
    	TC_U_Reject       = 4,        // ITU-T & ANSI Reject primitive - Request/Indication, TC-user rejected the component
    	TC_R_Reject       = 5,        // ITU-T & ANSI Reject primitive - Indication, Remote TC-user rejected the component
    	TC_L_Reject       = 6,        // ITU-T & ANSI Reject primitive - Indication, local Component Sublayer rejected the component
    	TC_InvokeNotLast  = 7,        // ANSI InvokeNotLast primitive - Request/Indication
    	TC_ResultNotLast  = 8,        // ITU-T & ANSI ResultNotLast primitive - Request/Indication
    	TC_L_Cancel       = 9,        // Local Cancel primitive - Indication, inform TC-user that an operation has timed out
    	TC_U_Cancel       = 10,       // User Cancel primitive - Request, TC-user request cancellation of an operation
    	TC_TimerReset     = 11,       // Timer Reset - Indication, allow TC-user to refresh an operation timer
        };

        /**
         * TCAP message primitives
         */
        enum TCAPUserTransActions {
    	TC_Unknown         = 0,
    	TC_Unidirectional  = 1,          // ITU-T & ANSI - Request/Indication, request Unidirectional message
    	TC_Begin,                   // ITU-T - Request/Indication, begin a dialogue
    	TC_QueryWithPerm,           // ANSI - Request/Indication, begin a dialogue with permission to end it
    	TC_QueryWithoutPerm,        // ANSI - Request/Indication, begin a dialogue without permission to end it
    	TC_Continue,                // ITU-T - Request/Indication, continue a dialogue
    	TC_ConversationWithPerm,    // ANSI - Request/Indication, continue a dialogue with permission
    	TC_ConversationWithoutPerm, // ANSI - Request/Indication, continue a dialogue without permission
    	TC_End,                     // ITU-T -Request/Indication, end a dialogue
    	TC_Response,                // ANSI - Request/Indication, end a dialogue
    	TC_U_Abort,                 // ITU-T & ANSI - Request/Indication, abort a dialogue per user's request
    	TC_P_Abort,                 // ITU-T & ANSI - Indication, notify the abort of a dialogue because of a protocol error
    	TC_Notice,                  // ITU-T & ANSI - Indication, notify the TC-user that the network was unable to provide the requested service
        };

        /**
         * Component Operation Classes
         */
        enum TCAPComponentOperationClass {
    	SuccessOrFailureReport    = 1,
    	FailureOnlyReport         = 2,
    	SuccessOnlyReport         = 3,
    	NoReport                  = 4,
        };

        /**
         * Type of message counters
         */
        enum TCAPCounter {
    	IncomingMsgs,
    	OutgoingMsgs,
    	DiscardedMsgs,
    	NormalMsgs,
    	AbnormalMsgs,
        };

        /**
         * Constructor
         * @param params Parameters for building this TCAP
         */
        SS7TCAP(const NamedList& params);

        /**
         * Destructor
         */
        virtual ~SS7TCAP();

        /**
         * Configure and initialize the component and any subcomponents it may have
         * @param config Optional configuration parameters override
         * @return True if the component was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Send a message to SCCP for transport, inherited from SCCPUser
         * @param data User data
         * @param params SCCP parameters
         */
        virtual bool sendData(DataBlock& data, NamedList& params);

        /**
         * Notification from SCCP that a message has arrived, inherited from SCCPUser
         * @param data Received user data
         * @param params SCCP parameters
         * @return True if this user has processed the message, false otherwise
         */
        virtual HandledMSU receivedData(DataBlock& data, NamedList& params);

        /**
         * Notification from SCCP that a message failed to arrive to its destination, inherited from SCCPUser
         * @param data User data sent.
         * @param params SCCP parameters
         * Note! The data may not contain the full message block previously sent (in case of SCCP segmentation),
         * but it must always must contain the first segment
         */
        virtual HandledMSU notifyData(DataBlock& data, NamedList& params);

        /**
         * Notification from SCCP layer about management status
         * @param type Type of management notification
         * @param params Notification params
         */
        bool managementNotify(SCCP::Type type, NamedList& params);

        /**
         * Attach a SS7 TCAP user
         * @param user Pointer to the TCAP user to attach
         */
        void attach(TCAPUser* user);

        /**
         * Detach a SS7 TCAP user
         * @param user TCAP user to detach
         */
        void detach(TCAPUser* user);

        /**
         * A TCAP user made a request
         * @param requestParams NamedList containing all the necessary data for the TCAP request
         * @return A SS7TCAPError reporting the status of the request
         */
        virtual SS7TCAPError userRequest(NamedList& requestParams);

        /**
         * Process received SCCP data
         * @param sccpData A TCAP message received from SCCP to process
         * @return A code specifying if this message was handled
         */
        virtual HandledMSU processSCCPData(SS7TCAPMessage* sccpData);

        /**
         * Report which TCAP implementation is in use
         */
        inline TCAPType tcapType()
    	{ return m_tcapType; }

        /**
         * Set TCAP version
         * @param type TCAP version
         */
        inline void setTCAPType(TCAPType type)
    	{ m_tcapType = type; }

        /**
         * Enqueue data received from SCCP as a TCAP message, kept in a processing queue
         * @param msg A SS7TCAPMessage pointer containing all data received from SSCP
         */
        virtual void enqueue(SS7TCAPMessage* msg);

        /**
         * Dequeue a TCAP message when ready to process it
         * @return A SS7TCAPMessage pointer dequeued from the queue
         */
        virtual SS7TCAPMessage* dequeue();

        /**
         * Get a new transaction ID
         * @return A transaction ID
         */
        virtual const String allocTransactionID();

        /**
         * Get a new transaction ID
         * @param str String into which to put the id
         */
        void allocTransactionID(String& str);

        /**
         * Dictionary for TCAP versions
         */
        static const TokenDict s_tcapVersion[];

        /**
         * Dictionary for component primitives
         */
        static const TokenDict s_compPrimitives[];

        /**
         * Dictionary for transaction primitives
         */
        static const TokenDict s_transPrimitives[];

        /**
         * Dictionary for component opearation classes
         */
        static const TokenDict s_compOperClasses[];

        /**
         * Build a transaction
         * @param type Type with which to build the transactions
         * @param transactID ID for the transaction
         * @param params Parameters for building the transaction
         * @param initLocal True if built by user, false if by remote end
         * @return A transaction
         */
        virtual SS7TCAPTransaction* buildTransaction(SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	bool initLocal = true) = 0;

        /**
         * Find the transaction with the given id
         * @param tid Searched local id
         * @return A pointer to the transaction or null if not found
         */
        SS7TCAPTransaction* getTransaction(const String& tid);

        /**
         * Remove transaction
         * @param tr The transaction to remove
         */
        void removeTransaction(SS7TCAPTransaction* tr);

        /**
         * Method called periodically to do processing and timeout checks
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Send to TCAP users a decode message
         * @param params Message in NamedList form
         * @return True if the message was handled by a user, false otherwise
         */
        virtual bool sendToUser(NamedList& params);

        /**
         * Build SCCP data
         * @param params NamedList containing the parameters to be given to SCCP
         * @param tr Transaction for which to build SCCP data
         */
        virtual void buildSCCPData(NamedList& params, SS7TCAPTransaction* tr);

        /**
         * Status of TCAP
         * @param status NamedList to fill with status information
         */
        virtual void status(NamedList& status);

        /**
         * Status of TCAP users
         * @param status NamedList to fill with user status information
         */
        virtual void userStatus(NamedList& status);

        /**
         * Handle an decoding error
         * @param error The encoutered error
         * @param params TCAP message parameters which where successfully decoded until the error was encoutered
         * @param data Data block containing the rest of the message
         * @param tr Transaction to which this message belongs to
         * @return Status if the error was handled or not
         */
        virtual HandledMSU handleError(SS7TCAPError& error, NamedList& params, DataBlock& data, SS7TCAPTransaction* tr = 0);

        /**
         * Update the SCCP Management state for this SSN when requested by a user
         * @param user The TCAP user which changed its state
         * @param status The state of the TCAP user
         * @param params Additional parameters to be transmitted to the SCPP
         */
        virtual void updateUserStatus(TCAPUser* user, SCCPManagement::LocalBroadcast status, NamedList& params);

        /**
         * Increment one of the status counters
         * @param counterType The type of the counter to increment
         */
        inline void incCounter(TCAPCounter counterType)
        {
    	switch (counterType) {
    	    case IncomingMsgs:
    		m_recvMsgs++;
    		break;
    	    case OutgoingMsgs:
    		m_sentMsgs++;
    		break;
    	    case DiscardedMsgs:
    		m_discardMsgs++;
    		break;
    	    case NormalMsgs:
    		m_normalMsgs++;
    		break;
    	    case AbnormalMsgs:
    		m_abnormalMsgs++;
    		break;
    	    default:
    		break;
    	}
        }

        /**
         * Retrieve one of the status counters
         * @param counterType The type of the counter to increment
         * @return The value of the counter
         */
        inline unsigned int count(TCAPCounter counterType)
        {
    	switch (counterType) {
    	    case IncomingMsgs:
    		return m_recvMsgs;
    	    case OutgoingMsgs:
    		return m_sentMsgs;
    	    case DiscardedMsgs:
    		return m_discardMsgs;
    	    case NormalMsgs:
    		return m_normalMsgs;
    	    case AbnormalMsgs:
    		return m_abnormalMsgs;
    	    default:
    		break;
    	}
    	return 0;
        }

        /**
         * Get the type of transaction in string form
         * @param tr Type of transaction
         * @return A string containing the string form of that type of transaction
         */
        static inline const char* lookupTransaction(int tr)
    	{ return lookup(tr,s_transPrimitives,"Unknown"); }

        /**
         * Get the type of transaction from string form
         * @param tr Type of transaction in string form
         * @return The type of transaction
         */
        static inline int lookupTransaction(const char* tr)
    	{ return lookup(tr,s_transPrimitives,TC_Unknown); }

        /**
         * Get the type of component in string form
         * @param comp Type of component
         * @return A string containing the string form of that type of component
         */
        static inline const char* lookupComponent(int comp)
    	{ return lookup(comp,s_compPrimitives,"Unknown"); }

        /**
         * Get the type of component from string form
         * @param comp Type of component
         * @return The type of component
         */
        static inline int lookupComponent(const char* comp)
    	{ return lookup(comp,s_compPrimitives,TC_Unknown); }

    protected:
        virtual SS7TCAPError decodeTransactionPart(NamedList& params, DataBlock& data) = 0;
        virtual void encodeTransactionPart(NamedList& params, DataBlock& data) = 0;
        bool sendSCCPNotify(NamedList& params);
        // list of TCAP users attached to this TCAP instance
        ObjList m_users;
        Mutex m_usersMtx;

        // list of messages received from sublayer, waiting to be processed
        ObjList m_inQueue;
        Mutex m_inQueueMtx;

        unsigned int m_SSN;
        unsigned int m_defaultRemoteSSN;
        unsigned int m_defaultHopCounter;
        SS7PointCode m_defaultRemotePC;
        SS7PointCode::Type m_remoteTypePC;
        u_int64_t m_trTimeout;

        // list of current TCAP transactions
        Mutex m_transactionsMtx;
        ObjList m_transactions;
        // type of TCAP
        TCAPType m_tcapType;

        // counter for allocating transaction IDs
        u_int32_t m_idsPool;

        // counters
        unsigned int m_recvMsgs;
        unsigned int m_sentMsgs;
        unsigned int m_discardMsgs;
        unsigned int m_normalMsgs;
        unsigned int m_abnormalMsgs;

        // Subsystem Status
        SCCPManagement::LocalBroadcast m_ssnStatus;
    };

    class YSIG_API SS7TCAPError
    {
    public:
        enum ErrorType {
    	// P-AbortCause TransactionProblems
    	Transact_UnrecognizedPackageType,     // named after the ANSI specification, equivalent to ITU-T UnrecongnizedMessageType P-AbortCause
    	Transact_IncorrectTransactionPortion, // <==> ITU-T incorrectTrasactionPortion P-AbortCause
    	Transact_BadlyStructuredTransaction,  // <==> ITU-T badlyFormattedTransactionPortion P-AbortCause
    	Transact_UnassignedTransactionID,
    	Transact_PermissionToReleaseProblem,  // HANDLING NOT DEFINED
    	Transact_ResourceUnavailable,         // <==> ITU-T resourceLimitation P-AbortCause

    	// P-AbortCause DialogProblem
    	Dialog_UnrecognizedDialoguePortionID, // ANSI only
    	Dialog_BadlyStructuredDialoguePortion,// ANSI only
    	Dialog_MissingDialoguePortion,        // ANSI only
    	Dialog_InconsistentDialoguePortion,   // ANSI only
    	Dialog_Abnormal,                      // ITU only, indication only

    	// GeneralProblem
    	General_UnrecognizedComponentType,   // named after the ANSI specification, equivalent to ITU-T UnrecognizedComponent General Problem
    	General_IncorrectComponentPortion,   // ANSI specification, equivalent to ITU-T MistypedComponent General Problem
    	General_BadlyStructuredCompPortion,  // ANSI specification, equivalent to ITU-T BadlyStructuredComponent General Problem
    	General_IncorrectComponentCoding,    // ANSI specification, no ITU-T equivalent

    	// InvokeProblem
    	Invoke_DuplicateInvokeID,           // ANSI & ITU-T specification
    	Invoke_UnrecognizedOperationCode,   // ANSI specification, equivalent to ITU-T UnrecognizedOperation Invoke Problem
    	Invoke_IncorrectParameter,          // ANSI specification, equivalent to ITU-T MistypedParameter Invoke Problem
    	Invoke_UnrecognizedCorrelationID,   // ANSI specification, equivalent to ITU-T UnrecognizedLinkedID Invoke Problem
    	Invoke_ResourceLimitation,          // ITU-T only
    	Invoke_InitiatingRelease,           // ITU-T only
    	Invoke_LinkedResponseUnexpected,    // ITU-T only
    	Invoke_UnexpectedLinkedOperation,   // ITU-T only

    	// ReturnResult
    	Result_UnrecognizedInvokeID,        // ITU-T only
    	Result_UnrecognisedCorrelationID,   // ANSI only
    	Result_UnexpectedReturnResult,      // ANSI specification, equivalent to ITU-T ReturnResultUnexpected Result Problem
    	Result_IncorrectParameter,          // ANSI specification, equivalent to ITU-T MistypedParameter Result Problem

    	// ReturnError
    	Error_UnrecognizedInvokeID,        // ITU-T only
    	Error_UnrecognisedCorrelationID,   // ANSI only
    	Error_UnexpectedReturnError,       // ANSI only
    	Error_UnrecognisedError,           // ANSI & ITU-T
    	Error_UnexpectedError,             // ANSI & ITU-T
    	Error_IncorrectParameter,          // ANSI specification, equivalent to ITU-T MistypedParameter Return Error Problem

    	Discard,
    	NoError,
        };

        /**
         * Constructor
         * @param tcapType TCAP specification user for this error
         */
        SS7TCAPError(SS7TCAP::TCAPType tcapType);

        /**
         * Constructor
         * @param tcapType TCAP specification used for this error
         * @param error The error
         */
        SS7TCAPError(SS7TCAP::TCAPType tcapType, ErrorType error);

        /**
         * Destructor
         */
        ~SS7TCAPError();

        /**
         * Get the error
         * @return The TCAP error
         */
        inline ErrorType error()
    	{ return m_error; }

        /**
         * Set the error
         * @param error Error to set
         */
        inline void setError(ErrorType error)
    	{ m_error = error; }

        /**
         * Error name
         * @return The error name
         */
        const String errorName();

        /**
         * The full value of the error
         * @return 2 byte integer containing the full code of the error
         */
        u_int16_t errorCode();

        /**
         * Obtain abstract TCAP error from TCAP protocol defined error value
         * @param tcapType Type of TCAP for which the error is searched
         * @param code TCAP protocol error value
         * @return The type of the error
         */
        static int errorFromCode(SS7TCAP::TCAPType tcapType, u_int16_t code);

        /**
         * Obtain TCAP specific error value from abstract TCAP error
         * @param tcapType Type of TCAP for which the error is searched
         * @param err Abstrat TCAP error
         * @return The error value as defined by the TCAP protocol
         */
        static u_int16_t codeFromError(SS7TCAP::TCAPType tcapType, int err);

        /**
         * Dictionary for error types
         */
        static const TokenDict s_errorTypes[];

    private:
        SS7TCAP::TCAPType m_tcapType;
        ErrorType m_error;
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part Transaction
     * @short SS7 TCAP transaction implementation
     */
    class YSIG_API SS7TCAPTransaction : public RefObject, public Mutex
    {
    public:
        enum TransactionState {
    	Idle                      = 0,
    	PackageSent               = 1,
    	PackageReceived           = 2,
    	Active                    = 3,
        };

        enum TransactionTransmit {
    	NoTransmit       = 0,
    	PendingTransmit  = 256,
    	Transmitted      = 521,
        };

        /**
         * Constructor
         * @param tcap TCAP holding this transaction
         * @param type Initiating type for transaction
         * @param transactID Transaction ID
         * @param params Decoded TCAP parameters for building the transaction
         * @param timeout Transaction timeout
         * @param initLocal True if the transaction was initiated locally, false if not
         */
        SS7TCAPTransaction(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	u_int64_t timeout, bool initLocal = true);

        /**
         * Destructor
         */
        ~SS7TCAPTransaction();

        /**
         * Process transaction data and fill the NamedList with the decoded data
         * @param params NamedList to fill with decoded data
         * @param data Data to decode
         * @return A TCAP error encountered whilst decoding
         */
        virtual SS7TCAPError handleData(NamedList& params, DataBlock& data) = 0;

        /**
         * An update request for this transaction
         * @param type The type of transaction to which this transaction should be updated
         * @param params Update parameter
         * @param updateByUser True if the update is made by the local user, false if it's made by the remote end
         * @return A TCAP Error
         */
        virtual SS7TCAPError update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser = true) = 0;

        /**
         * Handle TCAP relevant dialog data
         * @param params NamedList containing (if present) dialog information
         * @param byUser True if the dialog information is provided by the local user, false otherwise
         * @return A report error
         */
        virtual SS7TCAPError handleDialogPortion(NamedList& params,bool byUser = true) = 0;

        /**
         * Build a Reject component in answer to an encoutered error during decoding of the component portion
         * @param error The encountered error
         * @param params Decoded TCAP message parameters
         * @param data DataBlock containing the rest of the coded TCAP message
         * @return A report error
         */
        virtual SS7TCAPError buildComponentError(SS7TCAPError& error, NamedList& params, DataBlock& data);

        /**
         * Update components
         * @param params NamedList reference containing the update information
         * @param updateByUser Flag if the update was issued by local user or by remote
         * @return A report error
         */
        virtual SS7TCAPError handleComponents(NamedList& params, bool updateByUser = true);

        /**
         * Request encoding for the components of this transaction
         * @param params Components parameters to encode
         * @param data DataBlock reference in which to insert the encoded components
         */
        virtual void requestComponents(NamedList& params, DataBlock& data);

        /**
         * Fill the NamedList with transaction portion parameters
         * @param params NamedList reference to fill with transaction portion parameters
         */
        virtual void transactionData(NamedList& params);

        /**
         * Request content for this transaction
         * @param params List of parameters of this tranaction
         * @param data Data block to fill with encoded content
         */
        virtual void requestContent(NamedList& params, DataBlock& data) = 0;

        /**
         * Check components for timeouts
         */
        virtual void checkComponents();

        /**
         * Set the current type of transaction primitive
         * @param type The transaction primitive to be set
         */
        inline void setTransactionType(SS7TCAP::TCAPUserTransActions type)
    	{ Lock l(this); m_type = type; }

        /**
         * Retrieve the current type of primitive that is set for this transaction
         * @return The transaction primitive type
         */
        inline SS7TCAP::TCAPUserTransActions transactionType()
    	{ return m_type; }

        /**
         * Set the state of this transaction, trigger a transmission pending state
         * @param state The state to set for the transaction
         */
        inline void setState(TransactionState state)
        {
    	Lock l(this);
    	m_state = state;
    	// changing state automatically triggers a change in transmission state (except for Idle)
    	if (state != Idle)
    	    m_transmit = PendingTransmit;
        }

        /**
         * Retrieve the state of this transaction
         * @return The state of this transaction
         */
        inline TransactionState transactionState()
    	{ return m_state; }

        /**
         * Set the transmission state for this transaction
         * @param state The transmission state to be set
         */
        void setTransmitState(TransactionTransmit state);

        /**
         * The transmission state for this transaction
         * @return The current transmission state
         */
        inline TransactionTransmit transmitState()
    	{ return m_transmit; }

        /**
         * The TCAP to which this transaction belongs
         * @return A pointer to the TCAP component
         */
        inline SS7TCAP* tcap()
    	{ return m_tcap; }

        /**
         * Get the ID of the transaction so it can be used for list searches
         * @return A reference to the ID
         */
        const String& toString() const
    	{ return m_localID; }

        /**
         * Set the TCAP username to which this transaction belongs
         * @param name The name of the user to set
         */
        inline void setUserName(const String& name)
    	{ m_userName = name; }

        /**
         * Return the name of the TCAP user to which this transaction belongs
         * @return The name of the user
         */
        const String& userName()
    	{ return m_userName; }

        /**
         * Check if a basic end was set for this transaction
         * @return True if basic end was specified by the user, false if prearranged end was specified
         */
        inline bool basicEnd()
    	{ return m_basicEnd; }

        /**
         * Add SCCP Addressing information
         * @param fillParams NamedList to fill with addressing information
         * @param local True if the information is for the user, otherwise
         */
        void addSCCPAddressing(NamedList& fillParams, bool local);

        /**
         * Check if the flag to end this transaction immediately was set
         * @return True if the end flag was set, false otherwise
         */
        inline bool endNow()
    	{ return m_endNow; }

        /**
         * Set the flag to end this transaction immediately
         * @param endNow Boolean value to set to the end flag
         */
        inline void endNow(bool endNow)
    	{ m_endNow = endNow; }

        /**
         * Check if the transaction has timed out
         * @return True if the transaction timed out, false otherwise
         */
        inline bool timedOut()
    	{ return m_timeout.timeout(); }

        /**
         * Find a component with given id
         * @param id Id of component to find
         * @return The component with given id or null
         */
        SS7TCAPComponent* findComponent(const String& id);

        /**
         * Update the state of this transaction to end the transaction
         */
        virtual void updateToEnd();

        /**
         * Update transaction state
         * @param byUser True if update is requested by user, false if by remote
         */
        virtual void updateState(bool byUser = true) = 0;

        /**
         * Set information in case of abnormal dialog detection
         * @param params List of parameters where to set the abnormal dialog information
         */
        virtual void abnormalDialogInfo(NamedList& params);

        /**
         * @param params NamedList reference to fill with the decoded dialog information
         * @param data DataBlock reference from which to decode the dialog information
         * @return A TCAP error encountered whilst decoding
         */
        virtual SS7TCAPError decodeDialogPortion(NamedList& params, DataBlock& data) = 0;

        /**
         * @param params NamedList reference from which to take the dialog information to encode
         * @param data DataBlock reference into which to put the encoded dialog information
         */
        virtual void encodeDialogPortion(NamedList& params, DataBlock& data) = 0;

        /**
         * @param params NamedList reference to fill with the decoded component information
         * @param data DataBlock reference from which to decode the component information
         * @return A TCAP error encountered whilst decoding
         */
        virtual SS7TCAPError decodeComponents(NamedList& params, DataBlock& data) = 0;

        /**
         * @param params NamedList reference from which to take the component information to encode
         * @param data DataBlock reference into which to put the encoded component information
         */
        virtual void encodeComponents(NamedList& params, DataBlock& data) = 0;

    protected:
        SS7TCAP* m_tcap;
        SS7TCAP::TCAPType m_tcapType;
        String m_userName;
        String m_localID;
        String m_remoteID;
        SS7TCAP::TCAPUserTransActions m_type;
        TransactionState m_state;
        TransactionTransmit m_transmit;

        ObjList m_components;

        NamedList m_localSCCPAddr;
        NamedList m_remoteSCCPAddr;

        bool m_basicEnd; // basic or prearranged end (specified by user when sending a Response)
        bool m_endNow; // delete immediately after sending
        SignallingTimer m_timeout;
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part Component
     * @short SS7 TCAP component implementation
     */
    class YSIG_API SS7TCAPComponent : public GenObject
    {
    public:
        /**
         * Component state
         */
        enum TCAPComponentState {
    	Idle,
    	OperationPending,
    	OperationSent,
    	WaitForReject,
        };

        /**
         * Constructor
         * @param type TCAP type for which to build this component
         * @param trans TCAP transaction to which this component belongs to
         * @param params Parameters for building component
         * @param index Index in the list of parameters
         */
        SS7TCAPComponent(SS7TCAP::TCAPType type, SS7TCAPTransaction* trans, NamedList& params, unsigned int index);

        /**
         * Destructor
         */
        virtual ~SS7TCAPComponent();

        /**
         * Update this component's data
         * @param params Update parameters
         * @param index Index of parameters in the list for the update of this component
         */
        virtual void update(NamedList& params, unsigned int index);

        /**
         * Put the information of the component in a NamedList
         * @param index Index for build parameter names
         * @param fillIn NamedList to fill with this component's information
         */
        virtual void fill(unsigned int index, NamedList& fillIn);

        /**
         * Build a TCAP Component from a NamedList
         * @param type TCAP type of component
         * @param tr The transaction to which this component should belong
         * @param params Parameters for building the component
         * @param index Index in the list of parameters
         * @return A pointer to the built SS7TCAPComponent or nil if not all required parameters are present
         */
        static SS7TCAPComponent* componentFromNamedList(SS7TCAP::TCAPType type, SS7TCAPTransaction* tr, NamedList& params, unsigned int index);

        /**
         * Set the transaction to which this component belongs to
         * @param transact TCAP transaction
         */
        void setTransaction(SS7TCAPTransaction* transact);

        /**
         * Returns the transaction to which this component belongs to.
         */
        SS7TCAPTransaction* transaction();

        /**
         * Set the type for this component
         * @param type The type of the component
         */
        inline void setType(SS7TCAP::TCAPUserCompActions type)
    	{ m_type = type; }

        /**
         * Get the type of the component
         */
        inline SS7TCAP::TCAPUserCompActions type()
    	{ return m_type; }

        /**
         * Set the Invoke ID for this component
         * @param invokeID The invoke ID to assign
         */
        virtual void setInvokeID(String invokeID)
    	{ m_id = invokeID; }

        /**
         * String representation of this component's Invoke ID
         * @return String representation of Invoke ID
         */
        virtual const String&  toString () const
    	{ return m_id; }

        /**
         * String representation of this component's Correlation ID
         * @return String representation of Correlation ID
         */
        virtual const String&  correlationID() const
    	{ return m_corrID; }

        /**
         * Check if the component has timed out
         * @return True if the component timed out, false otherwise
         */
        inline bool timedOut()
    	{ return m_opTimer.timeout(); }

        /**
         * Set component state
         * @param state The state to be set
         */
        void setState(TCAPComponentState state);

        /**
         * Obtain the component state
         * @return The component state
         */
        inline TCAPComponentState state()
    	{ return m_state; }

        /**
         * Reset invocation timer on user request
         * @param params List of parameters
         * @param index Index of this component's parameters in the list
         */
        void resetTimer(NamedList& params, unsigned int index);

        /**
         * Retrieve operation class for this component
         * @return The class of the operation
         */
        SS7TCAP::TCAPComponentOperationClass operationClass()
    	{ return m_opClass; }

        /**
         * Dictionary for component states
         */
        static const TokenDict s_compStates[];

    private:
        SS7TCAPTransaction* m_transact;
        SS7TCAP::TCAPUserCompActions m_type;
        TCAPComponentState m_state;
        String m_id;
        String m_corrID;
        String m_opCode;
        String m_opType;
        SS7TCAP::TCAPComponentOperationClass m_opClass;
        SignallingTimer m_opTimer;
        SS7TCAPError m_error;
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part - specification ANSI
     * @short ANSI SS7 TCAP implementation
     */
    class YSIG_API SS7TCAPANSI : virtual public SS7TCAP
    {
        YCLASS(SS7TCAPANSI,SS7TCAP)
    public:
        enum TCAPTags {
    	TransactionIDTag    = 0xc7,
    	PCauseTag           = 0xd7,
    	UserAbortPTag       = 0xd8 , // Primitive
    	UserAbortCTag       = 0xf8,  // Constructor
        };

        enum TCAPDialogTags {
    	DialogPortionTag         = 0xf9,
    	ProtocolVersionTag       = 0xda,
    	IntApplicationContextTag = 0xdb,
    	OIDApplicationContextTag = 0xdc,
    	UserInformationTag       = 0xfd,
    	IntSecurityContextTag    = 0x80,
    	OIDSecurityContextTag    = 0x81,
    	ConfidentialityTag       = 0xa2,
        };

        enum UserInfoTags {
    	DirectReferenceTag       = 0x06,
    	DataDescriptorTag        = 0x07,
    	ExternalTag              = 0x28,
    	SingleASNTypePEncTag     = 0x80, // Primitive Single-ASN1-Type-Encoding
    	SingleASNTypeCEncTag     = 0xa0, // Constructor Single-ASN1-Type-Encoding
    	OctetAlignEncTag         = 0x81,
    	ArbitraryEncTag          = 0x82,
        };

        enum ConfidentialityTags {
    	IntConfidentialContextTag    = 0x80,
    	OIDConfidentialContextTag    = 0x81,
        };

        enum TCAPComponentTags {
    	ComponentPortionTag  = 0xe8,
    	ComponentsIDsTag     = 0xcf,
    	OperationNationalTag = 0xd0,
    	OperationPrivateTag  = 0xd1,
    	ErrorNationalTag     = 0xd3,
    	ErrorPrivateTag      = 0xd4,
    	ProblemCodeTag       = 0xd5,
    	ParameterSetTag      = 0xf2,
    	ParameterSeqTag      = 0x30,
        };

        /**
         * Constructor
         * @param params NamedList containing parameters for building TCAP
         */
        SS7TCAPANSI(const NamedList& params);

        /**
         * Destructor
         */
        ~SS7TCAPANSI();

        /**
         * Build a transaction
         * @param type Type with which to build the transactions
         * @param transactID ID for the transaction
         * @param params Parameters for building the transaction
         * @param initLocal True if built by user, false if by remote end
         * @return A transaction
         */
        virtual SS7TCAPTransaction* buildTransaction(SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	bool initLocal = true);

    private:
        SS7TCAPError decodeTransactionPart(NamedList& params, DataBlock& data);
        void encodeTransactionPart(NamedList& params, DataBlock& data);
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part Transaction - specification ANSI
     * @short ANSI SS7 TCAP transaction implementation
     */
    class YSIG_API SS7TCAPTransactionANSI : public SS7TCAPTransaction
    {
    public:
        enum TCAPANSIComponentType {
    	CompUnknown         = 0x0,
    	Local               = 0x1,
    	InvokeLast          = 0xe9,
    	ReturnResultLast    = 0xea,
    	ReturnError         = 0xeb,
    	Reject              = 0xec,
    	InvokeNotLast       = 0xed,
    	ReturnResultNotLast = 0xee,
        };

        enum ANSITransactionType {
    	Unknown                         = 0x0,
    	Unidirectional                  = 0xe1,
    	QueryWithPermission             = 0xe2,
    	QueryWithoutPermission          = 0xe3,
    	Response                        = 0xe4,
    	ConversationWithPermission      = 0xe5,
    	ConversationWithoutPermission   = 0xe6,
    	Abort                           = 0xf6,
        };

        /**
         * Constructor
         * @param tcap TCAP holding this transaction
         * @param type Initiating type for transaction
         * @param transactID Transaction ID
         * @param params Decoded TCAP parameters for building the transaction
         * @param timeout Transaction timeout
         * @param initLocal True if the transaction was initiated locally, false if not
         */
        SS7TCAPTransactionANSI(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	u_int64_t timeout, bool initLocal = true);

        /**
         * Destructor
         */
        ~SS7TCAPTransactionANSI();

        /**
         * Process transaction data and fill the NamedList with the decoded data
         * @param params NamedList to fill with decoded data
         * @param data Data to decode
         * @return A TCAP error encountered whilst decoding
         */
        virtual SS7TCAPError handleData(NamedList& params, DataBlock& data);

        /**
         * An update request for this transaction
         * @param type The type of transaction to which this transaction should be updated
         * @param params Update parameter
         * @param updateByUser True if the update is made by the local user, false if it's made by the remote end
         * @return A TCAP Error
         */
        virtual SS7TCAPError update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser = true);

        /**
         * Handle TCAP relevant dialog data
         * @param params NamedList containing (if present) dialog information
         * @param byUser True if the dialog information is provided by the local user, false otherwise
         * @return A report error
         */
        virtual SS7TCAPError handleDialogPortion(NamedList& params, bool byUser = true);

        /**
         * Encode P-Abort information
         * @param tr The transaction on which the abort was signalled
         * @param params NamedList reference from which to get the P-Abort information
         * @param data DataBlock reference in which to insert the encoded P-Abort information
         */
        static void encodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data);

        /**
         * Decode P-Abort TCAP message portion
         * @param tr The transaction on which the abort was signalled
         * @param params NamedList reference to fill with the decoded P-Abort information
         * @param data DataBlock reference from which to decode P-Abort information
         */
        static SS7TCAPError decodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data);

        /**
         * Update the state of this transaction to end the transaction
         */
        virtual void updateToEnd();

        /**
         * Update transaction state
         * @param byUser True if update is requested by user, false if by remote
         */
        virtual void updateState(bool byUser);

        /**
         * Request content for this transaction
         * @param params List of parameters of this tranaction
         * @param data Data block to fill with encoded content
         */
        virtual void requestContent(NamedList& params, DataBlock& data);

        /**
         * Dictionary keeping string versions of transaction types
         */
        static const TokenDict s_ansiTransactTypes[];

    private:
        SS7TCAPError decodeDialogPortion(NamedList& params, DataBlock& data);
        void encodeDialogPortion(NamedList& params, DataBlock& data);
        SS7TCAPError decodeComponents(NamedList& params, DataBlock& data);
        void encodeComponents(NamedList& params, DataBlock& data);

        SS7TCAP::TCAPUserTransActions m_prevType;
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part - specification ITU-T
     * @short ITU-T SS7 TCAP implementation
     */
    class YSIG_API SS7TCAPITU : virtual public SS7TCAP
    {
        YCLASS(SS7TCAPITU,SS7TCAP)
    public:
        enum TCAPTags {
    	OriginatingIDTag    = 0x48,
    	DestinationIDTag    = 0x49,
    	PCauseTag           = 0x4a,
        };

        enum TCAPDialogTags {
    	DialogPortionTag         = 0x6b,
    	ProtocolVersionTag       = 0x80,
    	ApplicationContextTag    = 0xa1,
    	UserInformationTag       = 0xbe,
        };

        enum UserInfoTags {
    	DirectReferenceTag       = 0x06,
    	DataDescriptorTag        = 0x07,
    	ExternalTag              = 0x28,
    	SingleASNTypePEncTag     = 0x80, // Primitive Single-ASN1-Type-Encoding
    	SingleASNTypeCEncTag     = 0xa0, // Constructor Single-ASN1-Type-Encoding
    	OctetAlignEncTag         = 0x81,
    	ArbitraryEncTag          = 0x82,
        };

        enum TCAPComponentTags {
    	ComponentPortionTag  = 0x6c,
    	LocalTag             = 0x02,
    	LinkedIDTag          = 0x80,
    	GlobalTag            = 0x06,
    	ParameterSeqTag      = 0x30,
    	ParameterSetTag      = 0x31,
        };

        /**
         * Constructor
         * @param params Parameters to build ITU TCAP
         */
        SS7TCAPITU(const NamedList& params);

        /**
         * Destructor
         */
        ~SS7TCAPITU();

        /**
         * Build a transaction
         * @param type Type with which to build the transactions
         * @param transactID ID for the transaction
         * @param params Parameters for building the transaction
         * @param initLocal True if built by user, false if by remote end
         * @return A transaction
         */
        virtual SS7TCAPTransaction* buildTransaction(SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	bool initLocal = true);

    private:
        SS7TCAPError decodeTransactionPart(NamedList& params, DataBlock& data);
        void encodeTransactionPart(NamedList& params, DataBlock& data);
    };

    /**
     * Implementation of SS7 Transactional Capabilities Application Part Transaction - specification ITU-T
     * @short ITU-T SS7 TCAP transaction implementation
     */
    class YSIG_API SS7TCAPTransactionITU : public SS7TCAPTransaction
    {
    public:
        enum ITUComponentType {
    	CompUnknown         = 0x0,
    	Local               = 0x1,
    	Invoke              = 0xa1,
    	ReturnResultLast    = 0xa2,
    	ReturnError         = 0xa3,
    	Reject              = 0xa4,
    	ReturnResultNotLast = 0xa7,
        };

        enum ITUTransactionType {
    	Unknown                         = 0x0,
    	Unidirectional                  = 0x61,
    	Begin                           = 0x62,
    	End                             = 0x64,
    	Continue                        = 0x65,
    	Abort                           = 0x67,
        };

        enum ITUDialogTags {
    	AARQDialogTag               = 0x60,
    	AAREDialogTag               = 0x61,
    	ABRTDialogTag               = 0x64,
    	ResultDiagnosticUserTag     = 0xa1,
    	ResultDiagnosticProviderTag = 0xa2,
    	ResultTag                   = 0xa2,
    	ResultDiagnosticTag         = 0xa3,
        };

        enum ITUDialogValues {
    	ResultAccepted                     = 0,
    	ResultRejected                     = 1,
    	DiagnosticUserNull                 = 0x10,
    	DiagnosticUserNoReason             = 0x11,
    	DiagnosticUserAppCtxtNotSupported  = 0x12,
    	DiagnosticProviderNull             = 0x20,
    	DiagnosticProviderNoReason         = 0x21,
    	DiagnosticProviderNoCommonDialog   = 0x22,
    	AbortSourceUser                    = 0x30,
    	AbortSourceProvider                = 0x31,
        };

        /**
         * Constructor
         * @param tcap TCAP holding this transaction
         * @param type Initiating type for transaction
         * @param transactID Transaction ID
         * @param params Parameters to build this transaction
         * @param timeout Transaction time out interval
         * @param initLocal True if the transaction was initiated locally, false if not
         */
        SS7TCAPTransactionITU(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params,
    	u_int64_t timeout, bool initLocal = true);

        /**
         * Destructor
         */
        ~SS7TCAPTransactionITU();

        /**
         * Process transaction data and fill the NamedList with the decoded data
         * @param params NamedList to fill with decoded data
         * @param data Data to decode
         * @return A TCAP error encountered whilst decoding
         */
        virtual SS7TCAPError handleData(NamedList& params, DataBlock& data);

        /**
         * An update request for this transaction
         * @param type The type of transaction to which this transaction should be updated
         * @param params Update parameter
         * @param updateByUser True if the update is made by the local user, false if it's made by the remote end
         * @return A TCAP Error
         */
        virtual SS7TCAPError update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser = true);

        /**
         * Handle TCAP relevant dialog data
         * @param params NamedList containing (if present) dialog information
         * @param byUser True if the dialog information is provided by the local user, false otherwise
         * @return A report error
         */
        virtual SS7TCAPError handleDialogPortion(NamedList& params, bool byUser = true);

        /**
         * Encode P-Abort information
         * @param tr The transaction on which the abort was signalled
         * @param params NamedList reference from which to get the P-Abort information
         * @param data DataBlock reference in which to insert the encoded P-Abort information
         */
        static void encodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data);

        /**
         * Decode P-Abort TCAP message portion
         * @param tr The transaction on which the abort was signalled
         * @param params NamedList reference to fill with the decoded P-Abort information
         * @param data DataBlock reference from which to decode P-Abort information
         * @return A report error
         */
        static SS7TCAPError decodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data);

        /**
         * Update the state of this transaction to end the transaction
         */
        virtual void updateToEnd();

        /**
         * Check if the transaction present dialog information
         * @return True if dialog information is present, false otherwise
         */
        inline bool dialogPresent()
    	{ return !(m_appCtxt.null()); }

        /**
         * Test for dialog when decoding
         * @param data Data from which the transaction is decoded
         * @return True if dialog portion is present, false otherwise
         */
        bool testForDialog(DataBlock& data);

        /**
         * Encode dialog portion of transaction
         * @param params List of parameters to encode
         * @param data Data block into which to insert the encoded data
         */
        void encodeDialogPortion(NamedList& params, DataBlock& data);

        /**
         * Decode dialog portion
         * @param params List into which to put the decoded dialog parameters
         * @param data Data to decodeCaps
         * @return A report error
         */
        SS7TCAPError decodeDialogPortion(NamedList& params, DataBlock& data);

        /**
         * Update transaction state
         * @param byUser True if update is requested by user, false if by remote
         */
        void updateState(bool byUser = false);

        /**
         * Request content for this transaction
         * @param params List of parameters of this tranaction
         * @param data Data block to fill with encoded content
         */
        virtual void requestContent(NamedList& params, DataBlock& data);

        /**
         * Set information in case of abnormal dialog detection
         * @param params List of parameters where to set the abnormal dialog information
         */
        virtual void abnormalDialogInfo(NamedList& params);

        /**
         * Dictionary for Dialogue PDUs
         */
        static const TokenDict s_dialogPDUs[];

        /**
         * Dictionary for dialogue result values
         */
        static const TokenDict s_resultPDUValues[];

    private:
        SS7TCAPError decodeComponents(NamedList& params, DataBlock& data);
        void encodeComponents(NamedList& params, DataBlock& data);

        String m_appCtxt;
    };

    // The following classes are ISDN, not SS7, but they use the same signalling
    //  interfaces so they will remain here

    /**
     * An interface to a Layer 2 (Q.921) ISDN message transport
     * @short Abstract ISDN layer 2 (Q.921) message transport
     */
    class YSIG_API ISDNLayer2 : virtual public SignallingComponent
    {
        YCLASS(ISDNLayer2,SignallingComponent)
        friend class ISDNQ921Management;
    public:
        /**
         * Layer states if it has a TEI assigned
         */
        enum State {
    	Released,                        // Multiple frame acknowledged not allowed
    	WaitEstablish,                   // Wating to establish 'multiple frame acknowledged' mode
    	Established,                     // Multiple frame acknowledged allowed
    	WaitRelease,                     // Wating to release 'multiple frame acknowledged' mode
        };

        /**
         * Destructor
         */
        virtual ~ISDNLayer2();

        /**
         * Get the ISDN Layer 3 attached to this layer
         */
        inline ISDNLayer3* layer3() const
    	{ return m_layer3; }

        /**
         * Get the layer's state
         * @return The layer's state as enumeration
         */
        inline State state() const
    	{ return m_state; }

        /**
         * Check if this interface is the network or CPE (user) side of the link
         * @return True if this interface is the network side of the link
         */
        inline bool network() const
    	{ return m_network; }

        /**
         * Check if this interface should change its type
         * @return True if type change is allowed
         */
        inline bool detectType() const
    	{ return m_detectType; }

        /**
         * Get the SAPI (Service Access Point Identifier) of this interface
         * @return The SAPI (Service Access Point Identifier) of this interface
         */
        inline u_int8_t localSapi() const
    	{ return m_sapi; }

        /**
         * Get the TEI (Terminal Endpoint Identifier) of this interface
         * @return The TEI (Terminal Endpoint Identifier) of this interface
         */
        inline u_int8_t localTei() const
    	{ return m_tei; }

        /**
         * Get the maximum length of user data transported through this layer
         * @return The maximum length of user data transported through this layer
         */
        inline u_int32_t maxUserData() const
    	{ return m_maxUserData; }

        /**
         * Check if this interface has a TEI assigned
         * @return True if this interface has a TEI assigned
         */
        inline bool teiAssigned() const
    	{ return m_teiAssigned; }

        /**
         * Check if this interface will automatically re-establish when released
         * @return The auto restart flag
         */
        inline bool autoRestart() const
    	{ return m_autoRestart; }

        /**
         * Get the uptime of the interface
         * @return Time since interface got up in seconds
         */
        unsigned int upTime() const
    	{ return m_lastUp ? (Time::secNow() - m_lastUp) : 0; }

        /**
         * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives
         * Descendants must implement this method to fullfill the request
         * @param tei This layer TEI (Terminal Endpoint Identifier)
         * @param establish True to establish. False to release
         * @param force True to establish even if we already are in this mode. This
         *  parameter is ignored if establish is false
         * @return True if the request was accepted
         */
        virtual bool multipleFrame(u_int8_t tei, bool establish, bool force)
    	{ return false; }

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives
         * Descendants must implement this method to fullfill the request
         * @param data Data to send
         * @param tei This layer TEI
         * @param ack True to send an acknowledged frame, false to send an unacknowledged one
         * @return True if the request was accepted
         */
        virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack)
    	{ return false; }

        /**
         * Emergency release.
         * Descendants must implement this method to cleanup/reset data
         */
        virtual void cleanup() = 0;

        /**
         * Attach an ISDN Q.931 Layer 3 if the given parameter is different from the one we have
         * Cleanup the object before ataching the new Layer 3
         * This method is thread safe
         * @param layer3 Pointer to the Q.931 Layer 3 to attach
         */
        virtual void attach(ISDNLayer3* layer3);

        /**
         * Get the text associated with a given state
         * @param s The state to get the text for
         * @return The text associated with the given state
         */
        static inline const char* stateName(State s)
    	{ return lookup((int)s,m_states); }

    protected:
        /**
         * Constructor
         * Initialize this interface and the component
         * @param params Layer's parameters
         * @param name Optional name of the component
         * @param tei Value of TEI for this layer
         */
        ISDNLayer2(const NamedList& params, const char* name = 0, u_int8_t tei = 0);

        /**
         * Retrieve the layer's mutex
         * @return Reference to the Layer 2 mutex
         */
        inline Mutex& l2Mutex()
    	{ return m_layerMutex; }

        /**
         * Implements Q.921 DL-ESTABLISH indication/confirmation primitive
         *  of 'multiple frame acknowledged' mode established
         * @param tei The TEI requested
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         */
        void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout);

        /**
         * Implements Q.921 DL-RELEASE indication/confirmation primitive
         *  of 'multiple frame acknowledged' mode released
         * @param tei The TEI released
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         */
        void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout);

        /**
         * Notify layer 3 of data link set/release command or response
         * Used for stateless layer 2
         * @param tei The TEI of this layer
         * @param cmd True if received a command, false if received a response
         * @param value The value of the notification
         *	If 'cmd' is true (command), the value is true if a request to establish data link was received
         *   or false if received a request to release data link
         *	If 'cmd' is false (response), the value is the response
         */
        void dataLinkState(u_int8_t tei, bool cmd, bool value);

        /**
         * Notify layer 3 of data link idle timeout
         * Used for stateless layer 2
         */
        void idleTimeout();

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives
         * Receive data from remote peer
         * @param data Received data
         * @param tei The TEI for which the data was received
         */
        void receiveData(const DataBlock& data, u_int8_t tei);

        /**
         * Set TEI assigned status. Print a debug message. If status is false calls cleanup()
         * Descendants are responsable for TEI assigned status management
         * @param status The new TEI assigned status
         */
        void teiAssigned(bool status);

        /**
         * Set the state
         * Descendants are responsable for multiple frame status management
         * @param newState The new state
         * @param reason Reason of state change, NULL if unspecified
         */
        void changeState(State newState, const char* reason = 0);

        /**
         * Change the interface type
         * @return True if interface type changed
         */
        bool changeType();

        /**
         * Set the automatically re-establish when released flag
         * @param restart The new value of the auto restart flag
         */
        inline void autoRestart(bool restart)
    	{ m_autoRestart = restart; }

        /**
         * Set the Reference Identifier used in management procedures
         * @param ri The new reference number
         */
        inline void setRi(u_int16_t ri)
    	{ m_ri = ri; }

        /**
         * Parse a received packet
         * @param packet The packet received
         * @return Pointer to a newly created frame, NULL if an error occured
         */
        ISDNFrame* parsePacket(const DataBlock& packet);

    private:
        ISDNLayer3* m_layer3;                // The attached Layer 3 interface
        Mutex m_layerMutex;                  // Layer 2 operations mutex
        Mutex m_layer3Mutex;                 // Control m_layer3 operations
        State m_state;                       // Layer's state
        bool m_network;                      // Network/CPE type of the interface
        bool m_detectType;                   // Detect interface type
        u_int8_t m_sapi;                     // SAPI value
        u_int8_t m_tei;                      // TEI value
        u_int16_t m_ri;                      // Reference number
        u_int32_t m_lastUp;                  // Time when the interface got up
        bool m_checked;                      // Flag to indicate if the layer was checked
        bool m_teiAssigned;                  // The TEI status
        bool m_autoRestart;                  // True to restart when released
        u_int32_t m_maxUserData;             // Maximum length of user data transported trough this layer
        unsigned int m_teiRefNumber;         // The Reference Number (Ri) carried by a TEI management frame
        static const TokenDict m_states[];   // Keep the string associated with each state
    };

    /**
     * An interface to a Layer 3 (Q.931) ISDN message transport
     * @short Abstract ISDN layer 3 (Q.931) message transport
     */
    class YSIG_API ISDNLayer3 : virtual public SignallingComponent
    {
        YCLASS(ISDNLayer3,SignallingComponent)
    public:
        /**
         * Implements Q.921 DL-ESTABLISH indication/confirmation primitive:
         *  'multiple frame acknowledged' mode established
         * @param tei The TEI of the frame
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2)
    	{ }

        /**
         * Implements Q.921 DL-RELEASE indication/confirmation primitive:
         *  'multiple frame acknowledged' mode released
         * @param tei The TEI of the frame
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2)
    	{ }

        /**
         * Notification from layer 2 of data link set/release command or response
         * Used for stateless layer 2
         * @param tei The TEI of the command or response
         * @param cmd True if received a command, false if received a response
         * @param value The value of the notification
         *	If 'cmd' is true (command), the value is true if a request to establish data link was received
         *   or false if received a request to release data link
         *	If 'cmd' is false (response), the value is the response
         * @param layer2 Pointer to the notifier
         */
        virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2)
    	{ }

        /**
         * Notification from layer 2 of data link idle timeout
         * Used for stateless layer 2
         * @param layer2 Pointer to the notifier
         */
        virtual void idleTimeout(ISDNLayer2* layer2)
    	{ }

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives
         * Receive data from remote peer
         * @param data Received data
         * @param tei The TEI of the received frame
         * @param layer2 Pointer to the sender
         */
        virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2) = 0;

        /**
         * Attach an ISDN Q.921 Layer 2
         * @param layer2 Pointer to the Q.921 Layer 2 to attach
         * @return Pointer to the detached Layer 2 or NULL
         */
        virtual ISDNLayer2* attach(ISDNLayer2* layer2)
    	{ return 0; }

    protected:
        /**
         * Constructor
         * Initialize the component
         * @param name Name of this component
         */
        inline ISDNLayer3(const char* name = 0)
    	: SignallingComponent(name),
    	  m_layerMutex(true,"ISDNLayer3::layer")
    	{ }

        /**
         * Retrieve the layer's mutex
         * @return Reference to the Layer 3 mutex
         */
        inline Mutex& l3Mutex()
    	{ return m_layerMutex; }

    private:
        Mutex m_layerMutex;                  // Layer 3 operations mutex
    };

    /**
     * Encapsulates an ISDN (Q.921) frame exchanged over a hardware HDLC interface
     * @short An ISDN frame
     */
    class YSIG_API ISDNFrame : public RefObject
    {
        friend class ISDNQ921;
        friend class ISDNQ921Management;
    public:
        /**
         * Frame type according to Q.921 3.6
         */
        enum Type {
    	DISC = 1,                        // disconnect (command)
    	DM = 2,                          // disconnected (response)
    	FRMR = 3,                        // frame reject (response)
    	I = 4,                           // information transfer (response)
    	REJ = 5,                         // reject (command/response)
    	RNR = 6,                         // receive not ready (command/response)
    	RR = 7,                          // receive ready (command/response)
    	SABME = 8,                       // set asynchronous balanced mode extended (command)
    	UA = 9,                          // unnumbered acknoledgement (response)
    	UI = 10,                         // unnumbered information (command)
    	XID = 11,                        // exchange identification (command/response)
    	// Note: Keep all errors greater then Invalid: The code relies on it
    	Invalid = 100,
    	ErrUnknownCR = 101,              // Error: Unknown command/response. Set by parser
    	ErrHdrLength = 102,              // Error: Invalid header length. Set by parser
    	ErrDataLength = 103,             // Error: Information field too long
    	ErrRxSeqNo = 104,                // Error: Invalid receive sequence number
    	ErrTxSeqNo = 105,                // Error: Invalid send sequence number
    	ErrInvalidEA = 106,              // Error: Invalid 'extended address' bit(s). Set by parser
    	ErrInvalidAddress = 107,         // Error: Invalid SAPI/TEI
    	ErrUnsupported = 108,            // Error: Unsupported command. E.g. XID
    	ErrInvalidCR = 109,              // Error: Invalid command/response flag
        };

        /**
         * Codes used for TEI management procedures (Q.921 Table 8)
         */
        enum TeiManagement {
    	TeiReq       = 1,                // TEI request (user to network)
    	TeiAssigned  = 2,                // TEI assigned (network to user)
    	TeiDenied    = 3,                // TEI denied (network to user)
    	TeiCheckReq  = 4,                // TEI check request (network to user)
    	TeiCheckRsp  = 5,                // TEI check response (user to network)
    	TeiRemove    = 6,                // TEI remove (network to user)
    	TeiVerify    = 7                 // TEI verify (user to network)
        };

        /**
         * Frame category
         */
        enum Category {
    	Data,                            // I, UI
    	Supervisory,                     // RR, RNR, REJ
    	Unnumbered,                      // SABME, DISC, UA DM, FRMR XID
    	Error
        };

        /**
         * Destructor
         */
        virtual ~ISDNFrame();

        /**
         * Get the type of this frame
         * @return The type of this frame as enumeration
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Get the error type
         * @return The error type of this frame as enumeration
         */
        inline Type error() const
    	{ return m_error; }

        /**
         * Get the category of this frame
         * @return The category of this frame as enumeration
         */
        inline Category category() const
    	{ return m_category; }

        /**
         * Check if this frame is a command
         * @return True if this frame is a command. False if it is a response
         */
        inline bool command() const
    	{ return m_command; }

        /**
         * Get the SAPI of this frame
         * @return The SAPI of this frame
         */
        inline u_int8_t sapi() const
    	{ return m_sapi; }

        /**
         * Get the TEI of this frame
         * @return The TEI of this frame
         */
        inline u_int8_t tei() const
    	{ return m_tei; }

        /**
         * Check if this frame is a poll (expect response) or a final one
         * @return True if this a poll frame. False if it is a final one
         */
        inline bool poll() const
    	{ return m_poll; }

        /**
         * Get the transmitter send sequence number
         * @return The transmitter send sequence number
         */
        inline u_int8_t ns() const
    	{ return m_ns; }

        /**
         * Get the transmitter receive sequence number
         * @return The transmitter receive sequence number
         */
        inline u_int8_t nr() const
    	{ return m_nr; }

        /**
         * Get the length of the frame's header
         * @return The length of the frame's header
         */
        inline u_int8_t headerLength() const
    	{ return m_headerLength; }

        /**
         * Get the length of the data carried by this frame
         * @return The length of the data carried by this frame
         */
        inline u_int32_t dataLength() const
    	{ return m_dataLength; }

        /**
         * Get the frame's buffer
         * @return The frame's buffer
         */
        inline const DataBlock& buffer() const
    	{ return m_buffer; }

        /**
         * Check if the frame was sent
         * @return True if the frame was sent
         */
        inline bool sent() const
    	{ return m_sent; }

        /**
         * Set transmitted flag
         */
        inline void sent(bool value)
    	{ m_sent = value; }

        /**
         * Get the text associated with the frame's type
         * @return The text associated with the frame's type
         */
        inline const char* name() const
    	{ return typeName(type()); }

        /**
         * Update sequence numbers for I frames
         * @param ns Optional update send sequence number
         * @param nr Optional update receive sequence number
         */
        void update(u_int8_t* ns = 0, u_int8_t* nr = 0);

        /**
         * Get the data transferred with this frame
         * @param dest The destination buffer
         */
        inline void getData(DataBlock& dest) const
    	{ dest.assign((u_int8_t*)m_buffer.data() + m_headerLength,m_dataLength); }

        /**
         * Write this frame to a string for debug purposes
         * @param dest The destination string
         * @param extendedDebug True to dump message header and data
         */
        void toString(String& dest, bool extendedDebug) const;

        /**
         * Check if the received frame is a TEI management frame
         * @return True if the frame is a TEI management one, false if it's not
         */
        bool checkTeiManagement() const;

        /**
         * Get reference number from frame
         * @param data The data block which contains it
         * @return Reference number
         */
        static u_int16_t getRi(const DataBlock& data);

        /**
         * Get frame message type
         * @param data The data block which contains it
         * @return Message type
         */
        inline static u_int8_t getType(const DataBlock& data)
    	{ return static_cast(data.at(3,0)); }

        /**
         * Get action indicator
         * @param data Data block which contains it
         * @return Action indicator value
         */
        inline static u_int8_t getAi(const DataBlock& data)
    	{ return static_cast(data.at(4,0) >> 1); }

        /**
         * Parse a received data block
         * @param data Data to parse
         * @param receiver The receiver of the data
         * @return ISDNFrame pointer or 0 (no control field)
         */
        static ISDNFrame* parse(const DataBlock& data, ISDNLayer2* receiver);

        /**
         * Build a TEI management message buffer
         * @param data Destination buffer to fill
         * @param type The message type
         * @param ri The reference number
         * @param ai The action indicator
         * @return True on succes
         */
        static bool buildTeiManagement(DataBlock& data, u_int8_t type, u_int16_t ri,
    	u_int8_t ai);

        /**
         * Get the command bit value for a given side of a data link
         * @param network True for the network side,
         *  false for the user side of a data link
         * @return The appropriate command bit value
         */
        static inline bool commandBit(bool network)
    	{ return network; }

        /**
         * Get the response bit value for a given side of a data link
         * @param network True for the network side,
         *  false for the user side of a data link
         * @return The appropriate response bit value
         */
        static inline bool responseBit(bool network)
    	{ return !network; }

        /**
         * Get the command/response type from C/R bit value and sender type
         * @param cr The value of the C/R bit
         * @param sentByNetwork True if the sender is the network side of the data link
         * @return True if it is a command
         */
        static inline bool isCommand(u_int8_t cr, bool sentByNetwork)
    	{ return cr ? sentByNetwork : !sentByNetwork; }

        /**
         * Get the text associated with the given frame type
         * @param type Frame type to get the text for
         * @return The text associated with the given frame type
         */
        static inline const char* typeName(Type type)
    	{ return lookup(type,s_types,"Invalid frame"); }

        /**
         * Keep the association between frame types and texts
         */
        static const TokenDict s_types[];

    protected:
        /**
         * Constructor
         * Used by the parser
         * @param type Frame type
         */
        ISDNFrame(Type type = Invalid);

        /**
         * Constructor
         * Create U/S frames: SABME/DM/DISC/UA/FRMR/XID/RR/RNR/REJ
         * Set data members. Encode frame in buffer according to Q.921
         * Used by ISDNLayer2 to create outgoing frames
         * @param type Frame type
         * @param command Frame command/response's flag
         * @param senderNetwork True if the sender is the network side of the data link
         * @param sapi SAPI value
         * @param tei TEI value
         * @param pf Poll/final flag
         * @param nr Optional transmitter receive sequence number
         */
        ISDNFrame(Type type, bool command, bool senderNetwork,
    	u_int8_t sapi, u_int8_t tei, bool pf, u_int8_t nr = 0);

        /**
         * Constructor
         * Create I/UI frames
         * Set data members. Encode frame in buffer according to Q.921
         * Used by ISDNLayer2 to create outgoing frames
         * @param ack True to create an I frame. False to create an UI frame
         * @param senderNetwork True if the sender is the network side of the data link
         * @param sapi SAPI value
         * @param tei TEI value
         * @param pf Poll/final flag
         * @param data Transmitted data
         */
        ISDNFrame(bool ack, bool senderNetwork, u_int8_t sapi, u_int8_t tei,
    	bool pf, const DataBlock& data);

    private:
        Type m_type;                         // Frame type
        Type m_error;                        // Frame error type
        Category m_category;                 // Frame category
        // Address
        bool m_command;                      // Command/Response frame
        bool m_senderNetwork;                // True if the sender of this frame is the network side of the data link
        u_int8_t m_sapi;                     // SAPI value
        u_int8_t m_tei;                      // TEI value
        // Control
        bool m_poll;                         // Poll/Final flag
        u_int8_t m_ns;                       // N(S) value (when applicable): transmitter send sequence number
        u_int8_t m_nr;                       // N(R) value (when applicable): transmitter receive sequence number
        // Data
        u_int8_t m_headerLength;             // Header length
        u_int32_t m_dataLength;              // Data length
        DataBlock m_buffer;                  // Whole frame: header + data + FCS (frame check sequence = 2 bytes)
        // Outgoing frames only
        bool m_sent;                         // True if already sent
    };

    /**
     * Q.921 ISDN Layer 2 implementation on top of a hardware HDLC interface
     * @short ISDN Q.921 implementation on top of a hardware interface
     */
    class YSIG_API ISDNQ921 : public ISDNLayer2, public SignallingReceiver, public SignallingDumpable
    {
        YCLASS2(ISDNQ921,ISDNLayer2,SignallingReceiver)
        friend class ISDNQ921Management;
    public:
        /**
         * Constructor
         * Initialize this object and the component
         * @param params Layer's and @ref TelEngine::ISDNLayer2 parameters
         * @param name Name of this component
         * @param mgmt TEI management component
         * @param tei Value of TEI for this component
         */
        ISDNQ921(const NamedList& params, const char* name = 0, ISDNQ921Management* mgmt = 0, u_int8_t tei = 0);

        /**
         * Destructor
         */
        virtual ~ISDNQ921();

        /**
         * Configure and initialize Q.921 and its interface
         * @param config Optional configuration parameters override
         * @return True if Q.921 and the interface were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Get the timeout of a data frame. After that, a higher layer may retransmit data
         * @return The timeout of a data frame
         */
        inline u_int64_t dataTimeout() const
    	{ return m_retransTimer.interval() * m_n200.maxVal(); }

        /**
         * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives
         * If accepted, the primitive is enqueued for further processing
         * This method is thread safe
         * @param tei This layer's TEI
         * @param establish True to establish. False to release
         * @param force True to establish even if we already are in this mode. This
         *  parameter is ignored if establish is false
         * @return True if the request was accepted
         */
        virtual bool multipleFrame(u_int8_t tei, bool establish, bool force);

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives
         * Send data through the HDLC interface
         * This method is thread safe
         * @param data Data to send
         * @param tei The TEI to send with the data frane
         * @param ack True to send an acknowledged frame, false to send an unacknowledged one
         * @return False if the request was not accepted or send operation failed
         */
        virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack);

        /**
         * Send a SABME frame to reset the layer
         * @return True if a SABME frame was sent
         */
        inline bool sendSabme()
    	{ return sendUFrame(ISDNFrame::SABME,true,true); }

        /**
         * Emergency release.
         * Send 'disconnect' command. Reset all data. Set state to 'Released'
         * This method is thread safe
         */
        virtual void cleanup();

        /**
         * Set debug data of this layer
         * @param printFrames Enable/disable frame printing on output
         * @param extendedDebug Enable/disable hex data dump if print frames is enabled
         */
        inline void setDebug(bool printFrames, bool extendedDebug)
    	{ m_extendedDebug = ((m_printFrames = printFrames) && extendedDebug); }

    protected:
        /**
         * Detach links. Disposes memory
         */
        virtual void destroyed()
        {
    	ISDNLayer2::attach((ISDNLayer3*)0);
    	TelEngine::destruct(SignallingReceiver::attach(0));
    	SignallingComponent::destroyed();
        }

        /**
         * Method called periodically to check timeouts
         * This method is thread safe
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a packet received by the receiver's interface
         * This method is thread safe
         * @param packet The received packet
         * @return True if message was successfully processed
         */
        virtual bool receivedPacket(const DataBlock& packet);

        /**
         * Process the frame received
         * @param frame Pointer to frame to process
         * @return True if the frame was processed
         */
        bool receivedFrame(ISDNFrame* frame);

        /**
         * Process a notification generated by the attached interface
         * This method is thread safe
         * @param event Notification event reported by the interface
         * @return True if notification was processed
         */
        virtual bool notify(SignallingInterface::Notification event);

        /**
         * Reset object if not in Released state. Drop all frames
         * This method is thread safe
         */
        void reset();

    private:
        virtual bool control(NamedList& params)
    	{ return SignallingDumpable::control(params,this); }
        // Acknoledge outgoing frames
        // @param frame The acknoledging frame
        bool ackOutgoingFrames(const ISDNFrame* frame);
        // Process a received I/UI frame
        // @param ack True for I frame, false for UI frame
        // @return True to send data to Layer 3
        bool processDataFrame(const ISDNFrame* frame, bool ack);
        // Process a received S frame
        // @return True to exit from timer recovery state
        bool processSFrame(const ISDNFrame* frame);
        // Process a received U frame
        // @param newState The new state if true is returned
        // @param confirmation True if the new state is Established or Released and
        //  this is a confirmation
        // @return True to change state
        bool processUFrame(const ISDNFrame* frame, State& newState,
    	bool& confirmation);
        // Accept frame according to Q.921 5.8.5
        // Update counters.
        // If not accepted the frame is rejected or dropped
        // reject is set to true if the frame is rejected
        bool acceptFrame(ISDNFrame* frame, bool& reject);
        // Update rejected frames counter. Print message. Send FRMR (frame reject)
        void rejectFrame(const ISDNFrame* frame, const char* reason = 0);
        // Update dropped frames counter. Print message
        void dropFrame(const ISDNFrame* frame, const char* reason = 0);
        // Send S frames other then UI frames
        bool sendUFrame(ISDNFrame::Type type, bool command, bool pf,
    	bool retrans = false);
        // Send U frames
        bool sendSFrame(ISDNFrame::Type type, bool command, bool pf);
        // Send a frame to remote peer
        // @param frame Frame to send
        // @return False if the operation failed
        bool sendFrame(const ISDNFrame* frame);
        // Send pending outgoing I frames
        // @param retrans: True   Send all transmission window
        //                 False  Send only the unsent frames in transmission window
        // @return True if a transmission took place
        bool sendOutgoingData(bool retrans = false);
        // Start/Stop T200. Stop/Start T203
        // If start is false reset N200 (retransmission counter)
        // @param start True to start. False to stop
        // @param t203 Start/don't start T203. Ignored if start is false
        // @param time Current time if known
        void timer(bool start, bool t203, u_int64_t time = 0);

        ISDNQ921Management* m_management;    // TEI management component
        // State variables
        bool m_remoteBusy;                   // Remote peer is busy: don't send any I frames
        bool m_timerRecovery;                // T200 expired
        bool m_rejectSent;                   // True if we've sent a REJ frame
        bool m_pendingDMSabme;               // True if we have a pending SABME on DM received
        bool m_lastPFBit;                    // Last P/F bit sent with an I or S frame
        u_int8_t m_vs;                       // Sequence number of the next transmitted I frame
        u_int8_t m_va;                       // Last ack'd I frame by remote peer
        u_int8_t m_vr;                       // Expected I frame sequence number
        // Timers and counters
        SignallingTimer m_retransTimer;      // T200: Retransmission interval
        SignallingTimer m_idleTimer;         // T203: Channel idle interval
        SignallingCounter m_window;          // Maximum/current number of pending outgoing I frames
        SignallingCounter m_n200;            // Maximum/current retransmission counter
        // Data
        ObjList m_outFrames;                 // Outgoing I frames queue
        // Statistics
        u_int32_t m_txFrames;                // The number of frames accepted by layer 1 to be transmitted
        u_int32_t m_txFailFrames;            // The number of frames not accepted by layer 1 to be transmitted
        u_int32_t m_rxFrames;                // The number of successfully parsed frames
        u_int32_t m_rxRejectedFrames;        // The number of rejected frames. Doesn't include dropped frames
        u_int32_t m_rxDroppedFrames;         // The number of dropped frames. Doesn't include rejected frames
        u_int32_t m_hwErrors;                // The number of hardware notifications
        // Debug flags
        bool m_printFrames;                  // Print frames to output
        bool m_extendedDebug;                // Extended debug flag
        // Flags used to avoid repetitive errors
        bool m_errorSend;                    // Send error
        bool m_errorReceive;                 // Receive error
    };

    /**
     * This class is intended to be used as a proxy between an ISDN Layer 3 and multiple
     *  Layer 2 objects sharing the same signalling interface.
     * It is used for BRI TEI management or PRI with D-channel backup.
     * It also keeps a list of ISDN Layer 2 object(s) used for the designated purpose
     * @short ISDN Layer 2 BRI TEI management or PRI with D-channel(s) backup
     */
    class YSIG_API ISDNQ921Management : public ISDNLayer2, public ISDNLayer3, public SignallingReceiver, public SignallingDumpable
    {
        YCLASS3(ISDNQ921Management,ISDNLayer2,ISDNLayer3,SignallingReceiver)
    public:
        /**
         * Constructor - initialize this Layer 2 and the component
         * @param params Layer's parameters
         * @param name Optional name of the component
         * @param net True if managing the network side of Q.921
         */
        ISDNQ921Management(const NamedList& params, const char* name = 0, bool net = true);

        /**
         * Destructor
         */
        virtual ~ISDNQ921Management();

        /**
         * Configure and initialize Q.921 Management and its children
         * @param config Optional configuration parameters override
         * @return True if Q.921 management was initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Set the engine for this management and all Layer 2 children
         * @param eng Pointer to the engine that will manage this mangement
         */
        virtual void engine(SignallingEngine* eng);

        /**
         * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives
         * @param tei This layer TEI (-1 to apply it to all targets this object may have attached)
         * @param establish True to establish. False to release
         * @param force True to establish even if we already are established.
         * @return True if the request was accepted
         */
        virtual bool multipleFrame(u_int8_t tei, bool establish, bool force);

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives
         * @param data Data to send
         * @param tei This layer TEI (-1 for broadcast)
         * @param ack True to send an acknowledged frame, false to send an unacknowledged one
         * @return True if the request was accepted
         */
        virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack);

        /**
         * Implements Q.921 send frame to the interface
         * @param frame The frame to be sent
         * @param q921 Pointer to the Q.921 that sends the frame, if any
         * @return True if the frame was sent
         */
        bool sendFrame(const ISDNFrame* frame, const ISDNQ921* q921 = 0);

        /**
         * Emergency release.
         * Cleanup all Layer 2 objects attached to this Management
         */
        virtual void cleanup();

        /**
         * Implements Q.921 DL-ESTABLISH indication/confirmation primitive:
         *  'multiple frame acknowledged' mode established
         * @param tei This layer TEI
         * @param confirm True if this is a confirmation of a previous request,
         *  false if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2);

        /**
         * Implements Q.921 DL-RELEASE indication/confirmation primitive:
         *  'multiple frame acknowledged' mode released
         * @param tei This layer TEI
         * @param confirm True if this is a confirmation of a previous request,
         *  false if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2);

        /**
         * Notification from layer 2 of data link set/release command or response
         * Used for stateless layer 2
         * @param tei This layer TEI
         * @param cmd True if received a command, false if received a response
         * @param value The value of the notification
         * If 'cmd' is true (command), the value is true if a request to establish data link was received
         *   or false if received a request to release data link
         * If 'cmd' is false (response), the value is the response
         * @param layer2 Pointer to the notifier
         */
        virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2);

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives
         * Receive data from an encapsulated Layer 2 and send it to the attached Layer 3
         * @param data Received data
         * @param tei The TEI as received in the packet
         * @param layer2 Pointer to the sender
         */
        virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2);

    protected:
        /**
         * Method called periodically to check timeouts
         * This method is thread safe
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a Signalling Packet received by the interface.
         * Parse the data and send all non-UI frames to the appropriate Layer 2.
         * Process UI frames
         * @return True if message was successfully processed
         */
        virtual bool receivedPacket(const DataBlock& packet);

        /**
         * Process a notification generated by the attached interface
         * @param event Notification event reported by the interface
         * @return True if notification was processed
         */
        virtual bool notify(SignallingInterface::Notification event);

        /**
         * Process UI frames carrying TEI management messages
         * @param frame The parsed frame
         * @return False if the frame is not a TEI management one, true otherwise
         */
        bool processTeiManagement(ISDNFrame* frame);

        /**
         * Send a TEI management frame
         * @param type Type of the frame to send
         * @param ri Reference number to send in frame
         * @param ai Action indicator to send in frame
         * @param tei The TEI to send the frame to, default use broadcast
         * @param pf The Poll/Final bit to set in the frame
         * @return True if frame was sent successfully
         */
        bool sendTeiManagement(ISDNFrame::TeiManagement type, u_int16_t ri, u_int8_t ai, u_int8_t tei = 127, bool pf = false);

        /**
         * Process TEI request message and send back to TE:
         *  TEI Assigned message if the request succeeded;
         *  TEI Denied message with the received reference number if the
         *   reference number is already in use;
         *  TEI Denied message with the reference number set to 127 if
         *   there is no TEI value available.
         * @param ri The reference number
         * @param ai Action indicator
         * @param pf The Poll/Final bit in the incoming frame
         */
        void processTeiRequest(u_int16_t ri, u_int8_t ai, bool pf);

        /**
         * Process Tei remove message removing the tei(s) contained by ai
         * @param ai Contains the TEI value to remove or 127 to remove all TEI values
         */
        void processTeiRemove(u_int8_t ai);

        /**
         * Process TEI Check Request message and send to the NET a message with
         *  the TEI and the asociated reference number
         * @param ai Contains the TEI value to check or 127 to check all TEI values
         * @param pf The Poll/Final bit in the incoming frame
         */
        void processTeiCheckRequest(u_int8_t ai, bool pf);

        /**
         * Process TEI Check Response message and set the check flag to true to know that we have a response for that TEI
         * @param ri The associated reference number to the ai
         * @param ai The TEI value as received in the answer
         */
        void processTeiCheckResponse(u_int16_t ri, u_int8_t ai);

        /**
         * Process TEI Assigned message
         * @param ri The reference number assigned to the ai
         * @param ai The TEI value assigned
         */
        void processTeiAssigned(u_int16_t ri, u_int8_t ai);

        /**
         * Process TEI Denied message
         * @param ri The reference number of the denied request
         */
        void processTeiDenied(u_int16_t ri);

        /**
         * Process TEI verify
         * @param ai The TEI value of the message initiator
         * @param pf The Poll/Final bit in the incoming frame
         */
        void processTeiVerify(u_int8_t ai, bool pf);

        /**
         * Send TEI request message
         * @param tei TEI value to assign
         */
        void sendTeiReq(u_int8_t tei);

        /**
         * Send a TEI remove frame
         */
        void sendTeiRemove();

    private:
        ISDNQ921* m_layer2[127];             // The list of Layer 2 objects attached to this Layer 3
        SignallingTimer m_teiManTimer;       // T202
        SignallingTimer m_teiTimer;          // T201
    };

    /**
     * Q.921 ISDN Layer 2 pasive (stateless) implementation on top of a hardware HDLC interface
     * @short Stateless pasive ISDN Q.921 implementation on top of a hardware interface
     */
    class YSIG_API ISDNQ921Passive : public ISDNLayer2, public SignallingReceiver, public SignallingDumpable
    {
        YCLASS2(ISDNQ921Passive,ISDNLayer2,SignallingReceiver)
    public:
        /**
         * Constructor
         * Initialize this object and the component
         * @param params Layer's and @ref TelEngine::ISDNLayer2 parameters
         * @param name Name of this component
         */
        ISDNQ921Passive(const NamedList& params, const char* name = 0);

        /**
         * Destructor
         */
        virtual ~ISDNQ921Passive();

        /**
         * Emergency release
         * Reset all data. Set state to 'Released'
         * This method is thread safe
         */
        virtual void cleanup();

        /**
         * Configure and initialize the passive Q.921 and its interface
         * @param config Optional configuration parameters override
         * @return True if Q.921 and the interface were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Set debug data of this layer
         * @param printFrames Enable/disable frame printing on output
         * @param extendedDebug Enable/disable hex data dump if print frames is enabled
         */
        inline void setDebug(bool printFrames, bool extendedDebug)
    	{ m_extendedDebug = ((m_printFrames = printFrames) && extendedDebug); }

    protected:
        /**
         * Detach links. Disposes memory
         */
        virtual void destroyed()
        {
    	ISDNLayer2::attach(0);
    	TelEngine::destruct(SignallingReceiver::attach(0));
    	SignallingComponent::destroyed();
        }

        /**
         * Method called periodically to check timeouts
         * This method is thread safe
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Process a packet received by the receiver's interface
         * This method is thread safe
         * @param packet The received packet
         * @return True if message was successfully processed
         */
        virtual bool receivedPacket(const DataBlock& packet);

        /**
         * Process a notification generated by the attached interface
         * This method is thread safe
         * @param event Notification event reported by the interface
         * @return True if notification was processed
         */
        virtual bool notify(SignallingInterface::Notification event);

    private:
        virtual bool control(NamedList& params)
    	{ return SignallingDumpable::control(params,this); }
        // Filter received frames. Accept only frames that would generate a notification to the upper layer:
        // UI/I, and Valid SABME/DISC/UA/DM
        // On success, if frame is not a data one, prepare cmd and value to notify layer 3
        bool acceptFrame(ISDNFrame* frame, bool& cmd, bool& value);
        // Show debug message. Count dropped frames
        bool dropFrame(const ISDNFrame* frame, const char* reason = 0);

        bool m_checkLinkSide;                // Check if this is the correct side of the data link
        SignallingTimer m_idleTimer;         // Channel idle interval
        u_int8_t m_lastFrame;                // Transmitter send number of the last received frame
        u_int32_t m_rxFrames;                // The number of successfully parsed frames
        u_int32_t m_rxRejectedFrames;        // The number of rejected frames. Doesn't include dropped frames
        u_int32_t m_rxDroppedFrames;         // The number of dropped frames. Doesn't include rejected frames
        u_int32_t m_hwErrors;                // The number of hardware notifications
        bool m_printFrames;                  // Print frames to output
        bool m_extendedDebug;                // Extended debug flag
        bool m_errorReceive;                 // Receive error
    };

    /**
     * The common client side of SIGTRAN ISDN Q.921 User Adaptation (RFC4233)
     * @short Client side of SIGTRAN ISDN Q.921 UA
     */
    class YSIG_API ISDNIUAClient : public SIGAdaptClient
    {
        YCLASS(ISDNIUAClient,SIGAdaptClient)
    public:
        /**
         * Contructor of an empty IUA client
         */
        inline ISDNIUAClient(const NamedList& params)
    	: SIGAdaptClient(params.safe("ISDNIUAClient"),¶ms,1,9900)
    	{ }

        virtual bool processMSG(unsigned char msgVersion, unsigned char msgClass,
    	unsigned char msgType, const DataBlock& msg, int streamId);
    };

    /**
     * RFC4233 ISDN Layer 2 implementation over SCTP/IP
     * IUA is intended to be used as a Provider-User where Q.921 runs on a
     *  Signalling Gateway and the user (Q.931) runs on an Application Server.
     * @short SIGTRAN ISDN Q.921 User Adaptation Layer
     */
    class YSIG_API ISDNIUA : public ISDNLayer2, public SIGAdaptUser
    {
        friend class ISDNIUAClient;
        YCLASS(ISDNIUA,ISDNLayer2)
    public:
        /**
         * Constructor
         * Initialize this object and the layer 2
         * @param params Object and Layer 2 parameters
         * @param name Optional name for Layer 2
         * @param tei Value of TEI for this component
         */
        ISDNIUA(const NamedList& params, const char* name = 0, u_int8_t tei = 0);

        /**
         * Destructor
         */
        virtual ~ISDNIUA();

        /**
         * Configure and initialize IUA and its transport
         * @param config Optional configuration parameters override
         * @return True if IUA and the transport were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Implements Q.921 DL-ESTABLISH and DL-RELEASE request primitives
         * @param tei This layer's TEI
         * @param establish True to establish. False to release
         * @param force True to establish even if we already are in this mode. This
         *  parameter is ignored if establish is false
         * @return True if the request was accepted
         */
        virtual bool multipleFrame(u_int8_t tei, bool establish, bool force);

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA request primitives
         * @param data Data to send
         * @param tei The TEI to send with the data frane
         * @param ack True to send an acknowledged frame, false to send an unacknowledged one
         * @return False if the request was not accepted or send operation failed
         */
        virtual bool sendData(const DataBlock& data, u_int8_t tei, bool ack);

        /**
         * Emergency release.
         */
        virtual void cleanup();

        /**
         * Traffic activity state change notification
         * @param active True if the ASP is active and traffic is allowed
         */
        virtual void activeChange(bool active);

        /**
         * Retrieve the numeric Interface Identifier (if any)
         * @return IID value, -1 if not set
         */
        inline int32_t iid() const
    	{ return m_iid; }

    protected:
        ISDNIUAClient* client() const
    	{ return static_cast(adaptation()); }
        virtual bool processMGMT(unsigned char msgType, const DataBlock& msg, int streamId);
        virtual bool processQPTM(unsigned char msgType, const DataBlock& msg, int streamId);
        int32_t m_iid;
    };

    /**
     * Q.931 ISDN Layer 3 message Information Element
     * @short A Q.931 ISDN Layer 3 message Information Element
     */
    class YSIG_API ISDNQ931IE : public NamedList
    {
        friend class ISDNQ931Message;
    public:
        /**
         * Keep IE type enumerations. See Q.931 4.5
         */
        enum Type {
    	// Fixed (1 byte) length information element
    	Shift = 0x90,                    // Shift
    	MoreData = 0xa0,                 // More data
    	SendComplete = 0xa1,             // Sending complete
    	Congestion = 0xb0,               // Congestion level
    	Repeat = 0xd0,                   // Repeat indicator
    	// Variable length information element
    	Segmented = 0x00,                // Segmented message
    	BearerCaps = 0x04,               // Bearer capability
    	Cause = 0x08,                    // Cause
    	CallIdentity = 0x10,             // Call identity
    	CallState = 0x14,                // Call state
    	ChannelID = 0x18,                // Channel identification
    	Progress = 0x1e,                 // Progress indicator
    	NetFacility = 0x20,              // Network-specific facilities
    	Notification = 0x27,             // Notification indicator
    	Display = 0x28,                  // Display
    	DateTime = 0x29,                 // Date/time
    	Keypad = 0x2c,                   // Keypad facility
    	Signal = 0x34,                   // Signal
    	ConnectedNo = 0x4c,              // Connected number (Q.951)
    	CallingNo = 0x6c,                // Calling party number
    	CallingSubAddr = 0x6d,           // Calling party subaddress
    	CalledNo = 0x70,                 // Called party number
    	CalledSubAddr = 0x71,            // Called party subaddress
    	NetTransit = 0x78,               // Transit network selection
    	Restart = 0x79,                  // Restart indicator
    	LoLayerCompat = 0x7c,            // Low layer compatibility
    	HiLayerCompat = 0x7d,            // High layer compatibility
    	// Not used
    	UserUser = 0x7e,                 // User-user
    	Escape = 0x7f,                   // Escape for extension
        };

        /**
         * Constructor
         * Constructs an unknown IE with raw data
         * @param type The type of this IE
         */
        ISDNQ931IE(u_int16_t type);

        /**
         * Destructor
         */
        virtual ~ISDNQ931IE();

        /**
         * Get the type of this IE
         * @return The type of this IE
         */
        inline u_int8_t type() const
    	{ return (u_int8_t)m_type; }

        /**
         * Add a parameter using the IE name as prefix
         * @param name Parameter name
         * @param value Parameter value
         */
        inline void addParamPrefix(const char* name, const char* value)
    	{ addParam(*this+"."+name,value); }

        /**
         * Put this message into a string for debug purposes
         * @param dest The destination string
         * @param extendedDebug True to add the content of this IE and dump data.
         *  If false, only the IE name is added to the destination string
         * @param before Optional string to be added before
         */
        void toString(String& dest, bool extendedDebug, const char* before = 0);

        /**
         * Get the string associated with a given IE type
         * @param type The IE type whose string we want to get
         * @param defVal The value to return if not found
         * @return Pointer to the requested string or defValue
         */
        static inline const char* typeName(int type, const char* defVal = 0)
    	{ return lookup(type,s_type,defVal); }

        /**
         * Keep the string associated with IE types
         */
        static const TokenDict s_type[];

        /**
         * Internally used buffer
         */
        DataBlock m_buffer;

    private:
        u_int16_t m_type;                    // IE type
    };

    /**
     * Q.931 ISDN Layer 3 message
     * @short A Q.931 ISDN Layer 3 message
     */
    class YSIG_API ISDNQ931Message : public SignallingMessage
    {
    public:
        /**
         * Message type enumeration
         */
        enum Type {
    	Alerting = 0x01,                 // ALERTING
    	Proceeding = 0x02,               // CALL PROCEEDING
    	Connect = 0x07,                  // CONNECT
    	ConnectAck = 0x0f,               // CONNECT ACK
    	Progress = 0x03,                 // PROGRESS
    	Setup = 0x05,                    // SETUP
    	SetupAck = 0x0d,                 // SETUP ACK
    	Resume = 0x26,                   // RESUME
    	ResumeAck = 0x2e,                // RESUME ACK
    	ResumeRej = 0x22,                // RESUME REJECT
    	Suspend = 0x25,                  // SUSPEND
    	SuspendAck = 0x2d,               // SUSPEND ACK
    	SuspendRej = 0x21,               // SUSPEND REJECT
    	UserInfo = 0x20,                 // USER INFO
    	Disconnect = 0x45,               // DISCONNECT
    	Release = 0x4d,                  // RELEASE
    	ReleaseComplete = 0x5a,          // RELEASE COMPLETE
    	Restart = 0x46,                  // RESTART
    	RestartAck = 0x4e,               // RESTART ACK
    	Segment = 0x60,                  // SEGMENT
    	CongestionCtrl = 0x79,           // CONGESTION CONTROL
    	Info = 0x7b,                     // INFORMATION
    	Notify = 0x6e,                   // NOTIFY
    	Status = 0x7d,                   // STATUS
    	StatusEnquiry = 0x75,            // STATUS ENQUIRY
        };

        /**
         * Constructor
         * Constructs a message from given data. Used for incoming messages
         * @param type Message type
         * @param initiator The call initiator flag: True: this is the initiator
         * @param callRef The call reference
         * @param callRefLen The call reference length
         */
        ISDNQ931Message(Type type, bool initiator, u_int32_t callRef, u_int8_t callRefLen);

        /**
         * Constructor
         * Constructs a message with dummy call reference
         * @param type Message type
         */
        ISDNQ931Message(Type type);

        /**
         * Constructor
         * Constructs a message for a given call. Used for outgoing messages
         * @param type Message type
         * @param call The call this message belongs to
         */
        ISDNQ931Message(Type type, ISDNQ931Call* call);

        /**
         * Destructor
         */
        virtual ~ISDNQ931Message();

        /**
         * Get the type of this message
         * @return The type of this message as enumeration
         */
        inline Type type() const
    	{ return m_type; }

        /**
         * Check if the sender of this message is the call initiator
         * @return True if the sender of this message is the call initiator
         */
        inline bool initiator() const
    	{ return m_initiator; }

        /**
         * Get the id of the call this message belongs to
         * @return The call reference
         */
        inline u_int32_t callRef() const
    	{ return m_callRef; }

        /**
         * Get the length of the call reference
         * @return The length of the call reference
         */
        inline u_int8_t callRefLen() const
    	{ return m_callRefLen; }

        /**
         * Check if this message has a dummy call reference
         * @return True if this message has a dummy call reference
         */
        inline bool dummyCallRef() const
    	{ return m_dummy; }

        /**
         * Check if this message contains unknown mandatory IE(s)
         * @return True if this message contains unknown mandatory IE(s)
         */
        inline bool unknownMandatory() const
    	{ return m_unkMandatory; }

        /**
         * Set the unknown mandatory IE(s) flag
         */
        inline void setUnknownMandatory()
    	{ m_unkMandatory = true; }

        /**
         * Get the IE list of this message
         * @return A valid pointer to the list of this message's IEs
         */
        inline ObjList* ieList()
    	{ return &m_ie; }

        /**
         * Get a pointer to the first IE with the given type
         * @param type Requested IE's type
         * @param base Optional search starting element. If 0, search is started from the first IE following base
         * @return Pointer to the IE or 0 if not found
         */
        ISDNQ931IE* getIE(ISDNQ931IE::Type type, ISDNQ931IE* base = 0);

        /**
         * Remove an IE from list without destroying it
         * @param type Requested IE's type
         * @param base Optional search starting element. If 0, search is started from the first IE following base
         * @return Pointer to the IE or 0 if not found
         */
        ISDNQ931IE* removeIE(ISDNQ931IE::Type type, ISDNQ931IE* base = 0);

        /**
         * Get the value of a given parameter of a given IE
         * @param type Requested IE's type
         * @param param Requested IE's parameter. Set to 0 to use IE's name
         * @param defVal Default value to return if IE is missing or the parameter is missing
         * @return Pointer to the requested value or 0
         */
        inline const char* getIEValue(ISDNQ931IE::Type type, const char* param,
    	const char* defVal = 0)
        {
    	ISDNQ931IE* ie = getIE(type);
    	return (ie ? ie->getValue(param?param:ie->c_str(),defVal) : defVal);
        }

        /**
         * Append an IE with a given parameter
         * @param type IE's type
         * @param param IE's parameter. Set to 0 to use IE's name
         * @param value IE parameter's value
         * @return Pointer to the requested value or 0
         */
        inline ISDNQ931IE* appendIEValue(ISDNQ931IE::Type type, const char* param,
    	const char* value)
        {
    	ISDNQ931IE* ie = new ISDNQ931IE(type);
    	ie->addParam(param?param:ie->c_str(),value);
    	appendSafe(ie);
    	return ie;
        }

        /**
         * Append an information element to this message
         * @param ie Information element to add
         * @return True if the IE was added or replaced, false if it was invalid
         */
        inline bool append(ISDNQ931IE* ie)
    	{ return 0 != m_ie.append(ie); }

        /**
         * Append/insert an information element to this message. Check the IE list consistency
         * The given IE is 'consumed': deleted or appended to the list
         * @param ie Information element to add
         * @return True if the IE was added or replaced, false if it was invalid
         */
        bool appendSafe(ISDNQ931IE* ie);

        /**
         * Put this message into a string for debug purposes
         * @param dest The destination string
         * @param extendedDebug True to add the content of IEs and dump data.
         *  If false, only the IE name is added to the destination string
         * @param indent The line indent
         */
        void toString(String& dest, bool extendedDebug, const char* indent = 0) const;

        /**
         * Get a pointer to a data member or this message
         * @param name Object name
         * @return The requested pointer or 0 if not exists
         */
        virtual void* getObject(const String& name) const;

        /**
         * Encode this message
         * If message segmentation is allowed and the message is longer then maximum allowed,
         *  split it into Segment messages
         * @param parserData The parser settings
         * @param dest The destination list.
         *  If 1 is returned the list contains a DataBuffer with this message.
         *  If more then 1 is returned, the list is filled with data buffers with Segment messages
         * @return The number of segments on success or 0 on failure.
         */
        u_int8_t encode(ISDNQ931ParserData& parserData, ObjList& dest);

        /**
         * Parse received data
         * If the message type is Segment, decode only the header and the first IE
         *  If valid, fills the given buffer with the rest of the message. If segData is 0, drop the message.
         * @param parserData The parser settings
         * @param buffer The received data
         * @param segData Segment message data. If 0, received segmented messages will be dropped
         * @return Valid ISDNQ931Message pointer on success or 0
         */
        static ISDNQ931Message* parse(ISDNQ931ParserData& parserData,
    	const DataBlock& buffer, DataBlock* segData);

        /**
         * Get the string associated with a given message type
         * @param t The message type whose string we want to get
         * @return Pointer to the string associated with the given message type or 0
         */
        static inline const char* typeName(int t)
    	{ return lookup(t,s_type,"Unknown"); }

        /**
         * Keep the string associated with message types
         */
        static const TokenDict s_type[];

        /**
         * Internally used buffer for debug purposes
         */
        DataBlock m_buffer;

    private:
        Type m_type;                         // Message type
        bool m_initiator;                    // The call initiator flag: True: this is the initiator
        u_int32_t m_callRef;                 // The call reference
        u_int8_t m_callRefLen;               // The call reference length
        bool m_unkMandatory;                 // True if this message contains unknown mandatory IE(s)
        bool m_dummy;                        // True if this message has a dummy call reference
        ObjList m_ie;                        // IE list
    };

    /**
     * Extract data from IEs. Append IEs to Q.931 messages
     * @short A Q.931 message IE data processor
     */
    class YSIG_API ISDNQ931IEData
    {
        friend class ISDNQ931Call;
        friend class ISDNQ931CallMonitor;
        friend class ISDNQ931;
        friend class ISDNQ931Monitor;
    private:
        // Constructor
        ISDNQ931IEData(bool bri = false);
        // Process received IEs
        // If add is true, append an IE to the message
        // If add is false, extract data from message. Set data to default values if IE is missing
        // @return False if the IE is missing when decoding or the IE wasn't added
        bool processBearerCaps(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processCause(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processDisplay(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processKeypad(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processChannelID(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processProgress(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processRestart(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processNotification(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processCalledNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);
        bool processCallingNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0);

        // IE parameters
        String m_display;                    // Display: The data
        String m_callerNo;                   // CallingNo: Number
        String m_callerType;                 // CallingNo: Number type
        String m_callerPlan;                 // CallingNo: Number plan
        String m_callerPres;                 // CallingNo: Number presentation
        String m_callerScreening;            // CallingNo: Number screening
        String m_calledNo;                   // CalledNo: Number
        String m_calledType;                 // CalledNo: Number type
        String m_calledPlan;                 // CalledNo: Number plan
        String m_transferCapability;         // BearerCaps: Transfer capability
        String m_transferMode;               // BearerCaps: Transfer mode
        String m_transferRate;               // BearerCaps: Transfer rate
        String m_format;                     // BearerCaps: Layer 1 protocol
        String m_reason;                     // Cause
        String m_keypad;                     // Keypad: 'keypad' parameter
        String m_progress;                   // Progress: Progress description
        String m_notification;               // Notify: Notification indicator
        bool m_bri;                          // ChannelID: BRI interface flag
        bool m_channelMandatory;             // ChannelID: Indicated channel is mandatory/preferred
        bool m_channelByNumber;              // ChannelID: m_channels contains a channel list or a slot map
        String m_channelType;                // ChannelID: Channel type
        String m_channelSelect;              // ChannelID: Channel select
        String m_channels;                   // ChannelID: Channel list or slot map
        String m_restart;                    // Restart: The class of restarting circuits
    };

    /**
     * Q.931 ISDN call and call controller state
     * @short Q.931 ISDN call and call controller state
     */
    class YSIG_API ISDNQ931State
    {
    public:
        /**
         * Call and call controller state enumeration values
         */
        enum State {
    	// Common state
    	Null                    = 0x00,  // Null
    	// Call states
    	CallInitiated           = 0x01,  // Call initiated: sent SETUP
    	OverlapSend             = 0x02,  // Overlap sending
    	OutgoingProceeding      = 0x03,  // Outgoing call proceeding: received valid CALL PROCEEDING
    	CallDelivered           = 0x04,  // Call delivered: received valid ALERTING
    	CallPresent             = 0x06,  // Call present: received valid SETUP or recover from STATUS
    	CallReceived            = 0x07,  // Call received: sent ALERTING or recover from STATUS
    	ConnectReq              = 0x08,  // Connect request: sent/received valid CONNECT or recover from STATUS
    	IncomingProceeding      = 0x09,  // Incoming call proceeding: sent CALL PROCEEDING or recover from STATUS
    	Active                  = 0x0a,  // Active: sent/received valid CONNECT ACK
    	DisconnectReq           = 0x0b,  // Disconnect request: sent DISCONNECT
    	DisconnectIndication    = 0x0c,  // Disconnect indication: received valid DISCONNECT
    	SuspendReq              = 0x0f,  // Suspend request
    	ResumeReq               = 0x11,  // Resume reques
    	ReleaseReq              = 0x13,  // Release request: sent/received valid RELEASE
    	CallAbort               = 0x16,  // Call abort: received STATUS in Null state with remote not in Null state
    	OverlapRecv             = 0x19,  // Overlap receiving
    	// Call controller states
    	RestartReq              = 0x3d,  // Restart request
    	Restart                 = 0x3e,  // Restart
        };

        /**
         * Constructor
         */
       inline ISDNQ931State() : m_state(Null)
    	{ }

        /**
         * Get the state
         * @return The state as enumeration
         */
        inline State state() const
    	{ return m_state; }

        /**
         * Get the text associated with a given state value
         * @param s The requested state value
         * @return The text associated with the given state value or 0
         */
        static const char* stateName(u_int8_t s)
    	{ return lookup(s,s_states,0); }

        /**
         * Keep the association between state values and their texts
         */
        static const TokenDict s_states[];

    protected:
        /**
         * Check if a received message type is valid in the current call state
         * @param type The type of the received message
         * @param retrans Optional flag to set on failure if the message is a retransmission
         * @return False if the message is not valid in the current call state
         */
        bool checkStateRecv(int type, bool* retrans);

        /**
         * Check if a message is allowed to be sent in the current call state
         * @param type The type of the received message
         * @return False if the message is not valid in the current call state
         */
        bool checkStateSend(int type);

        /**
         * The call and call controller state
         */
        State m_state;

    };

    /**
     * Q.931 ISDN call
     * @short A Q.931 ISDN call
     */
    class YSIG_API ISDNQ931Call : public ISDNQ931State, public SignallingCall
    {
        friend class ISDNQ931;
    public:
        /**
         * Destructor
         */
        virtual ~ISDNQ931Call();

        /**
         * Get the id of this call
         * @return The call reference
         */
        inline u_int32_t callRef() const
    	{ return m_callRef; }

        /**
         * Get the length of the call reference
         * @return The length of the call reference
         */
        inline u_int32_t callRefLen() const
    	{ return m_callRefLen; }

        /**
         * Get the Terminal Equipment Indicator for this call
         * @return Value of TEI used in this call
         */
        inline u_int8_t callTei() const
    	{ return m_tei; }

        /**
         * Get the circuit this call had reserved
         * @return The circuit reserved by this call
         */
        inline SignallingCircuit* circuit()
    	{ return m_circuit; }

        /**
         * Set termination (and destroy) flags
         * This method is thread safe
         * @param destroy The destroy flag. If true, the call will be destroyed
         * @param reason Terminate reason
         */
        void setTerminate(bool destroy, const char* reason);

        /**
         * Send an event to this call
         * This method is thread safe
         * @param event The sent event
         * @return True if the operation succedded
         */
        virtual bool sendEvent(SignallingEvent* event);

        /**
         * Get an event from this call
         * This method is thread safe
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events
         */
        virtual SignallingEvent* getEvent(const Time& when);

        /**
         * Data link (interface) state notification
         * This method is thread safe
         * @param up The data link state
         */
        void dataLinkState(bool up);

        /**
         * Get a pointer to a data member or this call
         * @param name Object name
         * @return The requested pointer or 0 if not exists
         */
        virtual void* getObject(const String& name) const;

    protected:
        /**
         * Constructor
         * @param controller The call controller
         * @param outgoing The call direction
         * @param callRef The call reference
         * @param callRefLen The call reference length in bytes
         * @param tei The Terminal Equipment Identifier used in this call
         */
        ISDNQ931Call(ISDNQ931* controller, bool outgoing, u_int32_t callRef,
    	u_int8_t callRefLen, u_int8_t tei = 0);

        /**
         * Send RELEASE COMPLETE if not in Null state.
         * Clear all call data.
         * Remove from controller's queue. Decrease the object's refence count
         * @param reason Optional release reason. If missing, the last reason is used
         * @param diag Optional hexified string for the cause diagnostic
         * @return Pointer to an SignallingEvent of type Release, with no message
         */
        SignallingEvent* releaseComplete(const char* reason = 0, const char* diag = 0);

        /**
         * Get an event from the circuit reserved for this call
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events
         */
        SignallingEvent* getCircuitEvent(const Time& when);

    private:
        // Reserve and connect a circuit. Change the reserved one if it must to
        bool reserveCircuit();
        // Process call when terminate flag is set. Check timeout
        // @param msg Optional message extracted from queue
        SignallingEvent* processTerminate(ISDNQ931Message* msg = 0);
        // Check timer(s)
        SignallingEvent* checkTimeout(u_int64_t time);
        // Check received messages for valid state
        // True to send status if not accepted
        bool checkMsgRecv(ISDNQ931Message* msg, bool status);
        // Process received messages
        // @param msg Valid ISDNQ931Message pointer
        SignallingEvent* processMsgAlerting(ISDNQ931Message* msg);
        SignallingEvent* processMsgCallProceeding(ISDNQ931Message* msg);
        SignallingEvent* processMsgConnect(ISDNQ931Message* msg);
        SignallingEvent* processMsgConnectAck(ISDNQ931Message* msg);
        SignallingEvent* processMsgDisconnect(ISDNQ931Message* msg);
        SignallingEvent* processMsgInfo(ISDNQ931Message* msg);
        SignallingEvent* processMsgNotify(ISDNQ931Message* msg);
        SignallingEvent* processMsgProgress(ISDNQ931Message* msg);
        SignallingEvent* processMsgRelease(ISDNQ931Message* msg);
        SignallingEvent* processMsgSetup(ISDNQ931Message* msg);
        SignallingEvent* processMsgSetupAck(ISDNQ931Message* msg);
        SignallingEvent* processMsgStatus(ISDNQ931Message* msg);
        SignallingEvent* processMsgStatusEnquiry(ISDNQ931Message* msg);
        // Send message
        // @param msg Pointer to SignallingMessage with parameters
        bool sendAlerting(SignallingMessage* sigMsg);
        bool sendCallProceeding(SignallingMessage* sigMsg);
        bool sendConnect(SignallingMessage* sigMsg);
        bool sendConnectAck(SignallingMessage* sigMsg);
        bool sendDisconnect(SignallingMessage* sigMsg);
        bool sendInfo(SignallingMessage* sigMsg);
        bool sendProgress(SignallingMessage* sigMsg);
        bool sendRelease(const char* reason = 0, SignallingMessage* sigMsg = 0);
        bool sendReleaseComplete(const char* reason = 0, const char* diag = 0, u_int8_t tei = 0);
        bool sendSetup(SignallingMessage* sigMsg);
        bool sendSuspendRej(const char* reason = 0, SignallingMessage* sigMsg = 0);
        bool sendSetupAck();
        // Errors on processing received messages
        // Missing mandatory IE
        // @param release True to send release complete and generate a release event
        SignallingEvent* errorNoIE(ISDNQ931Message* msg, ISDNQ931IE::Type type, bool release);
        SignallingEvent* errorWrongIE(ISDNQ931Message* msg, ISDNQ931IE::Type type, bool release);
        // Change call state
        void changeState(State newState);
        // Remove the call from controller's list
        void removeFromController();
        // Get the Q931 call controller
        inline ISDNQ931* q931();

        // Call data
        u_int32_t m_callRef;                 // Call reference
        u_int32_t m_callRefLen;              // Call reference length
        u_int8_t m_tei;                      // TEI used for the call
        SignallingCircuit* m_circuit;        // Circuit reserved for this call
        bool m_circuitChange;                // True if circuit changed
        bool m_channelIDSent;                // Incoming calls: ChannelID IE already sent
        bool m_rspBearerCaps;                // Incoming calls: Send BearerCaps IE in the first response
        bool m_inbandAvailable;              // Inband data is available
        bool m_net;                          // Flag indicating call is sent by a NT
        ISDNQ931IEData m_data;               // Data to process IEs
        ObjList m_inMsg;                     // Incoming message queue
        bool m_broadcast[127];               // TEIs that answered to PTMP SETUP
        // Timers
        SignallingTimer m_discTimer;         // T305: sending DISCONNECT
        SignallingTimer m_relTimer;          // T308: sending RELEASE
        SignallingTimer m_conTimer;          // T313: sending CONNECT
        SignallingTimer m_overlapSendTimer;  // T302 for overlapped sending
        SignallingTimer m_overlapRecvTimer;  // T304
        SignallingTimer m_retransSetupTimer; // T302 for setup retransmission (PTMP)
        // Termination
        bool m_terminate;                    // Terminate flag: send RELEASE
        bool m_destroy;                      // Destroy flag: call releaseComplete()
        bool m_destroyed;                    // Call destroyed flag
    };

    /**
     * Q.931 ISDN call monitor
     * @short A Q.931 ISDN call monitor
     */
    class YSIG_API ISDNQ931CallMonitor : public ISDNQ931State, public SignallingCall
    {
        friend class ISDNQ931Monitor;
    public:
        /**
         * Destructor
         */
        virtual ~ISDNQ931CallMonitor();

        /**
         * Check if the initiator is from the network side of the data link
         * @return True if the initiator is from the network side of the data link, false if it is from the user side
         */
        inline bool netInit() const
    	{ return m_netInit; }

        /**
         * Get an event from this call
         * This method is thread safe
         * @param when The current time
         * @return SignallingEvent pointer or 0 if no events
         */
        virtual SignallingEvent* getEvent(const Time& when);

        /**
         * Set termination flag
         * This method is thread safe
         * @param reason Terminate reason
         */
        void setTerminate(const char* reason);

        /**
         * Get a pointer to a data member or this call
         * @param name Object name
         * @return The requested pointer or 0 if not exists
         */
        virtual void* getObject(const String& name) const;

    protected:
        /**
         * Constructor
         * @param controller The call controller
         * @param callRef The call reference
         * @param netInit True if the initiator is from the network side of the link
         */
        ISDNQ931CallMonitor(ISDNQ931Monitor* controller, u_int32_t callRef, bool netInit);

        /**
         * Clear all call data
         * Remove from controller's queue. Decrease the object's refence count
         * @param reason Optional release reason. If missing, the last reason is used
         * @return Pointer to an SignallingEvent of type Release
         */
        SignallingEvent* releaseComplete(const char* reason = 0);

    private:
        // Get an event from one of the reserved circuits
        SignallingEvent* getCircuitEvent(const Time& when);
        // Process received setup message
        SignallingEvent* processMsgSetup(ISDNQ931Message* msg);
        // Process received responses to setup message (Proceeding, Alerting, Connect)
        SignallingEvent* processMsgResponse(ISDNQ931Message* msg);
        // Process termination messages (Disconnect, Release, Release Complete)
        SignallingEvent* processMsgTerminate(ISDNQ931Message* msg);
        // Process INFORMATION messages to get tones
        SignallingEvent* processMsgInfo(ISDNQ931Message* msg);
        // Reserve/release the circuits
        bool reserveCircuit();
        void releaseCircuit();
        // Connect the caller's or called's circuit
        bool connectCircuit(bool caller);
        // Change call state
        void changeState(State newState);
        // Remove the call from controller's list
        void removeFromController();
        // Get the Q931Monitor call controller
        inline ISDNQ931Monitor* q931();

        u_int32_t m_callRef;                 // Call reference
        SignallingCircuit* m_callerCircuit;  // Circuit reserved for caller
        SignallingCircuit* m_calledCircuit;  // Circuit reserved for called
        SignallingCircuit* m_eventCircuit;   // Last circuit that generated an event
        bool m_netInit;                      // The call initiator is from the network side of the link
        bool m_circuitChange;                // True if circuit changed
        ISDNQ931IEData m_data;               // Data to process IEs
        bool m_terminate;                    // Terminate flag
        String m_terminator;                 // The name of the entity that terminated the call
        ObjList m_inMsg;                     // Incoming messages queue
    };

    /**
     * This class holds Q.931 parser settings used to encode/decode Q.931 messages
     * @short Q.931 message parser data
     */
    class YSIG_API ISDNQ931ParserData
    {
    public:
        /**
         * Constructor
         * @param params Parser settings
         * @param dbg The debug enabler used for output
         */
        ISDNQ931ParserData(const NamedList& params, DebugEnabler* dbg = 0);

        /**
         * Check the state of a flag
         * @param mask The flag to check
         * @return True if the given flag is set
         */
        inline bool flag(int mask) const
    	{ return (0 != (m_flags & mask)); }

        DebugEnabler* m_dbg;                 // The debug enabler used for output
        u_int32_t m_maxMsgLen;               // Maximum length of outgoing messages (or message segments)
        int m_flags;                         // The current behaviour flags
        int m_flagsOrig;                     // The original behaviour flags
        u_int8_t m_maxDisplay;               // Max Display IE size
        bool m_allowSegment;                 // True if message segmentation is allowed
        u_int8_t m_maxSegments;              // Maximum allowed segments for outgoing messages
        bool m_extendedDebug;                // True to fill message/IE buffer
    };

    /**
     * Q.931 ISDN Layer 3 implementation on top of a Layer 2
     * @short ISDN Q.931 implementation on top of Q.921
     */
    class YSIG_API ISDNQ931 : public SignallingCallControl, public SignallingDumpable, public ISDNLayer3
    {
        YCLASS(ISDNQ931,ISDNLayer3)
        friend class ISDNQ931Call;
    public:
        /**
         * Enumeration flags defining the behaviour of the ISDN call controller and
         *  any active calls managed by it
         */
        enum BehaviourFlags {
    	// Append the progress indicator 'non-isdn-source' if present when
    	// sending SETUP. If this flag is not set, the indicator will be
    	// removed from the message
    	SendNonIsdnSource = 0x00000001,
    	// Ignore (don't send) the progress indicator 'non-isdn-destination'
    	// if present when sending SETUP ACKNOWLEDGE or CONNECT
    	IgnoreNonIsdnDest = 0x00000002,
    	// Always set presentation='allowed' and screening='network-provided'
    	// for Calling Party Number IE
    	ForcePresNetProv = 0x00000004,
    	// Translate '3.1khz-audio' transfer capability code 0x10 to/from 0x08
    	Translate31kAudio = 0x00000008,
    	// Send only transfer mode and rate when sending the Bearer Capability IE
    	// with transfer capability 'udi' or 'rdi' (unrestricted/restricted
    	// digital information)
    	URDITransferCapsOnly = 0x00000010,
    	// Don't send Layer 1 capabilities (data format) with the
    	// Bearer Capability IE when in circuit switch mode
    	NoLayer1Caps = 0x00000020,
    	// Don't parse incoming IEs found after a temporary (non-locking) shift
    	IgnoreNonLockedIE = 0x00000040,
    	// Don't send the Display IE
    	// This flag is internally set for EuroIsdnE1 type when the call
    	// controller is the CPE side of the link
    	NoDisplayIE = 0x00000080,
    	// Don't append a charset byte 0xb1 before Display data
    	NoDisplayCharset = 0x00000100,
    	// Send a Sending Complete IE even if no overlap dialing
    	ForceSendComplete = 0x00000200,
    	// Don't change call state to Active instead of ConnectRequest after
    	// sending CONNECT. This flag is internally set when the call
    	// controller is the CPE side of the data link
    	NoActiveOnConnect = 0x00000400,
    	// Check the validity of the notification indicator when sending a NOTIFY message
    	CheckNotifyInd = 0x00000800,
    	// Request an outbound PRI channel to be exclusive
    	ChannelExclusive = 0x00001000,
        };

        /**
         * Call controller switch type. Each value is a mask of behaviour flags
         */
        enum SwitchType {
    	Unknown      = 0,
    	// Standard Euro ISDN (CTR4, ETSI 300-102)
    	EuroIsdnE1   = ForceSendComplete|CheckNotifyInd|NoDisplayCharset|URDITransferCapsOnly,
    	// T1 Euro ISDN variant (ETSI 300-102)
    	EuroIsdnT1   = ForceSendComplete|CheckNotifyInd,
    	// National ISDN
    	NationalIsdn = SendNonIsdnSource,
    	// DMS 100
    	Dms100       = ForcePresNetProv|IgnoreNonIsdnDest,
    	// Lucent 5E
    	Lucent5e     = IgnoreNonLockedIE,
    	// AT&T 4ESS
    	Att4ess      = ForcePresNetProv|IgnoreNonLockedIE|Translate31kAudio|NoLayer1Caps,
    	// QSIG Switch
    	QSIG         = NoActiveOnConnect|NoDisplayIE|NoDisplayCharset
        };

        /**
         * Constructor
         * Initialize this object and the component
         * @param params Layer's parameters and parser settings
         * @param name Name of this component
         */
        ISDNQ931(const NamedList& params, const char* name = 0);

        /**
         * Destructor
         * Destroy all calls
         */
        virtual ~ISDNQ931();

        /**
         * Configure and initialize Q.931 and its layer 2
         * @param config Optional configuration parameters override
         * @return True if Q.931 and the layer 2 were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Get the controller's status as text
         * @return Controller status name
         */
        virtual const char* statusName() const;

        /**
         * Get the layer 2 attached to this object
         * @return Pointer to the layer 2 attached to this object or 0 if none
         */
        inline const ISDNLayer2* layer2() const
    	{ return m_q921; }

        /**
         * Check if this call controller supports primary or basic rate transfer
         * @return True for primary rate. False for basic rate
         */
        inline bool primaryRate() const
    	{ return m_primaryRate; }

        /**
         * Chech if this call controller is at the NET or CPE side of the link
         * @return True if we are NET, false if we are CPE
         */
        inline bool network() const
    	{ return m_q921 ? m_q921->network() : m_networkHint; }

        /**
         * Check if this call controller supports circuit switch or packet mode transfer
         * @return True for circuit switch. False for packet mode
         */
        inline bool transferModeCircuit() const
    	{ return m_transferModeCircuit; }

        /**
         * Get the parser settings of this call control
         * @return The parser settings
         */
        inline ISDNQ931ParserData& parserData()
    	{ return m_parserData; }

        /**
         * Get the default numbering plan for outgoing calls
         * @return The default numbering plan for outgoing calls
         */
        inline const String& numPlan() const
    	{ return m_numPlan; }

        /**
         * Get the default number type for outgoing calls
         * @return The default number type for outgoing calls
         */
        inline const String& numType() const
    	{ return m_numType; }

        /**
         * Get the default number presentation for outgoing calls
         * @return The default number presentation for outgoing calls
         */
        inline const String& numPresentation() const
    	{ return m_numPresentation; }

        /**
         * Get the default number screening for outgoing calls
         * @return The default number screening for outgoing calls
         */
        inline const String& numScreening() const
    	{ return m_numScreening; }

        /**
         * Get the default data format for outgoing calls
         * @return The default data format for outgoing calls
         */
        inline const String& format() const
    	{ return m_format; }

        /**
         * Send a message
         * @param msg The message to be sent
         * @param tei TEI value to use at Layer 2
         * @param reason Optional string to write the failure reason
         * @return False if the message is invalid, Layer 2 is missing or refused the data
         */
        bool sendMessage(ISDNQ931Message* msg, u_int8_t tei, String* reason = 0);

        /**
         * Notification of Layer 2 up state
         * @param tei TEI received by the Layer 2
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2);

        /**
         * Notification of Layer 2 down state
         * @param tei TEI received by the Layer 2
         * @param confirm True if this is a confirmation of a previous request.
         *  False if it is an indication of state change on remote request
         * @param timeout True if the reason is a timeout.
         * @param layer2 Pointer to the notifier
         */
        virtual void multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2);

        /**
         * Receive data from Layer 2
         * @param data Received data
         * @param tei TEI received by the Layer 2
         * @param layer2 Pointer to the sender Layer 2
         */
        virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2);

        /**
         * Attach an ISDN Q.921 transport
         * This method is thread safe
         * @param q921 Pointer to the Q.921 transport to attach
         * @return Pointer to the detached Layer 2 or NULL
         */
        virtual ISDNLayer2* attach(ISDNLayer2* q921);

        /**
         * Create an outgoing call. Send a NewCall event with the given msg parameter
         * @param msg Call parameters
         * @param reason Failure reason if any
         * @return Referenced SignallingCall pointer on success or 0 on failure
         */
        SignallingCall* call(SignallingMessage* msg, String& reason);

        /**
         * Restart one or more the circuits
         * @param circuits Comma separated list of circuits to be restarted
         * @return True if the procedure was successfully started or enqueued
         */
        bool restart(const char* circuits);

        /**
         * Send a STATUS message for a given call
         * @param call The call requesting the operation
         * @param tei The TEI to send with the STATUS message
         * @param cause Value for Cause IE
         * @param display Optional value for Display IE
         * @param diagnostic Optional value for cause diagnostic value
         * @return The result of the operation (true if successfully sent)
         */
        inline bool sendStatus(ISDNQ931Call* call, const char* cause, u_int8_t tei = 0,
    	const char* display = 0, const char* diagnostic = 0)
        {
    	return call && sendStatus(cause,call->callRefLen(),call->callRef(),tei,
    	    call->outgoing(),call->state(),display,diagnostic);
        }

        /**
         * Send a RELEASE or RELEASE COMPLETE message for a given call
         * @param call The call requesting the operation
         * @param release True to send RELEASE, false to send RELEASE COMPLETE
         * @param cause Value for Cause IE
         * @param tei TEI to which the release is sent to
         * @param diag Optional hexified string for cause dignostic
         * @param display Optional value for Display IE
         * @param signal Optional value for Signal IE
         * @return The result of the operation (true if successfully sent)
         */
        inline bool sendRelease(ISDNQ931Call* call, bool release, const char* cause, u_int8_t tei = 0,
    	const char* diag = 0, const char* display = 0, const char* signal = 0)
        {
    	return call && sendRelease(release,call->callRefLen(),call->callRef(),tei,
    	    call->outgoing(),cause,diag,display,signal);
        }

        /**
         * Set terminate to all calls
         * This method is thread safe
         * @param reason Cleanup reason
         */
        virtual void cleanup(const char* reason = "offline");

        /**
         * Set the timeout interval for a given timer if implemented
         * If the timer is not implemented the interval is set to 0
         * @param timer The destination timer
         * @param id The timer number as defined in Q.931
         */
        void setInterval(SignallingTimer& timer, int id);

        /**
         * Manage timeout for the call setup message
         */
        void manageTimeout();

        /**
         * Set debug data of this call controller
         * @param printMsg Enable/disable message printing on output
         * @param extendedDebug Enable/disable hex data dump if print messages is enabled
         */
        inline void setDebug(bool printMsg, bool extendedDebug)
    	{ m_parserData.m_extendedDebug = m_extendedDebug =
    	    ((m_printMsg = printMsg) && extendedDebug); }

        /**
         * The list of behaviour flag names
         */
        static const TokenDict s_flags[];

         /**
         * The list of switch type names
         */
        static const TokenDict s_swType[];

    protected:
        /**
         * Detach links. Disposes memory
         */
        virtual void destroyed()
        {
    	TelEngine::destruct(attach(0));
    	TelEngine::destruct(SignallingCallControl::attach(0));
    	ISDNLayer3::destroyed();
        }

        /**
         * Method called periodically to check timeouts
         * This method is thread safe
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Find a call given its call reference and direction
         * @param callRef The call reference to find
         * @param outgoing True to find an outgoing call, false to find an incoming one
         * @param tei TEI of the layer associated to the call to find
         * @return A referenced pointer to a call or 0
         */
        ISDNQ931Call* findCall(u_int32_t callRef, bool outgoing, u_int8_t tei = 0);

        /**
         * Find a call given a circuit number
         * @param circuit The circuit number to find
         * @return A referenced pointer to a call or 0
         */
        ISDNQ931Call* findCall(unsigned int circuit);

        /**
         * Terminate calls. If list is 0 terminate all calls
         * @param list Optional list of circuits (strings) to be released
         * @param reason The reason to be passed to each terminated call
         */
        void terminateCalls(ObjList* list, const char* reason);

        /**
         * Check if this call control can accept new calls
         * @param outgoing Call direction (true for outgoing)
         * @param reason String to be filled with the reason if not accepted
         * @return True if the call request is accepted
         */
        bool acceptNewCall(bool outgoing, String& reason);

        /**
         * Process received data. Process received message segments if any
         * @param data The received data
         * @return ISDNQ931Message pointer or 0
         */
        ISDNQ931Message* getMsg(const DataBlock& data);

        /**
         * End waiting for message segments
         * If reason is 0 parse already received data for the segmented message
         * This method is thread safe
         * @param reason Debug info reason. If non 0 drop the received segment(s)
         * @return ISDNQ931Message pointer or 0
         */
        ISDNQ931Message* endReceiveSegment(const char* reason = 0);

        /**
         * Process messages with global call reference or should have one
         * @param msg The received message
         * @param tei The TEI received with the message
         */
        void processGlobalMsg(ISDNQ931Message* msg, u_int8_t tei = 0);

        /**
         * Process a restart request
         * @param msg The received message
         * @param tei The TEI received with the message
         */
        void processMsgRestart(ISDNQ931Message* msg, u_int8_t tei = 0);

        /**
         * Process messages with invalid call reference
         * @param msg The received message
         * @param tei The TEI received with the message
         */
        void processInvalidMsg(ISDNQ931Message* msg, u_int8_t tei = 0);

        /**
         * Try to reserve a circuit for restarting if none. Send a restart request on it's behalf
         * Start counting the restart interval if no circuit reserved
         * This method is thread safe
         * @param time The time of the transmission
         * @param retrans Retransmission flag (true if a previous request timed out)
         */
        void sendRestart(u_int64_t time = Time::msecNow(), bool retrans = false);

        /**
         * End restart procedure on timeout or restart acknoledge
         * This method is thread safe
         * @param restart True to try to send restart for the next circuit
         * @param time The time of the transmission
         * @param timeout True if a restart request timed out
         */
        void endRestart(bool restart, u_int64_t time, bool timeout = false);

        /**
         * Send a STATUS message
         * @param cause Value for Cause IE
         * @param callRefLen The call reference length parameter.
         * @param callRef The call reference
         * @param tei The TEI to send with the STATUS message
         * @param initiator True if this is from the call initiator
         * @param state The state for CallState IE
         * @param display Optional value for Display IE
         * @param diagnostic Optional value for cause diagnostic value
         * @return The result of the operation (true if successfully sent)
         */
        bool sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t callRef = 0,
    	u_int8_t tei = 0, bool initiator = false, ISDNQ931Call::State state = ISDNQ931Call::Null,
    	const char* display = 0, const char* diagnostic = 0);

        /**
         * Send a RELEASE or RELEASE COMPLETE message
         * @param release True to send RELEASE, false to send RELEASE COMPLETE
         * @param callRefLen The call reference length parameter
         * @param callRef The call reference
         * @param tei The TEI of the Layer 2 associated with the call
         * @param initiator The call initiator flag
         * @param cause Value for Cause IE
         * @param diag Optional hexified string for cause dignostic
         * @param display Optional value for Display IE
         * @param signal Optional value for Signal IE
         * @return The result of the operation (true if successfully sent)
         */
        bool sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, u_int8_t tei,
    	bool initiator, const char* cause = 0, const char* diag = 0,
    	const char* display = 0, const char* signal = 0);

    private:
        virtual bool control(NamedList& params)
    	{ return SignallingDumpable::control(params,this); }
        bool q921Up() const;                 // Check if layer 2 may be up
        ISDNLayer2* m_q921;                  // The attached layer 2
        bool m_q921Up;                       // Layer 2 state
        // Protocol data
        bool m_networkHint;                  // NET / CPE hint, layer 2 is authoritative
        bool m_primaryRate;                  // Primary/base rate support
        bool m_transferModeCircuit;          // Circuit switch/packet mode transfer
        u_int32_t m_callRef;                 // Current available call reference for outgoing calls
        u_int8_t m_callRefLen;               // Call reference length
        u_int32_t m_callRefMask;             // Call reference mask
        ISDNQ931ParserData m_parserData;     // Parser settings
        ISDNQ931IEData m_data;               // Process IEs
        // Timers & counters
        SignallingTimer m_l2DownTimer;       // T309: Layer 2 is down timeout
        SignallingTimer m_recvSgmTimer;      // T314: Receive segment timeout
        SignallingTimer m_syncCicTimer;      // T316: Restart individual circuit timeout
        SignallingCounter m_syncCicCounter;  // RESTART retransmission counter
        SignallingTimer m_callDiscTimer;     // Q931 call value (see ISDQ931Call)
        SignallingTimer m_callRelTimer;      // Q931 call value (see ISDQ931Call)
        SignallingTimer m_callConTimer;      // Q931 call value (see ISDQ931Call)
        // Default values
        String m_numPlan;                    // Numbering plan
        String m_numType;                    // Number type
        String m_numPresentation;            // Number presentation
        String m_numScreening;               // Number screening
        String m_format;                     // Data format
        String m_cpeNumber;                  // The number of the BRI CPE
        // Restart data
        SignallingCircuit* m_restartCic;     // Currently restarting circuit
        unsigned int m_lastRestart;          // Last restarted circuit's code
        SignallingTimer m_syncGroupTimer;    // Restarting circuit group interval
        // Message segmentation data
        DataBlock m_segmentData;             // Message segments buffer
        ISDNQ931Message* m_segmented;        // Segmented message
        u_int8_t m_remaining;                // Remaining segments
        // Debug
        bool m_printMsg;                     // True to print messages to output
        bool m_extendedDebug;                // Extended debug flag
        // Flags used to print error messages
        bool m_flagQ921Down;                 // Layer 2 is down period timed out
        bool m_flagQ921Invalid;              // Refusing to send message when Layer 2 is missing or down
    };

    /**
     * Q.931 ISDN Layer 3 implementation on top of a Layer 2. Manage Q.931 monitors
     * @short ISDN Q.931 implementation on top of Q.921 of call controller monitor
     */
    class YSIG_API ISDNQ931Monitor : public SignallingCallControl, public ISDNLayer3
    {
        YCLASS(ISDNQ931Monitor,ISDNLayer3)
        friend class ISDNQ931CallMonitor;
    public:
        /**
         * Constructor
         * Initialize this object and the component
         * @param params Layer's parameters and parser settings
         * @param name Name of this component
         */
        ISDNQ931Monitor(const NamedList& params, const char* name = 0);

        /**
         * Destructor
         * Destroy all calls
         */
        virtual ~ISDNQ931Monitor();

        /**
         * Configure and initialize the Q.931 monitor and its interfaces
         * @param config Optional configuration parameters override
         * @return True if Q.931 monitor and both interfaces were initialized properly
         */
        virtual bool initialize(const NamedList* config);

        /**
         * Get the controller's status as text
         * @return Controller status name
         */
        virtual const char* statusName() const;

        /**
         * Notification from layer 2 of data link set/release command or response
         * @param tei The TEI of the notification
         * @param cmd True if received a command, false if received a response
         * @param value The value of the notification
         *	If 'cmd' is true (command), the value is true if a request to establish data link was received
         *   or false if received a request to release data link
         *	If 'cmd' is false (response), the value is the response
         * @param layer2 Pointer to the notifier
         */
        virtual void dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2);

        /**
         * Notification from layer 2 of data link idle timeout
         * @param layer2 Pointer to the notifier
         */
        virtual void idleTimeout(ISDNLayer2* layer2);

        /**
         * Implements Q.921 DL-DATA and DL-UNIT DATA indication primitives
         * @param data Received data
         * @param tei The TEI of the Layer 2
         * @param layer2 Pointer to the sender
         */
        virtual void receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2);

        /**
         * Attach ISDN Q.921 pasive transport that monitors one side of the link
         * This method is thread safe
         * @param q921 Pointer to the monitor to attach
         * @param net True if this is the network side of the data link, false for user (CPE) side
         * @return Pointer to detached monitor or NULL
         */
        virtual ISDNQ921Passive* attach(ISDNQ921Passive* q921, bool net);

        /**
         * Attach a circuit group to this call controller
         * This method is thread safe
         * @param circuits Pointer to the SignallingCircuitGroup to attach
         * @param net True if this group belongs to the network side of the data link, false for user (CPE) side
         * @return Pointer to the old group that was detached, NULL if none or no change
         */
        virtual SignallingCircuitGroup* attach(SignallingCircuitGroup* circuits, bool net);

        /**
         * Get a pointer to the NET or CPE circuit group
         * @param net True to get the network side circuits, false for user (CPE) side
         * @return Pointer to the circuit group requested, NULL if none attached
         */
        inline ISDNQ921Passive* circuits(bool net) const
    	{ return net ? m_q921Net : m_q921Cpe; }

        /**
         * Set debug data of this call controller
         * @param printMsg Enable/disable message printing on output
         * @param extendedDebug Enable/disable hex data dump if print messages is enabled
         */
        inline void setDebug(bool printMsg, bool extendedDebug)
    	{ m_parserData.m_extendedDebug = m_extendedDebug =
    	    ((m_printMsg = printMsg) && extendedDebug); }

        /**
         * Terminate all monitors
         * This method is thread safe
         * @param reason Cleanup reason
         */
        virtual void cleanup(const char* reason = "offline")
    	{ terminateMonitor(0,reason); }

        /**
         * Terminate all monitors or only one
         * This method is thread safe
         * @param mon The monitor to terminate, 0 to terminate all
         * @param reason The termination reason
         */
        void terminateMonitor(ISDNQ931CallMonitor* mon, const char* reason);

    protected:
        /**
         * Detach links. Disposes memory
         */
        virtual void destroyed()
        {
    	TelEngine::destruct(SignallingCallControl::attach(0));
    	TelEngine::destruct(attach((ISDNQ921Passive*)0,true));
    	TelEngine::destruct(attach((ISDNQ921Passive*)0,false));
    	attach((SignallingCircuitGroup*)0,true);
    	attach((SignallingCircuitGroup*)0,false);
    	ISDNLayer3::destroyed();
        }

        /**
         * Method called periodically to check timeouts
         * This method is thread safe
         * @param when Time to use as computing base for events and timeouts
         */
        virtual void timerTick(const Time& when);

        /**
         * Reserve the same circuit code from both circuit groups
         * This is an atomic operation: if one circuit fails to be reserved, both of them will fail
         * Release both circuits on failure
         * This method is thread safe
         * @param code The circuit code to reserve
         * @param netInit True if the caller is from the network side of the link, false if it's from CPE side
         * @param caller The destination caller circuit
         * @param called The destination called circuit
         * @return True on success
         */
        bool reserveCircuit(unsigned int code, bool netInit,
    	SignallingCircuit** caller, SignallingCircuit** called);

        /**
         * Release a circuit
         * This method is thread safe
         * @param circuit The circuit to release
         * @return True on success
         */
        bool releaseCircuit(SignallingCircuit* circuit);

        /**
         * Process a restart or restart acknoledge message
         * Terminate the monitor having the circuit given in restart message
         * @param msg The received message
         */
        void processMsgRestart(ISDNQ931Message* msg);

    private:
        // Find a call monitor by call reference or reserved circuit
        // @return Referenced call monitor pointer or 0 if not found
        ISDNQ931CallMonitor* findMonitor(unsigned int value, bool byCallRef);
        // Drop some messages. Return true if the message should be dropped
        bool dropMessage(const ISDNQ931Message* msg);

        ISDNQ921Passive* m_q921Net;          // Net side of the link
        ISDNQ921Passive* m_q921Cpe;          // CPE side of the link
        SignallingCircuitGroup* m_cicNet;    // Circuit group for the net side of the link
        SignallingCircuitGroup* m_cicCpe;    // Circuit group for the cpe side of the link
        ISDNQ931ParserData m_parserData;     // Parser settings
        ISDNQ931IEData m_data;               // Process IEs
        // Debug
        bool m_printMsg;                     // True to print messages to output
        bool m_extendedDebug;                // Extended debug flag
    };

    }

    #endif /* __YATESIG_H */

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

Generated by: paulc on bussard on Mon Feb 10 13:15:47 2014, using kdoc 2.0a54.