smart-monopoly/src/NFCHandler.ts
2024-04-09 04:02:09 +02:00

235 lines
8.2 KiB
TypeScript

import {NFC} from "nfc-pcsc";
import {IPCSend} from "./IPCHandler";
import {
IPCListenChannels,
NFCAccountCard,
NFCCard,
NFCCardType,
NFCPropertyCard,
NFCTaskCard,
PropertyColor, TaskType
} from "./RawConstants";
import * as fs from "fs";
import path from "path";
export default class NFCHandler {
public static initPCSC() {
const nfc = new NFC(); // optionally you can pass logger
nfc.on('reader', reader => {
console.log(`${reader.name} device attached`);
// enable when you want to auto-process ISO 14443-4 tags (standard=TAG_ISO_14443_4)
// when an ISO 14443-4 is detected, SELECT FILE command with the AID is issued
// the response is available as card.data in the card event
// see examples/basic.js line 17 for more info
// reader.aid = 'F222222222';
reader.on('card', card => {
// card is object containing following data
// [always] String type: TAG_ISO_14443_3 (standard nfc tags like MIFARE) or TAG_ISO_14443_4 (Android HCE and others)
// [always] String standard: same as type
// [only TAG_ISO_14443_3] String uid: tag uid
// [only TAG_ISO_14443_4] Buffer data: raw data from select APDU response
console.log(`${reader.name} card detected`, card);
let parsed = this.formClass(card.uid, card.data.toString("utf8"));
if (parsed && parsed.isComplete)
IPCSend(IPCListenChannels.NFC_CARD, {status: true, data: parsed.card});
IPCSend(IPCListenChannels.NFC_RAW, {status: !!parsed, data: parsed});
});
reader.on('card.off', card => {
console.log(`${reader.name} card removed`, card);
});
reader.on('error', err => {
console.log(`${reader.name} an error occurred`, err);
});
reader.on('end', () => {
console.log(`${reader.name} device removed`);
});
});
nfc.on('error', err => {
console.log('an error occurred', err);
});
}
public static initTest() {
fs.watchFile(path.resolve(process.cwd(), "nfc.test"), (curr, prev) => {
let contents = fs.readFileSync(path.resolve(process.cwd(), "nfc.test")).toString("utf-8");
let parsed = this.formClass("xyz", contents);
if (parsed)
IPCSend(IPCListenChannels.NFC_CARD, {status: true, data: parsed.card});
IPCSend(IPCListenChannels.NFC_RAW, {status: parsed.isComplete, data: parsed.card});
console.log("Got NFC", parsed);
})
}
/**
* Returns formed nfc card or null if nothing can be parsed
* @param uid
* @param raw
*/
public static formClass(uid: string, raw: string): formClassReturn | null {
/*
NFC-Card FULL
CARD_TYPE|PROPERTIES
- ACCOUNT
- PROPERTY
- TASK
*/
let preParsed: NFCCard = {
uid: uid,
raw: raw,
cardType: NFCCardType.INVALID,
};
try {
let parsing = raw.split("|");
switch (parsing[0]) {
case "PROPERTY": {
preParsed.cardType = NFCCardType.PROPERTY;
const splitProperty = parsing[1].split(";");
/*
0 Name
1 Color
2 Fullset Amount
3 Buy Value
4 Mortgage Value
5 Rent Object (split with ,)
0 Basic rent
[1 Rent with 1 house
2 Rent with 2 houses
3 Rent with 3 houses
4 Rent with 4 houses
5 Rent with 1 hotel]
6 Special Props
""
"TRAINSTATION"
"UTILITY"
*/
let name = this.valueOr(splitProperty, 0, "");
let color = this.valueOr(splitProperty, 1, PropertyColor.WHITE);
let fullSetAmount = this.valueOr(splitProperty, 2, 0);
let buyValue = this.valueOr(splitProperty, 3, 0);
let mortgageValue = this.valueOr(splitProperty, 4, 0);
let rent = 0;
let rent1 = 0;
let rent2 = 0;
let rent3 = 0;
let rent4 = 0;
let rentHotel = 0;
if (splitProperty[5]) {
let rentSplit = splitProperty[5].split(",");
rent = this.valueOr(rentSplit, 0, 0);
rent1 = this.valueOr(rentSplit, 1, undefined);
rent2 = this.valueOr(rentSplit, 2, undefined);
rent3 = this.valueOr(rentSplit, 3, undefined);
rent4 = this.valueOr(rentSplit, 4, undefined);
rentHotel = this.valueOr(rentSplit, 5, undefined);
}
let specialProperties = this.valueOr(splitProperty, 6, "");
let propertyParsed: NFCPropertyCard = {
...preParsed,
name: name,
color: color,
fullSetAmount: fullSetAmount,
buyValue: buyValue,
mortgageValue: mortgageValue,
rent: rent,
rent1: rent1,
rent2: rent2,
rent3: rent3,
rent4: rent4,
rentHotel: rentHotel,
specialProperties: specialProperties,
};
if (splitProperty.length != 7)
return {card: propertyParsed, isComplete: false};
return {card: propertyParsed, isComplete: true};
}
case "ACCOUNT": {
preParsed.cardType = NFCCardType.ACCOUNT;
const splitAccount = parsing[1].split(";");
let symbol = this.valueOr(splitAccount, 0, "");
let nickname = this.valueOr(splitAccount, 1, "");
let pin = this.valueOr(splitAccount, 2, 0);
const splitParsed: NFCAccountCard = {
...preParsed,
symbol: symbol,
nickname: nickname,
pin: Number.parseInt(pin)
}
if (splitAccount.length != 3)
return {card: splitParsed, isComplete: false};
return {card: splitParsed, isComplete: true}
}
case "TASK": {
preParsed.cardType = NFCCardType.ACCOUNT;
const splitTask = parsing[1].split(";");
let taskType = this.valueOr(splitTask, 0, "");
let taskAmount1 = this.valueOr(splitTask, 1, 0);
let taskAmount2 = this.valueOr(splitTask, 2, 0);
const splitParsed: NFCTaskCard = {
...preParsed,
type: taskType as TaskType,
amount: Number.parseInt(taskAmount1) || 0,
amount2: Number.parseInt(taskAmount2) || 0
}
if (splitTask.length < 1 || splitTask.length > 3)
return {card: splitParsed, isComplete: false};
return {card: splitParsed, isComplete: true}
}
default: {
return {card: preParsed, isComplete: false};
}
}
} catch (e) {
return null;
}
}
private static valueOr(arr: string[], index: number, def: any) {
try {
if (arr[index])
return arr[index];
return def;
} catch (e) {
return def;
}
}
}
interface formClassReturn {
card: NFCCard;
isComplete: boolean;
}