diff --git a/database/schema/prodLabels.ts b/database/schema/prodLabels.ts index 2b97dd7..52924e6 100644 --- a/database/schema/prodLabels.ts +++ b/database/schema/prodLabels.ts @@ -17,8 +17,9 @@ export const prodlabels = pgTable( line: integer("line"), runningNr: integer("runningNr").notNull(), status: text("status"), - add_date: timestamp("add_date"), - upd_date: timestamp("upd_date"), + add_user: text("add_user").default("lst"), + add_date: timestamp("add_date").defaultNow(), + upd_date: timestamp("upd_date").defaultNow(), }, (table) => [ //uniqueIndex("emailUniqueIndex").on(sql`lower(${table.email})`), diff --git a/frontend/src/components/production/ocp/LabelLog.tsx b/frontend/src/components/production/ocp/LabelLog.tsx index a3096dc..b891d7c 100644 --- a/frontend/src/components/production/ocp/LabelLog.tsx +++ b/frontend/src/components/production/ocp/LabelLog.tsx @@ -73,7 +73,7 @@ export default function LabelLog() { ); } - + const labelData = data ? data : []; return (

Labels for the last 2 hours

@@ -113,7 +113,7 @@ export default function LabelLog() { ) : ( - {data?.map((label: any) => ( + {labelData.map((label: any) => ( {label.line} diff --git a/server/services/ocp/controller/labeling/bookIn.ts b/server/services/ocp/controller/labeling/bookIn.ts new file mode 100644 index 0000000..8d30179 --- /dev/null +++ b/server/services/ocp/controller/labeling/bookIn.ts @@ -0,0 +1,89 @@ +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.0/Warehousing/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.data.Result !== 0) { + createLog( + "error", + "labeling", + "ocp", + `${data.printer.name}, Error:${res.data.Message}` + ); + //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 { + await db + .update(prodlabels) + .set({ + status: "Booked in", + upd_date: sql`NOW()`, + }) + .where( + eq(prodlabels.runningNr, parseInt(data.SSCC.slice(10, -1))) + ); + } catch (error) { + createLog( + "error", + "labeling", + "ocp", + `Error creating new runningNumber in the DB.` + ); + } + + // label was booked in + 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`, + }; + } catch (error) { + createLog( + "error", + "labeling", + "ocp", + `${data.printer.name}, "Error: ${error}` + ); + return { + success: false, + message: "There was an error booking in the label.", + data: error, + }; + } +}; diff --git a/server/services/ocp/controller/labeling/createLabel.ts b/server/services/ocp/controller/labeling/createLabel.ts new file mode 100644 index 0000000..29f12cd --- /dev/null +++ b/server/services/ocp/controller/labeling/createLabel.ts @@ -0,0 +1,158 @@ +import { eq, gte, sql } from "drizzle-orm"; +import { db } from "../../../../../database/dbclient.js"; +import { printers } 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"; + +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(printers) + .where(eq(printers.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 printer.", + 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 { + 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", + }); + } 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 { sucess: true, message: "Label created", data: returnData }; // returning label data to be able to book in if active + } catch (error) { + createLog( + "info", + "labeling", + "ocp", + `${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 +const deleteLabels = async () => { + /** + * Deletes labels older than 90 days from lst... all label data can be found in alpla prod. + */ + try { + await db + .delete(prodlabels) + .where( + gte(prodlabels.upd_date, sql.raw(`NOW() - INTERVAL '90 days'`)) + ); + } catch (error) { + createLog( + "error", + "labeling", + "ocp", + `Error deleting labels older than 90 days` + ); + } +}; diff --git a/server/services/ocp/controller/labeling/getLabels.ts b/server/services/ocp/controller/labeling/getLabels.ts index fe0f7eb..d5ce5e3 100644 --- a/server/services/ocp/controller/labeling/getLabels.ts +++ b/server/services/ocp/controller/labeling/getLabels.ts @@ -20,7 +20,7 @@ export const getLabels = async (hours: string) => { return { success: false, message: "There was an error getting the labels", - data: labelError, + data: [labelError], }; } diff --git a/server/services/ocp/controller/labeling/labelProcess.ts b/server/services/ocp/controller/labeling/labelProcess.ts new file mode 100644 index 0000000..0f27fbb --- /dev/null +++ b/server/services/ocp/controller/labeling/labelProcess.ts @@ -0,0 +1,236 @@ +import { eq } from "drizzle-orm"; +import { db } from "../../../../../database/dbclient.js"; +import { printers } from "../../../../../database/schema/printers.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 { getMac } from "../specialProcesses/utils/getMachineId.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"; + +interface Printer { + name: string; + // Add any other expected properties +} + +export const labelingProcess = async ({ + line = null as string | null, + printer = null as Printer | null, + userPrinted = null, + rfidTag = 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); + + if (filteredLot.length === 0) { + createLog( + "error", + "labeling", + "ocp", + `There is not a lot assigned to ${macId[0].Name}.` + ); + return { + success: false, + message: `There is not a lot assigned to ${macId[0].Name}.`, + }; + } + } + + // 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 === printer); + 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 mm is good + const mmStaged = await isMainMatStaged(filteredLot[0]); + + if (!mmStaged) { + createLog( + "error", + "labeling", + "ocp", + `Main material is not prepaired for lot ${filteredLot[0].lot}` + ); + + return; + } + + // 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; + } + createLog("info", "labeling", "ocp", `Is prolink good? ${prolink}`); + + // create the label + const label = await createLabel(filteredLot[0], userPrinted); + + if (!label.success) { + 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"); + + if (bookin[0].value === "1") { + const book = await bookInLabel(label.data); + } 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(label); + } + + return { + success: true, + message: "Label fully processed.", + data: label.data, + }; +}; diff --git a/server/services/ocp/controller/materials/mainMaterial.ts b/server/services/ocp/controller/materials/mainMaterial.ts new file mode 100644 index 0000000..aa439fc --- /dev/null +++ b/server/services/ocp/controller/materials/mainMaterial.ts @@ -0,0 +1,38 @@ +import { createLog } from "../../../logger/logger.js"; +import { query } from "../../../sqlServer/prodSqlServer.js"; +import { mmQuery } from "../../../sqlServer/querys/ocp/mainMaterial.js"; + +export const isMainMatStaged = async (lot: any) => { + // make staged false by deefault and error logged if theres an issue + let isStaged = false; + + // 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; + } + + const updateQuery = mmQuery.replaceAll("[lotNumber]", lot.lot); + + try { + const res = await query(updateQuery, "Main Material Check"); + + createLog( + "info", + "mainMaterial", + "ocp", + `MainMaterial results: ${JSON.stringify(res)}` + ); + + if (res[0].Staged >= 1) { + isStaged = true; + } + } catch (err) { + createLog( + "error", + "mainMaterial", + "ocp", + `Error from running the Main Material query: ${err}` + ); + } + return isStaged; +}; diff --git a/server/services/ocp/controller/printers/updatePrinters.ts b/server/services/ocp/controller/printers/updatePrinters.ts index b47e5f9..7a8fdcd 100644 --- a/server/services/ocp/controller/printers/updatePrinters.ts +++ b/server/services/ocp/controller/printers/updatePrinters.ts @@ -73,7 +73,7 @@ export const updatePrinters = async () => { ); } createLog( - "info", + "debug", "lst", "ocp", `${prodPrinterInfo[i].name} were just added/updated.` diff --git a/server/services/ocp/controller/specialProcesses/billingCheck/billingCheck.ts b/server/services/ocp/controller/specialProcesses/billingCheck/billingCheck.ts new file mode 100644 index 0000000..354b38a --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/billingCheck/billingCheck.ts @@ -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; +}; diff --git a/server/services/ocp/controller/specialProcesses/dyco/plcConnection.ts b/server/services/ocp/controller/specialProcesses/dyco/plcConnection.ts new file mode 100644 index 0000000..47257a8 --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/dyco/plcConnection.ts @@ -0,0 +1,76 @@ +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"; + +let PLC = new Controller(); +let isDycoRunning = false; + +// PLC address +let plcAddress = "10.44.5.4"; + +// 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; + let buffer = ""; + plcCycle = setInterval(async () => { + await PLC.readTag(labelerTag); + await PLC.readTag(palletSend); + + // send the labeler tag data over + labelerTagRead(labelerTag); + + // send the end of line check over. + palletSendTag(palletSend); + }, 500); + }); + } catch (error) { + createLog( + "error", + "dyco", + "ocp", + `There was an error in the dyco: ${error}` + ); + 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.", + }; + } +}; diff --git a/server/services/ocp/controller/specialProcesses/dyco/plcTags/labelerTag.ts b/server/services/ocp/controller/specialProcesses/dyco/plcTags/labelerTag.ts new file mode 100644 index 0000000..a3435d9 --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/dyco/plcTags/labelerTag.ts @@ -0,0 +1,66 @@ +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"; + +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 <= 5000) { + lastProcessedTimestamp = tagTime; + //console.log(numericString, tagData.state.timestamp); + if (dycoPrint[0].value === "1") { + createLog("info", "dyco", "ocp", "Dyco will be printing the label"); + // send over to print. + labelingProcess({ line: numericString }); + } + + 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. + await readTags("wrapper1"); + } + } +}; + +/** + * setting to switch between rfid and dyco labeling + */ diff --git a/server/services/ocp/controller/specialProcesses/dyco/plcTags/palletSendTag.ts b/server/services/ocp/controller/specialProcesses/dyco/plcTags/palletSendTag.ts new file mode 100644 index 0000000..7f1a08e --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/dyco/plcTags/palletSendTag.ts @@ -0,0 +1,54 @@ +import { createLog } from "../../../../../logger/logger.js"; +import { pickedup } from "../../../../../ocme/controller/pickedup.js"; +import { triggerScanner } from "../../../../../ocme/controller/triggerCamera.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 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 + ) { + 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 + ) { + await pickedup({ runningNr: 1234, all: true }); + } +}; diff --git a/server/services/ocp/controller/specialProcesses/inhouse/inhouseDelivery.ts b/server/services/ocp/controller/specialProcesses/inhouse/inhouseDelivery.ts new file mode 100644 index 0000000..a2e51de --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/inhouse/inhouseDelivery.ts @@ -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, + }; + } +}; diff --git a/server/services/ocp/controller/specialProcesses/lotChangeLabel/lotCHangeLabel.ts b/server/services/ocp/controller/specialProcesses/lotChangeLabel/lotCHangeLabel.ts new file mode 100644 index 0000000..277cc94 --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/lotChangeLabel/lotCHangeLabel.ts @@ -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; + } +}; diff --git a/server/services/ocp/controller/specialProcesses/utils/getMachineId.ts b/server/services/ocp/controller/specialProcesses/utils/getMachineId.ts new file mode 100644 index 0000000..86288c0 --- /dev/null +++ b/server/services/ocp/controller/specialProcesses/utils/getMachineId.ts @@ -0,0 +1,27 @@ +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 { machineCheck } from "../../../../sqlServer/querys/ocp/machineId.js"; + +export const getMac = async (machine: string) => { + let updateQuery = machineCheck.replaceAll("[loc]", machine); + + // create blank lots in case there is an error and dose not work + let mac = []; + + try { + mac = await query(updateQuery, "Machine id check"); + // console.log("Machine data", mac); // removed due to swr being activated + } catch (err) { + createLog( + "error", + "lst", + "ocp", + `Error with Machine id Check query: ${err}` + ); + } + + return mac; +}; diff --git a/server/services/ocp/ocpService.ts b/server/services/ocp/ocpService.ts index c82ecae..99a4820 100644 --- a/server/services/ocp/ocpService.ts +++ b/server/services/ocp/ocpService.ts @@ -9,6 +9,9 @@ import updateprinters from "./routes/printers/updatePrinters.js"; import { updatePrinters } from "./controller/printers/updatePrinters.js"; import getLots from "./routes/lots/getLots.js"; import getLabels from "./routes/labeling/getLabels.js"; +import { dycoConnect } from "./controller/specialProcesses/dyco/plcConnection.js"; +import dycoCon from "./routes/specialProcesses/dyco/connection.js"; +import dycoClose from "./routes/specialProcesses/dyco/closeConnection.js"; const app = new OpenAPIHono(); @@ -21,6 +24,9 @@ const routes = [ getLots, // labeling getLabels, + //dyco + dycoCon, + dycoClose, ] as const; const setting = await db.select().from(settings); @@ -40,4 +46,9 @@ setTimeout(() => { updatePrinters(); }, 3 * 1000); +// do the intnal connection to the dyco +setTimeout(() => { + dycoConnect(); +}, 3 * 1000); + export default app; diff --git a/server/services/ocp/routes/specialProcesses/dyco/closeConnection.ts b/server/services/ocp/routes/specialProcesses/dyco/closeConnection.ts new file mode 100644 index 0000000..90fed16 --- /dev/null +++ b/server/services/ocp/routes/specialProcesses/dyco/closeConnection.ts @@ -0,0 +1,56 @@ +// an external way to creating logs +import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; +import { responses } from "../../../../../globalUtils/routeDefs/responses.js"; +import { authMiddleware } from "../../../../auth/middleware/authMiddleware.js"; +import { tryCatch } from "../../../../../globalUtils/tryCatch.js"; +import { closeDyco } from "../../../controller/specialProcesses/dyco/plcConnection.js"; + +const app = new OpenAPIHono({ strict: false }); + +app.openapi( + createRoute({ + tags: ["ocp:dyco"], + summary: "Disconnect to the dyco.", + method: "get", + path: "/dycodisconnect", + middleware: authMiddleware, + description: + "Use this when you just want to stop the entire thing due to an error or what not.", + // request: { + // body: {content: {"application/json": {schema: CreateLog}}}, + // }, + responses: responses(), + }), + async (c) => { + //const body = await c.req.json(); + //apiHit(c, {endpoint: `api/logger/logs/id`}); + // const authHeader = c.req.header("Authorization"); + + // const token = authHeader?.split("Bearer ")[1] || ""; + // let user: User; + + // try { + // const payload = await verify(token, process.env.JWT_SECRET!); + // user = payload.user as User; + // } catch (error) { + // console.log(error); + // return c.json({message: "Unauthorized"}, 401); + // } + + const { data, error } = await tryCatch(closeDyco()); + + if (error) { + return c.json({ + success: false, + message: "Error in connecting to dyco", + data: error, + }); + } + const getData: any = data; + return c.json({ + success: getData?.success, + message: getData?.message, + }); + } +); +export default app; diff --git a/server/services/ocp/routes/specialProcesses/dyco/connection.ts b/server/services/ocp/routes/specialProcesses/dyco/connection.ts new file mode 100644 index 0000000..ff7aabd --- /dev/null +++ b/server/services/ocp/routes/specialProcesses/dyco/connection.ts @@ -0,0 +1,56 @@ +// an external way to creating logs +import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; +import { responses } from "../../../../../globalUtils/routeDefs/responses.js"; +import { authMiddleware } from "../../../../auth/middleware/authMiddleware.js"; +import { tryCatch } from "../../../../../globalUtils/tryCatch.js"; +import { dycoConnect } from "../../../controller/specialProcesses/dyco/plcConnection.js"; + +const app = new OpenAPIHono({ strict: false }); + +app.openapi( + createRoute({ + tags: ["ocp:dyco"], + summary: "Connects to the dyco.", + method: "get", + path: "/dycoconnect", + middleware: authMiddleware, + //description: "This might be a temp soltuin during the transtion between versions", + // request: { + // body: {content: {"application/json": {schema: CreateLog}}}, + // }, + responses: responses(), + }), + async (c) => { + //const body = await c.req.json(); + //apiHit(c, {endpoint: `api/logger/logs/id`}); + // const authHeader = c.req.header("Authorization"); + + // const token = authHeader?.split("Bearer ")[1] || ""; + // let user: User; + + // try { + // const payload = await verify(token, process.env.JWT_SECRET!); + // user = payload.user as User; + // } catch (error) { + // console.log(error); + // return c.json({message: "Unauthorized"}, 401); + // } + + const { data, error } = await tryCatch(dycoConnect()); + const dataError: any = error; + if (error) { + return c.json({ + success: false, + message: "Error in connecting to dyco", + data: dataError?.data, + }); + } + const getData: any = data; + return c.json({ + success: getData?.success, + message: getData?.message, + data: getData.data ?? [], + }); + } +); +export default app; diff --git a/server/services/rfid/controller/readTags.ts b/server/services/rfid/controller/readTags.ts index 8b3a353..8f45ee8 100644 --- a/server/services/rfid/controller/readTags.ts +++ b/server/services/rfid/controller/readTags.ts @@ -1,8 +1,8 @@ import axios from "axios"; -import {createLog} from "../../logger/logger.js"; -import {db} from "../../../../database/dbclient.js"; -import {rfidReaders} from "../../../../database/schema/rfidReaders.js"; -import {eq} from "drizzle-orm"; +import { createLog } from "../../logger/logger.js"; +import { db } from "../../../../database/dbclient.js"; +import { rfidReaders } from "../../../../database/schema/rfidReaders.js"; +import { eq } from "drizzle-orm"; const authData = btoa("admin:Zebra123!"); let token: string; @@ -12,10 +12,18 @@ export const readTags = async (reader: string) => { /** * Start the read for x seconds then auto stop it */ - - const readers = await db.select().from(rfidReaders).where(eq(rfidReaders.active, true)); + createLog("info", "rfid", "rfid", `Read was just triggered from ${reader}`); + const readers = await db + .select() + .from(rfidReaders) + .where(eq(rfidReaders.active, true)); if (readers.length === 0) { - createLog("error", "rfid", "rfid", `There are no active readers right now maybe one forgot to be activated`); + createLog( + "error", + "rfid", + "rfid", + `There are no active readers right now maybe one forgot to be activated` + ); return; } // get the auth token @@ -23,7 +31,7 @@ export const readTags = async (reader: string) => { try { const res = await axios.get(`https://${ip}/cloud/localRestLogin`, { - headers: {Authorization: `Basic ${authData}`}, + headers: { Authorization: `Basic ${authData}` }, }); token = res.data.message; // start the read @@ -47,7 +55,7 @@ const startRead = async () => { `https://${ip}/cloud/start`, {}, { - headers: {Authorization: `Bearer ${token}`}, + headers: { Authorization: `Bearer ${token}` }, } ); @@ -67,7 +75,12 @@ const startRead = async () => { startRead(); }, 1000); } - createLog("error", "rfid", "rfid", `There was an error Starting the read: ${error.response.data.message}`); + createLog( + "error", + "rfid", + "rfid", + `There was an error Starting the read: ${error.response.data.message}` + ); } }; const stopRead = async () => { @@ -76,10 +89,15 @@ const stopRead = async () => { `https://${ip}/cloud/stop`, {}, { - headers: {Authorization: `Bearer ${token}`}, + headers: { Authorization: `Bearer ${token}` }, } ); } catch (error: any) { - createLog("error", "rfid", "rfid", `There was an error Stopping the read: ${error.response.data.message}`); + createLog( + "error", + "rfid", + "rfid", + `There was an error Stopping the read: ${error.response.data.message}` + ); } }; diff --git a/server/services/rfid/controller/tagData.ts b/server/services/rfid/controller/tagData.ts index 2857100..f6c5611 100644 --- a/server/services/rfid/controller/tagData.ts +++ b/server/services/rfid/controller/tagData.ts @@ -1,5 +1,5 @@ -import {station3Tags} from "./stations/station3.js"; -import {wrapperStuff} from "./stations/wrappers.js"; +import { station3Tags } from "./stations/station3.js"; +import { wrapperStuff } from "./stations/wrappers.js"; export type TagData = { tagHex: string; @@ -8,6 +8,7 @@ export type TagData = { timeStamp: Date; antenna: number; tagStrength: number; + lastareaIn?: string; }; export const tagData = async (data: TagData[]) => { /** diff --git a/server/services/server/utils/settingsCheck.ts b/server/services/server/utils/settingsCheck.ts index e433447..1d19b26 100644 --- a/server/services/server/utils/settingsCheck.ts +++ b/server/services/server/utils/settingsCheck.ts @@ -3,20 +3,41 @@ * this will only run on a server start up */ -import {db} from "../../../../database/dbclient.js"; -import {settings} from "../../../../database/schema/settings.js"; +import { db } from "../../../../database/dbclient.js"; +import { settings } from "../../../../database/schema/settings.js"; -import {createLog} from "../../logger/logger.js"; +import { createLog } from "../../logger/logger.js"; // "view", "technician", "supervisor","manager", "admin", "systemAdmin" const newSettings = [ - {name: "server", value: "localhost", description: "Where the app runs at", moduleName: "server"}, - {name: "serverPort", value: "4400", description: "What are we listening on", moduleName: "server"}, - {name: "dbUser", value: "alplaprod", description: "What is the db username", moduleName: "server"}, - {name: "dbPass", value: "b2JlbGl4", description: "What is the db password", moduleName: "server"}, + { + name: "server", + value: "localhost", + description: "Where the app runs at", + moduleName: "server", + }, + { + name: "serverPort", + value: "4400", + description: "What are we listening on", + moduleName: "server", + }, + { + name: "dbUser", + value: "alplaprod", + description: "What is the db username", + moduleName: "server", + }, + { + name: "dbPass", + value: "b2JlbGl4", + description: "What is the db password", + moduleName: "server", + }, { name: "tcpPort", value: "2222", - description: "TCP port for printers to connect send data and the zedra cameras", + description: + "TCP port for printers to connect send data and the zedra cameras", moduleName: "server", }, { @@ -59,13 +80,15 @@ const newSettings = [ { name: "ocmeService", value: "0", - description: "Is the ocme service enabled. this is gernerally only for Dayton.", + description: + "Is the ocme service enabled. this is gernerally only for Dayton.", moduleName: "ocme", }, { name: "fifoCheck", value: "45", - description: "How far back do we want to check for fifo default 45, putting 0 will ignore.", + description: + "How far back do we want to check for fifo default 45, putting 0 will ignore.", moduleName: "ocme", }, { @@ -83,7 +106,8 @@ const newSettings = [ { name: "monitorAddress", value: "8", - description: "What address is monitored to be limited to the amount of lots that can be added to a truck.", + description: + "What address is monitored to be limited to the amount of lots that can be added to a truck.", moduleName: "ocme", }, { @@ -96,7 +120,8 @@ const newSettings = [ { name: "devDir", value: "C:\\Users\\matthes01\\Documents\\lstv2", - description: "This is the dev dir and strictly only for updating the servers.", + description: + "This is the dev dir and strictly only for updating the servers.", moduleName: "server", }, { @@ -116,7 +141,30 @@ const newSettings = [ { name: "ocpLogsCheck", value: "4", - description: "How long do we want to allow logs to show that have not been cleared?", + description: + "How long do we want to allow logs to show that have not been cleared?", + roles: "admin", + module: "ocp", + }, + { + name: "inhouseDelivery", + value: "0", + description: "Are we doing auto inhouse delivery?", + roles: "admin", + module: "ocp", + }, + // dyco settings + { + name: "dycoConnect", + value: "0", + description: "Are we running the dyco system?", + roles: "admin", + module: "ocp", + }, + { + name: "dycoPrint", + value: "0", + description: "Are we using the dyco to get the labels or the rfid?", roles: "admin", module: "ocp", }, @@ -129,16 +177,31 @@ export const areSettingsIn = async () => { if (settingsCheck.length !== newSettings.length) { try { const newRole = await db - .insert(settings) - .values(newSettings) - .onConflictDoNothing() // this will only update the ones that are new :D - .returning({name: settings.name}); - createLog("info", "lst", "server", "Settingss were just added due to missing them on server startup"); + .insert(settings) + .values(newSettings) + .onConflictDoNothing() // this will only update the ones that are new :D + .returning({ name: settings.name }); + createLog( + "info", + "lst", + "server", + "Settingss were just added due to missing them on server startup" + ); } catch (error) { - createLog("error", "lst", "server", "There was an error adding new roles to the db"); + createLog( + "error", + "lst", + "server", + "There was an error adding new roles to the db" + ); } } } catch (error) { - createLog("error", "lst", "server", "There was an error getting or adding new Settingss"); + createLog( + "error", + "lst", + "server", + "There was an error getting or adding new Settingss" + ); } }; diff --git a/server/services/sqlServer/querys/ocp/machineId.ts b/server/services/sqlServer/querys/ocp/machineId.ts new file mode 100644 index 0000000..cae8f5c --- /dev/null +++ b/server/services/sqlServer/querys/ocp/machineId.ts @@ -0,0 +1,9 @@ +export const machineCheck = ` + SELECT [HumanReadableId] + ,[Name] + ,[Location] + ,[Active] + ,[ImportSource] + ,[StagingMainMaterialMandatory] + FROM [test1_AlplaPROD2.0_Read].[masterData].[Machine] (nolock) + where Active = 1 and [Location] = [loc]`; diff --git a/server/services/sqlServer/querys/ocp/mainMaterial.ts b/server/services/sqlServer/querys/ocp/mainMaterial.ts new file mode 100644 index 0000000..ae3586f --- /dev/null +++ b/server/services/sqlServer/querys/ocp/mainMaterial.ts @@ -0,0 +1,17 @@ +export const mmQuery = ` +SELECT lot.ProductionLotHumanReadableId, + case when SourcingState in (1, 2) then 1 + when x.ProvidedAmount > 0 then 1 + when x.EffectiveConsumption > 0 then 1 + else 0 end as Staged, + x.ProvidedAmount as Provided, + x.EffectiveConsumption as consumption, + x.IsManualProcess as isManual, + MaterialHumanReadableId +FROM [test1_AlplaPROD2.0_Read].[issueMaterial].[MaterialDemand] x (nolock) +left join + [test1_AlplaPROD2.0_Read].[issueMaterial].[ProductionLot] as lot on + x.ProductionLotId = lot.Id +where lot.ProductionLotHumanReadableId = [lotNumber] + and IsMainMaterial = 1 +`;