From dab1d04a81de8e6e2e6f88b2a80b1bac99afa24f Mon Sep 17 00:00:00 2001 From: csawatzky Date: Tue, 15 Apr 2025 16:09:09 -0600 Subject: [PATCH] fixed grain colour, added nfc page --- src/bin/BinSVGV2.tsx | 2 +- src/navigation/Router.tsx | 2 + src/navigation/SideNavigator.tsx | 14 ++++ src/nfc/commandHistory.tsx | 32 +++++++++ src/nfc/nfcCommands.tsx | 109 +++++++++++++++++++++++++++++++ src/pages/Nfc.tsx | 34 ++++++++++ src/web-nfc.d.ts | 80 +++++++++++++++++++++++ 7 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 src/nfc/commandHistory.tsx create mode 100644 src/nfc/nfcCommands.tsx create mode 100644 src/pages/Nfc.tsx create mode 100644 src/web-nfc.d.ts diff --git a/src/bin/BinSVGV2.tsx b/src/bin/BinSVGV2.tsx index a6ad9d1..ea1b00d 100644 --- a/src/bin/BinSVGV2.tsx +++ b/src/bin/BinSVGV2.tsx @@ -75,7 +75,7 @@ const useStyles = makeStyles((theme: Theme) => { strokeOpacity: 0.85 }, grainInventory: { - fill: theme.palette.secondary.main, + fill: GRAIN_COLOUR, opacity: 0.5 }, cableLine: { diff --git a/src/navigation/Router.tsx b/src/navigation/Router.tsx index 6f5853d..79dd6c1 100644 --- a/src/navigation/Router.tsx +++ b/src/navigation/Router.tsx @@ -39,6 +39,7 @@ const Fields = lazy(()=> import("pages/Fields")); const Marketplace = lazy(() => import("userFeatures/UserFeatures")) const Logs = lazy(() => import("pages/Logs")) const Firmware = lazy(() => import("pages/Firmware")); +const Nfc = lazy(() => import("pages/Nfc")); export const appendToUrl = (appendage: number | string) => { const basePath = location.pathname.replace(/\/$/, ""); @@ -315,6 +316,7 @@ export default function Router() { {user.hasFeature("admin") && ( } /> )} + } /> {/* Map pages */} } /> diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index 6f7b5af..d0ed94f 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -396,6 +396,20 @@ export default function SideNavigator(props: Props) { } + {user.hasFeature("admin") && window.NDEFReader && + + goTo("/logs")} + classes={getClasses("/logs")} + > + + + + {open && } + + + } + Session Command History + + + + {commands.map((command, i) => ( + + + + {command.command + " : " + command.type} + + + + {command.data} + + + ))} + + + + ); +} diff --git a/src/nfc/nfcCommands.tsx b/src/nfc/nfcCommands.tsx new file mode 100644 index 0000000..ee92e13 --- /dev/null +++ b/src/nfc/nfcCommands.tsx @@ -0,0 +1,109 @@ +import { Box, Button, Grid, TextField } from "@mui/material"; +import { NfcCommand } from "pages/Nfc"; +import { useSnackbar } from "providers"; +import { useState } from "react"; + +interface Props { + onCommandRun: (command: NfcCommand[]) => void; +} + +export default function NfcCommands(props: Props) { + const { onCommandRun } = props; + const [textCommand, setTextCommand] = useState(""); + const { openSnack } = useSnackbar(); + const ndef = window.NDEFReader ? new NDEFReader() : undefined; + + const writeCommand = (commandData: string) => { + if (ndef) { + ndef + .write({ + records: [{ recordType: "text", data: commandData }] + }) + .then(resp => { + openSnack("Comand sent"); + onCommandRun([{ type: "text", command: "write", data: commandData }]); + }) + .catch(error => { + openSnack("Failed to send command"); + }); + } + }; + + const readCommand = () => { + if (ndef) { + ndef.scan().then(() => { + //the scan started looking for the tag + openSnack("Starting Scan"); + + //the tag is found and can be read + ndef.onreading = event => { + let records: NfcCommand[] = []; + openSnack("Scan Complete"); + //goes through the records found on the tag + event.message.records.forEach(record => { + //if the records type is set as tex + if (record.recordType === "text") { + //create a decoder using the records encoding method + let decoder = new TextDecoder(record.encoding); + //push the record type and the decoded data into the records to show in the command history + records.push({ + type: record.recordType, + command: "read", + data: decoder.decode(record.data) //decode the record into a string to display + }); + } + }); + onCommandRun(records); + }; + + ndef.onreadingerror = event => { + openSnack("Failed to Complete Scan"); + }; + }); + } + }; + + return ( + + { + setTextCommand(e.target.value); + }} + style={{ paddingTop: 5, paddingBottom: 5 }} + /> + + + + + + + + + + ); +} diff --git a/src/pages/Nfc.tsx b/src/pages/Nfc.tsx new file mode 100644 index 0000000..21740b3 --- /dev/null +++ b/src/pages/Nfc.tsx @@ -0,0 +1,34 @@ +import { Box } from "@mui/material"; +import { cloneDeep } from "lodash"; +import CommandHistory from "nfc/commandHistory"; +import NfcCommands from "nfc/nfcCommands"; +import React, { useState } from "react"; +import PageContainer from "./PageContainer"; + +export interface NfcCommand { + type: string; + command: "read" | "write"; + data: string; +} + +export default function Nfc() { + const [executedCommands, setExecutedCommands] = useState([]); + + return ( + + + + + { + //add to the list of executed commands + let c = cloneDeep(executedCommands); + c.push(...commands); + setExecutedCommands(c); + }} + /> + + + + ); +} diff --git a/src/web-nfc.d.ts b/src/web-nfc.d.ts new file mode 100644 index 0000000..f43a43c --- /dev/null +++ b/src/web-nfc.d.ts @@ -0,0 +1,80 @@ +// Type definitions for Web NFC +// Project: https://github.com/w3c/web-nfc +// Definitions by: Takefumi Yoshii +// TypeScript Version: 3.9 + +// This type definitions referenced to WebIDL. +// https://w3c.github.io/web-nfc/#actual-idl-index + +interface Window { + NDEFMessage: NDEFMessage; +} +declare class NDEFMessage { + constructor(messageInit: NDEFMessageInit); + records: ReadonlyArray; +} +declare interface NDEFMessageInit { + records: NDEFRecordInit[]; +} + +declare type NDEFRecordDataSource = string | BufferSource | NDEFMessageInit; + +interface Window { + NDEFRecord: NDEFRecord; +} +declare class NDEFRecord { + constructor(recordInit: NDEFRecordInit); + readonly recordType: string; + readonly mediaType?: string; + readonly id?: string; + readonly data?: DataView; + readonly encoding?: string; + readonly lang?: string; + toRecords?: () => NDEFRecord[]; +} +declare interface NDEFRecordInit { + recordType: string; + mediaType?: string; + id?: string; + encoding?: string; + lang?: string; + data?: NDEFRecordDataSource; +} + +declare type NDEFMessageSource = string | BufferSource | NDEFMessageInit; + +interface Window { + NDEFReader: NDEFReader; +} +declare class NDEFReader extends EventTarget { + constructor(); + onreading: (this: this, event: NDEFReadingEvent) => any; + onreadingerror: (this: this, error: Event) => any; + scan: (options?: NDEFScanOptions) => Promise; + write: (message: NDEFMessageSource, options?: NDEFWriteOptions) => Promise; + makeReadOnly: (options?: NDEFMakeReadOnlyOptions) => Promise; +} + +interface Window { + NDEFReadingEvent: NDEFReadingEvent; +} +declare class NDEFReadingEvent extends Event { + constructor(type: string, readingEventInitDict: NDEFReadingEventInit); + serialNumber: string; + message: NDEFMessage; +} +interface NDEFReadingEventInit extends EventInit { + serialNumber?: string; + message: NDEFMessageInit; +} + +interface NDEFWriteOptions { + overwrite?: boolean; + signal?: AbortSignal; +} +interface NDEFMakeReadOnlyOptions { + signal?: AbortSignal; +} +interface NDEFScanOptions { + signal: AbortSignal; +}