Add basic card/reader type identification

This commit is contained in:
Laura Hausmann 2023-05-18 23:42:54 +02:00
parent 15215f02d0
commit dfcfd3b5cb
Signed by: zotan
GPG key ID: D044E84C5BE01605
8 changed files with 61 additions and 16 deletions

View file

@ -8,6 +8,7 @@ class Reader {
public:
virtual bool canHaveUnstableIdentifier() = 0;
virtual bool isNewCardPresent() = 0;
virtual String getReaderName() = 0;
virtual String getCardUid() = 0;
virtual void begin() = 0;
virtual void reset() = 0;
@ -22,6 +23,7 @@ public:
explicit MFRC522Reader(byte chipSelectPin, byte resetPowerDownPin);
bool canHaveUnstableIdentifier() override;
bool isNewCardPresent() override;
String getReaderName() override;
String getCardUid() override;
void begin() override;
void reset() override;
@ -37,6 +39,7 @@ public:
explicit RDM6300Reader(int pin);
bool canHaveUnstableIdentifier() override;
bool isNewCardPresent() override;
String getReaderName() override;
String getCardUid() override;
void begin() override;
void reset() override;
@ -57,6 +60,7 @@ public:
explicit PN532Reader(uint8_t ss);
bool canHaveUnstableIdentifier() override;
bool isNewCardPresent() override;
String getReaderName() override;
String getCardUid() override;
void cycleMode();
void beginKeepMode();

View file

@ -4,9 +4,9 @@
unsigned long cooldownSecondsRemaining(unsigned long timeout, unsigned long timer);
String byteArrayAsHexString(byte* buffer, byte bufferSize);
String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId);
String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId);
String cardTransaction(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& amount);
String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& reader);
String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& reader);
String cardTransaction(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& amount, const String& reader);
String splitString(const String& data, char separator, int index);
String cardIdDisplay(const String& id);

View file

