www.pudn.com > d4j.zip > Station.java


// Station: Manage MSI stations 
// $Id: Station.java,v 1.7 2003/11/13 11:55:18 cgm8 Exp $ 
/*  
 * Copyright (c) 1999 Carlos G Mendioroz. 
 * 
 *  This file is part of D4J. 
 * 
 *  D4J is free software; you can redistribute it and/or 
 *  modify it under the terms of the GNU Lesser General Public 
 *  License as published by the Free Software Foundation; either 
 *  version 2 of the License, or (at your option) any later version. 
 *   
 *  D4J is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 *  Lesser General Public License for more details. 
 *   
 *  You should have received a copy of the GNU Lesser General Public 
 *  License along with this library; if not, write to the 
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
 *  Boston, MA  02111-1307, USA. 
 * 
 * Report problems and direct all questions to: 
 * 
 *	tron@acm.org 
 */ 
package local.dialogic; 
 
import java.util.*; 
/** 
 * Station: 
 * First API was clearly targetted at a resource for a channel, with: 
 * assign (to channel and route audio) and connect/disconnect with channel 
 * audio. 
 * Now the API is extended to cope with autonomous (kind of) behaviour, 
 * implementing assign(channel, boolean route) to do light attaching 
 */ 
 
public class Station extends Device implements Resource, ResourceServer 
{ 
    // Class variables 
    private static Hashtable extensions; 
    private static Vector stationAtDev; 
    private static StationServerThread stationServerThread; 
    private static LinkedList events; 
     
    static { 
        extensions = new Hashtable(); 
        stationAtDev = new Vector(5,4); 
        stationServerThread = new StationServerThread(); 
        stationServerThread.setName("Stations service thread"); 
        events = new LinkedList(); 
        stationServerThread.start(); 
    } 
     
    // Instance variables 
    private String extension;	// my address 
    private boolean hookState; 	// I know my hookstate 
    private boolean onHook; 	// my hook state 
    private boolean ringing;	// am I ringing ? 
     
    private int ts;             // my TX ts 
    private Channel channel;    // talking to channel 
    private boolean routedRx;   // Have we done routing of audio rx at attach time 
    private boolean routedTx;   // Have we done routing of audio tx via connect 
    private StationServer server; // what takes care of my dialing 
    private ResourceServer xserver; // who activelly looks after me (i.e. gets all events) 
    private Thread serviceThread; // the thread for this station server 
    private ThreadGroup serviceGroup; // a group for this station server 
    private Channel serviceChannel;  // a channel bound to this station server 
 
    /** Open an MSI station device */ 
    public Station(String name, String extension) 
    { 
        super(null, name); 
        // msiBnCm 
        if (! name.substring(0, 4).equals("msiB")) 
            throw new RuntimeException("Station: bad name"); 
        device = Dialogic.ms_open(name, 0); 
        // Extensions table 
        this.extension = extension; 
        extensions.put(extension, this); 
        // Device lookup  
        if (stationAtDev.size() <= device) { 
            stationAtDev.setSize(device + 1); 
        } 
        stationAtDev.setElementAt(this, device); 
	// etc...         
        ts = Dialogic.ms_getxmitslot(device); 
        onHook = (Dialogic.ATMS_TSSGBIT(device) == 0); 
        hookState = true; 
        channel = null; 
        routedRx = false; 
        routedTx = false; 
        server = null; 
        xserver = null; 
        serviceThread = null; 
        serviceGroup = new ThreadGroup(name + " group"); 
        serviceChannel = null; 
        Channel.register(this, device); 
        // Let us know onhook/offhook/flash events 
        Dialogic.ms_setevtmsk(device, Dialogic.DTG_SIGEVT,  
            Dialogic.MSMM_ONHOOK|Dialogic.MSMM_OFFHOOK|Dialogic.MSMM_HOOKFLASH, Dialogic.DTA_SETMSK); 
        register(); 
    } 
     
    // Get a reference to an extension by name. Kind of protect busy (offHook) extensions 
    public static Station getStation(String anExtension) { 
    	Station stat = (Station)extensions.get(anExtension); 
    	if (stat == null) 
    		throw new RuntimeException("Unknown extension"); 
    	if (!stat.onHook) 
    		throw new BusyException("Busy extension"); 
    	return stat; 
    } 
     
