Source code: fi/kvanttisofta/sms/SmsTerminal.java
1 //
2 // Copyright (c) 2001 Kvanttisofta oy. All rights reserved.
3 //
4 //
5 // a class modeling the communication with a SMS terminal device.
6 // (mikko.syrjalahti@iki.fi, aspa@users.sourceforge.net).
7 //
8 // $Id: SmsTerminal.java,v 1.1.1.1 2001/04/18 04:19:00 aspa Exp $.
9 //
10 // TODO:
11 // - error handling. how should errors be communicated to the user?
12 // - lineReceived: error handling
13 //
14
15 package fi.kvanttisofta.sms;
16
17 import java.io.*;
18 import java.util.*;
19 import javax.comm.*;
20 import fi.kvanttisofta.sms.*;
21
22 public class SmsTerminal implements SerialPortEventListener {
23 public static final int SC_OK = 0;
24 public static final int SC_ERROR = 1;
25 public static final int SC_PDU_PARSE_ERROR = 2;
26
27 private SerialPort serialPort;
28 private OutputStream outStream;
29 private InputStream inStream;
30 private SmsListenerInterface smsListener;
31
32 /* CMGF=0 -- SMS messages in PDU mode
33 AT+CNMI new mssage indications to DTE
34 - AT+CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]]
35 <mode> -- no indications when DTE-DCE link reserved
36 <mt> -- received messages (not class 2) routed to DTE with +CMT
37 <bm> -- no cell breadcast indications
38 <ds> -- no status reports
39 <bfr> -- flush indication buffer if <mode> = {1,2}
40 CSCS -- select DTE character set
41 */
42 private static final String initcmd1 = "ATV1E0Q0X5+CMGF=0";
43 private static final String initcmd2 = "AT+CNMI=1,2,0,0,0";
44 private static final String initcmd3 = "AT+CSCS=\"8859-1\"";
45 private static final String initcmd4 = "AT+CMEE=2";
46 private static final String lfcr = "\015";
47
48 private int portStatus = OK;
49 private Boolean portStatusLock = new Boolean(true);
50 private String portStatusMsg = "";
51 private static final int OK = 1;
52 private static final int WAIT = 2;
53 private static final int ERROR = 3;
54 private static final int WMSG = 4;
55 private static final int RMSG = 5;
56
57 private byte[] readBuffer = new byte[500]; // serialEvent
58 private int bufferOffset = 0; // serialEvent
59
60 private boolean should_run = true; // not used
61
62 public SmsTerminal(String portName, SmsListenerInterface smsListener)
63 throws Exception {
64
65 CommPortIdentifier portId = null;
66 Enumeration portList = CommPortIdentifier.getPortIdentifiers();
67
68 /* initialize variables */
69 this.smsListener = smsListener;
70
71 /* find the requested port */
72 while (portList.hasMoreElements()) {
73 portId = (CommPortIdentifier) portList.nextElement();
74
75 if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
76 if (portId.getName().equals(portName)) {
77 try {
78 serialPort = (SerialPort)
79 portId.open(SmsTerminal.class.getName(), 2000);
80 } catch (PortInUseException e) {
81 //throw new PortInUseException("Already opened: " + e);
82 throw new IOException("port "+portName+" in use: "+e);
83 }
84 }
85 }
86 }
87
88 /* port not found */
89 if(serialPort == null) {
90 //throw new NoSuchPortException();
91 throw new IOException("NoSuchPortException: '"+portName+"'");
92 }
93
94 /* Open streams to the port */
95 try {
96 outStream = serialPort.getOutputStream();
97 inStream = serialPort.getInputStream();
98 } catch (IOException e) {
99 serialPort.close();
100 throw new IOException("Cannot get a stream to port: " + e);
101 }
102
103 /* Configure port */
104 try {
105 serialPort.setSerialPortParams(9600,
106 SerialPort.DATABITS_8,
107 SerialPort.STOPBITS_1,
108 SerialPort.PARITY_NONE);
109 } catch (UnsupportedCommOperationException e) {
110 serialPort.close();
111 throw new IOException ("Operation not supported: " + e);
112 }
113
114
115 /* Start a thread for handling comminication with the terminal */
116 serialPort.addEventListener(this);
117
118 /*Add handler for serial events*/
119 serialPort.notifyOnDataAvailable(true);
120 atCmd(initcmd1);
121 atCmd(initcmd2);
122 atCmd(initcmd3);
123
124 }
125
126 private int atCmd(String cmd) {
127 boolean gotTimeout = false;
128
129 synchronized(portStatusLock) {
130 portStatus = WAIT;
131 try {
132 outStream.write((cmd + lfcr).getBytes());
133 } catch (IOException e) { ; }
134
135 /* wait for response from device */
136 try {
137 portStatusLock.wait(500); // millis
138 } catch (InterruptedException e) { gotTimeout=true; }
139 if(portStatus != OK) {
140 // port not ok
141 }
142 } // end: synchronized(portStatusLock) {
143
144 // failed to get device
145 if(gotTimeout) {
146 //System.err.println("SmsTerminal: timeout");
147 }
148
149 return OK;
150 }
151
152 private void close() {
153 serialPort.close();
154 should_run = false;
155 }
156
157
158 public void serialEvent(SerialPortEvent event) {
159
160 switch (event.getEventType()) {
161
162 case SerialPortEvent.BI:
163 case SerialPortEvent.OE:
164 case SerialPortEvent.FE:
165 case SerialPortEvent.PE:
166 case SerialPortEvent.CD:
167 case SerialPortEvent.CTS:
168 case SerialPortEvent.DSR:
169 case SerialPortEvent.RI:
170 case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
171 break;
172
173 case SerialPortEvent.DATA_AVAILABLE:
174 int n;
175
176 try {
177 while ( (n = inStream.available()) > 0) {
178 n = inStream.read(readBuffer, bufferOffset, n);
179 bufferOffset += n;
180
181 // lfcr detected, line ready
182 if((readBuffer[bufferOffset-1] == 10) &&
183 (readBuffer[bufferOffset-2] == 13)) {
184
185 String sbuf = new String(readBuffer,0,bufferOffset-2);
186 lineReceived(sbuf);
187 bufferOffset = 0;
188 }
189 }
190 } catch (IOException e) { ; }
191 break; // end: case SerialPortEvent.DATA_AVAILABLE:
192 }
193 }
194
195 private void lineReceived(String buffer) {
196 String response;
197 StringTokenizer st = new StringTokenizer(buffer, "\r\n");
198
199 synchronized(portStatusLock) {
200 while (st.hasMoreTokens()) {
201 response = st.nextToken();
202
203 if (response.equals("")) {
204 portStatus = OK;
205 } else if (response.startsWith("OK")) {
206 portStatus = OK;
207 } else if (response.startsWith(">")) {
208 portStatus = WMSG;
209 } else if (response.startsWith("ERROR")) {
210 portStatus = ERROR;
211 } else if (response.startsWith("+CME ERROR") ||
212 response.startsWith("+CMS ERROR")) {
213 portStatus = ERROR;
214 portStatusMsg = response;
215 } else if (response.startsWith("04")) { // NB: BUG!
216 SmsMsgIncoming msg = null;
217 try {
218 msg = new SmsMsgIncoming(response);
219 } catch (PduParseException e) {
220 smsListener.receiveSms(SC_PDU_PARSE_ERROR,
221 "unable to parse PDU: '"+
222 response+"'", null);
223 }
224 portStatus = OK;
225 smsListener.receiveSms(SC_OK, null, msg);
226 } else {
227 // unknown response from terminal
228 //System.err.println("unknown response: '"+response+"'");
229 }
230 } // end: while (st.hasMoreTokens()) {
231 portStatusLock.notify();
232 } // end: synchronized(portStatusLock) {
233
234 return;
235 }
236
237 public synchronized boolean sendMessage(String number, String msg) {
238 if(number.startsWith("+"))
239 number = number.substring(1);
240
241 SmsMsgOutgoing pdumsg = new SmsMsgOutgoing(number, msg);
242
243 String pdu = pdumsg.toString().toUpperCase();
244 String cmd = "AT+CMGS=" + (pdu.length()/2);
245
246 atCmd("");
247 atCmd(cmd);
248
249 atCmd(pdu);
250 atCmd("\032");
251
252 return true;
253 }
254
255 }