package net.sourceforge.gjtapi.raw.modem; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Properties; import net.sourceforge.gjtapi.CallId; /** * A concrete implementation of Modem used with properties files. This is based on * the AccuraV92.java modem config. * * This implementation is not tested. Still working on it. * Usage: * * Replace 2 lines in Modem.props * ModemClass=net.sourceforge.gjtapi.raw.modem.PropertiesModem * properties.modem.config=DefaultModem.props * * * @author Henry Voyer * @version $Revision$ $Date$ */ public class PropertiesModem extends AbstractModem implements PropertiesModemValues { //private static final String version_id = "@(#)$Id: PropertyModem 1.2 2004/12/18 by Henry Voyer"; private CallId currId; private StringBuffer digitBucket; Properties currentConfig; public PropertiesModem(ModemListener listener) throws Exception { super(listener); digitBucket = new StringBuffer(); Properties configProperties = loadResources(MODEM_RESOURCE_NAME); String modemConfigFile = configProperties .getProperty(PROPERTIES_MODEM_CONFIG); currentConfig = loadResources(PROPERTIES_FOLDER + modemConfigFile); } public boolean initialize(String portname) { boolean result = super.initialize(portname); //Now try to initialize the modem if (result) { //need to send the initialize string and wait for the //correct response try { io.writeLine(currentConfig.getProperty(INIT)); if (io.match(5000, currentConfig.getProperty(INIT_OK), currentConfig.getProperty(INIT_ERR)) == ModemIO.GOOD_MATCH) { state = IDLE; result = true; } else { state = INVALID; //result = false; //default } } catch (IOException ex) { state = INVALID; //result = false; } } return result; } public void drop(CallId id) { //this method needs some thought about how it interacts with calls //which are in progresss. state = DROPPING; try { io.writeLine(currentConfig.getProperty(HANGUP)); if (io.match(5000, currentConfig.getProperty(HANGUP_OK), currentConfig.getProperty(HANGUP_ERR)) == ModemIO.GOOD_MATCH) { state = IDLE; listener.modemDisconnected(id); currId = null; } else { state = INVALID; } } catch (IOException ex) { state = INVALID; //state possible not "BUSY" at time of hangup } } public boolean call(CallId id, String dest) { boolean result = false; // first note that the local leg is connected listener.modemConnected(id); try { //go to voice mode io.writeLine(currentConfig.getProperty(VOICE)); int matchState = io.match(5000, currentConfig.getProperty(VOICE_OK), currentConfig .getProperty(VOICE_ERR)); if (matchState == ModemIO.GOOD_MATCH) { //set "Ringback-Goes-Away Timer" to suitable value (what // units?) io.writeLine(currentConfig.getProperty(AWAY_TONE)); matchState = io.match(5000, OK, ERROR); } // note that we are dialing listener.modemDialing(id, dest); if (matchState == ModemIO.GOOD_MATCH) { //dial the number io.writeLine(currentConfig.getProperty(DIAL) + dest); //Need to loop until terminal is answered, line is busy //or call is dropped do { matchState = io.match(1000, currentConfig .getProperty(DIAL_OK), currentConfig .getProperty(DIAL_ERR)); } while (matchState == ModemIO.TIMEOUT | state == DROPPING); } if (matchState == ModemIO.GOOD_MATCH) { state = BUSY; // remote end now connected listener.modemConnected(id, dest); currId = id; result = true; } else { state = INVALID; if (matchState == ModemIO.TIMEOUT) { } else { } } } catch (IOException ex) { state = INVALID; } return result; } public void answer(CallId id) { super.answer(id); try { io.writeLine(currentConfig.getProperty(VOICE)); int matchState = io.match(5000, currentConfig.getProperty(VOICE_OK), currentConfig .getProperty(VOICE_ERR)); if (matchState == ModemIO.GOOD_MATCH) { io.writeLine(AT_VALIDATEMATCH); matchState = io.match(5000, OK, ERROR); } if (matchState == ModemIO.GOOD_MATCH) { io.writeLine(currentConfig.getProperty(ANSWER)); matchState = io.match(5000, currentConfig .getProperty(ANSWER_OK), currentConfig .getProperty(ANSWER_ERR)); } if (matchState == ModemIO.GOOD_MATCH) { listener.modemConnected(id); state = BUSY; currId = id; } else { state = INVALID; } } catch (IOException ex) { state = INVALID; } } public String reportDTMF(int num) { String result = ""; try { synchronized (digitBucket) { int len = digitBucket.length(); num = (num > len) ? len : num; result = digitBucket.substring(0, num); digitBucket.delete(0, num); } } catch (Exception ex) { } return result; } public void sendDTMF(String tones) { io.writeLine(currentConfig.getProperty(TONE_SEND) + tones); try { while (io.match(5000, currentConfig.getProperty(TONE_SEND_OK), currentConfig.getProperty(TONE_SEND_ERR)) == ModemIO.TIMEOUT) { //nop } } catch (IOException ex) { } } public void play(InputStream is) { try { io.writeLine(currentConfig.getProperty(AT_PLAY_STEP_1)); int matchState = io.match(1000, OK, ERROR); if (matchState == ModemIO.GOOD_MATCH) { io.writeLine(currentConfig.getProperty(AT_PLAY_STEP_2)); matchState = io.match(1000, "CONNECT", ERROR); } if (matchState == ModemIO.GOOD_MATCH) { int val; while ((val = is.read()) != -1) { io.write(val); } //Tell the modem that we have finished transmitting io.write(ModemIO.DLE); io.write(ModemIO.ETX); } } catch (Exception ex) { } } public void record(OutputStream os) { try { io.writeLine(currentConfig.getProperty(AT_RECORD_START_STEP_1)); int matchState = io.match(1000, OK, ERROR); if (matchState == ModemIO.GOOD_MATCH) { io.writeLine(currentConfig.getProperty(AT_RECORD_START_STEP_2)); matchState = io.match(1000, "CONNECT", ERROR); } if (matchState == ModemIO.GOOD_MATCH) { int val; while ((val = io.read()) != -1) { os.write(val); //the modem can be brought out of record by sending <DLE>! //the modem will send us <DLE>s or <DLE>q after detecting //silence for longer than the silence detection timer } } } catch (Exception ex) { } } public void dleReceived(char shielded) { try { if ((shielded >= '0' && shielded <= '9') || shielded == '*' || shielded == '#') { synchronized (digitBucket) { digitBucket.append(shielded); } } else { switch (shielded) { case 'd': case 's': case 'q': drop(currId); break; } } } catch (Exception ex) { } } private Properties loadResources(String propertyFile) throws Exception { Properties property = new Properties(); property.load(this.getClass().getResourceAsStream("/" + propertyFile)); return property; } }