/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2001-2008, Martin Schoeberl (martin@jopdesign.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Created on 15.04.2004
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package tal;
import joprt.RtThread;
import util.Dbg;
import util.Serial;
import util.Timer;
/**
* @author martin
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
/**
* @author martin
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
/**
* @author martin
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class Modem extends RtThread {
private final static int MAX_MSG_LEN = 300;
private final static int MAX_TRY = 5;
private static volatile Modem single;
private Serial ser;
/**
* Generate a singleton for a modem.
* @return the instance of Modem.
*/
public static Modem getInstance(int prio, int us, Serial ser) {
if (single==null) {
single = new Modem(prio, us, ser);
}
return single;
}
private StringBuffer strBuf;
private volatile int state;
private volatile boolean fwpDone;
private int connCnt;
Alarm fail3;
Alarm fail12;
static final int INIT = 0;
static final int TEST = 123;
static final int READY = 1;
static final int RING = 2;
static final int CONNECTED = 3;
static final int SEND_FWP = 4;
class Init {
String cmd;
String answ;
int time;
public Init(String c, String a, int t) {
cmd =c;
answ = a;
time = t;
}
}
private Init[] init;
/*
WARTEN_TIMEOUT(SEC5); / dient f?r UNF�HIGE HARDWARE /
watch |= 0x01;
OUT_B(ADR_TEST_LED,watch);
WARTEN_TIMEOUT(SEC2);
watch &= ~0x01;
OUT_B(ADR_TEST_LED,watch);
WARTEN_TIMEOUT(SEC2); / ab hier beginnt Init f?r normale Hardware /
modem_abort (); / disable DTR for 500 ms
*/
/**
* @param prio
* @param us
*/
private Modem(int prio, int us, Serial serial) {
super(prio, us);
ser = serial;
strBuf = new StringBuffer(40);
connCnt = 0;
fail3 = new Alarm(Alarm.SB1_OFF+1, 0);
fail12 = new Alarm(Alarm.SB1_OFF+2, 0);
state = INIT;
init = new Init[6];
// TODO <clinit> in JOP
// Initialization string for the modem.
// init[0] = new Init("|+++|", "", 3); // does not work for auto baud rate
// init[0] = new Init("ATH\r", "OK", 1);
init[0] = new Init("AT&F\r", "", 1); // default values
init[1] = new Init("ATV1\r", "OK", 1); // long, verbose result code (for ec 24)
init[2] = new Init("ATH\r", "OK", 1); //
init[3] = new Init("AT\\Q3\r", "", 1); // RTS/CTS hardware flow control
init[4] = new Init("ATS0=1\r", "OK", 1); // automatic answer after 1 ring (0 is default)
init[5] = new Init("ATS8=8\r", "OK", 1); // number of seconds for a comma dial modifier
/*
from fw.c in tal220
init[1] = new Init("ATE0\r", "OK", 1); // no echo in command mode
init[1] = new Init("ATQ0\r", "OK", 1); // return 'OK' (is default)
init[1] = new Init("AT&B1\r", "OK", 1); // a special cmd for robotics (fix baudrate)
init[1] = new Init("AT&W\r", "OK", 1); // store user profile (we don't need this)
*/
}
public void run() {
for (;;) {
if (state==INIT) {
if (init(Tal.par.modem)) {
state = READY;
} else {
waitSec(5);
}
} else if (state==READY || state==RING) {
checkModem();
} else if (state==TEST) {
connect(Tal.par.telnr[0], null);
sendWait(Tal.fwp.getXXX(), "", 5);
answerFwp();
state = READY;
} else if (state==CONNECTED) {
answerFwp();
connCnt = 0;
state = READY;
} else if (state==SEND_FWP) {
if (Tal.par.ok) sendFwp();
state = READY;
} else {
// we don't have a state for this
waitForNextPeriod();
}
}
}
public boolean startSend() {
// A minimum race condition if the phone rings,
// but this is not a major problem.
// We do the retry one level higher.
if (state==READY) {
fwpDone = false;
state = SEND_FWP;
return true;
} else {
return false;
}
}
private void sendFwp() {
int telNr = 0;
int oldVal = 0;
int newVal = 0;
while (connCnt<12 && !fwpDone) {
if (connCnt>=3) {
// set to ON_OFF_PEND
fail3.setState(true);
fail3.setState(false);
}
Dbg.wr("sendFwp: "); Dbg.intVal(connCnt); Dbg.lf();
if (connect(Tal.par.telnr[telNr], Tal.par.modem)) {
for (int i=0; i<MAX_TRY; ++i) {
// get values to send from Alarm
oldVal = Alarm.getOldMsg();
newVal = Alarm.getNewMsg();
if (sendWait(Tal.fwp.getFwpString(oldVal), "*\r\n", 5)
&& sendWait(Tal.fwp.getFwpString(newVal), "*\r\n", 5)) {
Alarm.stateTransmitted(oldVal, newVal);
connCnt = 0;
fwpDone = true;
break;
}
}
}
if (!fwpDone) ++connCnt;
++telNr;
if (telNr>=Tal.par.cntTel) telNr = 0;
waitSec(Tal.par.disconn);
disconnect();
}
if (!fwpDone){
// set to ON_OFF_PEND
fail3.setState(true);
fail3.setState(false);
}
}
private void checkModem() {
Dbg.wr('.');
readLine(strBuf, 1);
if (strBuf.length()!=0) {
Dbg.wr('^');
Dbg.wr(strBuf);
Dbg.wr('^');
if (startsWith(strBuf, "CONNECT")) {
Dbg.lf();
Dbg.wr("connect!");
Dbg.lf();
state = CONNECTED;
} else if (startsWith(strBuf, "RING")) {
Dbg.wr("ring!");
state = RING;
}
}
}
/**
* @param strBuf
* @param string
* @return
*/
private boolean startsWith(StringBuffer tmp, String string) {
int max = tmp.length()-1;
for (int i = 0; i < string.length(); i++) {
if (i>max || tmp.charAt(i)!=string.charAt(i)) {
return false;
}
}
return true;
}
private void answerFwp() {
// Wait for other modem after CONNECT!
// over-read all garbage characters
// Because this modem is dumb!
// readWait(10);
// walter will das nicht
StringBuffer in = Tal.fwp.getIn();
StringBuffer out = Tal.fwp.getOut();
for (;;) {
readLine(in, '*', 10);
if (in.length()==0) break;
if (startsWith(in, "*\r\n")) break;
Dbg.wr("handle");
Tal.fwp.handle();
sendWait(out, "", 3);
}
/*
// send data again for short
for (int i=0; i<MAX_TRY; ++i) {
// get values to send from Alarm
int oldVal = Alarm.getOldMsg();
int newVal = Alarm.getNewMsg();
if (sendWait(Tal.fwp.getFwpString(oldVal), "*\r\n", 5)
&& sendWait(Tal.fwp.getFwpString(newVal), "*\r\n", 5)) {
Alarm.stateTransmitted(oldVal, newVal);
connCnt = 0;
fwpDone = true;
break;
}
}
*/
// waitSec(Tal.par.disconn);
// don't wait, with new parameter we got a reset
disconnect();
}
/**
* Read one line until '\n' or timeout expired.
* @param in
* @param i
*/
private void readLine(StringBuffer in, int timeout) {
readLine(in, -1, timeout);
}
/**
* Throw awai all character till startChar and than read
* one line until '\n' ot timeout expired.
* @param in
* @param startChar
* @param timeout
*/
private void readLine(StringBuffer in, int startChar, int timeout) {
boolean start = startChar==-1;
in.setLength(0);
int timer = Timer.getTimeoutSec(timeout);
int val = 0;
while (!Timer.timeout(timer)) {
for (int i = ser.rxCnt(); i>0; --i) {
val = ser.rd();
Dbg.wr(val);
if (!start && val==startChar) start = true;
if (start) in.append((char) val);
if (in.length()==MAX_MSG_LEN) {
Dbg.wr("line too long");
return;
}
// we got a line and are ready to reutrn
if (start & val=='\n') return;
timer = Timer.getTimeoutSec(timeout);
}
waitForNextPeriod();
}
}
/**
*
*/
private void disconnect() {
// this is a simple dissconnect
ser.setDTR(false);
waitSec(1);
ser.setDTR(true);
waitSec(1);
sendWait("|+++|", "OK", 3);
sendWait("ATH\r", "OK", 10);
waitSec(1);
}
/**
* Start a connection.
*/
public boolean connect(StringBuffer telNr, StringBuffer modemStr) {
if (!init(modemStr)) return false;
strBuf.setLength(0);
strBuf.append("ATD");
strBuf.append(telNr);
strBuf.append('\r');
if (!sendWait(strBuf, "CONNECT", 30)) {
return false;
}
readWait(1); // read rest of CONNECT XXX string
// because first two telegrams get lost,
// other modem is not yet connected!
waitSec(5);
return true;
}
private boolean init(StringBuffer modemStr) {
ser.setDTR(false);
waitSec(2);
ser.setDTR(true);
waitSec(1);
for (int i = 0; i < init.length; i++) {
if (!sendWait(init[i].cmd, init[i].answ, init[i].time)) {
return false;
}
// read the answer when we are not interested
if (init[i].answ.length()==0) {
readWait(init[i].time);
}
}
// special handling for PIN
/*
if (sendWait("AT+CPIN?\r", "SIM PIN", 3)) {
readWait(1); // flush input ('OK')
sendWait("AT+CPIN=5644\r", "OK", 60); // pin number for GSM modem
}
*/
// do we have a modem string?
if (modemStr!=null && modemStr.length()!=0) {
strBuf.setLength(0);
strBuf.append(modemStr);
strBuf.append('\r');
sendWait(strBuf, "OK", 1);
readWait(1); // perhaps this is a compound string
}
return true;
}
/**
* wait seconds
*/
private void waitSec(int t) {
int timer = Timer.getTimeoutSec(t);
while (!Timer.timeout(timer)) {
waitForNextPeriod();
}
}
/**
* send a string to serial line buffer.
* '|' has special meaning: wait one second.
*/
private boolean wrString(StringBuffer s) {
int i, j, k, val;
i = ser.txFreeCnt();
j = s.length();
if (j>i) return false;
Dbg.wr('\'');
for (i=0; i<j; ++i) {
val = s.charAt(i);
if (val=='|') {
waitSec(2); // for shure if send buffer is full
} else {
ser.wr(val);
Dbg.wr(val);
}
}
Dbg.wr('\'');
return true;
}
private boolean sendWait(String snd, String rcv, int timeout) {
strBuf.setLength(0);
strBuf.append(snd);
return sendWait(strBuf, rcv, timeout);
}
/**
* send a string and loop until rcv string arrives or it times out
* timeout in seconds for receive string, is also used for send (if handshake lines are not set)
* return false means timeout.
*/
private boolean sendWait(StringBuffer snd, String rcv, int timeout) {
//
// send string
//
int timer = Timer.getTimeoutSec(timeout);
while (!wrString(snd)) {
waitForNextPeriod(); // wait till send buffer is free
if (Timer.timeout(timer)) return false; // timeout on send means problem with handshake lines
}
// return true; // for test without a modem
if (rcv==null || rcv.length()==0) {
return true; // no wait string, we're done
}
int ptr = 0;
int len = rcv.length();
//
// now wait on response string
//
timer = Timer.getTimeoutSec(timeout);
while (!Timer.timeout(timer)) {
waitForNextPeriod();
for (int i = ser.rxCnt(); i>0; --i) {
int val = ser.rd();
Dbg.wr(val);
if (val == rcv.charAt(ptr)) {
++ptr;
if (ptr==len) {
return true; // we're done
}
} else {
ptr = 0; // reset match pointer
}
}
}
Dbg.wr('?');
Dbg.wr('\n');
return false; // timeout expired
}
private void readWait(int timeout) {
int timer = Timer.getTimeoutSec(timeout);
while (!Timer.timeout(timer)) {
waitForNextPeriod();
for (int i = ser.rxCnt(); i>0; --i) {
int val = ser.rd();
Dbg.wr(val);
}
}
}
/**
* @return
*/
public boolean isFwpDone() {
return fwpDone;
}
/**
* @return
*/
public int getState() {
return state;
}
}