Errores por pantalla + logs en archivo
This commit is contained in:
@@ -11,12 +11,14 @@ export default defineConfig({
|
|||||||
"@renderer": resolve("src/renderer/src"),
|
"@renderer": resolve("src/renderer/src"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [vue({
|
plugins: [
|
||||||
template: {
|
vue({
|
||||||
compilerOptions: {
|
template: {
|
||||||
isCustomElement: tag => tag === "nfc-reader",
|
compilerOptions: {
|
||||||
|
isCustomElement: (tag) => tag === "nfc-reader",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
})],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 53 KiB |
4
src/config/base.json
Normal file
4
src/config/base.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"defultServer": "http://localhost:3000",
|
||||||
|
"defaultPrinter": "192.168.1.254"
|
||||||
|
}
|
||||||
74
src/main/LogService.ts
Normal file
74
src/main/LogService.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { app } from "electron";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
export class LogService {
|
||||||
|
private static instance: LogService;
|
||||||
|
private logPath: string;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
// Create logs directory in userData
|
||||||
|
const userDataPath = app.getPath("userData");
|
||||||
|
const logsDir = path.join(userDataPath, "logs");
|
||||||
|
|
||||||
|
if (!fs.existsSync(logsDir)) {
|
||||||
|
fs.mkdirSync(logsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monthly log file
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||||
|
const monthString = `${year}-${month}`; // YYYY-MM
|
||||||
|
this.logPath = path.join(logsDir, `app_${monthString}.log`);
|
||||||
|
|
||||||
|
this.info(`--- Log initialized at ${now.toISOString()} ---`);
|
||||||
|
console.log(`Log file: ${this.logPath}`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Garantizar que sea singleton
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static getInstance(): LogService {
|
||||||
|
if (!LogService.instance) {
|
||||||
|
LogService.instance = new LogService();
|
||||||
|
}
|
||||||
|
return LogService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public info(message: string): void {
|
||||||
|
this.write("INFO", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public error(message: string, error?: unknown): void {
|
||||||
|
const errorDetail = error ? ` | Details: ${JSON.stringify(error)}` : "";
|
||||||
|
this.write("ERROR", `${message}${errorDetail}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public logOperation(
|
||||||
|
operation: string,
|
||||||
|
data: unknown,
|
||||||
|
result?: unknown,
|
||||||
|
): void {
|
||||||
|
const entry = {
|
||||||
|
operation,
|
||||||
|
data,
|
||||||
|
result,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
this.write("OP", JSON.stringify(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
private write(level: string, message: string): void {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const logEntry = `[${timestamp}] [${level}] ${message}\n`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.appendFileSync(this.logPath, logEntry, "utf8");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to write to log file:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logger = LogService.getInstance();
|
||||||
115
src/main/handlers.ts
Normal file
115
src/main/handlers.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { IpcMainInvokeEvent } from "electron";
|
||||||
|
import { Result } from "../types/Result";
|
||||||
|
import { CodeRequest, CodeResponse, PrinterRequest } from "../preload/index.d";
|
||||||
|
import { createConnection } from "net";
|
||||||
|
import { logger } from "./LogService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLamada al servidor para obtener la etiqueta
|
||||||
|
* @param event --
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function labelReqHandler(
|
||||||
|
event: IpcMainInvokeEvent,
|
||||||
|
data: CodeRequest,
|
||||||
|
): Promise<Result<string, CodeResponse>> {
|
||||||
|
logger.logOperation("labelReq", data);
|
||||||
|
console.log("nfc:labelReq", data);
|
||||||
|
try {
|
||||||
|
const endpoint = "/nfc/generate";
|
||||||
|
const url = data.serverURL + endpoint;
|
||||||
|
console.log("fullUrl = ", url);
|
||||||
|
const response = await fetch(data.serverURL + endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
card_id: data.card_id,
|
||||||
|
override: data.override,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
console.log("Response", response.status);
|
||||||
|
const body: CodeResponse = await response.json();
|
||||||
|
console.log("Codigos:", body);
|
||||||
|
logger.info(`labelReq success: ${JSON.stringify(body)}`);
|
||||||
|
// body puede tener otro error dentro
|
||||||
|
return <Result<string, CodeResponse>>{
|
||||||
|
data: body,
|
||||||
|
};
|
||||||
|
} catch (error: unknown) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const err = error as any;
|
||||||
|
logger.error("labelReq error", err);
|
||||||
|
console.error("IPC ERROR", err);
|
||||||
|
let errorStr = "";
|
||||||
|
if (String(err.cause).includes("ECONNRESET")) {
|
||||||
|
errorStr = "Conexión perdida con el servidor";
|
||||||
|
} else {
|
||||||
|
errorStr = "Error de red";
|
||||||
|
}
|
||||||
|
return <Result<string, CodeResponse>>{
|
||||||
|
error: errorStr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Llamada principa a la impresora
|
||||||
|
* TODO:
|
||||||
|
* - Mejora de control de errores
|
||||||
|
*/
|
||||||
|
export async function printReqHandler(
|
||||||
|
_event: IpcMainInvokeEvent,
|
||||||
|
data: PrinterRequest,
|
||||||
|
): Promise<string> {
|
||||||
|
logger.logOperation("printReq", data);
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
console.log("nfc:printReq", data);
|
||||||
|
const port = 9100;
|
||||||
|
let finished = false;
|
||||||
|
|
||||||
|
const socket = createConnection(port, data.printerURL);
|
||||||
|
socket.setTimeout(3 * 1000);
|
||||||
|
|
||||||
|
socket.on("connect", (e) => {
|
||||||
|
console.log("Conectado!", e);
|
||||||
|
logger.info("Printer connected, sending label...");
|
||||||
|
socket.write(data.label, "utf-8", () => {
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("close", () => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
logger.info("Print job sent successfully");
|
||||||
|
console.log("Print job sent and connection closed.");
|
||||||
|
res("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("timeout", (err) => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
logger.error("Print timeout", err);
|
||||||
|
console.error("Timeout", err);
|
||||||
|
socket.destroy();
|
||||||
|
rej(new Error("timeout"));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("error", (err) => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
logger.error("Printer connection/transmission error", err);
|
||||||
|
let errorStr = "";
|
||||||
|
if (err.message.includes("ETIMEDOUT")) {
|
||||||
|
errorStr = "Error de conexión con la impresora";
|
||||||
|
rej(new Error(errorStr));
|
||||||
|
}
|
||||||
|
console.error("Printer Error:", err.message);
|
||||||
|
socket.destroy();
|
||||||
|
rej(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ import { join } from "path";
|
|||||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||||
import icon from "../../resources/icon.png?asset";
|
import icon from "../../resources/icon.png?asset";
|
||||||
import { NfcService } from "../services/NfcService";
|
import { NfcService } from "../services/NfcService";
|
||||||
import net, { createConnection } from "net";
|
import { labelReqHandler, printReqHandler } from "./handlers";
|
||||||
|
import { logger } from "./LogService";
|
||||||
|
|
||||||
function createWindow(): void {
|
function createWindow(): void {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
@@ -18,12 +20,16 @@ function createWindow(): void {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
new NfcService((event) => {
|
const nfcService = new NfcService((event) => {
|
||||||
if (!mainWindow.isDestroyed()) {
|
if (!mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send(`nfc:${event.type}`, event);
|
mainWindow.webContents.send(`nfc:${event.type}`, event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("nfc:getReaderName", () => {
|
||||||
|
return nfcService.getReaderName();
|
||||||
|
});
|
||||||
|
|
||||||
mainWindow.on("ready-to-show", () => {
|
mainWindow.on("ready-to-show", () => {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
});
|
});
|
||||||
@@ -60,64 +66,13 @@ app.whenReady().then(() => {
|
|||||||
ipcMain.on("ping", () => console.log("pong"));
|
ipcMain.on("ping", () => console.log("pong"));
|
||||||
|
|
||||||
// HANDLE es bidireccionar ON es unidireccional
|
// HANDLE es bidireccionar ON es unidireccional
|
||||||
ipcMain.handle("nfc:labelReq", async (_event, data) => {
|
/**
|
||||||
console.log("nfc:labelReq", data);
|
* LLamada principal
|
||||||
try {
|
* Cli - card_id -> Server - etiqueta/activacion -> Cli
|
||||||
const endpoint = "/nfc/generate";
|
*/
|
||||||
const url = data.serverURL + endpoint;
|
ipcMain.handle("nfc:labelReq", labelReqHandler);
|
||||||
console.log("fullUrl = ", url);
|
|
||||||
const response = await fetch(data.serverURL + endpoint, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
card_id: data.card_id,
|
|
||||||
override: data.override,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
console.log("Response", response.status);
|
|
||||||
const body = await response.json();
|
|
||||||
console.log("Codigos:", body);
|
|
||||||
return body;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("nfc:printReq", async (_event, data) => {
|
ipcMain.handle("nfc:printReq", printReqHandler);
|
||||||
console.log("nfc:printReq", data);
|
|
||||||
const client = new net.Socket();
|
|
||||||
const port = 9100;
|
|
||||||
const host = data.printerURL + port;
|
|
||||||
|
|
||||||
const socket = createConnection(port, data.printerURL);
|
|
||||||
socket.setTimeout(10 * 1000);
|
|
||||||
socket.on("connect", (e) => {
|
|
||||||
console.log("Conectado!", e);
|
|
||||||
socket.write(data.label);
|
|
||||||
socket.end();
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
client.connect(data.printerURL + port, () => {
|
|
||||||
console.log("Connected to ZSim Printer");
|
|
||||||
const res = client.write(data.label, (res) => {
|
|
||||||
console.log("resultado de la impresion", res);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Resultado", res);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
socket.on("close", () =>
|
|
||||||
console.log("Print job sent and connection closed."),
|
|
||||||
);
|
|
||||||
socket.on("error", (err) =>
|
|
||||||
console.error("Printer Error:", err.message),
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("ping:url", async (_event, url: string) => {
|
ipcMain.handle("ping:url", async (_event, url: string) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
10
src/preload/index.d.ts
vendored
10
src/preload/index.d.ts
vendored
@@ -1,4 +1,5 @@
|
|||||||
import { ElectronAPI } from "@electron-toolkit/preload";
|
import { ElectronAPI } from "@electron-toolkit/preload";
|
||||||
|
import { Result } from "../types/Result.js";
|
||||||
|
|
||||||
export type CodeResponse = {
|
export type CodeResponse = {
|
||||||
error?: string;
|
error?: string;
|
||||||
@@ -13,6 +14,7 @@ export type CodeResponse = {
|
|||||||
export type CodeRequest = {
|
export type CodeRequest = {
|
||||||
card_id: string; // UUIDv7
|
card_id: string; // UUIDv7
|
||||||
override?: boolean;
|
override?: boolean;
|
||||||
|
serverURL: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PrinterRequest = {
|
export type PrinterRequest = {
|
||||||
@@ -23,16 +25,14 @@ export type PrinterRequest = {
|
|||||||
export type PrinterResponse = unknown;
|
export type PrinterResponse = unknown;
|
||||||
|
|
||||||
export interface NfcAPI {
|
export interface NfcAPI {
|
||||||
|
getReaderName: () => Promise<string>;
|
||||||
|
onReader: (callback: (event: { name: string }) => void) => void;
|
||||||
onTag: (callback: (event: { uid: string }) => void) => void;
|
onTag: (callback: (event: { uid: string }) => void) => void;
|
||||||
onRemoved: (callback: (event: { uid: string }) => void) => void;
|
onRemoved: (callback: (event: { uid: string }) => void) => void;
|
||||||
onError: (callback: (event: { message: string }) => void) => void;
|
onError: (callback: (event: { message: string }) => void) => void;
|
||||||
ping: (url: string) => Promise<boolean>;
|
ping: (url: string) => Promise<boolean>;
|
||||||
removeAllListeners: () => void;
|
removeAllListeners: () => void;
|
||||||
labelReq: (args: {
|
labelReq: (args: CodeRequest) => Promise<Result<string, CodeResponse>>;
|
||||||
serverURL: string;
|
|
||||||
card_id: string;
|
|
||||||
override?: boolean;
|
|
||||||
}) => Promise<CodeResponse>;
|
|
||||||
printReq: (args: {
|
printReq: (args: {
|
||||||
printerURL: string;
|
printerURL: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import { CodeRequest, PrinterRequest } from "./index.d.js";
|
|||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
const api = {
|
const api = {
|
||||||
nfc: {
|
nfc: {
|
||||||
|
getReaderName: (): Promise<string> => {
|
||||||
|
return ipcRenderer.invoke("nfc:getReaderName");
|
||||||
|
},
|
||||||
|
onReader: (callback: (event: { name: string }) => void): void => {
|
||||||
|
ipcRenderer.on("nfc:reader", (_event, value) => callback(value));
|
||||||
|
},
|
||||||
onTag: (callback: (event: { uid: string }) => void): void => {
|
onTag: (callback: (event: { uid: string }) => void): void => {
|
||||||
ipcRenderer.on("nfc:tag", (_event, value) => callback(value));
|
ipcRenderer.on("nfc:tag", (_event, value) => callback(value));
|
||||||
},
|
},
|
||||||
@@ -24,6 +30,7 @@ const api = {
|
|||||||
return ipcRenderer.invoke("ping:url", url);
|
return ipcRenderer.invoke("ping:url", url);
|
||||||
},
|
},
|
||||||
removeAllListeners: (): void => {
|
removeAllListeners: (): void => {
|
||||||
|
ipcRenderer.removeAllListeners("nfc:reader");
|
||||||
ipcRenderer.removeAllListeners("nfc:tag");
|
ipcRenderer.removeAllListeners("nfc:tag");
|
||||||
ipcRenderer.removeAllListeners("nfc:removed");
|
ipcRenderer.removeAllListeners("nfc:removed");
|
||||||
ipcRenderer.removeAllListeners("nfc:error");
|
ipcRenderer.removeAllListeners("nfc:error");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Electron</title>
|
<title>Lector NFC</title>
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|||||||
@@ -30,3 +30,8 @@ body {
|
|||||||
);
|
);
|
||||||
background-size: 20px 20px;
|
background-size: 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type CodeResponse } from "src/preload/index.d.js";
|
import { type CodeResponse } from "src/preload/index.d.js";
|
||||||
|
import { tryCatch } from "../../../types/Result.js";
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
/**
|
/**
|
||||||
* waiting = esperando a leer una tarjeta (igual es mejor ready)
|
* waiting = esperando a leer una tarjeta (igual es mejor ready)
|
||||||
@@ -10,10 +11,18 @@ import { ref, onMounted, onUnmounted } from "vue";
|
|||||||
const readerState = ref<"waiting" | "reading" | "success" | "error">("waiting");
|
const readerState = ref<"waiting" | "reading" | "success" | "error">("waiting");
|
||||||
const uid = ref<string | null>(null);
|
const uid = ref<string | null>(null);
|
||||||
const errorMsg = ref<string | null>(null);
|
const errorMsg = ref<string | null>(null);
|
||||||
const labelOutput = ref<string>("");
|
|
||||||
const labelOutputStructured = ref<CodeResponse | null>(null);
|
const labelOutputStructured = ref<CodeResponse | null>(null);
|
||||||
|
const readerName = ref<string>("OFFLINE");
|
||||||
|
const errors = ref<string[]>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// Get initial reader name
|
||||||
|
readerName.value = await window.api.nfc.getReaderName();
|
||||||
|
|
||||||
|
window.api.nfc.onReader((event) => {
|
||||||
|
readerName.value = event.name;
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.api.nfc.onTag((event) => {
|
window.api.nfc.onTag((event) => {
|
||||||
uid.value = event.uid;
|
uid.value = event.uid;
|
||||||
readerState.value = "success";
|
readerState.value = "success";
|
||||||
@@ -64,43 +73,62 @@ const mockReadCard = (): void => {
|
|||||||
|
|
||||||
|
|
||||||
const readCard = async (): Promise<void> => {
|
const readCard = async (): Promise<void> => {
|
||||||
console.log("test readcard");
|
// Reset de los errores anteriores
|
||||||
|
errors.value = [];
|
||||||
|
|
||||||
if (readerState.value !== "waiting") return;
|
if (readerState.value !== "waiting") return;
|
||||||
readerState.value = "reading";
|
readerState.value = "reading";
|
||||||
|
|
||||||
const serverURL =
|
const serverURL =
|
||||||
localStorage.getItem("serverUrl") || "http://localhost:3000";
|
localStorage.getItem("serverUrl") || "http://localhost:3000";
|
||||||
const printerURL =
|
const printerURL = localStorage.getItem("printerUrl") || "192.168.1.254";
|
||||||
localStorage.getItem("printerUrl") || "192.168.1.254";
|
|
||||||
|
|
||||||
// TODO: De momento está hardcodeado porque no se puede leer la tarjeta
|
// TODO: De momento está hardcodeado porque no se puede leer la tarjeta
|
||||||
const card_id = "019cdd39-fc08-7417-b16d-a78794a24c01";
|
const card_id = "019cdd39-fc08-7417-b16d-a78794a24c01";
|
||||||
const override = false;
|
const override = false;
|
||||||
|
|
||||||
try {
|
const codePromise = window.api.nfc.labelReq({
|
||||||
const res: CodeResponse = await window.api.nfc.labelReq({
|
serverURL,
|
||||||
serverURL,
|
card_id,
|
||||||
card_id,
|
override,
|
||||||
override,
|
});
|
||||||
});
|
|
||||||
console.log("Res IPC", res);
|
|
||||||
labelOutput.value = res.data.label;
|
|
||||||
labelOutputStructured.value = res;
|
|
||||||
console.log("Imprimiendo", res);
|
|
||||||
|
|
||||||
const impRes = await window.api.nfc.printReq({
|
const res = await tryCatch(codePromise);
|
||||||
printerURL,
|
console.log("RES IPC:", res);
|
||||||
label: res.data.label,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Impreso", impRes);
|
// Error de la llamada -> sin conexion al servidor
|
||||||
|
if (res.error != undefined) {
|
||||||
|
errors.value = [...errors.value, String(res.error)];
|
||||||
|
delayWaiting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Error que viene del servidor
|
||||||
|
if (res.data?.error != undefined) {
|
||||||
|
errors.value = [...errors.value, String(res.data.error)];
|
||||||
|
delayWaiting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const labelData = res.data.data.data;
|
||||||
|
labelOutputStructured.value = res.data.data;
|
||||||
|
console.log("label data", labelData);
|
||||||
|
|
||||||
readerState.value = "waiting";
|
const impPromise = window.api.nfc.printReq({
|
||||||
} catch (e) {
|
printerURL,
|
||||||
console.error(e);
|
label: labelData.label,
|
||||||
readerState.value = "error";
|
});
|
||||||
|
|
||||||
|
const resPrint = await tryCatch(impPromise);
|
||||||
|
console.log("print", resPrint);
|
||||||
|
if (resPrint.error != undefined) {
|
||||||
|
let errorStr = "";
|
||||||
|
if (resPrint.error.message.includes("timeout")) {
|
||||||
|
errorStr = "Error de conexión con la impresora";
|
||||||
|
}
|
||||||
|
errors.value = [...errors.value, errorStr];
|
||||||
delayWaiting();
|
delayWaiting();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delayWaiting();
|
||||||
};
|
};
|
||||||
|
|
||||||
const delayWaiting = (): void => {
|
const delayWaiting = (): void => {
|
||||||
@@ -125,8 +153,8 @@ const triggerError = (): void => {
|
|||||||
|
|
||||||
<div class="top-meta">
|
<div class="top-meta">
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<span class="label">MODULE</span>
|
<span class="label">LECTOR</span>
|
||||||
<span class="value">NFC_RD_01</span>
|
<span class="value">{{ readerName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -134,7 +162,7 @@ const triggerError = (): void => {
|
|||||||
<div v-if="readerState === 'waiting'" class="waiting-state">
|
<div v-if="readerState === 'waiting'" class="waiting-state">
|
||||||
<div class="pulse-ring"></div>
|
<div class="pulse-ring"></div>
|
||||||
<div class="pulse-inner"></div>
|
<div class="pulse-inner"></div>
|
||||||
<h1 class="status-msg">TAP CARD TO INITIALIZE</h1>
|
<h1 class="status-msg">ACERCA UN NFC PARA EMPEZAR</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -171,7 +199,22 @@ const triggerError = (): void => {
|
|||||||
</div>
|
</div>
|
||||||
<!-- Salida por pantalla de las respuestas del servidor y la impresora -->
|
<!-- Salida por pantalla de las respuestas del servidor y la impresora -->
|
||||||
<div class="display-container bg-dots">
|
<div class="display-container bg-dots">
|
||||||
|
<!-- ERRORES -->
|
||||||
|
<div class="error">
|
||||||
|
<span v-for="(error, idx) in errors" :key="idx">
|
||||||
|
<span>Error: {{ error }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div v-if="labelOutputStructured != undefined" class="flex-col">
|
<div v-if="labelOutputStructured != undefined" class="flex-col">
|
||||||
|
<!-- Errores -->
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="labelOutputStructured.error != undefined"
|
||||||
|
class="error"
|
||||||
|
>
|
||||||
|
{{ JSON.stringify(labelOutputStructured.error) }}
|
||||||
|
</span>
|
||||||
|
<!-- Datos -->
|
||||||
<span> Código {{ labelOutputStructured.data.code }} </span>
|
<span> Código {{ labelOutputStructured.data.code }} </span>
|
||||||
<span>
|
<span>
|
||||||
Etiqueta {{ labelOutputStructured.data.label }}
|
Etiqueta {{ labelOutputStructured.data.label }}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import PCSC, { Tag, Reader } from "@tockawa/nfc-pcsc";
|
import PCSC, { Tag, Reader } from "@tockawa/nfc-pcsc";
|
||||||
|
import { logger } from "../main/LogService";
|
||||||
|
|
||||||
export type NfcEvent =
|
export type NfcEvent =
|
||||||
| { type: "tag"; uid: string }
|
| { type: "tag"; uid: string }
|
||||||
| { type: "removed"; uid: string }
|
| { type: "removed"; uid: string }
|
||||||
|
| { type: "reader"; name: string }
|
||||||
| { type: "error"; message: string };
|
| { type: "error"; message: string };
|
||||||
|
|
||||||
export class NfcService {
|
export class NfcService {
|
||||||
private nfc: PCSC;
|
private nfc: PCSC;
|
||||||
private onEvent?: (event: NfcEvent) => void;
|
private onEvent?: (event: NfcEvent) => void;
|
||||||
|
private currentReaderName: string = "OFFLINE";
|
||||||
|
|
||||||
constructor(onEvent?: (event: NfcEvent) => void) {
|
constructor(onEvent?: (event: NfcEvent) => void) {
|
||||||
this.nfc = new PCSC.default();
|
this.nfc = new PCSC.default();
|
||||||
@@ -15,12 +18,22 @@ export class NfcService {
|
|||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getReaderName(): string {
|
||||||
|
return this.currentReaderName;
|
||||||
|
}
|
||||||
|
|
||||||
private init(): void {
|
private init(): void {
|
||||||
this.nfc.on("reader", (reader: Reader) => {
|
this.nfc.on("reader", (reader: Reader) => {
|
||||||
console.log(`Lector detectado: ${reader.name}`);
|
console.log(`Lector detectado: ${reader.name}`);
|
||||||
|
logger.info(`NFC Reader detected: ${reader.name}`);
|
||||||
|
this.currentReaderName = reader.name;
|
||||||
|
if (this.onEvent) {
|
||||||
|
this.onEvent({ type: "reader", name: reader.name });
|
||||||
|
}
|
||||||
|
|
||||||
reader.on("card", async (card: Tag) => {
|
reader.on("card", async (card: Tag) => {
|
||||||
console.log(`Tarjeta detectada! UID: ${card.uid}`);
|
console.log(`Tarjeta detectada! UID: ${card.uid}`);
|
||||||
|
logger.info(`NFC Tag detected: ${card.uid}`);
|
||||||
if (this.onEvent) {
|
if (this.onEvent) {
|
||||||
this.onEvent({ type: "tag", uid: card.uid });
|
this.onEvent({ type: "tag", uid: card.uid });
|
||||||
}
|
}
|
||||||
@@ -28,6 +41,7 @@ export class NfcService {
|
|||||||
|
|
||||||
reader.on("card.off", async (card: Tag) => {
|
reader.on("card.off", async (card: Tag) => {
|
||||||
console.log(`Tarjeta retirada: ${card.uid}`);
|
console.log(`Tarjeta retirada: ${card.uid}`);
|
||||||
|
logger.info(`NFC Tag removed: ${card.uid}`);
|
||||||
if (this.onEvent) {
|
if (this.onEvent) {
|
||||||
this.onEvent({ type: "removed", uid: card.uid });
|
this.onEvent({ type: "removed", uid: card.uid });
|
||||||
}
|
}
|
||||||
@@ -45,6 +59,11 @@ export class NfcService {
|
|||||||
|
|
||||||
reader.on("end", () => {
|
reader.on("end", () => {
|
||||||
console.log(`Lector desconectado: ${reader.name}`);
|
console.log(`Lector desconectado: ${reader.name}`);
|
||||||
|
logger.info(`NFC Reader disconnected: ${reader.name}`);
|
||||||
|
this.currentReaderName = "OFFLINE";
|
||||||
|
if (this.onEvent) {
|
||||||
|
this.onEvent({ type: "reader", name: "OFFLINE" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
27
src/types/Result.ts
Normal file
27
src/types/Result.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export type Success<D> = {
|
||||||
|
error?: undefined | null;
|
||||||
|
data: D;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Failure<E = Error> = {
|
||||||
|
data?: undefined | null;
|
||||||
|
error: E;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result<Error,Data>
|
||||||
|
*/
|
||||||
|
export type Result<E, D> = Failure<E> | Success<D>;
|
||||||
|
|
||||||
|
export async function tryCatch<T>(func: Promise<T>): Promise<Result<Error, T>> {
|
||||||
|
try {
|
||||||
|
const res = await func;
|
||||||
|
return {
|
||||||
|
data: res,
|
||||||
|
};
|
||||||
|
} catch (e: unknown) {
|
||||||
|
return {
|
||||||
|
error: e as Error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||||
"include": [
|
"include": [
|
||||||
|
"src/config/**/*",
|
||||||
|
"src/types/**/*",
|
||||||
"electron.vite.config.*",
|
"electron.vite.config.*",
|
||||||
"src/main/**/*",
|
"src/main/**/*",
|
||||||
"src/preload/**/*",
|
"src/preload/**/*",
|
||||||
|
|||||||
Reference in New Issue
Block a user