205 lines
7.0 KiB
JavaScript
205 lines
7.0 KiB
JavaScript
const { parentPort } = require('worker_threads');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const pool = require('../db/pool');
|
|
const log4js = require('log4js');
|
|
const dotenv = require('dotenv');
|
|
const net = require('net');
|
|
const generateZPL = require('../resources/zplTemplate');
|
|
const label = require('../resources/label.json');
|
|
|
|
dotenv.config();
|
|
// Configuración de log4js
|
|
log4js.configure({
|
|
appenders: {
|
|
file: { type: 'file', filename: 'logs/app.log', maxLogSize: 10485760, backups: 3, compress: true },
|
|
console: { type: 'console' }
|
|
},
|
|
categories: {
|
|
default: { appenders: ['file', 'console'], level: 'info' }
|
|
}
|
|
});
|
|
|
|
const logger = log4js.getLogger('default');
|
|
|
|
// Función para obtener una conexión con reintentos
|
|
async function getConnectionWithRetries(retries = 3, delay = 5000) {
|
|
let attempt = 0;
|
|
while (attempt < retries) {
|
|
try {
|
|
return await pool.getConnection();
|
|
} catch (error) {
|
|
attempt++;
|
|
if (attempt >= retries)
|
|
throw new Error('No se pudo obtener conexión después de múltiples intentos.');
|
|
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
}
|
|
}
|
|
}
|
|
|
|
//Función para enviar ZPL a la impresora con TCP socket y reintentos
|
|
async function sendZPL(zplContent, ipAddress, retries = 2) {
|
|
const port = 9100;
|
|
try {
|
|
const result = await new Promise((resolve, reject) => {
|
|
const client = new net.Socket();
|
|
|
|
client.setTimeout(1000);
|
|
|
|
client.connect(port, ipAddress, () => {
|
|
client.write(zplContent, () => {
|
|
client.destroy();
|
|
resolve('success');
|
|
});
|
|
});
|
|
|
|
client.on('timeout', () => {
|
|
logger.error('Tiempo de espera agotado al conectar con la impresora');
|
|
client.destroy();
|
|
reject('error');
|
|
});
|
|
|
|
client.on('error', error => {
|
|
logger.error(`Error al enviar ZPL a la impresora: ${error.message || error}`);
|
|
|
|
client.destroy();
|
|
reject('error');
|
|
});
|
|
});
|
|
|
|
if (result === 'success') return result;
|
|
} catch (error) {
|
|
logger.error(`Error al enviar ZPL a la impresora: ${error.message || error}`);
|
|
if (retries > 0) {
|
|
logger.info(`Reintentando... Quedan ${retries} intentos.`);
|
|
return sendZPL(zplContent, ipAddress, retries - 1);
|
|
} else {
|
|
logger.error('Todos los intentos fallaron. No se pudo enviar el ZPL.');
|
|
return 'error';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Función para obtener la dirección IP de la impresora, realizando una llamada a la base de datos a la tabla de printer
|
|
async function getPrinterIpAddress(printerFk, connection) {
|
|
try {
|
|
const [rows] = await connection.query(`
|
|
SELECT ipAddress
|
|
FROM ${process.env.DB_PRINTER_SCHEMA}.printer
|
|
WHERE id = ?
|
|
`, [printerFk]);
|
|
if (!rows.length)
|
|
throw new Error(`No se encontró la impresora con id = ${printerFk}`);
|
|
return rows[0].ipAddress;
|
|
} catch (error) {
|
|
logger.error('Error al obtener la dirección IP de la impresora:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Función para obtener un único registro para procesar
|
|
async function getRecordForProcessing(retries = 5, delay = 4000) {
|
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
const conn = await getConnectionWithRetries();
|
|
try {
|
|
await conn.beginTransaction();
|
|
const [rows] = await conn.query(`
|
|
SELECT * FROM expedition_PrintOut
|
|
WHERE isPrinted = 10
|
|
LIMIT 1 FOR UPDATE
|
|
`);
|
|
if (!rows.length) {
|
|
await conn.commit();
|
|
return;
|
|
}
|
|
|
|
const record = rows[0];
|
|
await conn.query(`
|
|
UPDATE expedition_PrintOut
|
|
SET isPrinted = 12
|
|
WHERE expeditionFk = ?
|
|
`, [record.expeditionFk]);
|
|
await conn.commit();
|
|
return record;
|
|
} catch (error) {
|
|
await conn.rollback();
|
|
if (error.code === 'ER_LOCK_WAIT_TIMEOUT') {
|
|
logger.error('Lock wait timeout exceeded, retrying...');
|
|
if (attempt >= retries - 1)
|
|
throw new Error('No se pudo obtener el registro después de múltiples intentos.');
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
} else {
|
|
logger.error('Error al obtener y marcar el registro para procesamiento:', error);
|
|
return;
|
|
}
|
|
} finally {
|
|
conn.release();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Función para procesar un único registro
|
|
async function processRecord(record) {
|
|
const connection = await getConnectionWithRetries();
|
|
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
logger.info(`(${record.expeditionFk}) Procesando...`);
|
|
const zplData = generateZPL(record, label);
|
|
|
|
const filePath = path.join(__dirname, `zplData_${record.expeditionFk}.txt`);
|
|
|
|
await fs.writeFile(filePath, zplData["VerdNatura Label RFID"].zpl, 'utf8');
|
|
|
|
const zplContent = await fs.readFile(filePath, 'utf8');
|
|
const ipAddress = await getPrinterIpAddress(record.printerFk, connection);
|
|
|
|
const sendResult = await sendZPL(zplContent, ipAddress);
|
|
|
|
fs.unlink(filePath);
|
|
|
|
if (sendResult === 'success') {
|
|
await connection.query(`
|
|
UPDATE expedition_PrintOut
|
|
SET isPrinted = 11
|
|
WHERE expeditionFk = ?
|
|
`, [record.expeditionFk]);
|
|
logger.info(`(${record.expeditionFk}) Base de datos actualizada`);
|
|
} else {
|
|
await connection.query(`
|
|
UPDATE expedition_PrintOut
|
|
SET isPrinted = 13
|
|
WHERE expeditionFk = ?
|
|
`, [record.expeditionFk]);
|
|
logger.error(`(${record.expeditionFk}) Error al enviar ZPL a la impresora`);
|
|
}
|
|
parentPort.postMessage('done');
|
|
await connection.commit();
|
|
} catch (error) {
|
|
logger.error('Error al procesar el registro:', error);
|
|
parentPort.postMessage('error');
|
|
// if (!error.message === `Can't add new command when connection is in closed state`)
|
|
await connection.rollback();
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
}
|
|
|
|
// Escuchar mensajes del hilo principal
|
|
parentPort.on('message', async msg => {
|
|
if (msg === 'check') {
|
|
const record = await getRecordForProcessing();
|
|
if (record)
|
|
await processRecord(record);
|
|
else {
|
|
// Si no hay registros, espera y vuelve a verificar
|
|
setTimeout(async() => {
|
|
parentPort.postMessage('done');
|
|
}, 5000);
|
|
}
|
|
} else
|
|
processRecord(msg).catch(err => logger.error('Error en el worker:', err));
|
|
});
|