    // Start ringing 
    public void ring()  
    { 
        if (onHook() && !ringing) { 
            Dialogic.ms_genring(device, 100, Dialogic.EV_ASYNC); 
            ringing = true; 
            if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
                System.out.println(this.toString() + " ringing!"); 
            } 
        } 
    } 
     
    // Stop ringing 
    public void ringOff() 
    { 
        if (onHook() && ringing) { 
            Dialogic.ms_stopfn(device, Dialogic.MTF_RING); 
            ringing = false; 
            if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
                System.out.println(this.toString() + " stop ringing!"); 
            } 
        } 
    } 
     
    // Answer my hook state 
    public boolean onHook()  
    { 
    	if (!hookState)  
    		onHook = (Dialogic.ATMS_TSSGBIT(device) == 0); 
        return onHook; 
    } 
     
    // Answer my tx TS 
    public int getTs() { 
        return ts; 
    } 
 
    // Answer my tx TS, resource interface version (ours does not depend on Channel) 
    public int getTs(Channel aChannel) { 
        return ts; 
    } 
 
    // Listen to a device 
    public void listen(Device source) { 
        if (source != null) 
            Dialogic.ms_listen(device, source.getTs()); 
        else 
            Dialogic.ms_unlisten(device); 
    } 
 
    // Listen to a resource ???  
     public void listen(Channel ch, Resource res) { 
        int ts = res.getTs(ch); 
        if (ts < 0) 
            Dialogic.ms_unlisten(device); 
        else  
            Dialogic.ms_listen(device, ts); 
    } 
     
    public void finalize() throws Throwable  
    { 
        close(); 
        super.finalize(); 
    } 
     
    public void close() 
    { 
        if (channel != null && routedTx) 
            disconnect(channel); 
        listen(null); 
        Dialogic.ms_setevtmsk(device, Dialogic.DTG_SIGEVT,  
            0, Dialogic.DTA_SETMSK); 
        super.close(); 
        extensions.remove(extension); 
        stationAtDev.setElementAt(null, device); 
    } 
     
    /** 
     * assign: Assign to channel 
     * This can optionally route (default) audio from channel to station 
     */ 
    public void assign(Channel ch) { 
        assign(ch, true); 
    } 
     
    public void assign(Channel ch, boolean route) { 
        if (channel != null) 
            free(channel); 
 
        if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
            System.out.println(this.toString() + " assigned to " + ch + (route ? "(routed)":"")); 
        } 
        channel = ch; 
        routedRx = route; 
        if (routedRx) 
            Dialogic.ms_listen(device, ch.getNetwork().getTs()); 
        ch.attach(this); 
    } 
     
    // Channel getting cleared 
    public void free(Channel ch) { 
        if (channel != ch) 
            return; 
        if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
            System.out.println(this.toString() + " deassigned from " + ch); 
        } 
        if (routedTx) 
            disconnect(ch); 
        routedTx = false; 
        channel = null; 
        ch.dettach(this); 
        listen(null); 
        routedRx = false; 
    } 
     
    // Talk to channel 
    public void connect(Channel ch) { 
        if (channel != ch) 
            return; 
        if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
            System.out.println(this.toString() + " connected to " + ch); 
        } 
        ch.listen(this); 
        routedTx = true; 
    } 
     
    // Stop talking to channel 
    public void disconnect(Channel ch) { 
        if (channel != ch) 
            return; 
        if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
            System.out.println(this.toString() + " disconnected from " + ch); 
        } 
        ch.getNetwork().listen(ch.getVoice()); 
        routedTx = false; 
    } 
     
    public String toString() { 
        StringBuffer desc = new StringBuffer(); 
        desc.append("MSI Station " + extension); 
        return desc.toString(); 
    } 
 
    public boolean service(EVT evt) { 
        if (evt.type == EVT.DTEV_SIG) { 
            if ((evt.data & Dialogic.DTC_ABIT) != 0) { 
                if ((evt.data & Dialogic.DTB_ABIT) == 0)  
                    onHook = true; 
                else 
                    onHook = false; 
                hookState = true; 
                if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0)  
                    System.out.println(this.toString() + (onHook?" OnHook":" OffHook")); 
            } 
            // chain to xserver, deal if none or not serviced by it 
            if (xserver == null || !xserver.service(evt)) { 
                if (channel != null) 
                    channel.newEvent(evt); 
                else  
                	newEvent(evt); 
            } 
            return true; 
        } else if (evt.type == EVT.MSEV_RING || evt.type == EVT.MSEV_NORING) { 
            ringing = false; 
            // Carefull, usually hook state changed 
            // but we are at interrupt time now... so wait for next user action 
            // onHook = (Dialogic.ATMS_TSSGBIT(device) == 0); 
            hookState = false; 
            // chain to xserver, deal if none or not serviced by it 
            if (xserver == null || !xserver.service(evt)) { 
                if (channel != null) 
                    channel.newEvent(evt); 
                else  
                	newEvent(evt); 
            } 
            return true; 
        } 
        return false; 
    } 
 
    public void setServer(StationServer aServer) { 
    	server = aServer; 
    } 
 
    public void register(ResourceServer aServer) { 
    	xserver = aServer; 
    } 
 
    public void setServiceChannel(Channel ch) { 
        serviceChannel = ch; 
    } 
     
    private void newEvent(EVT evt) { 
        synchronized(events) { 
            events.add(evt); 
            events.notifyAll(); 
        } 
    } 
     
    private static class StationServerThread extends Thread { 
        // Run: Keep looking for station unclaimed events, dispatch new servers 
        public void run() 
        { 
            EVT evt; 
            if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
              System.out.println("Station thread started"); 
            } 
            while (true) { 
                synchronized(events) { 
                    while (events.size() == 0) { 
                        try {  
                           events.wait(); 
                        } catch (InterruptedException ie) {} 
                    } 
                    evt = (EVT)events.removeFirst(); 
                } 
	            if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
		            System.out.println("Station thread: " + evt); 
          	    } 
          	    if (evt.type == EVT.DTEV_SIG && (evt.data & Dialogic.DTB_ABIT) != 0) { 
          		    // Offhook 
          		    Station stat = (Station)stationAtDev.elementAt(evt.dev); 
          		    if (stat == null) continue; // ?? 
          		    if (stat.server == null) continue; // No server code 
          		    if (stat.serviceChannel != null) { 
          		        stat.serviceChannel.newEvent(evt); 
          		        continue; 
          		    } 
          		    if (stat.serviceThread != null) continue; // Already being served ??? 
          		    stat.serviceThread = new StationThread(stat); 
	                if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
		                System.out.println("Station thread: firing server for station "+stat.extension); 
          	        } 
          		    stat.serviceThread.start(); 
                } else if (evt.type == EVT.DTEV_SIG && (evt.data & Dialogic.DTB_ABIT) == 0) { 
                    // OnHook 
                    Station stat = (Station)stationAtDev.elementAt(evt.dev); 
          	        if (stat == null) continue; // ?? 
          	        if (stat.serviceChannel != null) { 
          	            stat.serviceChannel.newEvent(evt); 
          	            continue; 
          	        } 
          	        if (stat.serviceThread != null) { 
          	            // Has a server... 
        	            if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
    		                System.out.println("Station thread: killing server for station "+stat.extension); 
              	        } 
              		    stat.serviceThread.interrupt(); 
                    } 
                } 
            } 
        } 
    } 
     
 
    // Give service (e.g. dialtone) to an offhook station 
    private static class StationThread extends Thread { 
        Station myStation; 
         
        StationThread(Station aStation) { 
            super(aStation.serviceGroup, "Station " + aStation.extension + " thread"); 
            myStation = aStation; 
        } 
         
        public void run() 
        { 
            try { 
                myStation.server.service(myStation); 
            } finally { 
                myStation.serviceThread = null; 
          	    myStation.serviceChannel = null; 
                myStation.listen(null); 
        	    if ((Dialogic.debug & Dialogic.DEBUG_STATION) != 0) { 
		            System.out.println("Station thread: finished!"); 
          	    } 
          	} 
        } 
    } 
}