/*
* Created on Jul 25, 2006
*
*Copyright Reliable Response, 2006
*/
package net.reliableresponse.notification.providers;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;
import net.reliableresponse.notification.Notification;
import net.reliableresponse.notification.NotificationException;
import net.reliableresponse.notification.broker.BrokerFactory;
import net.reliableresponse.notification.broker.impl.clustered.ClusteredServiceManager;
import net.reliableresponse.notification.device.Device;
import net.reliableresponse.notification.device.PagerDevice;
import net.reliableresponse.notification.device.TAPDevice;
import net.reliableresponse.notification.device.TwoWayPagerDevice;
import net.reliableresponse.notification.sender.EmailSender;
import net.reliableresponse.notification.usermgmt.UnknownUser;
import net.reliableresponse.notification.util.StringUtils;
public class TAPNotificationProvider extends AbstractNotificationProvider implements SerialPortEventListener {
// ASCII defines
final String ESC = ""+(char)(0x1B);
final String STX = ""+(char)(2);
final String ETX = ""+(char)(3);
final String ETB = ""+(char)(0x17);
/*
String phoneNumber = "18002506325";
phoneNumber = "18009464644";
*/
//String phoneNumber = "18009464644";
String phoneNumber = "18002506325";
boolean hasData;
int speed = 2400;
String parity = "E";
int stopbits = 1;
int databits = 7;
String password = "";
public TAPNotificationProvider(String phoneNumber, int speed, int databits, String parity, int stopbits) {
this.phoneNumber = phoneNumber;
this.speed = speed;
this.databits = databits;
this.parity = parity;
this.stopbits = stopbits;
this.hasData = false;
}
public TAPNotificationProvider() {
}
public void init(Hashtable params) throws NotificationException {
if (!ClusteredServiceManager.getInstance().willRun("MSN")) {
return;
}
phoneNumber = (String)params.get("Phone Number");
try {
databits = Integer.parseInt ((String)params.get("Data Bits"));
} catch (NumberFormatException e) {
BrokerFactory.getLoggingBroker().logWarn(e.getMessage());
}
try {
stopbits = Integer.parseInt ((String)params.get("Stop Bits"));
} catch (NumberFormatException e) {
BrokerFactory.getLoggingBroker().logWarn(e.getMessage());
}
parity = (String)params.get("Parity");
hasData = false;
}
public void serialEvent(SerialPortEvent event) {
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
hasData = true;
}
}
private int getHigh(int nibble) {
int ret = nibble >> 4;
if (ret < 0) ret +=256;
return ret;
}
private int getLow(int nibble) {
int ret = (int)(nibble & 0xF);
if (ret < 0) ret +=256;
return ret;
}
public String getChecksum (String pagerNum, String message) {
char[] chars = {'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?'};
int total = 2;
byte[] bytes = pagerNum.getBytes();
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] < 0) {
total += 256;
}
total += bytes[i];
}
total +=13;
bytes = message.getBytes();
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] < 0) {
total += 256;
}
total += bytes[i];
}
total +=13;
total +=3;
StringBuffer ret = new StringBuffer();
ret.append (chars[(total>>8)&0xf]);
ret.append (chars[(total>>4)&0xf]);
ret.append (chars[total&0xf]);
return ret.toString();
}
private String[] getMessages (DataInputStream inputReader) throws IOException, NotificationException {
Vector messages = new Vector();
boolean gotControlCode = false;
while (!gotControlCode) {
String line = inputReader.readLine();
BrokerFactory.getLoggingBroker().logDebug("Message line = "+line);
byte[] bytes = line.getBytes();
if (bytes.length == 1) {
if (bytes[0] == 6) {
// ACK
gotControlCode = true;
BrokerFactory.getLoggingBroker().logDebug("Got ACK from TAP interface");
} else if (bytes[0] == 30) {
// RS
gotControlCode = true;
BrokerFactory.getLoggingBroker().logDebug("Got RS from TAP interface");
throw new NotificationException(NotificationException.FAILED, "Unknown number");
} else if (bytes[0] == 21) {
// NAK
gotControlCode = true;
BrokerFactory.getLoggingBroker().logInfo("Got NACK from TAP interface. Treating as ACK");
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Retry");
} else if (bytes[0] == 4) {
// EOT
throw new NotificationException(NotificationException.FAILED, "Server error");
}
} else {
messages.addElement(line);
}
}
return (String[])messages.toArray(new String[0]);
}
public Hashtable sendNotification(Notification notification, Device device)
throws NotificationException {
if (!ClusteredServiceManager.getInstance().willRun("TAP")) {
ClusteredServiceManager.getInstance().sendNotificationToDevice
(notification,
"TAP",
notification.getDisplayText(),
device.getUuid());
return new Hashtable();
}
boolean portFound = false;
SerialPort serialPort;
PrintStream outputWriter;
DataInputStream inputReader;
boolean outputBufferEmptyFlag = false;
String defaultPort = BrokerFactory.getConfigurationBroker().getStringValue("modem.port", "/dev/ttyS0");
if (!(device instanceof PagerDevice)) {
throw new NotificationException(NotificationException.FAILED, "Supplied device isn't a pager");
}
String pagerNumber = ((PagerDevice)device).getNormalizedNumber();
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (portId.getName().equals(defaultPort)) {
BrokerFactory.getLoggingBroker().logDebug("Using modem on port "+defaultPort+" with "+databits+parity+stopbits);
portFound = true;
try {
serialPort =
(SerialPort) portId.open("Reliable Response Notification", 2000);
} catch (PortInUseException e) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Modem in use");
}
try {
int parityInt = SerialPort.PARITY_EVEN;
if (parity.equals ("N")) {
parityInt = SerialPort.PARITY_NONE;
} else if (parity.equals ("O")) {
parityInt = SerialPort.PARITY_ODD;
}
serialPort.setSerialPortParams(speed,
databits,
stopbits,
parityInt);
} catch (UnsupportedCommOperationException e) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Temporary modem problem");
}
try {
outputWriter = new PrintStream(serialPort.getOutputStream());
inputReader = new DataInputStream(serialPort.getInputStream());
} catch (IOException e) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Could not open modem");
}
try {
// Write the modem init script
String modemInitString = BrokerFactory.getConfigurationBroker().getStringValue("modem.init", "ATH");
writeToModem(outputWriter, inputReader, modemInitString, "OK");
writeToModem(outputWriter, inputReader, "ATZ", "OK");
writeToModem(outputWriter, inputReader, "ATDT"+phoneNumber, "CONNECT");
// Start the section where we look for ID=
int totalTime = 0;
int count = 0;
String idLine = "";
while ((idLine.toLowerCase().indexOf("id=")<0) && (count <=3)) {
outputWriter.print ("\r");
outputWriter.flush();
BrokerFactory.getLoggingBroker().logDebug("TAP Sent blank line");
while ((!hasData) && (totalTime <= 2000)) {
// sleep 1/10 of a second to wait for data
Thread.sleep(100);
totalTime +=100;
BrokerFactory.getLoggingBroker().logDebug("Waiting for response");
}
BrokerFactory.getLoggingBroker().logDebug("Got some text, "+inputReader.available());
byte[] b = new byte[1024];
inputReader.read(b);
idLine = new String(b);
BrokerFactory.getLoggingBroker().logDebug("idLine = "+idLine);
count++;
totalTime = 0;
}
if (idLine.toLowerCase().indexOf("id=")<0) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Error during send to pager");
}
//idLine = idLine.substring(3);
outputWriter.print (ESC+"PG1000000\r");
BrokerFactory.getLoggingBroker().logDebug("Wrote ESC");
String[] messageSequence = getMessages(inputReader);
readOK(inputReader, ESC+"[p");
String textmessage = notification.getDisplayText().replace('\n', ' ');
//String textmessage = "test pager";
StringBuffer message = new StringBuffer();
message.append(STX);
message.append(pagerNumber);
message.append("\r");
message.append(textmessage);
message.append("\r");
message.append(ETX);
message.append(getChecksum(pagerNumber, textmessage));
message.append("\r");
count = 0;
boolean sent = false;
while ( (count < 10) && (!sent)) {
count++;
try {
BrokerFactory.getLoggingBroker().logDebug("Message: "+StringUtils.toHexString(message.toString().getBytes()));
outputWriter.write(message.toString().getBytes());
BrokerFactory.getLoggingBroker().logDebug("Write message: "+pagerNumber+":"+textmessage);
String[] responseMessageSequence = getMessages(inputReader);
sent = true;
} catch (NotificationException e) {
if (e.getCode() == NotificationException.TEMPORARILY_FAILED) {
sent = false;
}
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Error during send to pager");
} catch (IOException e) {
throw new NotificationException(NotificationException.TEMPORARILY_FAILED, "Error during send to pager");
}
try {
Thread.sleep(1000); // Be sure data is xferred before closing
} catch (Exception e) {}
serialPort.close();
}
}
}
if (!portFound) {
BrokerFactory.getLoggingBroker().logWarn("port " + defaultPort + " not found.");
}
return null;
}
/**
* @param inputReader
* @throws InterruptedException
* @throws IOException
* @throws NotificationException
*/
private void readOK(DataInputStream inputReader, String comparison) throws InterruptedException, IOException, NotificationException {
Thread.sleep(300); //wait for init to complete
String line = null;
while ( (line = inputReader.readLine()).equals ("")) {
}
BrokerFactory.getLoggingBroker().logDebug("TAP READ: "+line);
int count = 0;
while (count < 10) {
if (line.toLowerCase().indexOf(comparison.toLowerCase())>=0) {
BrokerFactory.getLoggingBroker().logDebug("Matched expected, "+comparison);
return;
} else {
BrokerFactory.getLoggingBroker().logDebug("Did not match expected, "+comparison);
}
line = inputReader.readLine();
BrokerFactory.getLoggingBroker().logDebug("TAP READ: "+line);
count++;
}
}
public boolean writeToModem (PrintStream out, DataInputStream in, String output, String expected) throws IOException {
BrokerFactory.getLoggingBroker().logDebug("TAP Out: "+output);
out.print (output+"\r\n");
out.flush();
try {
Thread.sleep (300);
} catch (InterruptedException e) {
BrokerFactory.getLoggingBroker().logError(e);
}
String line = "";
boolean found = false;
while (!found) {
line = in.readLine();
BrokerFactory.getLoggingBroker().logDebug("read: "+line);
if (line != null) {
if (line.length()>0) {
if (!line.startsWith(output)) {
found = line.toLowerCase().indexOf(expected.toLowerCase())>=0;
return found;
}
}
}
}
return false;
}
public Hashtable getParameters(Notification notification, Device device) {
// TODO Auto-generated method stub
return null;
}
public String[] getResponses(Notification notification) {
return new String[0];
}
public boolean cancelPage(Notification notification) {
return false;
}
public String getName() {
return "One-way modem pager";
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
BrokerFactory.getConfigurationBroker().setConfiguration(
new FileInputStream("conf/reliable.properties"));
TAPDevice device = new TAPDevice();
Hashtable options = new Hashtable ();
options.put("Pager Number", "7205308877");
options.put("Provider", "Cingular");
device.initialize(options);
NotificationProvider provider = device.getNotificationProvider();
Notification notif = new Notification(null, new UnknownUser(), new EmailSender("drig@noses.org"),
"test", "this is a test of cingular tap");
provider.sendNotification(notif, device);
//800-250-6325
}
}