@ -315,7 +315,7 @@ void loop() {
tone(PIN_BUZZER, NOTE_NONE, 150);
}
lastStatusText = cardTransaction(wifi, http, apiUrl, scannedCardId, String(GetTransactionAmount()));
lastStatusText = cardTransaction(wifi, http, apiUrl, scannedCardId, String(GetTransactionAmount()), scannedCardReader->getReaderName());
if (lastStatusText.startsWith("S:")) {
tone(PIN_BUZZER, NOTE_C7, 650);
state = STATE_RESULT_SUCCESS;
@ -385,7 +385,7 @@ void loop() {
tone(PIN_BUZZER, NOTE_NONE, 150);
}
lastStatusText = cardLink(wifi, http, apiUrl, scannedCardId);
lastStatusText = cardLink(wifi, http, apiUrl, scannedCardId, scannedCardReader->getReaderName());
if (lastStatusText.startsWith("S:")) {
tone(PIN_BUZZER, NOTE_C6, 100);
tone(PIN_BUZZER, NOTE_NONE, 10);
@ -404,6 +404,7 @@ void loop() {
for (Reader* reader : readers) {
if (reader->isNewCardPresent()) {
scannedCardId = reader->getCardUid();
scannedCardReader = reader;
lastStatusText = cardIdDisplay(scannedCardId);
state = STATE_BALANCE_VERIFY;
return;
@ -427,7 +428,7 @@ void loop() {
tone(PIN_BUZZER, NOTE_NONE, 150);
}
lastStatusText = cardBalance(wifi, http, apiUrl, scannedCardId);
lastStatusText = cardBalance(wifi, http, apiUrl, scannedCardId, scannedCardReader->getReaderName());
if (lastStatusText.startsWith("S:")) {
lastStatusText = lastStatusText + ":" + cardIdDisplay(scannedCardId);
state = STATE_RESULT_DISPLAY;

View file

@ -34,3 +34,7 @@ void MFRC522Reader::init() {
mfrc522->PCD_Init();
mfrc522->PCD_DumpVersionToSerial();
}
String MFRC522Reader::getReaderName() {
return "mfrc522";
}

View file

@ -98,3 +98,7 @@ void PN532Reader::init() {
Serial.print("Firmware ver. "); Serial.print((version>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((version>>8) & 0xFF, DEC);
}
String PN532Reader::getReaderName() {
return mode == PN532_MIFARE_ISO14443A ? "pn532-iso14443a" : "pn532-felica";
}

View file

@ -40,3 +40,7 @@ void RDM6300Reader::init() {
rdm6300->begin(pin);
rdm6300->set_tag_timeout(65);
}
String RDM6300Reader::getReaderName() {
return "rdm6300";
}

View file

@ -45,8 +45,8 @@ String splitString(const String& data, char separator, int index) {
}
String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId) {
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/link";
String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& reader) {
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/link" + "?reader=" + reader;
http->begin(*wifi, finalRequestUrl.c_str());
http->addHeader("Content-Type", "application/json");
int httpResponseCode = http->PUT("");
@ -84,8 +84,8 @@ String cardLink(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const
return String("E:Internal Error ") + httpResponseCode;
}
String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId) {
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/balance";
String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& reader) {
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/balance" + "?reader=" + reader;
http->begin(*wifi, finalRequestUrl.c_str());
http->addHeader("Content-Type", "application/json");
int httpResponseCode = http->GET();
@ -121,9 +121,9 @@ String cardBalance(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, con
return String("E:Internal Error ") + httpResponseCode;
}
String cardTransaction(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& amount) {
String cardTransaction(WiFiClient* wifi, HTTPClient* http, const String& apiUrl, const String& cardId, const String& amount, const String& reader) {
String idempotencyKey = getIdempotencyKey();
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/transaction/" + idempotencyKey + "?amount=" + amount;
String finalRequestUrl = apiUrl + "/api/card/" + cardId + "/transaction/" + idempotencyKey + "?amount=" + amount + "&reader=" + reader;
http->begin(*wifi, finalRequestUrl.c_str());
http->addHeader("Content-Type", "application/json");
int httpResponseCode = http->PUT("");

View file

@ -13,6 +13,7 @@ public class CardController : Controller {
/// Links the given card to the user currently in link mode.
/// </summary>
/// <param name="card">The ID of the card</param>
/// <param name="reader">Type of reader that scanned the card</param>
/// <response code="200">Returns 200 if the link succeeded</response>
/// <response code="304">Returns 304 if the card was already linked</response>
/// <response code="404">Returns 404 if no link process is active</response>
@ -24,7 +25,7 @@ public class CardController : Controller {
[SwaggerResponseExample(StatusCodes.Status200OK, typeof(UserUpdatedExample))]
[SwaggerResponseExample(StatusCodes.Status404NotFound, typeof(ErrorNoActiveLinkProcessExample))]
[Route("/api/card/{card}/link")]
public async Task<IActionResult> Link(string card) {
public async Task<IActionResult> Link(string card, [FromQuery] string? reader) {
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
return StatusCode(StatusCodes.Status304NotModified);
@ -48,7 +49,7 @@ public class CardController : Controller {
var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value));
linkFlag.Value = "";
var type = card.Length == 10 && long.TryParse(card, out _) ? Card.CardType.Rfid125KhzGeneric : Card.CardType.Unknown;
var type = GetCardType(reader, card);
db.Add(new Card { Id = card, User = user, Type = type });
await db.SaveChangesAsync();
@ -61,6 +62,7 @@ public class CardController : Controller {
/// <param name="card">The ID of the card</param>
/// <param name="ik">Random string (idempotency key) which is consistent across request retries</param>
/// <param name="amount">Positive or negative number of cents representing the relative change in balance</param>
/// <param name="reader">Type of reader that scanned the card</param>
/// <response code="200">Returns 200 if the transaction succeeded</response>
/// <response code="404">Returns 404 if the card isn't linked to any account</response>
/// <response code="412">Returns 412 if the transaction failed because the balance would be out of range after the transaction</response>
@ -73,7 +75,7 @@ public class CardController : Controller {
[SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))]
[SwaggerResponseExample(412, typeof(ErrorBalanceOutOfRangeExample))]
[Route("/api/card/{card}/transaction/{ik}")]
public async Task<IActionResult> Transaction(string card, string ik, [FromQuery] int amount) {
public async Task<IActionResult> Transaction(string card, string ik, [FromQuery] int amount, [FromQuery] string? reader) {
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
@ -88,6 +90,12 @@ public class CardController : Controller {
user.Balance += amount;
}
var dbCard = db.Cards.First(p => p.Id == card);
var newType = GetCardType(reader, card);
if (dbCard.Type != newType)
dbCard.Type = newType;
await db.SaveChangesAsync();
return Ok(new UserResponse(user));
}
@ -99,6 +107,7 @@ public class CardController : Controller {
/// Returns the balance of the the user the card is linked to.
/// </summary>
/// <param name="card">The ID of the card</param>
/// <param name="reader">Type of reader that scanned the card</param>
/// <response code="200">Returns 200 if the request succeeded</response>
/// <response code="404">Returns 404 if the card isn't linked to any account</response>
[HttpGet]
@ -108,10 +117,18 @@ public class CardController : Controller {
[SwaggerResponseExample(200, typeof(UserUpdatedExample))]
[SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))]
[Route("/api/card/{card}/balance")]
public IActionResult Balance(string card) {
public IActionResult Balance(string card, [FromQuery] string? reader) {
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
var dbCard = db.Cards.First(p => p.Id == card);
var newType = GetCardType(reader, card);
if (dbCard.Type != newType) {
dbCard.Type = newType;
db.SaveChangesAsync();
}
return Ok(new UserResponse(user));
}
@ -134,4 +151,15 @@ public class CardController : Controller {
private class ErrorNoActiveLinkProcessExample : IExamplesProvider<ErrorResponse> {
public ErrorResponse GetExamples() => new("No active link process");
}
private Card.CardType GetCardType(string? reader, string cardNumber) {
//TODO match more specific type based on card number format
return reader switch {
"rdm6300" => Card.CardType.Rfid125KhzGeneric,
"pn532-iso14443a" => Card.CardType.NfcGeneric,
"pn532-felica" => Card.CardType.FeliCaGeneric,
_ => Card.CardType.Unknown
};
}
}