I'm trying make a serial IEC communication for Android and there I need check if a device send some data. I started a timer on the begin of communication a reset it when some data comes. Reset on incoming data is working, but cancelling before data is saved not working. I can't understand why.
Here is my MainActivity.java code
package cz.trisko.jan.amr;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbDeviceConnection;
import com.felhr.usbserial.UsbSerialDevice;
import com.felhr.usbserial.UsbSerialInterface;
import static java.lang.Integer.parseInt;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "AMR";
private UsbManager usbManager;
private UsbDevice device;
private UsbDeviceConnection connection;
private UsbSerialDevice serialDevice;
private String handshake = "/?!\r\n";
MediaPlayer errSnd; // chyba
MediaPlayer bdSnd; // registry
MediaPlayer lpSnd; // profil
MediaPlayer infoSnd; // info
ProgressBar progressBar;
RadioGroup rg;
RadioButton rb;
TextView registryNam; // textview pro nazev registru
TextView registryVal; // textview pro hodnotu registru a stavova hlaseni
String regName; // nazev registru
String regValue; // hodnota registru
boolean readRegs; // typy odectu
boolean readLastMonth;
boolean readThisMonth;
Calendar calendar = Calendar.getInstance();
DateFormat dateFormat = new SimpleDateFormat("yyMMdd");
int currentMonth;
int currentYear;
Date today;
Date tomorrow;
String tomorrowAsString;
String lastMonthInterval;
String thisMonthInterval;
String lpInterval;
int startMonth;
int startYear;
int endMonth;
int endYear;
private static final Pattern communicationSpeedIndexPattern
= Pattern.compile("^.*(/)(\\w{3})(\\d{1})(.*)$"); // [0] - komplet, [1] - /, [2] - vyrobce, [3] - baudrate, [4] - model
private static final Pattern CRpat = Pattern.compile("^(\r)$"); // CR
private static final Pattern LFpat = Pattern.compile("^(\n)$"); // LF
private static final Pattern STXpat = Pattern.compile("^(\\x02)$"); // STX
private static final Pattern ETXpat = Pattern.compile("^(\\x03)$"); // ETX
private static final Pattern DLEpat = Pattern.compile("^(\\x10)$"); // DLE
private static final Pattern ETBpat = Pattern.compile("^(\\x17)$"); // ETB
private static final Pattern regNameValuePattern
= Pattern.compile("([^\\(\\)]*)\\(([^\\(\\)]*)\\)"); // [0] - komplet, [1] - Registr, [2] - hodnota
boolean CRLF; // konec radku prijimanych dat
boolean isCR;
boolean isLF;
boolean firstLine;
boolean isETX;
String data; // prichozi data na USB
String dataLine; // radek s prichozimi daty
String tmpDataLine; // radek prichozich dat, ktery je zpracovavan po znacich
//String tmpDataLine2; // radek prichozich dat, ktery je zpracovavan po znacich
String buffer; // buffer pro vystup do souboru
//String tmpBuffer = "";
int dataLength;
private int[] baudrateList = {300, 600, 1200, 2400, 4800, 9600, 19200};
String meterId;
Pattern meterIdPattern = Pattern.compile("0\\.0\\.0|0\\.0|0|C\\.1|C\\.1\\.0|0\\.0\\.1|0\\.0\\.2|0\\.0\\.3|C\\.1\\.1|C\\.90\\.1");
int speed; // komunikacni rychlost seriove link - pocatek 300 Bd
int readedSpeed; // maximalni komunikacni rychlost v zarizeni
String askData; // retezec pro odeslani pozadavku dat
char valueSOHchar = 0x01; // SOH
String SOH = String.valueOf(valueSOHchar);
char valueSTXchar = 0x02; // STX
String STX = String.valueOf(valueSTXchar);
char valueETXchar = 0x03; // ETX
String ETX = String.valueOf(valueETXchar);
char valueACKchar = 0x06; // ACK
String ACK = String.valueOf(valueACKchar);
char valueDLEchar = 0x10; // DLE
String DLE = String.valueOf(valueDLEchar);
char valueNAKchar = 0x15; // NAK
String NAK = String.valueOf(valueNAKchar);
char valueETBchar = 0x17; // ETB
String ETB = String.valueOf(valueETBchar);
int etxIndex; // pocet prichozich ETX
boolean registryReceived;
String filename;
String filepath;
File myExternalFile;
boolean successCreateDir;
boolean outputDirOk;
boolean meterEchoes;
//String debugStringValue = "";
long startTime;
long currentTime;
long timeLeft;
MyTimer responseTimeCounter;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "-------------- APP START --------------");
setDates(); // nastavime intervaly odectu
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
errSnd = MediaPlayer.create(this, R.raw.wrong);
bdSnd = MediaPlayer.create(this, R.raw.case_closed);
lpSnd = MediaPlayer.create(this, R.raw.beep_beep_beep);
infoSnd = MediaPlayer.create(this, R.raw.smack_that_bitch);
rg = findViewById(R.id.readoutType);
registryNam = findViewById(R.id.registryNames);
registryVal = findViewById(R.id.registryValues);
usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
// casovac pro prodlevu na vstupu
responseTimeCounter = new MyTimer(5000,1);
}
public void setDefaultValues() {
// nastaveni vychozich polozek pro odecet
regName = "";
regValue = "";
readRegs = false;
readLastMonth = false;
readThisMonth = false;
CRLF = false; // konec radku prijimanych dat
isCR = false;
isLF = false;
firstLine = true; // nacitani prvniho radku dat
isETX = false;
data = null; // prichozi data na USB
dataLine = ""; // radek s prichozimi daty
tmpDataLine = ""; // radek prichozich dat, ktery je zpracovavan po znacich
buffer = ""; // buffer pro vystup do souboru
dataLength = 0;
meterId = "";
speed = 300; // komunikacni rychlost - pocatek 300 Bd
readedSpeed = 0; // index komunikacni rychlosti nacteny ze zarizeni
askData = ""; // retezec pro odeslani pozadavku dat
tvSetContent(registryNam, "");
tvSetContent(registryVal, "");
meterEchoes = false;
registryReceived = false;
etxIndex = 0;
}
public String toHex(String arg) {
return String.format("%012x", new BigInteger(1, arg.getBytes()));
}
public void setDates() {
// vypocet dat pro interval odectu
String leadingStartMonthZero = "";
String leadingEndMonthZero = "";
String startDate;
String endDate;
currentMonth = calendar.get(Calendar.MONTH) + 1;
currentYear = calendar.get(Calendar.YEAR) % 100;
// dnes / today
today = calendar.getTime();
// zitra / tomorrow
calendar.add(Calendar.DAY_OF_YEAR, 1);
tomorrow = calendar.getTime();
dateFormat = new SimpleDateFormat("yyMMdd");
tomorrowAsString = dateFormat.format(tomorrow);
// interval pro odecet pri vymene - od 1. tohoto mesice do zitra / this month
if (currentMonth < 10) {
leadingStartMonthZero = "0";
}
startDate = "0" + currentYear + leadingStartMonthZero + currentMonth + "010000";
endDate = "0" + tomorrowAsString + "0000";
thisMonthInterval = startDate + ";" + endDate;
// interval pro std odecet - od 1. minuleho mesice do 1. tohoto mesice / last month
startYear = currentYear;
endYear = currentYear;
startMonth = currentMonth - 1;
endMonth = currentMonth;
if (currentMonth == 1) {
startYear = currentYear - 1;
startMonth = 12;
endYear = currentYear;
endMonth = currentMonth;
}
if (endMonth < 10) {
leadingEndMonthZero = "0";
}
if (startMonth < 10) {
leadingStartMonthZero = "0";
}
startDate = "0" + startYear + leadingStartMonthZero + startMonth + "010000";
endDate = "0" + endYear + leadingEndMonthZero + endMonth + "010000";
lastMonthInterval = startDate + ";" + endDate;
}
private boolean checkUSBdevice() {
boolean pass = false;
Map<String, UsbDevice> deviceList = usbManager.getDeviceList();
if (deviceList.isEmpty()) {
pass = false;
} else {
for (Map.Entry<String, UsbDevice> entry : deviceList.entrySet()) {
device = entry.getValue();
if (usbManager.hasPermission(device)) {
Log.d(TAG, "USB has permission");
pass = true;
} else {
Toast.makeText(getApplicationContext(), "Není oprávnění pro přístup k USB zařízení", Toast.LENGTH_LONG).show();
errSnd.start(); // chck OK
Log.d(TAG, "USB have not permission");
pass = false;
}
}
}
if (pass) {
return true;
} else {
return false;
}
}
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
} else {
return false;
}
}
public void getReadoutType() {
String selectedtext = (String) rb.getText();
int radioButtonID = rg.getCheckedRadioButtonId();
View radioButton = rg.findViewById(radioButtonID);
int rbIndex = rg.indexOfChild(radioButton);
switch (rbIndex) {
case 0: // odecet jen registru
readRegs = true;
readLastMonth = false;
readThisMonth = false;
break;
case 1: // odecet registru a LP za minuly mesic
readRegs = true;
readLastMonth = true;
readThisMonth = false;
break;
case 2: // odecet registru a LP za tent mesic
readRegs = true;
readLastMonth = false;
readThisMonth = true;
break;
}
Log.d(TAG, "Index radiobuttonu: " + rbIndex + " - " + selectedtext);
Log.d(TAG, "readRegs = " + readRegs);
Log.d(TAG, "readLastMonth = " + readLastMonth);
Log.d(TAG, "readThisMonth = " + readThisMonth);
}
private void tvSetContent(TextView tv, CharSequence text) {
final TextView ftv = tv;
final CharSequence ftext = text;
runOnUiThread(new Runnable() {
@Override public void run() {
ftv.setText(ftext);
}
});
}
public void getSpeed() throws InterruptedException {
// nacteni rychlostniho indexu z identifikacniho retezce / get device max speed
Matcher speedIndex = communicationSpeedIndexPattern.matcher(tmpDataLine);
Log.d(TAG, "getSpeed processing");
if (speedIndex.find()) {
readedSpeed = parseInt(speedIndex.group(3));
Log.d(TAG, "Speed index " + readedSpeed);
speed = baudrateList[readedSpeed];
Log.d(TAG, "New speed will be " + speed + " Bd");
tvSetContent(registryVal, "Komunikace na " + speed + " Bd");
if (readThisMonth || readLastMonth) {
askData = ACK + "0" + readedSpeed + "1\r\n";
Log.d(TAG, "Profile ACK");
} else {
askData = ACK + "0" + readedSpeed + "0\r\n";
Log.d(TAG, "Registry ACK");
}
Log.d(TAG, "Waiting 300 ms");
Thread.sleep(300);
Log.d(TAG, "Send to serial " + askData + "|" + toHex(askData));
serialDevice.write(askData.getBytes());
Log.d(TAG, "Waiting 300 ms");
Thread.sleep(300);
Log.d(TAG, "Serial line switching to " + speed + " Bd");
serialDevice.setBaudRate(speed);
Log.d(TAG, "Speed switched");
} else {
Log.d(TAG, "No speed index found");
}
}
public void showValues() {
// rozlozeni na nazev a hodnotu registru / show registry name and value
Matcher regNamVal = regNameValuePattern.matcher(tmpDataLine);
if (regNamVal.find()) {
regName = regNamVal.group(1).toString();
regValue = regNamVal.group(2).toString();
tvSetContent(registryNam, regName);
tvSetContent(registryVal, regValue);
if (meterId == "") {
// hledame cislo elektromeru / looking for meter ID
Matcher metId = meterIdPattern.matcher(regName);
if (metId.matches()) {
meterId = regValue;
Log.d(TAG, "meterId = " + meterId);
}
}
} else {
tvSetContent(registryVal, tmpDataLine);
Log.d(TAG, "Pickup registry and value fail");
tvSetContent(registryNam, "");
tvSetContent(registryVal, "");
}
}
public void chkDirectories() {
// kontrola/vytvoreni adresaru pro vystup a konfiguraci / chck output dir
outputDirOk = false;
File dirDocuments = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
String dirDocumentsPath = dirDocuments.getPath();
File dirAmr = new File(dirDocumentsPath+"/AMR");
filepath = dirAmr.getAbsolutePath();
if (!dirAmr.exists()) {
// adresar pro vystup neexistuje, vytvorime ho
successCreateDir = dirAmr.mkdir();
if (successCreateDir) {
Log.d(TAG,"Directory " + filepath + " created successfully");
} else {
Log.d(TAG,"Creating od directory " + filepath + " FAILED!");
}
} else {
Log.d(TAG,"AMR directory OK");
File dirReadouts = new File(filepath + "/Readouts");
filepath = dirReadouts.getAbsolutePath();
if (!dirReadouts.exists()) {
successCreateDir = dirReadouts.mkdir();
if (successCreateDir) {
outputDirOk = true;
Log.d(TAG,"Directory " + filepath + " created successfully");
} else {
Log.d(TAG,"Creating od directory " + filepath + " FAILED!");
}
} else {
outputDirOk = true;
Log.d(TAG,"Readouts directory OK");
}
}
Log.d(TAG,"Readouts output directory: " + filepath);
}
public void saveFile() {
// ukladame na SD kartu / saving to SD
try {
tvSetContent(registryVal, "Ukládám data" + filename);
Log.d(TAG, "Timer pred ukladanim: " + timeLeft);
responseTimeCounter.cancel(); // zastavime casovac odezvy elektromeru / cancel timer
/* THIS CANCEL DID NOT WORKING - WHY???????????????????????? */
Log.d(TAG, "Timer po jeho preruseni: " + timeLeft);
filename = meterId.trim() + ".rd";
myExternalFile = new File(filepath, filename);
Log.d(TAG,"Output stream start " + myExternalFile.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(myExternalFile);
Log.d(TAG, "Start writting data");
fos.write(buffer.getBytes());
Log.d(TAG, "Closing file - saved to " + myExternalFile.getAbsolutePath());
fos.close();
tvSetContent(registryVal, "Uloženo do " + filename);
Log.d(TAG, "Closing serial communication");
connection.close();
bdSnd.start();
} catch (IOException e) {
e.printStackTrace();
}
// ukladame do interniho uloziste / saving in to internal storage
/*
try {
filename = meterId.trim() + ".rd";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new
File(getFilesDir()+File.separator+filename)));
bufferedWriter.write(buffer);
bufferedWriter.close();
tvSetContent(registryVal, "Uloženo do " + filename);
//setDefaultValues();
bdSnd.start();
} catch (IOException e) {
e.printStackTrace();
}
*/
}
public void processData() throws InterruptedException {
// prochazeni prichozich dat po znacich
for (dataLength = 0; dataLength < data.length(); dataLength++) {
//Process char
char c = data.charAt(dataLength);
String charToString = c + "";
Log.d(TAG, "R <- " + charToString + " hex:" + toHex(charToString));
Matcher incomingOneChar = STXpat.matcher(charToString);
if (incomingOneChar.matches()) {
Log.d(TAG, "\n\nSTX found - dropping it\n\n");
charToString = ""; // drop STX char
}
Matcher nextIncomingOneChar = ETXpat.matcher(charToString);
if (nextIncomingOneChar.matches()) {
Log.d(TAG, "\n\nETX found - dropping it and next data\n\n");
charToString = ""; // drop ETX char a vsechno za tim
isETX = true;
etxIndex += 1;
dataLength = data.length(); // ukonci cyklus driv
registryReceived = true;
responseTimeCounter.cancel();
saveFile();
}
// je prichozi znak CR?
Matcher incomingCharCR = CRpat.matcher(charToString);
if (incomingCharCR.matches()) {
//tvAppend(textView, "CR");
isCR = true;
//Log.d(TAG, "CR");
} else {
// je prichozi znak LF?
Matcher incomingCharLF = LFpat.matcher(charToString);
if (incomingCharLF.matches()) {
//tvAppend(textView, "LF");
isLF = true;
//Log.d(TAG, "LF");
}
}
tmpDataLine += charToString; // pridame nacteny znak do radku
// reset casovace na vstupu
responseTimeCounter.cancel();
responseTimeCounter.start();
/* THIS TIMER RESET WORKING */
if (isCR && isLF) {
// je konec radku?
CRLF = true;
}
if (CRLF) {
// mame cely radek, zpracujeme jej / processing completed data row
//Log.d(TAG, "CRLF");
if (firstLine) {
// v prvnim radku je identifikace s indexem rychlosti
Log.d(TAG, "First line completed - " + tmpDataLine);
if (tmpDataLine.equals(handshake)) {
// test na echo
meterEchoes = true;
tmpDataLine = ""; // drop radku s echem
Log.d(TAG, "Meter send echo");
} else {
getSpeed();
firstLine = false;
Log.d(TAG, "First line operated");
}
}
if (meterEchoes && tmpDataLine.equals(askData)) {
// pokud elektromer vraci echo, drop data
tmpDataLine = "";
}
buffer += tmpDataLine;
Log.d(TAG, "tmpDataLine: " + tmpDataLine);
if (!tmpDataLine.equals("")) {
// pokud nejaka data jsou, zobraz je
showValues();
}
dataLine = ""; // vymazeme obsah radku
CRLF = false;
isCR = false;
isLF = false;
tmpDataLine = "";
}
}
}
UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
//Defining a Callback which triggers whenever data is read.
//@TargetApi(Build.VERSION_CODES.KITKAT)
//@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onReceivedData(byte[] arg0) {
//String data = null;
try {
data = new String(arg0);
//Log.d(TAG, "R <- " + data);
Log.d(TAG, "Timer pri vstupu dat na USB: " + timeLeft);
processData();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
private void startSerialConnection(UsbDevice device) {
Log.i(TAG, "Ready to open USB device connection");
connection = usbManager.openDevice(device);
serialDevice = UsbSerialDevice.createUsbSerialDevice(device, connection);
if (serialDevice != null) {
if (serialDevice.open()) {
serialDevice.setBaudRate(speed);
serialDevice.setDataBits(UsbSerialInterface.DATA_BITS_7);
serialDevice.setStopBits(UsbSerialInterface.STOP_BITS_1);
serialDevice.setParity(UsbSerialInterface.PARITY_EVEN);
serialDevice.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
Log.d(TAG, "mCallback Called");
serialDevice.read(mCallback);
Log.d(TAG, "Serial connection opened at " + speed + " Bd");
} else {
Log.d(TAG, "Cannot open serial connection");
tvSetContent(registryVal, "Nefunguje COM port");
Toast.makeText(getApplicationContext(), "Nefunguje COM port", Toast.LENGTH_LONG).show();
infoSnd.start();
}
} else {
Log.d(TAG, "Could not create USB Serial Device");
}
}
public class MyTimer extends CountDownTimer {
public MyTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
tvSetContent(registryNam, String.valueOf(millisUntilFinished));
}
@Override
public void onFinish() {
errSnd.start();
tvSetContent(registryVal, "Elektroměr neodpovídá"); // device did not respond
connection.close();
}
}
public void doReadout(View view) {
setDefaultValues();
chkDirectories();
int radioButtonId = rg.getCheckedRadioButtonId();
rb = findViewById(radioButtonId);
Log.d(TAG, "Do Readout pressed");
if (!isExternalStorageWritable()) {
Toast.makeText(getApplicationContext(), "Externí úložiště není k dispozici.", Toast.LENGTH_LONG).show();
errSnd.start();
}
// pro zapis na SD kartu
if (!outputDirOk) {
Toast.makeText(getApplicationContext(), "Adresář pro výstup není k dispozici.", Toast.LENGTH_LONG).show();
errSnd.start();
}
if (checkUSBdevice() && isExternalStorageWritable() && outputDirOk) {
startSerialConnection(device);
if (device != null) {
//tvSetContent(registryVal, "Sériová komunikace spuštěna");
Log.d(TAG, "Serial communication opened");
} else {
Log.d(TAG, "Serial communication opening FAIL");
}
if (rb == null) {
Toast.makeText(getApplicationContext(), "Není vybrán typ odečtu!", Toast.LENGTH_LONG).show();
Log.d(TAG, "No readout type selected");
errSnd.start();
} else {
getReadoutType();
tvSetContent(registryVal, "Posílam handshake");
serialDevice.write(handshake.getBytes());
Log.d(TAG, "Handshake sent " + toHex(handshake));
responseTimeCounter.start();
Log.d(TAG, "Response timer started");
}
} else {
// nepripojene USB nedelame nic
errSnd.start();
Toast.makeText(getApplicationContext(), "Není připojena USB optická sonda." ,Toast.LENGTH_LONG).show();
Log.d(TAG, "No USB device connected");
}
}
}
I found the reason, why a timer did not want to be cancelled. There is .cancel() and .start() called in wrong place. They are called in cycle where is input processed char by char and there is too many cancels and starts in short time and that probably confused the timer. Moving timer restart out of this cycle solved my problem.