From cb59f589264583b2ee6d8f0255d3ceb33f101a01 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Sun, 16 Mar 2025 15:37:40 -0500 Subject: [PATCH] feat(tag reading): more tag reading updates, with more contorl now station 1 and 2 are still pending but didnt have enough tags at the office trial it --- frontend/package-lock.json | 15 ++++ frontend/package.json | 4 +- .../controller/stations/shippedOutTags.ts | 3 + .../rfid/controller/stations/station1.ts | 3 + .../rfid/controller/stations/station2.ts | 4 + .../rfid/controller/stations/station3.ts | 27 ++++++ .../rfid/controller/stations/wrappers.ts | 43 +++++++++ server/services/rfid/controller/tagData.ts | 33 ++++--- .../services/rfid/controller/tags/crudTag.ts | 87 +++++++++++++++++++ server/services/rfid/route/manualTagRead.ts | 40 +++++++++ server/services/rfid/route/mgtEvents.ts | 57 +++--------- server/services/rfid/route/tagInfo.ts | 68 +++++---------- 12 files changed, 277 insertions(+), 107 deletions(-) create mode 100644 server/services/rfid/controller/stations/shippedOutTags.ts create mode 100644 server/services/rfid/controller/stations/station1.ts create mode 100644 server/services/rfid/controller/stations/station2.ts create mode 100644 server/services/rfid/controller/stations/station3.ts create mode 100644 server/services/rfid/controller/stations/wrappers.ts create mode 100644 server/services/rfid/controller/tags/crudTag.ts create mode 100644 server/services/rfid/route/manualTagRead.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 132084e..40581bf 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,6 +33,7 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.476.0", "next-themes": "^0.4.4", + "npm-check-updates": "^17.1.15", "react": "^19.0.0", "react-day-picker": "^8.10.1", "react-dom": "^19.0.0", @@ -4901,6 +4902,20 @@ "node": ">=0.10.0" } }, + "node_modules/npm-check-updates": { + "version": "17.1.15", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.15.tgz", + "integrity": "sha512-miATvKu5rjec/1wxc5TGDjpsucgtCHwRVZorZpDkS6NzdWXfnUWlN4abZddWb7XSijAuBNzzYglIdTm9SbgMVg==", + "license": "Apache-2.0", + "bin": { + "ncu": "build/cli.js", + "npm-check-updates": "build/cli.js" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9074f6e..4f0b3e5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,8 @@ "build": "rimraf dist && tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "shad": "npx shadcn@canary add " + "shad": "npx shadcn@canary add ", + "checkupdates": "npm-check-updates" }, "dependencies": { "@hookform/resolvers": "^4.1.2", @@ -36,6 +37,7 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.476.0", "next-themes": "^0.4.4", + "npm-check-updates": "^17.1.15", "react": "^19.0.0", "react-day-picker": "^8.10.1", "react-dom": "^19.0.0", diff --git a/server/services/rfid/controller/stations/shippedOutTags.ts b/server/services/rfid/controller/stations/shippedOutTags.ts new file mode 100644 index 0000000..add552f --- /dev/null +++ b/server/services/rfid/controller/stations/shippedOutTags.ts @@ -0,0 +1,3 @@ +/** + * we will monitor shipped out pallets every hour if they get shipped out + */ diff --git a/server/services/rfid/controller/stations/station1.ts b/server/services/rfid/controller/stations/station1.ts new file mode 100644 index 0000000..c5822a6 --- /dev/null +++ b/server/services/rfid/controller/stations/station1.ts @@ -0,0 +1,3 @@ +/** + * station 1 will just check for 10 tags + */ diff --git a/server/services/rfid/controller/stations/station2.ts b/server/services/rfid/controller/stations/station2.ts new file mode 100644 index 0000000..b687a82 --- /dev/null +++ b/server/services/rfid/controller/stations/station2.ts @@ -0,0 +1,4 @@ +/** + * we will have 3 reader points here station2.1 will require 10 tags, 2.2 and 2.3 will require 1 + * + */ diff --git a/server/services/rfid/controller/stations/station3.ts b/server/services/rfid/controller/stations/station3.ts new file mode 100644 index 0000000..7fe0a07 --- /dev/null +++ b/server/services/rfid/controller/stations/station3.ts @@ -0,0 +1,27 @@ +/** + * Phase 1 we link the tag to the line only the line3.x where x is the line number + * Phase 2 we will generate a label to be reprinted at staion 4 + */ + +import {createLog} from "../../../logger/logger.js"; +import type {TagData} from "../tagData.js"; +import {tagStuff} from "../tags/crudTag.js"; + +export const station3Tags = async (tagData: TagData[]) => { + // make sure we only have one tag or dont update + if (tagData.length != 1) { + createLog( + "error", + "rfid", + "rfid", + `There are ${tagData.length} tags, and ${tagData[0].reader} only allows 1 tag to create a label.` + ); + // get tag data + tagStuff(tagData); + } else { + //console.log("Generate the label and link it to the tag."); + const tagdata = await tagStuff(tagData); + + createLog("info", "rfid", "rfid", "Generate a label and link it to this tag."); + } +}; diff --git a/server/services/rfid/controller/stations/wrappers.ts b/server/services/rfid/controller/stations/wrappers.ts new file mode 100644 index 0000000..d4ad35e --- /dev/null +++ b/server/services/rfid/controller/stations/wrappers.ts @@ -0,0 +1,43 @@ +/** + * Phase 1 we will just follow the logic of printing a label when we are told requested to based on this tag. + * Phase 2 we will just reprint the tag that was generated at the line + */ + +import {createLog} from "../../../logger/logger.js"; +import type {TagData} from "../tagData.js"; +import {tagStuff} from "../tags/crudTag.js"; + +export const wrapperStuff = async (tagData: TagData[]) => { + if (tagData.length != 1) { + createLog( + "error", + "rfid", + "rfid", + `There are ${tagData.length} tags and this ${tagData[0].reader} only allows 1 tag to create a label.` + ); + tagStuff(tagData); + } else { + const tagdata = await tagStuff(tagData); + + /** + * we want to make sure this pallet came from a line as its last spot if not we need to have a manual check. + */ + const station3 = tagdata.some((n: any) => n.lastareaIn.includes("line3")); + + if (!station3) { + createLog( + "error", + "rfid", + "rfid", + `${tagdata.tag}, Did not come from a line please check the pallet and manually print the label.` + ); + } + + // check if a running number exists + if (station3.runningNumber) { + createLog("info", "rfid", "rfid", `Reprint label ${station3.runningNumber}`); + } else { + createLog("info", "rfid", "rfid", `A new labels will be created and linked to this ${tagdata.tag} tag`); + } + } +}; diff --git a/server/services/rfid/controller/tagData.ts b/server/services/rfid/controller/tagData.ts index 443fd2a..2857100 100644 --- a/server/services/rfid/controller/tagData.ts +++ b/server/services/rfid/controller/tagData.ts @@ -1,4 +1,14 @@ -type TagData = {tagHex: string; reader: string; tag: string; timeStamp: Date}; +import {station3Tags} from "./stations/station3.js"; +import {wrapperStuff} from "./stations/wrappers.js"; + +export type TagData = { + tagHex: string; + reader: string; + tag: string; + timeStamp: Date; + antenna: number; + tagStrength: number; +}; export const tagData = async (data: TagData[]) => { /** * We will always update a tag @@ -14,7 +24,7 @@ export const tagData = async (data: TagData[]) => { const station3 = data.some((n) => n.reader.includes("line3")); // at the wrapper - const station4 = data.some((n) => n.reader.includes("wrapper")); + const wrappers = data.some((n) => n.reader.includes("wrapper")); // station checks if (station1 && data.length != 10) { @@ -26,23 +36,10 @@ export const tagData = async (data: TagData[]) => { } if (station3) { - // make sure we only have one tag or dont update - if (data.length != 1) { - console.log(`There are ${data.length} tags, and ${data[0].reader} only allows 1 tag to create a label.`); - //throw new Error("There are more than 1 tag at this station and it is not allowed"); - } else { - console.log("Generate the tag and link it to the tag."); - } + station3Tags(data); } - if (station4) { - if (data.length != 1) { - console.log( - `There are ${data.length} tags and this ${data[0].reader} only allows 1 tag to create a label.` - ); - //throw new Error("There are more than 1 tag at this station and it is not allowed"); - } else { - console.log("reprint the label linked to the tag."); - } + if (wrappers) { + wrapperStuff(data); } }; diff --git a/server/services/rfid/controller/tags/crudTag.ts b/server/services/rfid/controller/tags/crudTag.ts new file mode 100644 index 0000000..99a8b06 --- /dev/null +++ b/server/services/rfid/controller/tags/crudTag.ts @@ -0,0 +1,87 @@ +import {eq} from "drizzle-orm"; +import {db} from "../../../../../database/dbclient.js"; +import {rfidTags} from "../../../../../database/schema/rfidTags.js"; +import type {TagData} from "../tagData.js"; +import {createLog} from "../../../logger/logger.js"; + +type ReturnTag = { + success: boolean; + tag: any; + error: any; +}; +export const tagStuff = async (tagData: TagData[]): Promise => { + const tags = await db.select().from(rfidTags); + + // look through each tag and either add it to the db or update the area and other relevent data. + for (let i = 0; i < tagData.length; i++) { + // check if the tag already exists + const tag = tags.filter((n) => n.tagHex === tagData[i].tagHex); + if (tag.length === 0) { + // add new tag + const newTag = { + tagHex: tagData[i].tagHex, + tag: tagData[i].tag, + lastRead: new Date(tagData[i].timeStamp), + counts: [{area: tagData[i].reader, timesHere: 1}], //jsonb("counts").notNull(), //.default([{area: 1, timesHere: 5}]).notNull(), + lastareaIn: tagData[i].reader, + antenna: tagData[i].antenna, + tagStrength: tagData[i].tagStrength, + }; + try { + // insert the tag with the onConflict update the tag + const tag = await db.insert(rfidTags).values(newTag).returning({ + tag: rfidTags.tag, + runningNumber: rfidTags.runningNumber, + counts: rfidTags.counts, + lastareaIn: rfidTags.lastareaIn, + }); + createLog("info", "rfid", "rfid", `Tags were jusdt updated.`); + return {success: true, tag}; + } catch (error) { + createLog("error", "rfid", "rfid", `${JSON.stringify(error)}`); + return {success: false, error}; + } + } else { + // update tag + //console.log("Updating existing tag"); + // make sure we actually have an array here + const countsArray = (tag[0]?.counts as {area: string; timesHere: number}[]) ?? []; + + // check if the reader exists on the array + const areaExists = countsArray.some((t) => t.area === tagData[0].reader); + + // run the update on the array + const updateCount = areaExists + ? countsArray.map((t) => { + if (t.area === tagData[0].reader) { + return {...t, timesHere: t.timesHere + 1}; + } else { + return {...t, area: tagData[i].reader, timesHere: 1}; + } + }) + : [...countsArray, {area: tagData[i].reader, timesHere: 1}]; + + const updateTag = { + lastRead: new Date(tagData[i].timeStamp), + counts: updateCount, //jsonb("counts").notNull(), //.default([{area: 1, timesHere: 5}]).notNull(), + lastareaIn: tagData[i].reader, + antenna: tagData[i].antenna, + tagStrength: tagData[i].tagStrength, + }; + + try { + await db.update(rfidTags).set(updateTag).where(eq(rfidTags.tagHex, tagData[0].tagHex)).returning({ + tag: rfidTags.tag, + runningNumber: rfidTags.runningNumber, + counts: rfidTags.counts, + lastareaIn: rfidTags.lastareaIn, + }); + createLog("info", "rfid", "rfid", `Tags were jusdt updated.`); + return {success: true, tag}; + } catch (error) { + createLog("error", "rfid", "rfid", `${JSON.stringify(error)}`); + return {success: false, error}; + } + } + } +}; diff --git a/server/services/rfid/route/manualTagRead.ts b/server/services/rfid/route/manualTagRead.ts new file mode 100644 index 0000000..91eda36 --- /dev/null +++ b/server/services/rfid/route/manualTagRead.ts @@ -0,0 +1,40 @@ +//http://usday1vms006:4000/api/v1/zebra/wrapper1 +import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; +import {responses} from "../../../globalUtils/routeDefs/responses.js"; +import {readTags} from "../controller/readTags.js"; + +const app = new OpenAPIHono(); + +const ParamsSchema = z.object({ + reader: z + .string() + .min(3) + .openapi({ + param: { + name: "reader", + in: "path", + }, + example: "1212121", + }), +}); + +app.openapi( + createRoute({ + tags: ["rfid"], + summary: "Manual triggers the read function", + method: "post", + path: "/manualtrigger/{reader}", + request: { + params: ParamsSchema, + }, + responses: responses(), + }), + async (c) => { + const {reader} = c.req.valid("param"); + const manualTrigger = await readTags(reader); + + return c.json({success: true, message: `A Manaul trigger was done on ${reader}`}, 200); + } +); + +export default app; diff --git a/server/services/rfid/route/mgtEvents.ts b/server/services/rfid/route/mgtEvents.ts index 26b1e50..4729615 100644 --- a/server/services/rfid/route/mgtEvents.ts +++ b/server/services/rfid/route/mgtEvents.ts @@ -2,12 +2,8 @@ import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; import {readTags} from "../controller/readTags.js"; import {createLog} from "../../logger/logger.js"; - -// Define the response schema -const responseSchema = z.object({ - success: z.boolean().openapi({example: true}), - message: z.string().optional(), -}); +import {responses} from "../../../globalUtils/routeDefs/responses.js"; +import {newHeartBeat} from "../controller/readerControl.js"; const app = new OpenAPIHono(); let lastGpiTimestamp = 0; @@ -28,64 +24,37 @@ const ParamsSchema = z.object({ app.openapi( createRoute({ tags: ["rfid"], - summary: "Adds a new module", + summary: "Post info from the reader", method: "post", path: "/mgtevents/{reader}", request: { params: ParamsSchema, }, - responses: { - 200: { - content: { - "application/json": {schema: responseSchema}, - }, - description: "Response message", - }, - 400: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}), - }, - }, - description: "Internal Server Error", - }, - 401: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}), - }, - }, - description: "Unauthorized", - }, - 500: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}), - }, - }, - description: "Internal Server Error", - }, - }, + responses: responses(), }), async (c) => { const {reader} = c.req.valid("param"); const body = await c.req.json(); if (body.type === "heartbeat") { - console.log("Heartbeat"); + const heart = await newHeartBeat(reader); + return c.json({success: heart.success, message: heart.message}, 200); } if (body.type === "gpi" && body.data.state === "HIGH") { const eventTimestamp = new Date(body.timestamp).getTime(); // Convert ISO timestamp to milliseconds - - if (eventTimestamp - lastGpiTimestamp > 10) { + if (eventTimestamp - lastGpiTimestamp > 5 * 1000) { // Check if it's been more than 2ms lastGpiTimestamp = eventTimestamp; // Update last seen timestamp + createLog("info", "rfid", "rfid", `${reader} is reading a tag.`); - readTags(reader); + await readTags(reader); } else { - console.log("Duplicate GPI event ignored."); + createLog("info", "rfid", "rfid", `A new trigger from ${reader} was to soon`); + lastGpiTimestamp = eventTimestamp; } + + //console.log(body); } return c.json({success: true, message: `New info from ${reader}`}, 200); diff --git a/server/services/rfid/route/tagInfo.ts b/server/services/rfid/route/tagInfo.ts index 35173e7..0b4404d 100644 --- a/server/services/rfid/route/tagInfo.ts +++ b/server/services/rfid/route/tagInfo.ts @@ -2,12 +2,9 @@ import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; import {ConsoleLogWriter} from "drizzle-orm"; import {tagData} from "../controller/tagData.js"; - -// Define the response schema -const responseSchema = z.object({ - success: z.boolean().openapi({example: true}), - message: z.string().optional(), -}); +import {responses} from "../../../globalUtils/routeDefs/responses.js"; +import {noRead} from "../controller/noRead.js"; +import {badRead, goodRead} from "../controller/readerControl.js"; const app = new OpenAPIHono(); @@ -27,44 +24,13 @@ const ParamsSchema = z.object({ app.openapi( createRoute({ tags: ["rfid"], - summary: "Adds a new module", + summary: "Tag info posted from the reader.", method: "post", path: "/taginfo/{reader}", request: { params: ParamsSchema, }, - responses: { - 200: { - content: { - "application/json": {schema: responseSchema}, - }, - description: "Response message", - }, - 400: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}), - }, - }, - description: "Internal Server Error", - }, - 401: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}), - }, - }, - description: "Unauthorized", - }, - 500: { - content: { - "application/json": { - schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}), - }, - }, - description: "Internal Server Error", - }, - }, + responses: responses(), }), async (c) => { const {reader} = c.req.valid("param"); @@ -74,17 +40,31 @@ app.openapi( for (let i = 0; i < body.length; i++) { const tag = Buffer.from(body[i].data.idHex, "hex").toString("utf-8"); - if (tag.includes("ALPLA")) { + //console.log("Raw value:", body[i].data.peakRssi, "Parsed:", parseInt(body[i].data.peakRssi)); + if (tag.includes("ALPLA") && parseInt(body[i].data.peakRssi) < -50) { tagdata = [ ...tagdata, - {tagHex: body[i].data.idHex, reader: reader, tag: tag, timeStamp: body[i].timestamp}, + { + tagHex: body[i].data.idHex, + reader: reader, + tag: tag, + timeStamp: body[i].timestamp, + antenna: body[i].data.antenna, + tagStrength: body[i].data.peakRssi, + }, ]; } } - tagData(tagdata); - - return c.json({success: true, message: `New info from ${reader}`}, 200); + if (tagdata.length === 0) { + noRead(reader); + badRead(reader); + return c.json({success: false, message: `There were no tags scanned.`}, 200); + } else { + tagData(tagdata); + goodRead(reader); + return c.json({success: true, message: `New info from ${reader}`}, 200); + } } );