Ya genera el codigo e imprime
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
|
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"dev": "electron-vite dev --watch",
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { join } from "path";
|
||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||
import icon from "../../resources/icon.png?asset";
|
||||
import { NfcService } from "../services/NfcService";
|
||||
|
||||
import net, { createConnection } from "net";
|
||||
function createWindow(): void {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
@@ -60,21 +60,26 @@ app.whenReady().then(() => {
|
||||
ipcMain.on("ping", () => console.log("pong"));
|
||||
|
||||
// HANDLE es bidireccionar ON es unidireccional
|
||||
ipcMain.handle("nfc:labelReq", async (_event, data: unknown) => {
|
||||
ipcMain.handle("nfc:labelReq", async (_event, data) => {
|
||||
console.log("nfc:labelReq", data);
|
||||
try {
|
||||
const response = await fetch("http://localhost:3000/nfc/generate", {
|
||||
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: "019cdd39-fc08-7417-b16d-a78794a24c01",
|
||||
override: false,
|
||||
card_id: data.card_id,
|
||||
override: data.override,
|
||||
}),
|
||||
});
|
||||
console.log("Codigos:", response);
|
||||
return 200;
|
||||
console.log("Response", response.status);
|
||||
const body = await response.json();
|
||||
console.log("Codigos:", body);
|
||||
return body;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return false;
|
||||
@@ -82,6 +87,38 @@ app.whenReady().then(() => {
|
||||
return data;
|
||||
});
|
||||
|
||||
ipcMain.handle("nfc:printReq", async (_event, data) => {
|
||||
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) => {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
|
||||
31
src/preload/index.d.ts
vendored
31
src/preload/index.d.ts
vendored
@@ -1,11 +1,42 @@
|
||||
import { ElectronAPI } from "@electron-toolkit/preload";
|
||||
|
||||
export type CodeResponse = {
|
||||
error?: string;
|
||||
data: {
|
||||
label: string;
|
||||
code: string;
|
||||
overriden: boolean;
|
||||
reused: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type CodeRequest = {
|
||||
card_id: string; // UUIDv7
|
||||
override?: boolean;
|
||||
};
|
||||
|
||||
export type PrinterRequest = {
|
||||
printerURL: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type PrinterResponse = unknown;
|
||||
|
||||
export interface NfcAPI {
|
||||
onTag: (callback: (event: { uid: string }) => void) => void;
|
||||
onRemoved: (callback: (event: { uid: string }) => void) => void;
|
||||
onError: (callback: (event: { message: string }) => void) => void;
|
||||
ping: (url: string) => Promise<boolean>;
|
||||
removeAllListeners: () => void;
|
||||
labelReq: (args: {
|
||||
serverURL: string;
|
||||
card_id: string;
|
||||
override?: boolean;
|
||||
}) => Promise<CodeResponse>;
|
||||
printReq: (args: {
|
||||
printerURL: string;
|
||||
label: string;
|
||||
}) => Promise<PrinterResponse>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { electronAPI } from "@electron-toolkit/preload";
|
||||
import { CodeRequest, PrinterRequest } from "./index.d.js";
|
||||
|
||||
// Custom APIs for renderer
|
||||
const api = {
|
||||
@@ -13,9 +14,12 @@ const api = {
|
||||
onError: (callback: (event: { message: string }) => void): void => {
|
||||
ipcRenderer.on("nfc:error", (_event, value) => callback(value));
|
||||
},
|
||||
labelReq: (data: unknown): Promise<void> => {
|
||||
labelReq: (data: CodeRequest): Promise<unknown> => {
|
||||
return ipcRenderer.invoke("nfc:labelReq", data);
|
||||
},
|
||||
printReq: (data: PrinterRequest): Promise<void> => {
|
||||
return ipcRenderer.invoke("nfc:printReq", data);
|
||||
},
|
||||
ping: (url: string): Promise<boolean> => {
|
||||
return ipcRenderer.invoke("ping:url", url);
|
||||
},
|
||||
|
||||
@@ -6,3 +6,27 @@ body {
|
||||
overflow: auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.bg-dots {
|
||||
background-image: radial-gradient(
|
||||
var(--te-gray-light) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
const printerUrl = ref(localStorage.getItem("printerUrl") || "192.168.1.100");
|
||||
const printerUrl = ref(localStorage.getItem("printerUrl") || "192.168.1.254");
|
||||
const serverUrl = ref(
|
||||
localStorage.getItem("serverUrl") || "http://localhost:3000",
|
||||
);
|
||||
@@ -29,16 +29,6 @@ const handleSave = (): void => {
|
||||
errorMsg.value = "";
|
||||
isSuccess.value = false;
|
||||
|
||||
if (!validateIpOrUrl(printerUrl.value)) {
|
||||
errorMsg.value = "Invalid Printer IP or URL format";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateIpOrUrl(serverUrl.value)) {
|
||||
errorMsg.value = "Invalid Server IP or URL format";
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem("printerUrl", printerUrl.value);
|
||||
localStorage.setItem("serverUrl", serverUrl.value);
|
||||
isSuccess.value = true;
|
||||
@@ -54,20 +44,9 @@ const performPing = async (type: "printer" | "server"): Promise<void> => {
|
||||
|
||||
const url = type === "printer" ? printerUrl.value : serverUrl.value;
|
||||
|
||||
if (!validateIpOrUrl(url)) {
|
||||
errorMsg.value = `Invalid ${type === "printer" ? "Printer" : "Server"} IP/URL format`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure URL has protocol for the ping check
|
||||
let pingUrl = url;
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
pingUrl = `http://${url}`;
|
||||
}
|
||||
|
||||
pingStatus.value[type] = "pinging";
|
||||
try {
|
||||
const ok = await window.api.nfc.ping(pingUrl);
|
||||
const ok = await window.api.nfc.ping(url);
|
||||
pingStatus.value[type] = ok ? "success" : "error";
|
||||
if (!ok) {
|
||||
errorMsg.value = `${type === "printer" ? "Printer" : "Server"} is unreachable`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { type CodeResponse } from "src/preload/index.d.js";
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import axios from "axios";
|
||||
/**
|
||||
* waiting = esperando a leer una tarjeta (igual es mejor ready)
|
||||
* reading = proceso de lectura
|
||||
@@ -10,6 +10,8 @@ import axios from "axios";
|
||||
const readerState = ref<"waiting" | "reading" | "success" | "error">("waiting");
|
||||
const uid = ref<string | null>(null);
|
||||
const errorMsg = ref<string | null>(null);
|
||||
const labelOutput = ref<string>("");
|
||||
const labelOutputStructured = ref<CodeResponse | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
window.api.nfc.onTag((event) => {
|
||||
@@ -60,36 +62,40 @@ const mockReadCard = (): void => {
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
type CodeResponse = {
|
||||
error?: string,
|
||||
data: {
|
||||
label: string;
|
||||
code: string;
|
||||
overriden: boolean;
|
||||
reused: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
type CodeRequest = {
|
||||
card_id: string; // UUIDv7
|
||||
override?: boolean;
|
||||
}
|
||||
|
||||
const readCard = async (): void => {
|
||||
console.log("test readcard")
|
||||
const readCard = async (): Promise<void> => {
|
||||
console.log("test readcard");
|
||||
if (readerState.value !== "waiting") return;
|
||||
readerState.value = "reading";
|
||||
|
||||
const serverURL =
|
||||
localStorage.getItem("serverUrl") || "http://localhost:3000";
|
||||
const endpoint = "/nfc/generate";
|
||||
const printerURL =
|
||||
localStorage.getItem("printerUrl") || "192.168.1.254";
|
||||
|
||||
// TODO: De momento está hardcodeado porque no se puede leer la tarjeta
|
||||
const card_id = "019cdd39-fc08-7417-b16d-a78794a24c01";
|
||||
const override = false;
|
||||
|
||||
try {
|
||||
window.api.nfc.labelReq({ card_id, override });
|
||||
const res: CodeResponse = await window.api.nfc.labelReq({
|
||||
serverURL,
|
||||
card_id,
|
||||
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({
|
||||
printerURL,
|
||||
label: res.data.label,
|
||||
});
|
||||
|
||||
console.log("Impreso", impRes);
|
||||
|
||||
readerState.value = "waiting";
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
readerState.value = "error";
|
||||
@@ -102,11 +108,10 @@ const delayWaiting = (): void => {
|
||||
readerState.value = "waiting";
|
||||
errorMsg.value = null;
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const triggerError = (): void => {
|
||||
if (readerState.value !== "waiting") return;
|
||||
|
||||
readerState.value = "error";
|
||||
delayWaiting();
|
||||
};
|
||||
@@ -114,52 +119,74 @@ const triggerError = (): void => {
|
||||
|
||||
<template>
|
||||
<div class="main-display">
|
||||
<div class="display-container">
|
||||
<div class="display-grid"></div>
|
||||
<div class="top-meta">
|
||||
<div class="meta-item">
|
||||
<span class="label">MODULE</span>
|
||||
<span class="value">NFC_RD_01</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row grow">
|
||||
<div class="display-container bg-dots">
|
||||
<div class="display-grid"></div>
|
||||
|
||||
<div class="status-center">
|
||||
<div v-if="readerState === 'waiting'" class="waiting-state">
|
||||
<div class="pulse-ring"></div>
|
||||
<div class="pulse-inner"></div>
|
||||
<h1 class="status-msg">TAP CARD TO INITIALIZE</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="readerState === 'reading'"
|
||||
class="reading-state"
|
||||
>
|
||||
<div class="reading-indicator"></div>
|
||||
<h1 class="status-msg reading">PROCESSING_DATA...</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="readerState === 'success'"
|
||||
class="success-state"
|
||||
>
|
||||
<div class="success-indicator"></div>
|
||||
<h1 class="status-msg success">SESSION_START_OK</h1>
|
||||
<div class="id-capture">
|
||||
<span class="id-label">UID_IDENTIFIED:</span>
|
||||
<span class="id-value">{{ uid }}</span>
|
||||
<div class="top-meta">
|
||||
<div class="meta-item">
|
||||
<span class="label">MODULE</span>
|
||||
<span class="value">NFC_RD_01</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="readerState === 'error'" class="error-state">
|
||||
<div class="error-indicator"></div>
|
||||
<h1 class="status-msg error">INTERFACE_ERROR</h1>
|
||||
<p class="error-desc">
|
||||
{{ errorMsg || "UNKNOWN_FAILURE" }}
|
||||
</p>
|
||||
<div class="status-center">
|
||||
<div v-if="readerState === 'waiting'" class="waiting-state">
|
||||
<div class="pulse-ring"></div>
|
||||
<div class="pulse-inner"></div>
|
||||
<h1 class="status-msg">TAP CARD TO INITIALIZE</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="readerState === 'reading'"
|
||||
class="reading-state"
|
||||
>
|
||||
<div class="reading-indicator"></div>
|
||||
<h1 class="status-msg reading">PROCESSING_DATA...</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="readerState === 'success'"
|
||||
class="success-state"
|
||||
>
|
||||
<div class="success-indicator"></div>
|
||||
<h1 class="status-msg success">SESSION_START_OK</h1>
|
||||
<div class="id-capture">
|
||||
<span class="id-label">UID_IDENTIFIED:</span>
|
||||
<span class="id-value">{{ uid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="readerState === 'error'"
|
||||
class="error-state"
|
||||
>
|
||||
<div class="error-indicator"></div>
|
||||
<h1 class="status-msg error">INTERFACE_ERROR</h1>
|
||||
<p class="error-desc">
|
||||
{{ errorMsg || "UNKNOWN_FAILURE" }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Salida por pantalla de las respuestas del servidor y la impresora -->
|
||||
<div class="display-container bg-dots">
|
||||
<div v-if="labelOutputStructured != undefined" class="flex-col">
|
||||
<span> Código {{ labelOutputStructured.data.code }} </span>
|
||||
<span>
|
||||
Etiqueta {{ labelOutputStructured.data.label }}
|
||||
</span>
|
||||
<span v-if="labelOutputStructured.data.reused == true">
|
||||
YA SE HA IMPRIMIDO
|
||||
</span>
|
||||
<span v-if="labelOutputStructured.data.overriden == true">
|
||||
SOBREESCRITA
|
||||
</span>
|
||||
<!-- Caso de error -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Discrete mock controls for dev testing -->
|
||||
<div class="dev-panel">
|
||||
<button
|
||||
@@ -221,11 +248,6 @@ const triggerError = (): void => {
|
||||
.display-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(
|
||||
var(--te-gray-light) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 20px 20px;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user