feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain
This commit is contained in:
108
lstV2/server/services/ocp/controller/labeling/bookIn.ts
Normal file
108
lstV2/server/services/ocp/controller/labeling/bookIn.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import axios from "axios";
|
||||
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
|
||||
import { lstAuth } from "../../../../index.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { prodlabels } from "../../../../../database/schema/prodLabels.js";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
|
||||
export const bookInLabel = async (data: any) => {
|
||||
// update sscc so we can book in
|
||||
const SSCC = data.SSCC.slice(2);
|
||||
|
||||
// api url
|
||||
const url = await prodEndpointCreation(
|
||||
"/public/v1.1/Manufacturing/ProductionControlling/BookIn"
|
||||
);
|
||||
|
||||
// create bookin
|
||||
const newBookin = {
|
||||
scannerId: 777,
|
||||
sscc: SSCC,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.post(url, newBookin, {
|
||||
headers: {
|
||||
Authorization: `Basic ${lstAuth}`,
|
||||
accept: "text/plain",
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status != 200) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${
|
||||
data.printer ? data.printer[0].name : "Manual book in"
|
||||
}, Error:${res.data}`
|
||||
);
|
||||
//printerUpdate(data.printer, 7, "Error while booking in.");
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error booking in the label.",
|
||||
data: res.data,
|
||||
};
|
||||
}
|
||||
// update the label.
|
||||
try {
|
||||
const booked = await db
|
||||
.update(prodlabels)
|
||||
.set({
|
||||
status: "Booked in",
|
||||
upd_date: sql`NOW()`,
|
||||
})
|
||||
.where(
|
||||
eq(prodlabels.runningNr, parseInt(data.SSCC.slice(10, -1)))
|
||||
)
|
||||
.returning({ runningNr: prodlabels.runningNr });
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${parseInt(data.SSCC.slice(10, -1))} , was just booked in.`
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${parseInt(
|
||||
data.SSCC.slice(10, -1)
|
||||
)}, was just booked in`,
|
||||
data: { SSCC: data.SSCC },
|
||||
};
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Error creating new runningNumber in the DB.`
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `${parseInt(
|
||||
data.SSCC.slice(10, -1)
|
||||
)}, encoutnered an error posting to the db`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${
|
||||
data.printer ? data.printer[0].name : "Manual book in"
|
||||
}, "Error: ${error?.response.data}`
|
||||
);
|
||||
// console.log(error.response.data);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error booking in the label.",
|
||||
data: error.response.data,
|
||||
};
|
||||
}
|
||||
};
|
||||
150
lstV2/server/services/ocp/controller/labeling/createLabel.ts
Normal file
150
lstV2/server/services/ocp/controller/labeling/createLabel.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { eq, gte, lt, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { printerData } from "../../../../../database/schema/printers.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { lstAuth } from "../../../../index.js";
|
||||
import axios from "axios";
|
||||
import { prodlabels } from "../../../../../database/schema/prodLabels.js";
|
||||
import { addDays } from "date-fns";
|
||||
|
||||
export const createLabel = async (data: any, userPrinted: any) => {
|
||||
createLog("info", "labeling", "ocp", `Label being created`);
|
||||
|
||||
const { data: printer, error: printerError } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(printerData)
|
||||
.where(eq(printerData.humanReadableId, data.printerID))
|
||||
);
|
||||
const { data: settingsData, error: settingsError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
if (printerError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the printer.",
|
||||
printerError,
|
||||
};
|
||||
}
|
||||
|
||||
if (settingsError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the settings.",
|
||||
settingsError,
|
||||
};
|
||||
}
|
||||
|
||||
const url = await prodEndpointCreation(
|
||||
"/public/v1.0/Warehousing/GenerateAndPrintLabelExtended"
|
||||
);
|
||||
const plantToken = settingsData.filter((n) => n.name === "plantToken");
|
||||
const newLabel = {
|
||||
scannerId: 99,
|
||||
lotNr: data.lot,
|
||||
machineId: data.machineID,
|
||||
printerId: data.printerID,
|
||||
//layoutId: cartonCustomers.includes(data.CustomerId.toString()) ? data.cartonLabel : data.palletLabel,
|
||||
layoutId:
|
||||
plantToken[0].value === "usksc1"
|
||||
? data.cartonLabel
|
||||
: data.palletLabel,
|
||||
//numberOfCopies: cartonCustomers.includes(data.CustomerId.toString()) ? data.cartonCopies : data.pallerCopies,
|
||||
numberOfCopies:
|
||||
plantToken[0].value === "usksc1"
|
||||
? data.cartonCopies
|
||||
: data.pallerCopies,
|
||||
};
|
||||
|
||||
// create the label
|
||||
// create the label with the data we have
|
||||
try {
|
||||
const res = await axios.post(url, newLabel, {
|
||||
headers: {
|
||||
Authorization: `Basic ${lstAuth}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
// error handling
|
||||
if (res.data.Result != 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${data.MachineDescription}, has an error while printing: Error: ${res.data.Message}.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
mesasge: `${data.MachineDescription}, has an error while printing`,
|
||||
data: res.data,
|
||||
};
|
||||
}
|
||||
|
||||
// save the data to lst db (only saved for x time see the db clean up functions)
|
||||
let newlabel = res.data;
|
||||
|
||||
try {
|
||||
const insertLabel = await db
|
||||
.insert(prodlabels)
|
||||
.values({
|
||||
printerID: parseInt(printer[0]?.humanReadableId!, 10),
|
||||
runningNr: parseInt(newlabel.SSCC.slice(10, -1)),
|
||||
printerName: printer[0].name.toLowerCase(),
|
||||
line: data.MachineLocation,
|
||||
status: "printed",
|
||||
add_user: userPrinted || "LST_System",
|
||||
})
|
||||
.returning({ runningNr: prodlabels.runningNr });
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${insertLabel[0]?.runningNr} was just inserted into the db.`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Error creating new runningNumber in the DB.`
|
||||
);
|
||||
}
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`New label was created for: ${
|
||||
data.MachineDescription
|
||||
}, Running number: ${parseInt(newlabel.SSCC.slice(10, -1))}`
|
||||
);
|
||||
|
||||
const returnData = {
|
||||
...newlabel,
|
||||
printer,
|
||||
};
|
||||
|
||||
// check if we can remove labels or not
|
||||
//deleteLabels();
|
||||
return { success: true, message: "Label created", data: returnData }; // returning label data to be able to book in if active
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There was an error creating the label for, ${printer[0].name}, "Error: ${error}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error creating the label",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// run the label delete process we want to delate them older than 90 days right now
|
||||
@@ -0,0 +1,23 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { labelRatio } from "../../../../../database/schema/ratios.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
export const getLabelRatio = async () => {
|
||||
const { data: labelInfo, error: labelError } = await tryCatch(
|
||||
db.select().from(labelRatio).where(eq(labelRatio.name, "label"))
|
||||
);
|
||||
|
||||
if (labelError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the labelratio",
|
||||
data: [labelError],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Current labelratio.",
|
||||
data: labelInfo,
|
||||
};
|
||||
};
|
||||
33
lstV2/server/services/ocp/controller/labeling/getLabels.ts
Normal file
33
lstV2/server/services/ocp/controller/labeling/getLabels.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { desc, gte, lte, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { prodlabels } from "../../../../../database/schema/prodLabels.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
export const getLabels = async (hours: string) => {
|
||||
const { data: labelInfo, error: labelError } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(prodlabels)
|
||||
.where(
|
||||
gte(
|
||||
prodlabels.upd_date,
|
||||
sql.raw(`NOW() - INTERVAL '${hours} hours'`)
|
||||
)
|
||||
)
|
||||
.orderBy(desc(prodlabels.upd_date))
|
||||
);
|
||||
|
||||
if (labelError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the labels",
|
||||
data: [labelError],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Current labels order by upd_Date.",
|
||||
count: labelInfo.length,
|
||||
data: labelInfo,
|
||||
};
|
||||
};
|
||||
322
lstV2/server/services/ocp/controller/labeling/labelProcess.ts
Normal file
322
lstV2/server/services/ocp/controller/labeling/labelProcess.ts
Normal file
@@ -0,0 +1,322 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { getLots } from "../lots/lots.js";
|
||||
import { billingCheck } from "../specialProcesses/billingCheck/billingCheck.js";
|
||||
import { isMainMatStaged } from "../materials/mainMaterial.js";
|
||||
import { firstLotLabel } from "../specialProcesses/lotChangeLabel/lotCHangeLabel.js";
|
||||
import { prolinkCheck } from "../lots/prolinkCheck.js";
|
||||
import { createLabel } from "./createLabel.js";
|
||||
import { bookInLabel } from "./bookIn.js";
|
||||
import { delieryInhouse } from "../specialProcesses/inhouse/inhouseDelivery.js";
|
||||
import { dualPrintingProcess } from "../specialProcesses/dualPrinting/dualPrinting.js";
|
||||
import { getMac } from "../../utils/getMachineId.js";
|
||||
|
||||
interface Printer {
|
||||
name: string;
|
||||
humanReadableId: string;
|
||||
// Add any other expected properties
|
||||
}
|
||||
|
||||
interface Zechetti {
|
||||
line: string;
|
||||
printer: number;
|
||||
printerName: string;
|
||||
}
|
||||
|
||||
export const labelingProcess = async ({
|
||||
line = null as string | null,
|
||||
printer = null as Printer | null,
|
||||
userPrinted = null,
|
||||
rfidTag = null,
|
||||
zechette = null as Zechetti | null,
|
||||
} = {}) => {
|
||||
/**
|
||||
* Creates a label once all logic is passed
|
||||
*/
|
||||
let filteredLot: any = [];
|
||||
createLog("debug", "labeling", "ocp", `Starting labeling process`);
|
||||
const { data: settingData, error: settingError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
|
||||
if (settingError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the settings.",
|
||||
settingError,
|
||||
};
|
||||
}
|
||||
// get the plantToken
|
||||
const plantToken = settingData.filter((n) => n.name === "plantToken");
|
||||
|
||||
// get the current lots
|
||||
const lots = await getLots();
|
||||
|
||||
// if we got a line passed over we need to get the machine id from this.
|
||||
if (line) {
|
||||
const macId = await getMac(line);
|
||||
// filter out the lot for the line
|
||||
filteredLot = lots.data.filter(
|
||||
(l: any) => l.MachineID === macId[0]?.HumanReadableId
|
||||
);
|
||||
|
||||
if (filteredLot.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There is not a lot assigned to ${line}.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `There is not a lot assigned to ${line}.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// if we are running the zechettii
|
||||
if (zechette) {
|
||||
const macId = await getMac(zechette.line);
|
||||
// filter out the lot for the line
|
||||
filteredLot = lots.data.filter(
|
||||
(l: any) => l.MachineID === macId[0]?.HumanReadableId
|
||||
);
|
||||
|
||||
if (filteredLot.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There is not a lot assigned to ${line}.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `There is not a lot assigned to ${line}.`,
|
||||
};
|
||||
}
|
||||
|
||||
// remap the printer so its the zechetti one
|
||||
filteredLot = filteredLot.map((p: any) => ({
|
||||
...p,
|
||||
printerID: zechette.printer,
|
||||
PrinterName: zechette.printerName,
|
||||
}));
|
||||
}
|
||||
// if we came from a printer
|
||||
if (printer) {
|
||||
// filter the lot based on the printerID
|
||||
// console.log(printer);
|
||||
filteredLot = lots.data.filter(
|
||||
(l: any) => l.printerID === parseInt(printer?.humanReadableId)
|
||||
);
|
||||
if (filteredLot.length === 0) {
|
||||
// console.log(`There is not a lot assigned to ${printer.name}`);
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There is not a lot assigned to ${printer.name}`
|
||||
);
|
||||
//printerUpdate(printer, 7, `There is no lot assigned to this printer.`);
|
||||
return {
|
||||
success: false,
|
||||
message: `There is not a lot assigned to ${printer.name}.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The checks we do before we can actually print a label will take place meow.
|
||||
*
|
||||
*/
|
||||
|
||||
// if the plant does not want to have dual printing and we have >2 assigned well return and send error.
|
||||
let dualPrinting = settingData.filter((d) => d.name === "dualPrinting")[0]
|
||||
?.value;
|
||||
|
||||
if (filteredLot.length > 1 && dualPrinting === "0") {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${printer?.name}, has more than one lot assigned to it, and dual printing shut off.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${printer?.name}, has more than one lot assigned to it, and dual printing shut off.`,
|
||||
};
|
||||
}
|
||||
|
||||
if (filteredLot.length > 1 && dualPrinting === "1") {
|
||||
// send over for dual printing processing
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${printer?.name}, being sent over for dual printing processing.`
|
||||
);
|
||||
// process what line to print the label for and return the lot info and change filteredLot to returned one.
|
||||
filteredLot = await dualPrintingProcess(filteredLot);
|
||||
}
|
||||
|
||||
// if there are more than 2 lots it might be an auto labeler, autolabeler will be defined by its id for now only dayton
|
||||
if (filteredLot?.length > 2 && plantToken[0].value !== "usday1") {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${printer?.name}, has more than 2 lot assigned to it, and not in ${plantToken[0].value}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${printer?.name}, has more than 2 lot assigned to it, and not in ${plantToken[0].value}`,
|
||||
};
|
||||
}
|
||||
|
||||
// special process for florence
|
||||
const isBillingTime = await billingCheck();
|
||||
|
||||
if (isBillingTime) {
|
||||
// billing is inside the window we dont want to bill now.
|
||||
return {
|
||||
success: false,
|
||||
message: "Billing time cant print.",
|
||||
};
|
||||
}
|
||||
|
||||
// check if we actaully have a lot passed over so we do not error out again.
|
||||
if (filteredLot.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Error getting lot info from ${
|
||||
printer ? printer.name : "Missing printer info"
|
||||
}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Error getting lot info from ${
|
||||
printer ? printer.name : "Missing printer info"
|
||||
}`,
|
||||
};
|
||||
}
|
||||
|
||||
// check the material... mm,color (auto and manual combined), pkg
|
||||
const mmStaged = await isMainMatStaged(filteredLot[0]);
|
||||
|
||||
if (!mmStaged.success) {
|
||||
createLog("error", "labeling", "ocp", mmStaged.message);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: mmStaged.message,
|
||||
};
|
||||
}
|
||||
|
||||
// comment only but will check for color
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Remaining pallets for: ${filteredLot[0].MachineDescription} is ${filteredLot[0].Remaining}`
|
||||
);
|
||||
|
||||
// do we want to over run
|
||||
if (filteredLot[0].overPrinting === "no" && filteredLot[0].Remaining <= 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Over Printing on ${filteredLot[0].MachineDescription} is not allowed please change the lot`
|
||||
);
|
||||
// for slc we want to run the first label for henkel
|
||||
firstLotLabel(filteredLot[0]);
|
||||
return {
|
||||
success: false,
|
||||
message: `Over Printing on ${filteredLot[0].MachineDescription} is not allowed please change the lot`,
|
||||
};
|
||||
}
|
||||
|
||||
// prolink check by lot
|
||||
const prolink = await prolinkCheck(filteredLot[0].lot);
|
||||
|
||||
if (!prolink) {
|
||||
//console.error(`Prolink does not match for ${filteredLot[0].MachineDescription}`);
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Prolink does not match for ${filteredLot[0].MachineDescription}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Prolink does not match for ${filteredLot[0].MachineDescription}`,
|
||||
};
|
||||
}
|
||||
createLog("info", "labeling", "ocp", `Is prolink good? ${prolink}`);
|
||||
|
||||
// create the label
|
||||
const label = await createLabel(filteredLot[0], userPrinted);
|
||||
|
||||
if (!label.success) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There was an error creating the label: ${label.message}`
|
||||
);
|
||||
return { sucess: false, message: label.message, data: label.data };
|
||||
}
|
||||
|
||||
// send over to be booked in if we can do it.
|
||||
const bookin = settingData.filter((s) => s.name === "bookin");
|
||||
let book: any = [];
|
||||
if (bookin[0].value === "1") {
|
||||
book = await bookInLabel(label.data);
|
||||
|
||||
if (!book.success) {
|
||||
// createLog(
|
||||
// "error",
|
||||
// "labeling",
|
||||
// "ocp",
|
||||
// `Error Booking in label: ${book.errors[0].message}`
|
||||
// );
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`There was an error booking in the label: ${JSON.stringify(
|
||||
book.data
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Error Booking in label`,
|
||||
data: book,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
createLog("info", "labeling", "ocp", "Bookin is turned off.");
|
||||
|
||||
// will add later!!! :P
|
||||
}
|
||||
|
||||
// inhouse - if the inhouse funtion is turned on we will deliver to inhouse as long as we did not hit an error state
|
||||
const inhouseDelivery = settingData.filter(
|
||||
(s) => s.name === "inhouseDelivery"
|
||||
);
|
||||
if (inhouseDelivery[0].value === "1") {
|
||||
const deliverPallet = await delieryInhouse(book.data);
|
||||
// console.log(deliverPallet);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Label fully processed.",
|
||||
data: label.data,
|
||||
};
|
||||
};
|
||||
84
lstV2/server/services/ocp/controller/labeling/labelRatio.ts
Normal file
84
lstV2/server/services/ocp/controller/labeling/labelRatio.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { labelRatio } from "../../../../../database/schema/ratios.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const autoLabelCreated = async () => {
|
||||
const { error } = await tryCatch(
|
||||
db
|
||||
.insert(labelRatio)
|
||||
.values({
|
||||
name: "label",
|
||||
autoLabel: 1,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: labelRatio.name,
|
||||
set: { autoLabel: sql`${labelRatio.autoLabel} + 1` },
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
"There was an error updating auto label ratio"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const manualLabelCreated = async () => {
|
||||
const { error } = await tryCatch(
|
||||
db
|
||||
.insert(labelRatio)
|
||||
.values({
|
||||
name: "label",
|
||||
manualLabel: 1,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: labelRatio.name,
|
||||
set: { manualLabel: sql`${labelRatio.manualLabel} + 1` },
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
"There was an error updating manual Label Ratio"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const resetLabelRatio = async () => {
|
||||
const { error } = await tryCatch(
|
||||
db
|
||||
.update(labelRatio)
|
||||
.set({
|
||||
name: sql`'label-' || (SELECT count(*) FROM ${labelRatio})`,
|
||||
lastReset: sql`NOW()`,
|
||||
})
|
||||
.where(eq(labelRatio.name, "label"))
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
"There was an error resetting Label Ratio"
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an issue resetting the label data.",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Label Ratio has been reset.",
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { manualPrinting } from "../../../../../database/schema/ocpManualPrint.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { manualTag } from "../../../rfid/controller/tags/manualTag.js";
|
||||
import { labelingProcess } from "./labelProcess.js";
|
||||
import { manualLabelCreated } from "./labelRatio.js";
|
||||
|
||||
export const manualPrint = async (manualPrint: any) => {
|
||||
/**
|
||||
* add the reason we did a manual print.
|
||||
*/
|
||||
|
||||
const manualPrintData = {
|
||||
line: manualPrint.line,
|
||||
printReason: manualPrint.printReason,
|
||||
initials: manualPrint.initials,
|
||||
additionalComments: manualPrint?.additionalComments,
|
||||
add_user: "lst",
|
||||
};
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db.insert(manualPrinting).values(manualPrintData).returning({
|
||||
line: manualPrinting.line,
|
||||
printReason: manualPrinting.printReason,
|
||||
initials: manualPrinting.initials,
|
||||
additionalComments: manualPrinting?.additionalComments,
|
||||
add_user: manualPrinting.add_user,
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error posting the manualPrintData",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
let label = await labelingProcess({ line: manualPrint.line });
|
||||
manualLabelCreated();
|
||||
if (manualPrint.rfidTag) {
|
||||
manualTag(
|
||||
manualPrint.rfidTag,
|
||||
"wrapper1",
|
||||
parseInt(label.data.SSCC.slice(10, -1))
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: "Log Entered label will be coming soon.",
|
||||
data,
|
||||
};
|
||||
};
|
||||
39
lstV2/server/services/ocp/controller/lots/lots.ts
Normal file
39
lstV2/server/services/ocp/controller/lots/lots.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { lotQuery } from "../../../sqlServer/querys/ocp/lots.js";
|
||||
|
||||
type LotInfo = {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data: any[];
|
||||
};
|
||||
export const getLots = async () => {
|
||||
const { data: l, error: lotError } = await tryCatch(
|
||||
query(lotQuery, "Alplalabel online lots")
|
||||
);
|
||||
|
||||
const lotInfo = l as LotInfo;
|
||||
|
||||
if (lotError) {
|
||||
console.log("lot error", lotError);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the lots",
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (lotInfo.success) {
|
||||
return {
|
||||
success: true,
|
||||
message: "Current active lots that are technically released.",
|
||||
data: lotInfo.data,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: `Error getting lot info due to: ${lotInfo.message}.`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
};
|
||||
67
lstV2/server/services/ocp/controller/lots/prolinkCheck.ts
Normal file
67
lstV2/server/services/ocp/controller/lots/prolinkCheck.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { prolinkQuery } from "../../../sqlServer/querys/ocp/prolinkCheck.js";
|
||||
|
||||
// check the if the lot matches
|
||||
export const prolinkCheck = async (lot: any) => {
|
||||
const { data, error: settingError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
|
||||
if (settingError) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`There was an error getting the settings.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const settingData: any = data;
|
||||
const plantToken = settingData.filter(
|
||||
(s: any) => s.name === "plantToken"
|
||||
)[0].value;
|
||||
const prolinkCheck = settingData.filter(
|
||||
(s: any) => s.name === "prolinkCheck"
|
||||
)[0].value;
|
||||
|
||||
// if we want to ignore prolink check it will be disabled and then just return a pass as true
|
||||
if (prolinkCheck === "0") {
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Prolink Check is disabled skipping check.`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// run the query
|
||||
try {
|
||||
const p: any = await query(prolinkQuery, "Prolink Checks");
|
||||
const prolink = p.data;
|
||||
// filter out the lot
|
||||
const filterdLot = prolink.filter(
|
||||
(p: any) => p.AlplaLabelOnline === lot
|
||||
);
|
||||
|
||||
//console.log(filterdLot);
|
||||
if (filterdLot[0].LotCheck === "Good") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Error from running the Prolink Check query: ${err}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
426
lstV2/server/services/ocp/controller/materials/lotTransfer.ts
Normal file
426
lstV2/server/services/ocp/controller/materials/lotTransfer.ts
Normal file
@@ -0,0 +1,426 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { printerData } from "../../../../../database/schema/printers.js";
|
||||
import { runProdApi } from "../../../../globalUtils/runProdApi.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { labelInfo } from "../../../sqlServer/querys/warehouse/labelInfo.js";
|
||||
import { format, formatDuration, intervalToDuration } from "date-fns";
|
||||
import { shiftChange } from "../../../sqlServer/querys/misc/shiftChange.js";
|
||||
import { success } from "zod/v4";
|
||||
|
||||
type NewLotData = {
|
||||
runningNumber: number;
|
||||
lotNumber: number;
|
||||
originalAmount: number;
|
||||
level: number;
|
||||
amount: number;
|
||||
type: "lot" | "eom";
|
||||
};
|
||||
|
||||
interface PendingJob {
|
||||
timeoutId: NodeJS.Timeout;
|
||||
runningNumber: string | number;
|
||||
data: any;
|
||||
consumeLot: any;
|
||||
newQty: any;
|
||||
scheduledFor: Date;
|
||||
}
|
||||
|
||||
export const pendingJobs = new Map<string | number, PendingJob>();
|
||||
|
||||
/**
|
||||
* Move manual material to a new lot.
|
||||
*
|
||||
* The data sent over should be
|
||||
* Running number
|
||||
* Lot number
|
||||
* Orignal Quantity
|
||||
* level of gaylord
|
||||
* amount can be sent over as a precise amount
|
||||
* type what way are we lots
|
||||
*/
|
||||
export const lotMaterialTransfer = async (data: NewLotData) => {
|
||||
// check if we already have this running number scheduled
|
||||
if (pendingJobs.has(data.runningNumber)) {
|
||||
const job = pendingJobs.get(data.runningNumber) as PendingJob;
|
||||
|
||||
const duration = intervalToDuration({
|
||||
start: new Date(),
|
||||
end: job.scheduledFor,
|
||||
});
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`${
|
||||
data.runningNumber
|
||||
} is pending to be transfered already, remaining time ${formatDuration(
|
||||
duration,
|
||||
{ format: ["hours", "minutes", "seconds"] }
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${
|
||||
data.runningNumber
|
||||
} is pending to be transfered already, remaining time ${formatDuration(
|
||||
duration,
|
||||
{ format: ["hours", "minutes", "seconds"] }
|
||||
)}`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
// get the shift time
|
||||
const { data: shift, error: shiftError } = (await tryCatch(
|
||||
query(shiftChange, "shift change from material.")
|
||||
)) as any;
|
||||
|
||||
if (shiftError) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
"There was an error getting the shift times will use fallback times"
|
||||
);
|
||||
}
|
||||
|
||||
// shift split
|
||||
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
|
||||
//console.log(shiftTimeSplit);
|
||||
// Current time
|
||||
const now = new Date();
|
||||
|
||||
// Target time: today at 06:35
|
||||
const target = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
1, //now.getDate(),
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) + 3 : 3,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
// to early time
|
||||
const early = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
1, //now.getDate(),
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) : 0,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
// next month just to be here
|
||||
const nextMonth = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth() + 1,
|
||||
1, //now.getDate(),
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
|
||||
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) : 0,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
// console.log(early, target);
|
||||
// if we are to early return early only if we are sending over eom
|
||||
if (data.type === "eom" && (early > now || target < now)) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`Eom transfers is not allowed right now please try again at ${format(
|
||||
nextMonth,
|
||||
"M/d/yyyy hh:mm"
|
||||
)} `
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Eom transfers is not allowed right now please try again at ${format(
|
||||
nextMonth,
|
||||
"M/d/yyyy hh:mm"
|
||||
)} `,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
let timeoutTrans: number = data.type === "lot" ? 30 : 10;
|
||||
// get the barcode, and layoutID from the running number
|
||||
const { data: label, error: labelError } = (await tryCatch(
|
||||
query(
|
||||
labelInfo.replace("[runningNr]", `${data.runningNumber}`),
|
||||
"Get label info"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (labelError) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
"There was an error getting the label info"
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the label info",
|
||||
data: labelError,
|
||||
};
|
||||
}
|
||||
|
||||
if (label.data.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`${data.runningNumber}: dose not exist or no longer in stock.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${data.runningNumber}: dose not exist or no longer in stock.`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
//console.log(label);
|
||||
|
||||
if (label.data[0]?.stockStatus === "onStock") {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`${data.runningNumber}: currently in stock and not consumed to a lot.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${data.runningNumber}: currently in stock and not consumed to a lot.`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
// get the pdf24 printer id
|
||||
const { data: printer, error: printerError } = (await tryCatch(
|
||||
db.select().from(printerData).where(eq(printerData.name, "PDF24"))
|
||||
)) as any;
|
||||
|
||||
if (printerError) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
"There was an error the printer info"
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error the printer info",
|
||||
data: printerError,
|
||||
};
|
||||
}
|
||||
// calculate the remaining amount bascially it will be orignal number * level sent over
|
||||
// level should be sent in a decimal .25 .5 .75 .95 the 95 will allow basically the what looks to be a full gaylord but we always want to consume something
|
||||
const newQty =
|
||||
data.amount > 0
|
||||
? data.amount
|
||||
: (data.originalAmount * data.level).toFixed(0);
|
||||
|
||||
//console.log(data.amount);
|
||||
|
||||
// reprint the label and send it to pdf24
|
||||
const reprintData = {
|
||||
clientId: 999,
|
||||
runningNo: label?.data[0].runnungNumber,
|
||||
printerId: printer[0].humanReadableId,
|
||||
layoutId: label?.data[0].labelLayout,
|
||||
noOfCopies: 0,
|
||||
quantity: newQty,
|
||||
} as any;
|
||||
|
||||
//console.log(reprintData);
|
||||
|
||||
const { data: reprint, error: reprintError } = (await tryCatch(
|
||||
runProdApi({
|
||||
endpoint: "/public/v1.0/ProductionLabelling/ReprintLabel",
|
||||
data: [reprintData],
|
||||
})
|
||||
)) as any;
|
||||
|
||||
if (!reprint.success) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`RN:${data.runningNumber}, Reprinting Error: ${reprint.data.data.message}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `RN:${data.runningNumber}, Reprinting Error: ${reprint.data.data.message}`,
|
||||
data: reprint,
|
||||
};
|
||||
}
|
||||
// return the label back to fm1 lane id 10001
|
||||
|
||||
const matReturnData = {
|
||||
barcode: label?.data[0].Barcode,
|
||||
laneId: 10001,
|
||||
};
|
||||
|
||||
//console.log(matReturnData);
|
||||
const { data: matReturn, error: matReturError } = (await tryCatch(
|
||||
runProdApi({
|
||||
endpoint:
|
||||
"/public/v1.0/IssueMaterial/ReturnPartiallyConsumedManualMaterial",
|
||||
data: [matReturnData],
|
||||
})
|
||||
)) as any;
|
||||
|
||||
if (!matReturn.success) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`RN:${data.runningNumber}, Return Error ${matReturn.data.data.errors[0].message}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `RN:${data.runningNumber}, Return Error ${matReturn.data.data.errors[0].message}`,
|
||||
data: matReturn,
|
||||
};
|
||||
}
|
||||
// consume to the lot provided.
|
||||
const consumeLot = {
|
||||
productionLot: data.lotNumber,
|
||||
barcode: label?.data[0].Barcode,
|
||||
};
|
||||
|
||||
const delay =
|
||||
data.type === "lot"
|
||||
? timeoutTrans * 1000
|
||||
: target.getTime() - now.getTime();
|
||||
|
||||
const transfer = await transferMaterial(delay, data, consumeLot, newQty);
|
||||
|
||||
if (!transfer.success) {
|
||||
return {
|
||||
success: transfer.success,
|
||||
message: transfer.message,
|
||||
data: transfer.data,
|
||||
};
|
||||
}
|
||||
|
||||
const duration = intervalToDuration({ start: now, end: target });
|
||||
const pretty = formatDuration(duration, {
|
||||
format: ["hours", "minutes", "seconds"],
|
||||
});
|
||||
|
||||
if (data.type === "eom") {
|
||||
return {
|
||||
success: true,
|
||||
message: `RN:${data.runningNumber}: qty: ${newQty}, will be transfered to lot: ${data.lotNumber}, in ${pretty} `,
|
||||
data: [],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: `RN:${data.runningNumber}: qty: ${newQty}, was transfered to lot: ${data.lotNumber}`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const transferMaterial = async (
|
||||
delay: number,
|
||||
data: any,
|
||||
consumeLot: any,
|
||||
newQty: any
|
||||
) => {
|
||||
//console.log(data);
|
||||
if (pendingJobs.has(data.runningNumber)) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`${data.runningNumber} is pending to be transfered already`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `${data.runningNumber} is pending to be transfered already`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const scheduledFor = new Date(Date.now() + delay);
|
||||
// sets the time out based on the type of transfer sent over.
|
||||
const timeoutId = setTimeout(async () => {
|
||||
try {
|
||||
const { data: matConsume, error: matConsumeError } =
|
||||
(await tryCatch(
|
||||
runProdApi({
|
||||
endpoint:
|
||||
"/public/v1.0/IssueMaterial/ConsumeNonPreparedManualMaterial",
|
||||
data: [consumeLot],
|
||||
})
|
||||
)) as any;
|
||||
|
||||
if (!matConsume?.success) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`RN:${data.runningNumber}, Consume Error ${
|
||||
matConsume?.data?.data?.errors?.[0]?.message ??
|
||||
"Unknown"
|
||||
}`
|
||||
);
|
||||
return; // still hits finally
|
||||
}
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"materials",
|
||||
"ocp",
|
||||
`RN:${data.runningNumber}: qty: ${newQty}, was transferred to lot:${data.lotNumber}`
|
||||
);
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"materials",
|
||||
"ocp",
|
||||
`RN:${data.runningNumber}, ${err}`
|
||||
);
|
||||
} finally {
|
||||
// Always clear the pending entry, even if error
|
||||
pendingJobs.delete(data.runningNumber);
|
||||
}
|
||||
}, delay);
|
||||
|
||||
pendingJobs.set(data.runningNumber, {
|
||||
timeoutId,
|
||||
runningNumber: data.runningNumber,
|
||||
data,
|
||||
consumeLot,
|
||||
newQty,
|
||||
scheduledFor,
|
||||
});
|
||||
|
||||
// Immediately say we scheduled it
|
||||
return {
|
||||
success: true,
|
||||
message: `Transfer for ${data.runningNumber} scheduled`,
|
||||
data: [],
|
||||
};
|
||||
};
|
||||
|
||||
// setInterval(() => {
|
||||
// console.log(pendingJobs);
|
||||
// }, 5000);
|
||||
|
||||
// setTimeout(async () => {
|
||||
// lotMaterialTransfer({
|
||||
// runnungNumber: 603468,
|
||||
// lotNumber: 24897,
|
||||
// originalAmount: 380,
|
||||
// level: 0.95,
|
||||
// });
|
||||
// }, 5000);
|
||||
235
lstV2/server/services/ocp/controller/materials/mainMaterial.ts
Normal file
235
lstV2/server/services/ocp/controller/materials/mainMaterial.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { serverSettings } from "../../../server/controller/settings/getSettings.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { machineCheck } from "../../../sqlServer/querys/ocp/machineId.js";
|
||||
import { mmQuery } from "../../../sqlServer/querys/ocp/mainMaterial.js";
|
||||
|
||||
export const isMainMatStaged = async (lot: any) => {
|
||||
const set = serverSettings.length === 0 ? [] : serverSettings;
|
||||
// make staged false by deefault and error logged if theres an issue
|
||||
let isStaged = { message: "Material is staged", success: true };
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
machineCheck.replace("where Active = 1 and [Location] = [loc]", ""),
|
||||
"check machine needs mm"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
const machine = data.data.filter(
|
||||
(m: any) => m.HumanReadableId === lot.machineID
|
||||
);
|
||||
// we have a check on ksc side to ignore the tetra machine for now as its not updating in 2.0
|
||||
if (!machine[0].StagingMainMaterialMandatory) {
|
||||
createLog(
|
||||
"info",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
`The machine dose not require mm to print and book in.`
|
||||
);
|
||||
return {
|
||||
message: "Machine dose not require material to be staged",
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
// strangly the lot is not always sending over in slc so adding this in for now to see what line is cauing this issue
|
||||
if (!lot) {
|
||||
return isStaged;
|
||||
}
|
||||
|
||||
if (typeof lot !== "object" || lot === null || Array.isArray(lot)) {
|
||||
createLog(
|
||||
"info",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
`The lot sent over is not an object: ${JSON.stringify(lot)}`
|
||||
);
|
||||
return isStaged;
|
||||
}
|
||||
|
||||
const updateQuery = mmQuery.replaceAll("[lotNumber]", lot.lot);
|
||||
|
||||
try {
|
||||
const r: any = await query(updateQuery, "Main Material Check");
|
||||
|
||||
const res: any = r.data;
|
||||
|
||||
// if (res[0].Staged >= 1) {
|
||||
// isStaged = true;
|
||||
// }
|
||||
|
||||
type CheckConditionArgs = {
|
||||
results: any[];
|
||||
filterFn: (n: any) => boolean;
|
||||
failCondition: (n: any) => boolean;
|
||||
failMessage: string;
|
||||
successMessage: string;
|
||||
lot: { lot: string | number };
|
||||
};
|
||||
|
||||
const checkCondition = ({
|
||||
results,
|
||||
filterFn,
|
||||
failCondition,
|
||||
failMessage,
|
||||
successMessage,
|
||||
lot,
|
||||
}: CheckConditionArgs): { message: string; success: boolean } => {
|
||||
const subset = results.filter(filterFn);
|
||||
|
||||
if (subset.some(failCondition)) {
|
||||
const failing = subset.filter(failCondition);
|
||||
return {
|
||||
message: `lot: ${lot.lot}, is missing: ${failing
|
||||
.map(
|
||||
(o: any) =>
|
||||
`${o.MaterialHumanReadableId} - ${o.MaterialDescription}`
|
||||
)
|
||||
.join(",\n ")} ${failMessage}`,
|
||||
success: false,
|
||||
};
|
||||
} else {
|
||||
return { message: successMessage, success: true };
|
||||
}
|
||||
};
|
||||
|
||||
createLog("info", "mainMaterial", "ocp", `Maint material query ran.`);
|
||||
|
||||
const mainMaterial = res.find((n: any) => n.IsMainMaterial);
|
||||
if (mainMaterial?.noMMShortage === "noMM") {
|
||||
return {
|
||||
message: `Main material: ${mainMaterial.MaterialHumanReadableId} - ${mainMaterial.MaterialDescription}: is not staged for ${lot.lot}`,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
// we need to filter the color stuff and then look for includes instead of a standard name. this way we can capture a everything and not a single type
|
||||
// for manual consume color if active to check colors
|
||||
const checkColorSetting = set.filter((n) => n.name === "checkColor");
|
||||
|
||||
// 2. Auto color
|
||||
if (checkColorSetting[0].value === "1") {
|
||||
// auto check
|
||||
// 2. Auto color
|
||||
const autoColor = checkCondition({
|
||||
results: res,
|
||||
lot,
|
||||
filterFn: (n) =>
|
||||
n.isManual &&
|
||||
!("noPKGAutoShortage" in n) &&
|
||||
!("noPKGManualShortage" in n), // pool = non-main, auto
|
||||
failCondition: (n) => n.autoConsumeCheck === "autoConsumeNOK", // column = autoConsumeCheck
|
||||
failMessage: "for autoconsume",
|
||||
successMessage: "auto color is good",
|
||||
});
|
||||
if (!autoColor.success) return autoColor;
|
||||
console.log(autoColor);
|
||||
|
||||
// 3. Manual color
|
||||
const manualColor = checkCondition({
|
||||
results: res,
|
||||
lot,
|
||||
filterFn: (n) =>
|
||||
!n.IsMainMaterial &&
|
||||
n.isManual &&
|
||||
!("noPKGAutoShortage" in n) &&
|
||||
!("noPKGManualShortage" in n), // pool = non-main, manual
|
||||
failCondition: (n) => n.noManualShortage === "noOK", // column = noManualShortage
|
||||
failMessage: "for manual material",
|
||||
successMessage: "manual color is good",
|
||||
});
|
||||
if (!manualColor.success) return manualColor;
|
||||
console.log(manualColor);
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
"Color check is not active."
|
||||
);
|
||||
}
|
||||
|
||||
// // if we want to check the packaging
|
||||
const checkPKGSetting = set.filter((n) => n.name === "checkPKG");
|
||||
if (checkPKGSetting[0].value === "1") {
|
||||
const pkgAuto = checkCondition({
|
||||
results: res,
|
||||
lot,
|
||||
filterFn: (n) =>
|
||||
!n.IsMainMaterial &&
|
||||
!n.isManual &&
|
||||
"noPKGAutoShortage" in n,
|
||||
failCondition: (n) => n.noPKGAutoShortage === "noAutoPkg",
|
||||
failMessage: "for pkg",
|
||||
successMessage: "auto PKG is good",
|
||||
});
|
||||
if (!pkgAuto.success) return pkgAuto;
|
||||
console.log(pkgAuto);
|
||||
|
||||
// 5. Packaging manual
|
||||
const pkgManual = checkCondition({
|
||||
results: res,
|
||||
lot,
|
||||
filterFn: (n) =>
|
||||
!n.IsMainMaterial &&
|
||||
n.isManual &&
|
||||
"noPKGManualShortage" in n,
|
||||
failCondition: (n) => n.noPKGManualShortage === "noManPkg",
|
||||
failMessage: "for pkg",
|
||||
successMessage: "manual PKG is good",
|
||||
});
|
||||
|
||||
if (!pkgManual.success) return pkgManual;
|
||||
console.log(pkgManual);
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
"PKG check is not active."
|
||||
);
|
||||
}
|
||||
|
||||
// manual pkg
|
||||
if (checkPKGSetting[0].value === "1") {
|
||||
const packagingCheck = res.filter(
|
||||
(n: any) =>
|
||||
!n.IsMainMaterial &&
|
||||
n.isManual &&
|
||||
"noPKGManualShortage" in n
|
||||
);
|
||||
if (
|
||||
packagingCheck.some(
|
||||
(n: any) => n.noPKGManualShortage === "noManPkg"
|
||||
)
|
||||
) {
|
||||
return (isStaged = {
|
||||
message: `lot: ${lot.lot}, is missing: ${packagingCheck
|
||||
.map(
|
||||
(o: any) =>
|
||||
`${o.MaterialHumanReadableId} - ${o.MaterialDescription}`
|
||||
)
|
||||
.join(",\n ")} for pkg`,
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
"PKG check is not active."
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"mainMaterial",
|
||||
"ocp",
|
||||
`Error from running the Main Material query: ${err}`
|
||||
);
|
||||
}
|
||||
return isStaged;
|
||||
};
|
||||
19
lstV2/server/services/ocp/controller/printers/getPrinters.ts
Normal file
19
lstV2/server/services/ocp/controller/printers/getPrinters.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { printerData } from "../../../../../database/schema/printers.js";
|
||||
|
||||
export const getPrinters = async () => {
|
||||
const { data: printers, error: printerError } = await tryCatch(
|
||||
db.select().from(printerData).orderBy(printerData.name)
|
||||
);
|
||||
|
||||
if (printerError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "there was an error getting the printers",
|
||||
data: printerError,
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true, message: "Printers", data: printers };
|
||||
};
|
||||
113
lstV2/server/services/ocp/controller/printers/printerCycle.ts
Normal file
113
lstV2/server/services/ocp/controller/printers/printerCycle.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { getPrinters } from "./getPrinters.js";
|
||||
import { autoLabelingStats, printerStatus } from "./printerStatus.js";
|
||||
|
||||
let isPrinterCycling = false;
|
||||
let actualPrinterCycle: any;
|
||||
|
||||
export const printerCycle = async () => {
|
||||
/**
|
||||
* We will cycle through the printers to check there states.
|
||||
*/
|
||||
|
||||
if (isPrinterCycling)
|
||||
return {
|
||||
success: false,
|
||||
message: "Printers are already being cycled.",
|
||||
};
|
||||
|
||||
createLog("info", "ocp", "ocp", "Printer cycle has started.");
|
||||
// get the printers
|
||||
const { data: s, error: se } = await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "ocpCycleDelay"))
|
||||
);
|
||||
if (se) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"There was an error getting the ocpCycleDelay."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting printers.",
|
||||
};
|
||||
}
|
||||
|
||||
const ocpDelay: any = s;
|
||||
isPrinterCycling = true;
|
||||
// start the actual printer cycle
|
||||
actualPrinterCycle = setInterval(async () => {
|
||||
const { data, error } = await tryCatch(getPrinters());
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"There was an error getting the printers."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting printers.",
|
||||
};
|
||||
}
|
||||
let printers: any = data.data;
|
||||
|
||||
// only keep the assigned ones
|
||||
printers = printers.filter((p: any) => p.assigned === true);
|
||||
|
||||
// for printers we want to ignore there must be a remark stateing to ignore.
|
||||
printers = printers.filter((p: any) => !p.remark.includes("ignore"));
|
||||
|
||||
printers.forEach(async (p: any) => {
|
||||
/**
|
||||
* if the last timeprinted would be greater than x well just change the status to idle and extended based on the 2 times.
|
||||
*
|
||||
* to get a printer going again label will need to come from the front end as that will just unpause the printer and start the labeling, or the api for manual print
|
||||
* well need to adjust this to actually print the label then unpause it.
|
||||
*
|
||||
* it will be
|
||||
*
|
||||
* less than x since time printed run the printer status
|
||||
* greater than x but less than y change the status to idle, but ping to make sure its online,
|
||||
* if greater than y change to extended idle but stil also ping to make sure its online.
|
||||
*/
|
||||
|
||||
// ignore pdf printer as we want it here for testing purposes
|
||||
if (p.name.toLowerCase() === "pdf24") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.name === "Autolabeler") {
|
||||
//await autoLabelingStats(p);
|
||||
return;
|
||||
}
|
||||
|
||||
// for all other printers
|
||||
await printerStatus(p);
|
||||
});
|
||||
}, parseInt(ocpDelay[0]?.value) * 1000);
|
||||
|
||||
return { success: true, message: "Printer cycle has been started." };
|
||||
};
|
||||
|
||||
export const stopPrinterCycle = async () => {
|
||||
/**
|
||||
* We will stop the print cylce this is more an emergancy thing.
|
||||
*/
|
||||
if (actualPrinterCycle && !actualPrinterCycle._destroyed) {
|
||||
createLog("info", "ocp", "ocp", "Printer cycle is being stopped.");
|
||||
clearInterval(actualPrinterCycle);
|
||||
isPrinterCycling = false;
|
||||
return { success: true, message: "Printer cycle has been stopped." };
|
||||
} else {
|
||||
createLog("info", "ocp", "ocp", "Printer cycle is already stopped.");
|
||||
isPrinterCycling = false;
|
||||
return { success: true, message: "Printer cycle is already Stopped." };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { getPrinters } from "./getPrinters.js";
|
||||
import { autoLabelingStats } from "./printerStatus.js";
|
||||
|
||||
let isPrinterCycling = false;
|
||||
let actualPrinterCycle: any;
|
||||
|
||||
export const printerCycleAutoLabelers = async () => {
|
||||
/**
|
||||
* Will only check the auto labelers for status updates.
|
||||
*/
|
||||
|
||||
if (isPrinterCycling)
|
||||
return {
|
||||
success: false,
|
||||
message: "Printers are already being cycled.",
|
||||
};
|
||||
|
||||
createLog("info", "ocp", "ocp", "AutoLabeling cycle has started.");
|
||||
// get the printers
|
||||
const { data: s, error: se } = await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "ocpCycleDelay"))
|
||||
);
|
||||
if (se) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"There was an error getting the ocpCycleDelay."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting printers.",
|
||||
};
|
||||
}
|
||||
|
||||
const ocpDelay: any = s;
|
||||
isPrinterCycling = true;
|
||||
// start the actual printer cycle
|
||||
actualPrinterCycle = setInterval(async () => {
|
||||
const { data, error } = await tryCatch(getPrinters());
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"There was an error getting the printers."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting printers.",
|
||||
};
|
||||
}
|
||||
let printers: any = data.data;
|
||||
|
||||
// only keep the assigned ones
|
||||
printers = printers.filter((p: any) => p.assigned === true);
|
||||
|
||||
// for printers we want to ignore there must be a remark stateing to ignore.
|
||||
printers = printers.filter((p: any) => !p.remark.includes("ignore"));
|
||||
|
||||
printers.forEach(async (p: any) => {
|
||||
/**
|
||||
* if the last timeprinted would be greater than x well just change the status to idle and extended based on the 2 times.
|
||||
*
|
||||
* to get a printer going again label will need to come from the front end as that will just unpause the printer and start the labeling, or the api for manual print
|
||||
* well need to adjust this to actually print the label then unpause it.
|
||||
*
|
||||
* it will be
|
||||
*
|
||||
* less than x since time printed run the printer status
|
||||
* greater than x but less than y change the status to idle, but ping to make sure its online,
|
||||
* if greater than y change to extended idle but stil also ping to make sure its online.
|
||||
*/
|
||||
|
||||
// ignore pdf printer as we want it here for testing purposes
|
||||
if (p.name.toLowerCase() === "pdf24") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.name === "Autolabeler") {
|
||||
await autoLabelingStats(p);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}, parseInt(ocpDelay[0]?.value) * 2 * 1000);
|
||||
|
||||
return { success: true, message: "AutoLabeling cycle has been started." };
|
||||
};
|
||||
|
||||
export const stopPrinterCycle = async () => {
|
||||
/**
|
||||
* We will stop the print cylce this is more an emergancy thing.
|
||||
*/
|
||||
if (actualPrinterCycle && !actualPrinterCycle._destroyed) {
|
||||
createLog("info", "ocp", "ocp", "AutoLabeling cycle is being stopped.");
|
||||
clearInterval(actualPrinterCycle);
|
||||
isPrinterCycling = false;
|
||||
return {
|
||||
success: true,
|
||||
message: "AutoLabeling cycle has been stopped.",
|
||||
};
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"AutoLabeling cycle is already stopped."
|
||||
);
|
||||
isPrinterCycling = false;
|
||||
return {
|
||||
success: true,
|
||||
message: "AutoLabeling cycle is already Stopped.",
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { printerData } from "../../../../../database/schema/printers.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const printStatus = [
|
||||
{ code: 1, text: "Printing" },
|
||||
{ code: 2, text: "Pending labels" },
|
||||
{ code: 3, text: "Printing to fast" },
|
||||
{ code: 4, text: "Creating label" },
|
||||
{ code: 5, text: "Waiting" },
|
||||
{ code: 6, text: "Printer Paused" },
|
||||
{ code: 7, text: "Printer error" },
|
||||
{
|
||||
code: 8,
|
||||
text: "First time printing",
|
||||
},
|
||||
{
|
||||
code: 9,
|
||||
text: "Offline",
|
||||
},
|
||||
];
|
||||
|
||||
export const printerUpdate = async (printer: any, status: any) => {
|
||||
/**
|
||||
* Updates the status of the printer.
|
||||
*/
|
||||
|
||||
// get current time
|
||||
let pd = {};
|
||||
const currentTime = sql`NOW()`;
|
||||
|
||||
if (status === 3) {
|
||||
pd = {
|
||||
status: status,
|
||||
statusText: printStatus.filter((c) => c.code === status)[0]?.text,
|
||||
};
|
||||
} else if (status === 6) {
|
||||
pd = {
|
||||
status: status,
|
||||
statusText: printStatus.filter((c) => c.code === status)[0]?.text,
|
||||
};
|
||||
} else {
|
||||
pd = {
|
||||
lastTimePrinted: currentTime,
|
||||
status: status,
|
||||
statusText: printStatus.filter((c) => c.code === status)[0]?.text,
|
||||
};
|
||||
}
|
||||
|
||||
if (printer.humanReadableId) {
|
||||
try {
|
||||
await db
|
||||
.update(printerData)
|
||||
.set(pd)
|
||||
.where(
|
||||
eq(printerData.humanReadableId, printer.humanReadableId)
|
||||
);
|
||||
} catch (error) {
|
||||
createLog("error", "ocp", "ocp", `Error updating printer state`);
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(printerUpdate.name);
|
||||
return;
|
||||
};
|
||||
285
lstV2/server/services/ocp/controller/printers/printerStatus.ts
Normal file
285
lstV2/server/services/ocp/controller/printers/printerStatus.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
import net from "net";
|
||||
import { pausePrinter } from "../../utils/pausePrinter.js";
|
||||
import { addHours, differenceInSeconds } from "date-fns";
|
||||
import { printerUpdate } from "./printerStatUpdate.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { unPausePrinter } from "../../utils/unpausePrinter.js";
|
||||
|
||||
import { labelingProcess } from "../labeling/labelProcess.js";
|
||||
import { timeZoneFix } from "../../../../globalUtils/timeZoneFix.js";
|
||||
import { autoLabelCreated } from "../labeling/labelRatio.js";
|
||||
|
||||
let logLevel: string = process.env.LOG_LEVEL || "info";
|
||||
let errorCheck = false;
|
||||
export const printerStatus = async (p: any) => {
|
||||
/**
|
||||
* Checks each printer to see what the current state is
|
||||
*/
|
||||
createLog("debug", "ocp", "ocp", `Printer cycling`);
|
||||
|
||||
const printer = new net.Socket();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// connect to the printer, and check its status
|
||||
printer.connect(p.port, p.ipAddress, async () => {
|
||||
// write the message to the printer below gives us a feedback of the printer
|
||||
printer.write("~HS");
|
||||
});
|
||||
|
||||
// read the data from the printer
|
||||
printer.on("data", async (data) => {
|
||||
const res = data.toString();
|
||||
|
||||
// turn the data into an array to make it more easy to deal with
|
||||
const tmp = res.split(",");
|
||||
|
||||
//--------------- time stuff-----------------------------------------------------------------
|
||||
// get last time printed
|
||||
const lastTime = new Date(p.lastTimePrinted).toISOString();
|
||||
// console.log(lastTime);
|
||||
|
||||
// current time?
|
||||
|
||||
/**
|
||||
*
|
||||
* add the time zone to the settings db
|
||||
*/
|
||||
// const currentTime = addHours(
|
||||
// new Date(Date.now()),
|
||||
// -6
|
||||
// ).toISOString();
|
||||
|
||||
const currentTime = timeZoneFix();
|
||||
|
||||
let timeBetween = 0;
|
||||
// if this is our first time printing pause the printer to start the timer, else we just update the time between timer
|
||||
if (lastTime === undefined) {
|
||||
printer.end();
|
||||
printerUpdate(p, 8);
|
||||
pausePrinter(p);
|
||||
resolve({ success: true, message: "First Time printing" });
|
||||
} else {
|
||||
timeBetween = differenceInSeconds(currentTime, lastTime);
|
||||
}
|
||||
|
||||
// --- end time ---
|
||||
|
||||
// --- printer logic ---
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name}: timeBetween: ${timeBetween}, delay ${parseInt(
|
||||
p.printDelay
|
||||
)}, ${currentTime}... ${lastTime}`
|
||||
);
|
||||
|
||||
if (tmp[2] === "0" && tmp[4] !== "000") {
|
||||
// unpaused and printing labels - reset timer
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Unpaused and printing labels, time remaing ${differenceInSeconds(
|
||||
parseInt(p.printDelay),
|
||||
timeBetween
|
||||
)}`
|
||||
);
|
||||
|
||||
// update last time printed in the array
|
||||
printerUpdate(p, 1);
|
||||
} else if (tmp[2] === "1" && tmp[4] !== "000") {
|
||||
// was paused or label sent from somewhere else
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${
|
||||
p.name
|
||||
} paused to soon, unpausing, remaining time: ${differenceInSeconds(
|
||||
parseInt(p.printDelay),
|
||||
timeBetween
|
||||
)}`
|
||||
);
|
||||
|
||||
// reset the timer for this printer as well other labels shouldnt be sent but if we send them ok
|
||||
printerUpdate(p, 2);
|
||||
|
||||
unPausePrinter(p);
|
||||
} else if (tmp[2] === "0" && timeBetween < parseInt(p.printDelay)) {
|
||||
// was unpaused to soon so repause it
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name} Unpaused before the time allowed, time left ${
|
||||
differenceInSeconds(parseInt(p.printDelay), timeBetween) //seconds
|
||||
}`
|
||||
);
|
||||
|
||||
printerUpdate(p, 3);
|
||||
pausePrinter(p);
|
||||
} else if (tmp[2] === "0" && timeBetween > parseInt(p.printDelay)) {
|
||||
// its been long enough we can print a label
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name} Allowed time passed and printing new label`
|
||||
);
|
||||
|
||||
// update last time printed in the array
|
||||
printerUpdate(p, 4);
|
||||
|
||||
// sending over for labeling.
|
||||
labelingProcess({ printer: p });
|
||||
autoLabelCreated();
|
||||
} else if (tmp[2] === "0") {
|
||||
// printer was unpaused for the first time or made it here
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name} Frist time printing`
|
||||
);
|
||||
|
||||
// add the time and printer
|
||||
printerUpdate(p, 4);
|
||||
|
||||
// sending over for labeling.
|
||||
labelingProcess({ printer: p });
|
||||
autoLabelCreated();
|
||||
} else if (tmp[2] === "1") {
|
||||
// printer is paused and waiting
|
||||
createLog(
|
||||
"debug",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name} paused and waiting`
|
||||
);
|
||||
|
||||
printerUpdate(p, 6);
|
||||
}
|
||||
|
||||
printer.end();
|
||||
|
||||
resolve({ success: true, message: "Print cycle completed." });
|
||||
});
|
||||
|
||||
// as a safety destory it if its still there
|
||||
printer.on("end", () => {
|
||||
setTimeout(() => {
|
||||
if (!printer.destroyed) {
|
||||
createLog(
|
||||
"info",
|
||||
"printerState",
|
||||
"ocp",
|
||||
`${p.name}: was force closed, during normal cycle counting`
|
||||
);
|
||||
printer.destroy();
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
printer.on("error", async (error: any) => {
|
||||
// just going to say theres an error with the printer
|
||||
//console.log(error.code);
|
||||
if (error.code.includes("ETIMEDOUT") && !errorCheck) {
|
||||
createLog("error", "ocp", "ocp", `${p.name} is offline`);
|
||||
|
||||
await printerUpdate(p, 9);
|
||||
errorCheck = true;
|
||||
printer.end();
|
||||
|
||||
resolve({
|
||||
success: false,
|
||||
message: "The printer is offline.",
|
||||
});
|
||||
}
|
||||
if (!error.code.includes("ETIMEDOUT") && !errorCheck) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name} encountered an error: ${error}`
|
||||
);
|
||||
|
||||
await printerUpdate(p, 7);
|
||||
errorCheck = true;
|
||||
|
||||
// send log data
|
||||
// fake line
|
||||
printer.end();
|
||||
resolve({
|
||||
success: false,
|
||||
message: "There was an error with the printer.",
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const autoLabelingStats = async (p: any) => {
|
||||
/**
|
||||
* Checks autolabeling printers just to see what they are doing.
|
||||
*/
|
||||
createLog("debug", "ocp", "ocp", `Printer cycling`);
|
||||
|
||||
const printer = new net.Socket();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// connect to the printer, and check its status
|
||||
printer.connect(p.port, p.ipAddress, async () => {
|
||||
// write the message to the printer below gives us a feedback of the printer
|
||||
printer.write("~HS");
|
||||
});
|
||||
|
||||
// read the data from the printer
|
||||
printer.on("data", async (data) => {
|
||||
const res = data.toString();
|
||||
|
||||
// turn the data into an array to make it more easy to deal with
|
||||
const tmp = res.split(",");
|
||||
|
||||
if (tmp[4] !== "000") {
|
||||
// unpaused and printing labels - reset timer
|
||||
createLog("debug", "ocp", "ocp", `Printing Labels`);
|
||||
|
||||
// update last time printed in the array
|
||||
printerUpdate(p, 1);
|
||||
}
|
||||
|
||||
if (tmp[4] === "000") {
|
||||
// unpaused and printing labels - reset timer
|
||||
createLog("debug", "ocp", "ocp", `Printing Labels`);
|
||||
|
||||
// update last time printed in the array
|
||||
printerUpdate(p, 5);
|
||||
}
|
||||
});
|
||||
|
||||
printer.on("error", async (error) => {
|
||||
// just going to say theres an error with the printer
|
||||
console.log(error);
|
||||
if (!errorCheck) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`${p.name}, encountered an error: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
await printerUpdate(p, 7);
|
||||
errorCheck = true;
|
||||
|
||||
// send log data
|
||||
// fake line
|
||||
printer.end();
|
||||
resolve({
|
||||
success: false,
|
||||
message: "There was an error with the printer.",
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
import axios from "axios";
|
||||
import { printerData } from "../../../../../database/schema/printers.js";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { lstAuth } from "../../../../index.js";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { assignedPrinters } from "../../utils/checkAssignments.js";
|
||||
|
||||
export const updatePrinters = async () => {
|
||||
const currentTime = new Date(Date.now());
|
||||
|
||||
// get the printers from prod
|
||||
let url = await prodEndpointCreation(
|
||||
"/public/v1.0/Administration/Printers"
|
||||
);
|
||||
|
||||
const { data: prodPrinters, error: prodError } = await tryCatch(
|
||||
axios.get(url, {
|
||||
headers: {
|
||||
Authorization: `Basic ${lstAuth}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (prodError || prodPrinters?.data.length > 10000) {
|
||||
//console.log(prodError);
|
||||
return {
|
||||
success: false,
|
||||
message: "there was an error getting the printers.",
|
||||
data: prodError,
|
||||
};
|
||||
}
|
||||
|
||||
// do the printer update into our db
|
||||
const prodPrinterInfo = prodPrinters?.data;
|
||||
|
||||
for (let i = 0; i < prodPrinterInfo.length; i++) {
|
||||
const printerStuff: any = {
|
||||
humanReadableId: prodPrinterInfo[i].humanReadableId,
|
||||
name: prodPrinterInfo[i].name,
|
||||
ipAddress: prodPrinterInfo[i].ipAddress,
|
||||
port: prodPrinterInfo[i].port,
|
||||
remark: prodPrinterInfo[i].remark,
|
||||
processes: prodPrinterInfo[i].processes,
|
||||
};
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.insert(printerData)
|
||||
.values(printerStuff)
|
||||
.onConflictDoUpdate({
|
||||
target: printerData.humanReadableId,
|
||||
set: {
|
||||
//humanReadableId: prodPrinterInfo[i].humanReadableId,
|
||||
name: prodPrinterInfo[i].name,
|
||||
ipAddress: prodPrinterInfo[i].ipAddress,
|
||||
port: prodPrinterInfo[i].port,
|
||||
remark: prodPrinterInfo[i].remark,
|
||||
processes: prodPrinterInfo[i].processes,
|
||||
upd_date: sql`NOW()`,
|
||||
//printDelay: "90", // need to remove in a couple weeks
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"ocp",
|
||||
`${
|
||||
prodPrinterInfo[i].name
|
||||
} encoutered and error adding/updating ${JSON.stringify(error)}`
|
||||
);
|
||||
}
|
||||
createLog(
|
||||
"debug",
|
||||
"lst",
|
||||
"ocp",
|
||||
`${prodPrinterInfo[i].name} were just added/updated.`
|
||||
);
|
||||
}
|
||||
|
||||
await assignedPrinters();
|
||||
|
||||
return { success: true, message: "Printers were just added or updated." };
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
|
||||
const st = 5;
|
||||
const sm = 55;
|
||||
const et = 6;
|
||||
const em = 5;
|
||||
|
||||
export const billingCheck = async () => {
|
||||
const now = new Date();
|
||||
const startTime = new Date();
|
||||
const endTime = new Date();
|
||||
|
||||
let isOutsideBilling = false;
|
||||
const { data, error } = await tryCatch(db.select().from(settings));
|
||||
if (error) {
|
||||
return { success: false, message: "Error in getting settings." };
|
||||
}
|
||||
const plant = data.filter((n) => n.name === "plantToken");
|
||||
// set everything up
|
||||
startTime.setHours(st, sm, 0, 0);
|
||||
endTime.setHours(et, em, 0, 0);
|
||||
|
||||
if (plant[0].value === "usflo1") {
|
||||
if (now >= startTime && now <= endTime) {
|
||||
createLog(
|
||||
"warn",
|
||||
"specialProcess",
|
||||
"ocp",
|
||||
`You are inside the billing window no labels will print! please wait`
|
||||
);
|
||||
isOutsideBilling = true;
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"specialProcess",
|
||||
"ocp",
|
||||
"Out side billing window ok to print"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
createLog(
|
||||
"debug",
|
||||
"specialProcess",
|
||||
"ocp",
|
||||
"This plant dose not check for billing"
|
||||
);
|
||||
}
|
||||
|
||||
return isOutsideBilling;
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
import { query } from "../../../../sqlServer/prodSqlServer.js";
|
||||
|
||||
export const dualPrintingProcess = async (lotInfo: any) => {
|
||||
// currently we will only allow for 2 printers to be linked only crazy people want more than 2
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"Dual printing running logic here to see what to print"
|
||||
);
|
||||
// each check
|
||||
let lastPrinted;
|
||||
let firstLine;
|
||||
let secondLine;
|
||||
|
||||
// this query checks the last line to print a label
|
||||
const printedQuery = `SELECT TOP(1) IdProdPlanung,
|
||||
IdArtikelvarianten,
|
||||
IdMaschine,
|
||||
LfdNr,
|
||||
ArtikelVariantenBez,
|
||||
Add_User,
|
||||
Add_Date,
|
||||
Upd_User,
|
||||
Upd_Date,
|
||||
IdProdBereich
|
||||
FROM AlplaPROD_test1.dbo.T_EtikettenGedruckt (nolock)
|
||||
where IdMaschine in (${lotInfo[0].machineID},${lotInfo[1].machineID}) order by Add_Date desc`;
|
||||
|
||||
const fisrtLineDT = `SELECT top(1) IdMaschine,
|
||||
case when Endzeit ='1900-01-01 00:00:00.000' and DATEDIFF(MINUTE, Startzeit, GETDATE()) >= 3 Then 1 else 0 end as 'LineCheck'
|
||||
FROM AlplaPROD_test1.dbo.T_HistoryStillstandsereignis (nolock) where IdMaschine = ${lotInfo[0].machineID} order by Add_Date desc`;
|
||||
|
||||
const secondLineDT = `SELECT top(1) IdMaschine,
|
||||
case when Endzeit ='1900-01-01 00:00:00.000' and DATEDIFF(MINUTE, Startzeit, GETDATE()) >= 3 Then 1 else 0 end as 'LineCheck'
|
||||
FROM AlplaPROD_test1.dbo.T_HistoryStillstandsereignis (nolock) where IdMaschine = ${lotInfo[1].machineID} order by Add_Date desc`;
|
||||
|
||||
try {
|
||||
// get what was last printed
|
||||
const result: any = await query(printedQuery, "Last Printed Query");
|
||||
lastPrinted = result.data[0].IdMaschine;
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Error: getting last printed querty: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// get if if running or down
|
||||
const first: any = await query(fisrtLineDT, "First line DT Check");
|
||||
firstLine = first.data[0].LineCheck;
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Error: with getting ${lotInfo[0].machineID} data query: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const second: any = await query(secondLineDT, "Second line DT Check");
|
||||
secondLine = second.data[0].LineCheck;
|
||||
} catch (err) {
|
||||
createLog(
|
||||
"error",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Error: with getting ${lotInfo[1].machineID} data query: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
// first check if both are up and running
|
||||
if (firstLine === 0 && secondLine === 0) {
|
||||
createLog("info", "ocp", "ocp", "Both lines are up");
|
||||
// what line last printed
|
||||
if (lastPrinted === lotInfo[0].machineID) {
|
||||
// IE 7 then print 8, usslc was the main example for all of this
|
||||
let whatToPrint = lotInfo.filter(
|
||||
(m: any) => m.machineID === lotInfo[1].machineID
|
||||
);
|
||||
// send to label
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Printing label for ${whatToPrint[0].MachineDescription}`
|
||||
);
|
||||
return whatToPrint;
|
||||
}
|
||||
|
||||
if (lastPrinted === lotInfo[1].machineID) {
|
||||
// IE 8 then print 7, usslc was the main example for all of this
|
||||
let whatToPrint = lotInfo.filter(
|
||||
(m: any) => m.machineID === lotInfo[0].machineID
|
||||
);
|
||||
// send to label
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Printing label for ${whatToPrint[0].MachineDescription}`
|
||||
);
|
||||
return whatToPrint;
|
||||
}
|
||||
}
|
||||
|
||||
// as we are only allowing 2 lines if one is down the other must be up... still going to conditional it to be sure
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
"Looking at what line is down and printing the other one!"
|
||||
);
|
||||
if (firstLine === 1 && secondLine === 0) {
|
||||
// print the other line
|
||||
let whatToPrint = lotInfo.filter(
|
||||
(m: any) => m.machineID === lotInfo[1].machineID
|
||||
);
|
||||
|
||||
// send to label
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Printing label for ${whatToPrint.MachineDescription}`
|
||||
);
|
||||
return whatToPrint;
|
||||
} else if (firstLine === 0 && secondLine === 1) {
|
||||
// print the other line
|
||||
let whatToPrint = lotInfo.filter(
|
||||
(m: any) => m.machineID === lotInfo[0].machineID
|
||||
);
|
||||
// send to label
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Printing label for ${whatToPrint.MachineDescription}`
|
||||
);
|
||||
return whatToPrint;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Controller, Tag } from "st-ethernet-ip";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
import { labelerTagRead } from "./plcTags/labelerTag.js";
|
||||
import { palletSendTag } from "./plcTags/palletSendTag.js";
|
||||
import { strapperFaults } from "./plcTags/strapperFault.js";
|
||||
|
||||
let PLC = new Controller();
|
||||
let isDycoRunning = false;
|
||||
|
||||
// PLC address
|
||||
let plcAddress = "10.44.5.4";
|
||||
let isReading = false;
|
||||
// Initialize the interval variable outside the function
|
||||
let plcCycle: any;
|
||||
let plcInterval = 500;
|
||||
|
||||
// Create Tag Instances
|
||||
const labelerTag: any = new Tag("labeler.line_info");
|
||||
const palletSend = new Tag("Zone_6.Ready_to_Send");
|
||||
const strapperError = new Tag("Zone_3.Strapper_Faulted");
|
||||
|
||||
export const dycoConnect = async () => {
|
||||
// if we crash or start over reset the timers so we dont get duplicates
|
||||
clearInterval(plcCycle);
|
||||
if (isDycoRunning)
|
||||
return { success: false, message: "Dyco is already connected." };
|
||||
|
||||
// Remove all listeners before adding a new one to prevent memory leaks
|
||||
PLC.removeAllListeners("error");
|
||||
|
||||
try {
|
||||
await PLC.connect(plcAddress, 0).then(async () => {
|
||||
createLog("info", "dyco", "ocp", `We are connected to the dyco.`);
|
||||
isDycoRunning = true;
|
||||
|
||||
plcCycle = setInterval(async () => {
|
||||
if (isReading) {
|
||||
createLog(
|
||||
"debug",
|
||||
"dyco",
|
||||
"ocp",
|
||||
"Skipping cycle: previous read still in progress."
|
||||
);
|
||||
return;
|
||||
}
|
||||
isReading = true; // Set flag
|
||||
try {
|
||||
await PLC.readTag(labelerTag);
|
||||
await PLC.readTag(palletSend);
|
||||
await PLC.readTag(strapperError);
|
||||
|
||||
// strapper check
|
||||
strapperFaults(strapperError);
|
||||
|
||||
// send the labeler tag data over
|
||||
labelerTagRead(labelerTag);
|
||||
|
||||
// send the end of line check over.
|
||||
palletSendTag(palletSend);
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Error reading PLC tag: ${error.message}`
|
||||
);
|
||||
} finally {
|
||||
isReading = false; // Reset flag
|
||||
}
|
||||
}, plcInterval);
|
||||
});
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`There was an error in the dyco: ${error}`
|
||||
);
|
||||
await PLC.disconnect();
|
||||
isDycoRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
export const closeDyco = async () => {
|
||||
if (!isDycoRunning)
|
||||
return { success: false, message: "Dyco is not connected." };
|
||||
|
||||
console.log(`Closing the connection`);
|
||||
try {
|
||||
await PLC.disconnect();
|
||||
isDycoRunning = false;
|
||||
return {
|
||||
success: true,
|
||||
message: "Dyco Connection is now closed.",
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error closing the dyco connection.",
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
import { db } from "../../../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../../../logger/logger.js";
|
||||
import { readTags } from "../../../../../rfid/controller/readTags.js";
|
||||
import { labelingProcess } from "../../../labeling/labelProcess.js";
|
||||
import { autoLabelCreated } from "../../../labeling/labelRatio.js";
|
||||
import { stapperFaulted, strapperFaults } from "./strapperFault.js";
|
||||
|
||||
export let cameraPalletCheck = 20;
|
||||
export let currentPalletCheck = 0;
|
||||
let lastProcessedTimestamp = 0;
|
||||
|
||||
export const labelerTagRead = async (tagData: any) => {
|
||||
/**
|
||||
* Reads the tag data from the Dyco PLC and processes it based on the feedback.
|
||||
*/
|
||||
// Convert tag data buffer to a string and extract numeric values
|
||||
const buffer = Buffer.from(tagData.value);
|
||||
const numericString = buffer.toString("utf8").replace(/[^0-9#]/g, "");
|
||||
|
||||
// Ignore empty or invalid tag data
|
||||
if (numericString === "#") {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagTime = new Date(tagData.state.timestamp).getTime();
|
||||
// get the settings
|
||||
const { data: settingData, error: settingError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
|
||||
if (settingError) {
|
||||
createLog(
|
||||
"error",
|
||||
"dyco",
|
||||
"ocp",
|
||||
"There was an error getting the settings"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// check the dyco settings
|
||||
const dycoPrint = settingData.filter((n) => n.name === "dycoPrint");
|
||||
// Only process if this is a new timestamp within the last 5 seconds
|
||||
if (tagTime !== lastProcessedTimestamp && Date.now() - tagTime <= 8000) {
|
||||
lastProcessedTimestamp = tagTime;
|
||||
/**
|
||||
* add logic in to see if this is the first time we run this so we return an error to validate we are in sync.
|
||||
*/
|
||||
createLog(
|
||||
"info",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Current pallet at the wrapper is: ${numericString}`
|
||||
);
|
||||
if (dycoPrint[0].value === "1") {
|
||||
createLog("info", "dyco", "ocp", "Dyco will be printing the label");
|
||||
// if (!dycoControlCheck) {
|
||||
// createLog(
|
||||
// "error",
|
||||
// "dyco",
|
||||
// "ocp",
|
||||
// `Dyco was switch to be the printer guys. please validate the line is in sync, Current line is ${numericString}`
|
||||
// );
|
||||
// dycoControlCheck = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// check the stapper error logic.
|
||||
|
||||
if (stapperFaulted) {
|
||||
createLog("error", "dyco", "ocp", `Strapper is faulted.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we need to manual check due to 20 pallets.
|
||||
if (currentPalletCheck <= cameraPalletCheck) {
|
||||
currentPalletCheck++;
|
||||
labelingProcess({ line: numericString });
|
||||
createLog(
|
||||
"info",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`You have printed ${currentPalletCheck} pallets, remaining until ${
|
||||
cameraPalletCheck - currentPalletCheck
|
||||
}.`
|
||||
);
|
||||
autoLabelCreated();
|
||||
} else {
|
||||
currentPalletCheck = 0;
|
||||
createLog(
|
||||
"warn",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`You have reached 20 pallets since the last check please validate the labeler is still in sync.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dycoPrint[0].value === "0") {
|
||||
createLog(
|
||||
"info",
|
||||
"dyco",
|
||||
"ocp",
|
||||
"Rfid system is contorlling the printing"
|
||||
);
|
||||
// trigger the reader so we can get the label from the tag readers.
|
||||
setTimeout(async () => {
|
||||
await readTags("wrapper1");
|
||||
}, 500); // half second delay on this guy
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setting to switch between rfid and dyco labeling
|
||||
*/
|
||||
@@ -0,0 +1,59 @@
|
||||
import { createLog } from "../../../../../logger/logger.js";
|
||||
import { pickedup } from "../../../../../ocme/controller/pickedup.js";
|
||||
import { triggerScanner } from "../../../../../ocme/controller/triggerCamera.js";
|
||||
import { serverSettings } from "../../../../../server/controller/settings/getSettings.js";
|
||||
|
||||
let lastProcessedTimestamp = 0;
|
||||
|
||||
export const palletSendTag = async (tagData: any) => {
|
||||
/**
|
||||
* Reads the tag data from the Dyco PLC and processes it based on the feedback.
|
||||
* We will only trigger the camera and removal of pending tags
|
||||
*/
|
||||
|
||||
const ocmeActive = serverSettings.filter((n) => n.name === "ocmeService");
|
||||
|
||||
const tagTime = new Date(tagData.state.timestamp).getTime();
|
||||
|
||||
// Only process if this is a new timestamp within the last 5 seconds
|
||||
if (
|
||||
tagTime !== lastProcessedTimestamp &&
|
||||
Date.now() - tagTime <= 5000 &&
|
||||
tagData.value &&
|
||||
ocmeActive[0].value === "1"
|
||||
) {
|
||||
lastProcessedTimestamp = tagTime;
|
||||
//console.log(tagData.state.timestamp);
|
||||
createLog(
|
||||
"info",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Station 6 is ${tagData.value ? "full" : "empty"}`
|
||||
);
|
||||
|
||||
// take the picture
|
||||
setTimeout(async () => {
|
||||
const scan: any = await triggerScanner();
|
||||
if (!scan.success) {
|
||||
createLog(
|
||||
"error",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Scanner failed to take a picture trying one more time in 10 seconds`
|
||||
);
|
||||
setTimeout(async () => {
|
||||
await triggerScanner();
|
||||
}, 10 * 1000);
|
||||
}
|
||||
}, 15 * 1000);
|
||||
}
|
||||
|
||||
if (
|
||||
tagTime !== lastProcessedTimestamp &&
|
||||
Date.now() - tagTime <= 5000 &&
|
||||
!tagData.value &&
|
||||
ocmeActive[0].value === "1"
|
||||
) {
|
||||
await pickedup({ runningNr: 1234, all: true, areaFrom: "wrapper_1" });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import { db } from "../../../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../../../logger/logger.js";
|
||||
|
||||
// strapper related issues
|
||||
export let strapperActive = false;
|
||||
export let stapperFaulted = false;
|
||||
export let strapperFaultCount = 3; // move to db so we can control it outside the app
|
||||
|
||||
export const strapperFaults = async (tagData: any) => {
|
||||
const { data, error } = await tryCatch(db.select().from(settings));
|
||||
|
||||
// failed to get settings
|
||||
if (error) {
|
||||
return { success: false, message: "Failed to get settings." };
|
||||
}
|
||||
const strapperCheckSetting = data.filter((n) => n.name === "strapperCheck");
|
||||
|
||||
// strapper error is off
|
||||
if (strapperCheckSetting[0]?.value === "0") {
|
||||
return;
|
||||
}
|
||||
|
||||
// strapper error is on
|
||||
if (strapperCheckSetting[0]?.value === "1") {
|
||||
// faulted and still has a check or 2 to go
|
||||
if (stapperFaulted && strapperFaultCount > 0) {
|
||||
createLog(
|
||||
"warn",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`There was a strapper error, remaining pallets to check ${strapperFaultCount}.`
|
||||
);
|
||||
strapperFaultCount = strapperFaultCount - 1;
|
||||
return {
|
||||
success: true,
|
||||
message: `There was a strapper error, remaining pallets to check ${strapperFaultCount}.`,
|
||||
};
|
||||
} else {
|
||||
// no more checks needed clearing everything
|
||||
createLog(
|
||||
"debug",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Strapper check is active but not faulted, remaining pallets to check ${strapperFaultCount}.`
|
||||
);
|
||||
|
||||
// reset everything
|
||||
stapperFaulted = false;
|
||||
strapperFaultCount = 3; // move to db as well
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Strapper check is active but not faulted, remaining pallets to check ${strapperFaultCount}.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// strapper was triggered turning on the counter.
|
||||
//console.log(`Strapper fault is ${strapperError.value}`);
|
||||
if (tagData.value && strapperFaultCount > 0) {
|
||||
// strapper faulted we want to start the trigger to force the check
|
||||
if (!stapperFaulted) {
|
||||
createLog(
|
||||
"error",
|
||||
"dyco",
|
||||
"ocp",
|
||||
`Strapper errored triggering, manual checks will be required for the next ${strapperFaultCount}`
|
||||
);
|
||||
stapperFaulted = true;
|
||||
// change move fault count to db....
|
||||
strapperFaultCount = 3;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Strapper errored triggering, manual checks will be required for the next ${strapperFaultCount}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Some how we made it here and just going to say we are good. :)`,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
import axios from "axios";
|
||||
import { prodEndpointCreation } from "../../../../../globalUtils/createUrl.js";
|
||||
import { lstAuth } from "../../../../../index.js";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
import { db } from "../../../../../../database/dbclient.js";
|
||||
import { prodlabels } from "../../../../../../database/schema/prodLabels.js";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
|
||||
export const delieryInhouse = async (data: any) => {
|
||||
// update sscc so we can book in
|
||||
const SSCC = data.SSCC.slice(2);
|
||||
// api url
|
||||
const url = await prodEndpointCreation(
|
||||
"/public/v1.0/Warehousing/InhouseDelivery"
|
||||
);
|
||||
|
||||
// create bookin
|
||||
const newDelivery = {
|
||||
scannerId: 99,
|
||||
sscc: SSCC,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.post(url, newDelivery, {
|
||||
headers: {
|
||||
Authorization: `Basic ${lstAuth}`,
|
||||
accept: "text/plain",
|
||||
},
|
||||
});
|
||||
|
||||
if (res.data.Result !== 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${data.printer?.name}, Error:${res.data.Message}`
|
||||
);
|
||||
//printerUpdate(data.printer, 7, "Error while deliverying inhouse.");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${data.printer?.name} had an error while trying to deliver.`,
|
||||
data: res.data,
|
||||
};
|
||||
} // label was just delivered
|
||||
createLog(
|
||||
"info",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${parseInt(data.SSCC.slice(10, -1))}, was just delivered`
|
||||
);
|
||||
|
||||
try {
|
||||
await db
|
||||
.update(prodlabels)
|
||||
.set({
|
||||
status: "Delivered",
|
||||
upd_date: sql`NOW()`,
|
||||
})
|
||||
.where(
|
||||
eq(prodlabels.runningNr, parseInt(data.SSCC.slice(10, -1)))
|
||||
);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Error updating ${parseInt(data.SSCC.slice(10, -1))} in the DB.`
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `Error updating ${parseInt(
|
||||
data.SSCC.slice(10, -1)
|
||||
)} in the DB.`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`${data.printer.name}, "Error: ${error}`
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `Error deliverying ${parseInt(data.SSCC.slice(10, -1))}.`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
import { db } from "../../../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
import { query } from "../../../../sqlServer/prodSqlServer.js";
|
||||
|
||||
export const firstLotLabel = async (lot: any) => {
|
||||
/*
|
||||
currently this is only requested by slc
|
||||
|
||||
when a lot is finished and not set to over run we will trigger this function to insert data into the custom T_firstLotLabel table.
|
||||
|
||||
in nice label we look for unique id to run the process and then if the add date is older than 7 days we delete it for tracablility purpose.
|
||||
|
||||
we will also only do this for specific customers currently henkel is the requested.
|
||||
*/
|
||||
|
||||
/* work on the server side
|
||||
new table created in the _cus db
|
||||
CREATE TABLE T_firstLotLabel
|
||||
(
|
||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
||||
printerName varchar(max),
|
||||
add_date datetime
|
||||
-- Add other columns here as needed
|
||||
);
|
||||
the nicelabel trigger to be activated.
|
||||
*/
|
||||
const { data: settingData, error: settingError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
|
||||
if (settingError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the settings.",
|
||||
settingError,
|
||||
};
|
||||
}
|
||||
// get the plantToken
|
||||
const plantToken = settingData.filter((n) => n.name === "plantToken");
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Checking if we can create the first label lot.`
|
||||
);
|
||||
|
||||
if (plantToken[0].value === "usslc1") {
|
||||
const newLabel = `insert into T_firstLotLabel (printerName, add_date) VALUES('${lot.PrinterName}', getdate())`;
|
||||
|
||||
// if (lot.CustomerId === 40) {
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Creating first lot label for ${lot.MachineDescription}`
|
||||
);
|
||||
await query(newLabel, "newLotLabel");
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"ocp",
|
||||
"ocp",
|
||||
`Was created for ${lot.MachineDescription}`
|
||||
);
|
||||
//}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
import { Controller, Tag } from "st-ethernet-ip";
|
||||
import { labelingProcess } from "../../labeling/labelProcess.js";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
|
||||
let plcAddress = "192.168.193.97"; // zechetti 2
|
||||
let lastProcessedTimestamp = 0;
|
||||
let PLC = new Controller() as any;
|
||||
const labelerTag = new Tag("N7[0]"); // change the car to a or b depending on what zechetti.
|
||||
//const t = new Tag("CONV_M01_SHTL_UNLD_IN_FROM_PREV_CONV_TRACK_CODE.PAL_ORIGIN_LINE_N") // this is for the new zechette to reach the pallet form
|
||||
|
||||
let pollingInterval: any = null;
|
||||
let heartbeatInterval: any = null;
|
||||
let reconnecting = false;
|
||||
let lastTag = 0;
|
||||
// Track last successful read
|
||||
let lastHeartbeat: number = Date.now();
|
||||
|
||||
export async function zechitti1Connect() {
|
||||
try {
|
||||
createLog(
|
||||
"info",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
`Connecting to PLC at ${plcAddress}...`
|
||||
);
|
||||
await PLC.connect(plcAddress, 0);
|
||||
createLog("info", "zechitti1", "ocp", "Zechetti 2 connected.");
|
||||
|
||||
// Start polling tags
|
||||
startPolling();
|
||||
|
||||
// Start heartbeat
|
||||
// startHeartbeat();
|
||||
|
||||
// Handle disconnects/errors
|
||||
PLC.on("close", () => {
|
||||
console.warn("PLC connection closed.");
|
||||
handleReconnect();
|
||||
});
|
||||
|
||||
PLC.on("error", (err: any) => {
|
||||
createLog("error", "zechitti1", "ocp", `PLC error: ${err.message}`);
|
||||
handleReconnect();
|
||||
});
|
||||
} catch (err: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
`Initial connection failed: ${err.message}`
|
||||
);
|
||||
handleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (pollingInterval) clearInterval(pollingInterval);
|
||||
|
||||
pollingInterval = setInterval(async () => {
|
||||
try {
|
||||
await PLC.readTag(labelerTag);
|
||||
//lastHeartbeat = Date.now();
|
||||
|
||||
const tagTime: any = new Date(labelerTag.timestamp);
|
||||
|
||||
// so we make sure we are not missing a pallet remove it from the lastTag so we can get this next label correctly
|
||||
if (
|
||||
labelerTag.value == 0 &&
|
||||
Date.now() - lastProcessedTimestamp >= 45000
|
||||
) {
|
||||
lastTag = labelerTag.value;
|
||||
}
|
||||
|
||||
// if the tag is not zero and its been longer than 30 seconds and the last tag is not equal to the current tag we can print
|
||||
if (
|
||||
labelerTag.value !== 0 &&
|
||||
lastTag !== labelerTag.value &&
|
||||
tagTime !== lastProcessedTimestamp &&
|
||||
Date.now() - lastProcessedTimestamp >= 30000
|
||||
) {
|
||||
lastProcessedTimestamp = tagTime;
|
||||
lastTag = labelerTag.value;
|
||||
console.log(
|
||||
`Time since last check: ${
|
||||
Date.now() - tagTime
|
||||
}, greater than 30000, ${
|
||||
Date.now() - lastProcessedTimestamp >= 30000
|
||||
}, the line to be printed is ${labelerTag.value}`
|
||||
);
|
||||
//console.log(labelerTag);
|
||||
const zechette = {
|
||||
line: labelerTag.value.toString(),
|
||||
printer: 22, // this is the id of the zechetti 2 to print we should move this to the db
|
||||
printerName: "Zechetti1",
|
||||
};
|
||||
labelingProcess({ zechette: zechette });
|
||||
}
|
||||
} catch (err: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
`Polling error: ${err.message}`
|
||||
);
|
||||
handleReconnect();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// function startHeartbeat() {
|
||||
// if (heartbeatInterval) clearInterval(heartbeatInterval);
|
||||
|
||||
// heartbeatInterval = setInterval(() => {
|
||||
// const diff = Date.now() - lastHeartbeat;
|
||||
// if (diff > 60000) {
|
||||
// // 1 minute
|
||||
// console.warn(`⚠️ Heartbeat timeout: no data for ${diff / 1000}s`);
|
||||
// handleReconnect();
|
||||
// }
|
||||
// }, 10000); // check every 10s
|
||||
// }
|
||||
|
||||
async function handleReconnect() {
|
||||
if (reconnecting) return;
|
||||
reconnecting = true;
|
||||
|
||||
if (pollingInterval) {
|
||||
clearInterval(pollingInterval);
|
||||
pollingInterval = null;
|
||||
}
|
||||
|
||||
let delay = 2000; // start at 2s
|
||||
let attempts = 0;
|
||||
const maxAttempts = 10; // or limit by time, e.g. 2 min total
|
||||
|
||||
while (!PLC.connected && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
createLog(
|
||||
"info",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
`Reconnect attempt ${attempts}/${maxAttempts} in ${
|
||||
delay / 1000
|
||||
}s...`
|
||||
);
|
||||
await new Promise((res) => setTimeout(res, delay));
|
||||
|
||||
try {
|
||||
PLC = new Controller(); // fresh instance
|
||||
await PLC.connect(plcAddress, 0);
|
||||
createLog("info", "zechitti1", "ocp", "Reconnected to PLC!");
|
||||
reconnecting = false;
|
||||
startPolling();
|
||||
return;
|
||||
} catch (err: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
`Reconnect attempt failed: ${err.message}`
|
||||
);
|
||||
delay = Math.min(delay * 2, 30000); // exponential backoff up to 30s
|
||||
}
|
||||
}
|
||||
|
||||
if (!PLC.connected) {
|
||||
createLog(
|
||||
"error",
|
||||
"zechitti1",
|
||||
"ocp",
|
||||
"Max reconnect attempts reached. Stopping retries."
|
||||
);
|
||||
reconnecting = false;
|
||||
// optional: exit process or alert someone here
|
||||
// process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user