package uk.co.mmscomputing.device.capi;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import uk.co.mmscomputing.util.metadata.*;
import uk.co.mmscomputing.concurrent.*;
import uk.co.mmscomputing.device.capi.ctrl.*;
import uk.co.mmscomputing.device.capi.plc.*;
import uk.co.mmscomputing.device.capi.ncc.*;
import uk.co.mmscomputing.device.capi.protocol.*;
import uk.co.mmscomputing.device.capi.q931.*;
import uk.co.mmscomputing.device.capi.parameter.*;
import uk.co.mmscomputing.device.capi.exception.*;
import uk.co.mmscomputing.device.capi.facility.*;
public class CapiApplication extends Thread implements CapiConstants{
protected int appid=-1;
protected int maxLogicalCon=-1;
protected boolean listeningToSupServices=false; // listen to Supplementary Services
protected Metadata md=null;
private boolean running=true;
private Semaphore rs=new Semaphore(0,true);
private Vector plcs=new Vector(); // set of active physical connections
public CapiApplication(Metadata md)throws CapiException{
this.md=md;
md.getInt(capiSpeechCodingID,LAYER1USERINFO_ALAW); // use A-Law, if not set
maxLogicalCon=md.getInt(capiMaxLogicalConnectionsID,2); // use two b-channels, if not set
jcapi.checkInstalled();
appid=jcapi.register(maxLogicalCon,MaxNumB3DataBlocks,DefaultB3DataBlockSize);
}
public int getApplID(){ return appid;}
public boolean isRunning(){ return running;}
public boolean isListeningToSupService(){ return listeningToSupServices;}
protected CapiController getController()throws CapiException{
int ctrlid=md.getInt(capiControllerID,1); // System.out.println("\b5capi.controller.id "+ctrlid);
return CapiEnumerator.getController(ctrlid);
}
protected int getControllerId()throws CapiException{
return getController().getId();
}
/*synchronized*/public void put(MsgOut msg)throws CapiException{// THE ONLY place where we send our capi req or resp
if(!running){return;}
// if(msg.cmd!=CAPI_DATA_B3){md.fireListenerUpdate(msg);} // testing; tell listeners; somebody might want to know.
jcapi.putMessage(appid,msg.getBytes());
}
public void close(){
if(!running){return;}
try{
int cntrl = getControllerId();
// put(new ListenReq(appid,cntrl,0)); // stop listening
closePLCs();
running=false;
MsgOut msg=new ListenReq(appid,cntrl,0); // send dummy message to unblock jcapi.waitForMessage
jcapi.putMessage(appid,msg.getBytes()); // cannot use put here! running is false
try{rs.acquire();}catch(InterruptedException ie){}
System.err.println(getClass().getName()+".close: RELEASED CAPI APPLICATION "+appid);
}catch(Exception ce){
System.err.println(getClass().getName()+".close:\n\t"+ce);
md.fireListenerUpdate("9\b"+getClass().getName()+".close:\n\t"+ce);
}
}
public CapiPLC getPLC(int plci)throws CapiException{
plci&=0x0000FFFF;
if(plci<256){return null;} // msg for controller only
for(Enumeration e=plcs.elements();e.hasMoreElements();){
CapiPLC plc=(CapiPLC)e.nextElement();
if(plc.lineid==plci){
return plc;
}
}
throw new CapiException(getClass().getName()+".getPLC("+plci+")\n\tCannot find PLC with plci = 0x"+Integer.toHexString(plci)+" !");
}
private CapiPLC newPLC(ConnectInd msg){
CapiPLC plc=new CapiPLC(this,msg);plcs.add(plc);return plc;
}
protected CapiPLC newPLC(ConnectReq msg){
CapiPLC plc=new CapiPLC(this,msg);plcs.add(plc);return plc;
}
private CapiPLC getPLC(ConnectConf msg)throws CapiException{
int msgno = msg.msgno;
for(Enumeration e=plcs.elements();e.hasMoreElements();){
CapiPLC plc=(CapiPLC)e.nextElement();
if(plc.getMsgno()==msgno){
plc.setLineid(msg.lineid);
return plc;
}
}
throw new CapiException(getClass().getName()+".getPLC("+msgno+")\n\tCannot find PLC with connect msgno = "+msgno+" !");
}
public void closePLCs(){
CapiPLC[] lines=(CapiPLC[])plcs.toArray(new CapiPLC[0]);
for(int i=0;i<lines.length;i++){
try{
lines[i].close();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
}
// ---- Conf/Ind methods
protected void received(CapiPLC plc,ConnectInd msg)throws CapiException{
put(new ConnectResp(msg.appid,msg.lineid,IGNORE)); // ignore call
// see CapiServerApplication
}
protected void received(CapiNCC ncc,ConnectB3ActiveInd msg)throws CapiException{
BProtocol protocol=ncc.getProtocol();
NCPI ncpi=msg.getNCPI(protocol);
if(ncpi!=null){md.fireListenerUpdate(ncpi);} // ncpi gives some useful info; i.e. T30 Fax protocol
if(ncpi!=null){
switch(protocol.B3Protocol){
case CAPI_PROTOCOL_T30_FAX: break;
case CAPI_PROTOCOL_ISO8208: break;
case CAPI_PROTOCOL_MODEM: put(new V42InfoReq(msg.appid,msg.lineid));break;
}
}
}
protected void receivedResetB3Ind(CapiNCC ncc,ResetB3Ind msg)throws CapiException{
NCPI ncpi=msg.getNCPI(ncc.getProtocol());
if(ncpi!=null){md.fireListenerUpdate(ncpi);} // ncpi gives some useful info; i.e. T30 Fax protocol
// ncc.receivedResetB3Ind((ResetB3Ind)msg);
}
protected void receivedConnectB3T90ActiveInd(CapiNCC ncc,ConnectB3T90ActiveInd msg)throws CapiException{
NCPI ncpi=msg.getNCPI(ncc.getProtocol());
if(ncpi!=null){md.fireListenerUpdate(ncpi);} // ncpi gives some useful info; i.e. T30 Fax protocol
// ncc.receivedConnectB3T90ActiveInd((ConnectB3T90ActiveInd)msg);
}
// ---- private methods
private void handleIndMsg(CapiIndMsg msg)throws CapiException{
CapiPLC plc=null;CapiNCC ncc=null;
if((msg.lineid&0x0000FF00)!=0){
if(msg.cmd==CAPI_CONNECT){
plc=newPLC((ConnectInd)msg); // create new plc
}else{
plc=getPLC(msg.lineid); // get existing plc
}
if((msg.lineid&0xFFFF0000)!=0){
if(msg.cmd==CAPI_CONNECT_B3){
ncc=plc.addNCC((ConnectB3Ind)msg); // create ncc
}else{
ncc=plc.getNCC(msg.lineid);
}
}
}
switch(msg.cmd){
case CAPI_DATA_B3: ncc.received((DataB3Ind)msg); break; // receive incoming data
case CAPI_ALERT: break;
case CAPI_CONNECT: // incoming call
plc.setSpeechCoding(md.getInt(capiSpeechCodingID)); // default set by main application
received(plc,(ConnectInd)msg); // let subclasses handle it
break;
case CAPI_CONNECT_ACTIVE: // physically connected
put(new ConnectActiveResp(msg.appid,msg.lineid)); // tell capi to set up call
plc.received((ConnectActiveInd)msg);
break;
case CAPI_CONNECT_B3: // incoming connection in set-up phase
put(new ConnectB3Resp(msg.appid,msg.lineid)); // accept call
NCPI ncpi=((ConnectB3Ind)msg).getNCPI(ncc.getProtocol());
if(ncpi!=null){md.fireListenerUpdate(ncpi);} // ncpi gives some useful info; i.e. T30 Fax protocol
if(ncpi!=null){
switch(ncc.getProtocol().B3Protocol){
case CAPI_PROTOCOL_T30_FAX: break;
case CAPI_PROTOCOL_ISO8208: break;
case CAPI_PROTOCOL_MODEM: put(new V42InfoReq(msg.appid,msg.lineid));break;
}
}
break;
case CAPI_CONNECT_B3_ACTIVE: // connected
ncc.received((ConnectB3ActiveInd)msg); // ncc is open for business now
ncc.setPickedUp(plc.getPickedUp()); // if plc has been picked up manually set ncc pickedUp=true
received(ncc,(ConnectB3ActiveInd)msg); // tell CapiServerApplication about new connection
put(new ConnectB3ActiveResp(msg.appid,msg.lineid));
ncc.releasePickupBlocker(); // This releases blocker in (outgoing) CapiCallApplication.connect
// or (incoming) in CapiServerApplication$Pickup.indicateCall
break;
case CAPI_CONNECT_B3_T90_ACTIVE:
if(!ncc.isOpen()){
receivedConnectB3T90ActiveInd(ncc,(ConnectB3T90ActiveInd)msg);
put(new ConnectB3T90ActiveResp(msg.appid,msg.lineid));
}
break;
case CAPI_DISCONNECT_B3:
put(new DisconnectB3Resp(msg.appid,msg.lineid));
ncc.received((DisconnectB3Ind)msg);
plc.remove(ncc); // capi won't send anything anymore to this ncc
break;
case CAPI_DISCONNECT:
plc.received((DisconnectInd)msg);
put(new DisconnectResp(msg.appid,msg.lineid));
plcs.remove(plc);
break;
case CAPI_FACILITY:
plc.received((FacilityInd)msg);
break;
case CAPI_INFO:
put(new InfoResp(msg.appid,msg.lineid));
/*
if(msg instanceof InfoInd.IIBearerCapability){
BearerCapability bc=((InfoInd.IIBearerCapability)msg).bc;
plc.setSpeechCoding(bc.getSpeechCoding());
}
*/
break;
case CAPI_RESET_B3:
receivedResetB3Ind(ncc,(ResetB3Ind)msg);
put(new ResetB3Resp(msg.appid,msg.lineid));
break;
case CAPI_MANUFACTURER:
break;
default:
System.err.println("3\b"+getClass().getName()+".handleIndMsg:\n\tUnknown indication message\n\t"+msg);
break;
}
}
private void handleConfMsg(CapiConfMsg msg)throws IOException{
CapiPLC plc=null;CapiNCC ncc=null;
if((msg.lineid&0x0000FF00)!=0){
if(msg.cmd==CAPI_CONNECT){ // outgoing call
plc=getPLC((ConnectConf)msg); // get plc with msg.msgno that belonged to ConnectReq
}else{
plc=getPLC(msg.lineid); // get existing plc
}
if((msg.lineid&0xFFFF0000)!=0){
if(msg.cmd==CAPI_CONNECT_B3){ // outgoing connection in set-up phase
ncc=plc.addNCC((ConnectB3Conf)msg); // create ncc
}else{
ncc=plc.getNCC(msg.lineid);
}
}
}
int info=msg.getInfo();
if((info&0x0000FF00)==0){ // no errors occurred
switch(msg.cmd){
case CAPI_DATA_B3: // data out confirmation
ncc.received((DataB3Conf)msg);
break;
case CAPI_ALERT:
break;
case CAPI_CONNECT: // outgoing call
plc.setSpeechCoding(md.getInt(capiSpeechCodingID)); // default set by main application
break;
case CAPI_CONNECT_B3: // outgoing connection in set-up phase
break;
case CAPI_DISCONNECT_B3: // logical connection clear down has been initiated
// ncc.received((DisconnectB3Conf)msg);
break;
case CAPI_DISCONNECT:
break;
case CAPI_FACILITY:
if(msg instanceof SupServiceConf.ListenConf){
SupServiceConf.ListenConf lcmsg=(SupServiceConf.ListenConf)msg;
if(lcmsg.getInfo()==0){
listeningToSupServices=true;
}else{
System.err.println("SupServiceReq.ListenReq failed\n"+msg);
}
}
if(plc!=null){
plc.received((FacilityConf)msg);
}
break;
case CAPI_INFO:
break;
case CAPI_LISTEN:
break;
case CAPI_MANUFACTURER:
break;
case CAPI_RESET_B3:
break;
case CAPI_SELECT_B_PROTOCOL:
System.err.println(getClass().getName()+": Switched Protocol: 0x"+Integer.toHexString(msg.lineid));
// System.err.println(msg.toString());
break;
default:
System.err.println("3\b"+getClass().getName()+".handleConfMsg:\n\tUnknown confirmation message\n\t"+msg);
break;
}
}else{ // some error occurred
switch(msg.cmd){
case CAPI_FACILITY:
plc.received((FacilityConf)msg);
break;
case CAPI_CONNECT_B3: // outgoing connection failed in set-up phase
put(new DisconnectReq(msg.appid,msg.lineid)); // disconnect line
break;
}
}
}
public void run( ){ // Runnable interface
MsgIn msg;
Rider r=new Rider();
byte[] data=new byte[128];
Thread.currentThread().setName(getClass().getName()+"."+getApplID());
while(running){
msg=null;
try{
jcapi.waitForMessage(appid); // wait for capi message
if(!running){break;}
data=jcapi.getMessage(appid,data); // use msg byte array if big enough otherwise return new byte array
r.set(data);
msg=MsgIn.create(r); // turn capi message into java object
if(msg.cmd!=CAPI_DATA_B3){md.fireListenerUpdate(msg);} // testing; tell listeners; somebody might want to know.
if(msg.scmd==CAPI_CONF){ // if confirmation message
handleConfMsg((CapiConfMsg)msg);
}else{ // else indication message
handleIndMsg((CapiIndMsg)msg);
}
}catch(CapiException cioe){
cioe.printStackTrace();
if(msg!=null){System.err.println(msg.toString());}
md.fireListenerUpdate(cioe);
}catch(Throwable t){
t.printStackTrace();
if(msg!=null){System.err.println(msg.toString());}
md.fireListenerUpdate("9\b"+getClass().getName()+".run:\n\tFatal Error; Stopped capi application thread "+appid+".\n\t"+t);
break;
}
}
try{
jcapi.release(appid); // release resources
rs.release();
System.err.println(getClass().getName()+".run: RELEASED CAPI APPLICATION "+appid);
}catch(Exception e){e.printStackTrace();}
}
protected void finalize()throws Throwable{
close();super.finalize();
}
}