Skip to content
This repository has been archived by the owner on Mar 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request #16 from inventid/catch-throwable
Browse files Browse the repository at this point in the history
Better exception catching
  • Loading branch information
rogierslag committed Oct 14, 2015
2 parents 8b70c4e + fb0c0c9 commit 4b3ca31
Showing 1 changed file with 123 additions and 83 deletions.
206 changes: 123 additions & 83 deletions src/main/java/nl/inventid/rfidReader2keyboard/Read.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@
import java.util.concurrent.TimeUnit;

import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.binary.Hex;

/**
* This little program handles the reading of the RFID miFare chips.
* An exit code of 0 means everything went well. An exit code of 1 means no suitable terminal was found. An exit code of
* 2 means no type robot could be started.
* This little program handles the reading of the RFID miFare chips. An exit code of 0 means everything went well. An
* exit code of 1 means no suitable terminal was found. An exit code of 2 means no type robot could be started.
*/
public class Read {
public class Read implements Runnable {

private static final String NO_CONNECT = "connect() failed";
private static final String EMPTY_CODE = "Scanned code was empty";
private static final String NO_CARD = "sun.security.smartcardio.PCSCException: SCARD_E_NO_SMARTCARD";
private static final String REMOVED_CARD = "sun.security.smartcardio.PCSCException: SCARD_W_REMOVED_CARD";
private static final String READER_UNAVAILABLE =
"sun.security.smartcardio.PCSCException: SCARD_E_READER_UNAVAILABLE";
private static final String FAILED_CARD_TRANSACTION =
Expand All @@ -48,11 +49,17 @@ public class Read {
private static ScheduledThreadPoolExecutor executorService;

private final Reconnector reconnector;
private final Keyboard keyboard;
private final Object[] synchronizer = new Object[0];

private String oldUid;
private CardTerminal terminal;
private int i;

@Getter
@Setter
private Instant lastScan;
private Instant lastAction;
private String usedCardTerminalName;

public static void main(String[] args) {
System.out.println("Starting rfid-reader2keyboard");
Expand All @@ -72,7 +79,7 @@ public static void main(String[] args) {
executorService.scheduleAtFixedRate(detectorLoop, 10, 15, TimeUnit.SECONDS);

Read reader = new Read(executorService);
reader.loop();
reader.startRunning();

Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Expand All @@ -92,7 +99,7 @@ public static List<CardTerminal> listTerminals() {
try {
return TerminalFactory.getDefault().terminals().list();
}
catch (Exception e) {
catch (Throwable e) {
return Lists.newArrayList();
}
}
Expand All @@ -103,44 +110,81 @@ public Read(ScheduledThreadPoolExecutor executorService) {
TERMINAL_PREFERENCES.add(""); // Fuck, attach with anything (SHOULD BE LAST)

reconnector = new Reconnector(this, 2); // Reconnect every two seconds
executorService.scheduleAtFixedRate(reconnector, 1, 1, TimeUnit.SECONDS);
keyboard = new Keyboard();
lastAction = Instant.now();
determineCardTerminalToUse();
}

/**
* Find and connect to a terminal, based on the preferences in TERMINAL_PREFERENCES
*
* @return a valid CardTerminal or halts the system if no match can be found
*/
public void findAndConnectToTerminal() {
private void determineCardTerminalToUse() {
try {
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
synchronized (synchronizer) {
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();

System.out.println("There are " + TERMINAL_PREFERENCES.size() + " possible terminal matches");
System.out.println("There are " + terminals.size() + " terminals attached to this machine");

for (int j = 0; j < TERMINAL_PREFERENCES.size(); j++) {
String requiredTerminal = TERMINAL_PREFERENCES.get(j);
System.out.println("Trying to attach to '" + requiredTerminal + "'");
for (int i = 0; i < terminals.size(); i++) {
if (terminals.get(i).getName().contains(requiredTerminal)) {
usedCardTerminalName = terminals.get(i).getName();
return;
}
}
}
}
}
catch (Throwable e) {
// Probably no reader found...
System.err.println("Unable to connect to RFID reader");
e.printStackTrace();
}
return;
}

System.out.println("There are " + TERMINAL_PREFERENCES.size() + " possible terminal matches");
System.out.println("There are " + terminals.size() + " terminals attached to this machine");
/**
* Find and connect to a terminal, based on the preferences in TERMINAL_PREFERENCES
*
* @return a valid CardTerminal or halts the system if no match can be found
*/
public void findAndConnectToTerminal() {
try {
synchronized (synchronizer) {
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();

for (int j = 0; j < TERMINAL_PREFERENCES.size(); j++) {
String requiredTerminal = TERMINAL_PREFERENCES.get(j);
System.out.println("Trying to attach to '" + requiredTerminal + "'");
for (int i = 0; i < terminals.size(); i++) {
if (terminals.get(i).getName().contains(requiredTerminal)) {
CardTerminal newTerminal = terminals.get(i);
System.out.println("Attached to '" + requiredTerminal + "'");
terminal = newTerminal;
if (terminals.get(i).getName().equals(usedCardTerminalName)) {
System.out.println("Attached to '" + usedCardTerminalName + "'");
terminal = terminals.get(i);
return;
}
}
}
}
catch (Exception e) {
catch (Throwable e) {
// Probably no reader found...
System.err.println("Unable to connect to RFID reader");
e.printStackTrace();
}
return;
}

public void startRunning() {
findAndConnectToTerminal();
executorService.scheduleWithFixedDelay(this, 1000, 100, TimeUnit.MILLISECONDS);
executorService.scheduleWithFixedDelay(reconnector, 1, 1, TimeUnit.SECONDS);
}

/**
* Do the actual work of the program by looping over it and writing/exceptioning In case you are looking at this
* code and thinking "OMG why not just use the CardTerminal methods instead of catching?": There is a very good
Expand All @@ -150,74 +194,72 @@ public void findAndConnectToTerminal() {
* In the exception handling, possibly we will reconnect to a terminal, if that is the best thing to do for
* stability
*/
public void loop() {

findAndConnectToTerminal();

public void run() {
if (terminal == null) {
System.err.println("No terminal connected!");
return;
}
Keyboard keyboard = new Keyboard();

// Random String; no UID of any chip. Still true though
String oldUID = "inventid bravo!";
int i = 0;
// Keep looping
while (true) {
try {
try {
synchronized (synchronizer) {
// Connect to card and read
Card card = terminal.connect("T=1");
CardChannel channel = card.getBasicChannel();

CardChannel channel = card.getBasicChannel();
// Send data and retrieve output
String uid = getCardUid(channel);

if (!isNewCard(uid, oldUID, lastScan)) {
continue;
if (!isNewCard(uid, oldUid, lastAction)) {
return;
}

System.out.println("This is a new card! " + uid);
lastAction = Instant.now();
// Emulate a keyboard and "type" the uid, followed by a newline
keyboard.type(uid);
keyboard.type("\n");

i++;
oldUID = uid;
lastScan = Instant.now();
reconnector.setLastAction(lastScan);
card.disconnect(false);
oldUid = uid;

System.out.println("ready for next card");
System.out.println("Card scan run: " + i);
}
catch (CardException e) {
// Something went wrong when scanning the card
if (e.getMessage().equals(FAILED_CARD_TRANSACTION) || e.getMessage().equals(READER_UNAVAILABLE)) {
logError(e.getMessage());
findAndConnectToTerminal();
continue;
}
// Card is not present while scanning
if (e.getMessage().equals(NO_CARD) || e instanceof CardNotPresentException) {
logError(e.getMessage());
continue;
}
// Could not reliably connect to the reader (this can mean there is simply no card)
if (e.getMessage().equals(NO_CONNECT) || e.getMessage().equals(CARD_READ_FAILURE)) {
logError(e.getMessage());
continue;
}
if (e.getMessage().equals(EMPTY_CODE)) {
logError(e.getMessage());
System.err.println("Empty code was read");
continue;
}
System.err.println("Help something uncatched happened! This should not happen!");
}
catch (CardException e) {
// Something went wrong when scanning the card
if (e.getMessage().equals(FAILED_CARD_TRANSACTION) || e.getMessage().equals(READER_UNAVAILABLE)) {
logError(e.getMessage());
// findAndConnectToTerminal();
return;
}
// Card is not present while scanning
if (e.getMessage().equals(NO_CARD) || e instanceof CardNotPresentException || e.getMessage()
.equals(REMOVED_CARD)) {
logError(e.getMessage());
e.printStackTrace();
System.out.println(e.getMessage());
findAndConnectToTerminal();
return;
}
// Could not reliably connect to the reader (this can mean there is simply no card)
if (e.getMessage().equals(NO_CONNECT) || e.getMessage().equals(CARD_READ_FAILURE)) {
logError(e.getMessage());
return;
}
if (e.getMessage().equals(EMPTY_CODE)) {
logError(e.getMessage());
System.err.println("Empty code was read");
return;
}
System.err.println("Help something uncatched happened! This should not happen!");
logError(e.getMessage());
e.printStackTrace();
System.out.println(e.getMessage());
findAndConnectToTerminal();
}
catch (Throwable e) {
System.err.println("Throwable was thrown!");
logError(e.getMessage());
e.printStackTrace();
System.out.println(e.getMessage());
findAndConnectToTerminal();
}
}

Expand All @@ -229,15 +271,17 @@ public void loop() {
* @throws CardException in case of an error
*/
private String getCardUid(CardChannel channel) throws CardException {
ResponseAPDU response = channel.transmit(READ_COMMAND);
String uid = new String(Hex.encodeHex(response.getData())).toUpperCase();
if (!new String(Hex.encodeHex(response.getBytes())).endsWith("9000")) {
throw new CardException(CARD_READ_FAILURE);
}
if (uid.isEmpty()) {
throw new CardException(EMPTY_CODE);
synchronized (synchronizer) {
ResponseAPDU response = channel.transmit(READ_COMMAND);
String uid = new String(Hex.encodeHex(response.getData())).toUpperCase();
if (!new String(Hex.encodeHex(response.getBytes())).endsWith("9000")) {
throw new CardException(CARD_READ_FAILURE);
}
if (uid.isEmpty()) {
throw new CardException(EMPTY_CODE);
}
return uid;
}
return uid;
}

/**
Expand All @@ -248,7 +292,7 @@ private String getCardUid(CardChannel channel) throws CardException {
*/
private boolean isNewCard(String newUid, String oldUid, Instant lastScan) {
return !newUid.equals(oldUid) || lastScan == null ||
(lastScan != null && lastScan.plus(1, ChronoUnit.SECONDS).isBefore(Instant.now()));
(lastScan != null && lastScan.plus(850, ChronoUnit.MILLIS).isBefore(Instant.now()));
}

/**
Expand Down Expand Up @@ -286,9 +330,6 @@ public void run() {
private static class Reconnector implements Runnable {

private final Read read;

@Setter
private Instant lastAction;
private int reconnectTime;

public Reconnector(Read read, int reconnectTime) {
Expand All @@ -300,12 +341,11 @@ public Reconnector(Read read, int reconnectTime) {
@Override
public void run() {
Instant now = Instant.now();
if (lastAction == null ||
lastAction.plus(reconnectTime, ChronoUnit.SECONDS).isBefore(now)) {
if (read.getLastAction() == null ||
read.getLastAction().plus(reconnectTime, ChronoUnit.SECONDS).isBefore(now)) {
System.out.println("Reconnect due to lack of scan actions");
read.findAndConnectToTerminal();
read.setLastScan(now);
this.setLastAction(now);
read.setLastAction(now);
}
}
}
Expand Down

0 comments on commit 4b3ca31

Please sign in to comment.