Update configuration, add dependencies, and enhance socket event handling
Some checks failed
farmcontrol/farmcontrol-ws/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-ws/pipeline/head There was a failure building this commit
- Changed log level in config.json from "debug" to "trace" for more detailed logging. - Added new dependencies "pdf-to-img" and "sharp" in package.json for image processing capabilities. - Introduced 'renderTemplateJPG' event handling in SocketHost and SocketUser classes to support JPG rendering. - Implemented the renderJPG method in TemplateManager for converting templates to JPG format, including error handling and logging.
This commit is contained in:
parent
53b5924767
commit
c2372f661e
@ -35,9 +35,11 @@
|
|||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
|
"pdf-to-img": "^6.1.0",
|
||||||
"posthtml": "^0.16.7",
|
"posthtml": "^0.16.7",
|
||||||
"puppeteer": "^24.31.0",
|
"puppeteer": "^24.31.0",
|
||||||
"redis": "^5.10.0",
|
"redis": "^5.10.0",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-adapter-mongo": "^2.0.5",
|
"socket.io-adapter-mongo": "^2.0.5",
|
||||||
"socketio-jwt": "^4.6.2"
|
"socketio-jwt": "^4.6.2"
|
||||||
|
|||||||
@ -7,40 +7,40 @@ const subJobSchema = new mongoose.Schema({
|
|||||||
printer: {
|
printer: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'printer',
|
ref: 'printer',
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
job: {
|
job: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'job',
|
ref: 'job',
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
moonrakerJobId: {
|
moonrakerJobId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
gcodeFile: {
|
gcodeFile: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'gcodeFile',
|
ref: 'gcodeFile',
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
type: { required: true, type: String },
|
type: { required: true, type: String },
|
||||||
progress: { required: false, type: Number }
|
progress: { required: false, type: Number },
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: Date.now
|
default: Date.now,
|
||||||
},
|
},
|
||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: Date.now
|
default: Date.now,
|
||||||
},
|
},
|
||||||
startedAt: { required: false, type: Date, default: null },
|
startedAt: { required: false, type: Date, default: null },
|
||||||
finishedAt: { required: false, type: Date, default: null }
|
finishedAt: { required: false, type: Date, default: null },
|
||||||
});
|
});
|
||||||
|
|
||||||
subJobSchema.index({ moonrakerJobId: 'text', 'state.type': 'text' });
|
subJobSchema.index({ moonrakerJobId: 'text', 'state.type': 'text' });
|
||||||
|
|||||||
@ -68,6 +68,10 @@ export class SocketHost {
|
|||||||
'renderTemplatePDF',
|
'renderTemplatePDF',
|
||||||
this.handleRenderTemplatePDFEvent.bind(this)
|
this.handleRenderTemplatePDFEvent.bind(this)
|
||||||
);
|
);
|
||||||
|
this.socket.on(
|
||||||
|
'renderTemplateJPG',
|
||||||
|
this.handleRenderTemplateJPGEvent.bind(this)
|
||||||
|
);
|
||||||
this.socket.on('objectEvent', this.handleObjectEventEvent.bind(this));
|
this.socket.on('objectEvent', this.handleObjectEventEvent.bind(this));
|
||||||
this.socket.on('disconnect', this.handleDisconnect.bind(this));
|
this.socket.on('disconnect', this.handleDisconnect.bind(this));
|
||||||
}
|
}
|
||||||
@ -262,6 +266,16 @@ export class SocketHost {
|
|||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleRenderTemplateJPGEvent(data, callback) {
|
||||||
|
const result = await this.templateManager.renderJPG(
|
||||||
|
data._id,
|
||||||
|
data.content,
|
||||||
|
data.object,
|
||||||
|
{ width: data.width }
|
||||||
|
);
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
|
||||||
async setDevicesState(state, online, connectedAt) {
|
async setDevicesState(state, online, connectedAt) {
|
||||||
logger.info('Setting devices state to', state, 'and online to', online);
|
logger.info('Setting devices state to', state, 'and online to', online);
|
||||||
|
|
||||||
|
|||||||
@ -93,6 +93,10 @@ export class SocketUser {
|
|||||||
'renderTemplatePDF',
|
'renderTemplatePDF',
|
||||||
this.handleRenderTemplatePDFEvent.bind(this)
|
this.handleRenderTemplatePDFEvent.bind(this)
|
||||||
);
|
);
|
||||||
|
this.socket.on(
|
||||||
|
'renderTemplateJPG',
|
||||||
|
this.handleRenderTemplateJPGEvent.bind(this)
|
||||||
|
);
|
||||||
this.socket.on(
|
this.socket.on(
|
||||||
'generateHostOtp',
|
'generateHostOtp',
|
||||||
this.handleGenerateHostOtpEvent.bind(this)
|
this.handleGenerateHostOtpEvent.bind(this)
|
||||||
@ -342,6 +346,26 @@ export class SocketUser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleRenderTemplateJPGEvent(data, callback) {
|
||||||
|
try {
|
||||||
|
const result = await this.templateManager.renderJPG(
|
||||||
|
data._id,
|
||||||
|
data.content,
|
||||||
|
data.object,
|
||||||
|
{ width: data.width }
|
||||||
|
);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Render template JPG event error:', err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback({ error: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async handleGenerateHostOtpEvent(data, callback) {
|
async handleGenerateHostOtpEvent(data, callback) {
|
||||||
const result = await generateHostOTP(data._id);
|
const result = await generateHostOTP(data._id);
|
||||||
callback(result);
|
callback(result);
|
||||||
|
|||||||
78
src/templates/pdfUtils.js
Normal file
78
src/templates/pdfUtils.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* PDF conversion utilities loaded on demand so startup
|
||||||
|
* does not require ESM-only pdf-to-img at module load time.
|
||||||
|
*/
|
||||||
|
import { pathToFileURL } from 'node:url';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
async function importPackage(name) {
|
||||||
|
const packagePath = require.resolve(name);
|
||||||
|
return import(pathToFileURL(packagePath).href);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDefault(module) {
|
||||||
|
return module?.default ?? module;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertPDFToImage(pdfInput, options = {}) {
|
||||||
|
const [pdfToImgModule, sharpModule] = await Promise.all([
|
||||||
|
importPackage('pdf-to-img'),
|
||||||
|
importPackage('sharp')
|
||||||
|
]);
|
||||||
|
const { pdf } = pdfToImgModule;
|
||||||
|
const sharp = resolveDefault(sharpModule);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { width, height, page_numbers, scale = 2, ...pdfOnlyOptions } =
|
||||||
|
options;
|
||||||
|
|
||||||
|
const pdfOptions = {
|
||||||
|
scale,
|
||||||
|
...pdfOnlyOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = await pdf(pdfInput, pdfOptions);
|
||||||
|
const outputImages = [];
|
||||||
|
|
||||||
|
if (
|
||||||
|
page_numbers &&
|
||||||
|
Array.isArray(page_numbers) &&
|
||||||
|
page_numbers.length > 0
|
||||||
|
) {
|
||||||
|
for (const pageNum of page_numbers) {
|
||||||
|
let image = await document.getPage(pageNum);
|
||||||
|
|
||||||
|
if (width || height) {
|
||||||
|
const resizeOptions = {};
|
||||||
|
if (width) resizeOptions.width = width;
|
||||||
|
if (height) resizeOptions.height = height;
|
||||||
|
image = await sharp(image).resize(resizeOptions).toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
outputImages.push(image);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for await (const image of document) {
|
||||||
|
let processedImage = image;
|
||||||
|
|
||||||
|
if (width || height) {
|
||||||
|
const resizeOptions = {};
|
||||||
|
if (width) resizeOptions.width = width;
|
||||||
|
if (height) resizeOptions.height = height;
|
||||||
|
processedImage = await sharp(image)
|
||||||
|
.resize(resizeOptions)
|
||||||
|
.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
outputImages.push(processedImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputImages;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error converting PDF to image:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,7 @@ import { dirname, join } from 'path';
|
|||||||
import { getObject, listObjects } from '../database/database.js';
|
import { getObject, listObjects } from '../database/database.js';
|
||||||
import { getModelByName } from '../utils.js';
|
import { getModelByName } from '../utils.js';
|
||||||
import { generatePDF } from './pdffactory.js';
|
import { generatePDF } from './pdffactory.js';
|
||||||
|
import { convertPDFToImage } from './pdfUtils.js';
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
@ -595,6 +596,31 @@ export class TemplateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a template to image format for receipt printers
|
||||||
|
* @param {string} id - The document template ID
|
||||||
|
* @param {string} content - The template content
|
||||||
|
* @param {Object} data - Data object to pass to the template
|
||||||
|
* @param {Object} options - Image conversion options (e.g. width, height)
|
||||||
|
* @returns {Promise<Object>} Object containing image buffers or error
|
||||||
|
*/
|
||||||
|
async renderJPG(id, content, data = {}, options = {}) {
|
||||||
|
try {
|
||||||
|
logger.debug('Rendering JPG for template:', id);
|
||||||
|
|
||||||
|
const pdfResult = await this.renderPDF(id, content, data, options);
|
||||||
|
if (pdfResult.error != undefined) {
|
||||||
|
return { error: pdfResult.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = await convertPDFToImage(pdfResult.pdf, options);
|
||||||
|
return { images };
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('Error whilst rendering JPG:', error.message);
|
||||||
|
return { error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async listObjects(objectType, filter = {}, populate = []) {
|
async listObjects(objectType, filter = {}, populate = []) {
|
||||||
const model = getModelByName(objectType);
|
const model = getModelByName(objectType);
|
||||||
if (model == undefined) {
|
if (model == undefined) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user