fixed grain colour, added nfc page

This commit is contained in:
csawatzky 2025-04-15 16:09:09 -06:00
parent 2ab02a0349
commit dab1d04a81
7 changed files with 272 additions and 1 deletions

View file

@ -75,7 +75,7 @@ const useStyles = makeStyles((theme: Theme) => {
strokeOpacity: 0.85 strokeOpacity: 0.85
}, },
grainInventory: { grainInventory: {
fill: theme.palette.secondary.main, fill: GRAIN_COLOUR,
opacity: 0.5 opacity: 0.5
}, },
cableLine: { cableLine: {

View file

@ -39,6 +39,7 @@ const Fields = lazy(()=> import("pages/Fields"));
const Marketplace = lazy(() => import("userFeatures/UserFeatures")) const Marketplace = lazy(() => import("userFeatures/UserFeatures"))
const Logs = lazy(() => import("pages/Logs")) const Logs = lazy(() => import("pages/Logs"))
const Firmware = lazy(() => import("pages/Firmware")); const Firmware = lazy(() => import("pages/Firmware"));
const Nfc = lazy(() => import("pages/Nfc"));
export const appendToUrl = (appendage: number | string) => { export const appendToUrl = (appendage: number | string) => {
const basePath = location.pathname.replace(/\/$/, ""); const basePath = location.pathname.replace(/\/$/, "");
@ -315,6 +316,7 @@ export default function Router() {
{user.hasFeature("admin") && ( {user.hasFeature("admin") && (
<Route path="firmware" element={<Firmware />} /> <Route path="firmware" element={<Firmware />} />
)} )}
<Route path="nfc" element={<Nfc />} />
{/* Map pages */} {/* Map pages */}
<Route path="visualFarm" element={<FieldMap />} /> <Route path="visualFarm" element={<FieldMap />} />

View file

@ -396,6 +396,20 @@ export default function SideNavigator(props: Props) {
</ListItemButton> </ListItemButton>
</Tooltip> </Tooltip>
} }
{user.hasFeature("admin") && window.NDEFReader &&
<Tooltip title="Logs" placement="right">
<ListItemButton
id="tour-logs"
onClick={() => goTo("/logs")}
classes={getClasses("/logs")}
>
<ListItemIcon>
<DataDuckIcon />
</ListItemIcon>
{open && <ListItemText primary="Logs" />}
</ListItemButton>
</Tooltip>
}
<Tooltip title="Marketplace" placement="right"> <Tooltip title="Marketplace" placement="right">
<ListItemButton <ListItemButton
id="tour-marketplace" id="tour-marketplace"

View file

@ -0,0 +1,32 @@
import { Box, List, ListItem, ListItemIcon, ListItemText, Typography } from "@mui/material";
import { NfcCommand } from "pages/Nfc";
interface Props {
commands: NfcCommand[];
}
export default function CommandHistory(props: Props) {
const { commands } = props;
return (
<Box>
<Typography style={{ fontSize: 20, fontWeight: 650 }}>Session Command History</Typography>
<Box height={250} overflow="scroll" border={"2px solid white"} borderRadius={10}>
<List>
{commands.map((command, i) => (
<ListItem key={i}>
<ListItemIcon style={{ marginRight: 10 }}>
<Typography style={{ fontWeight: 650 }}>
{command.command + " : " + command.type}
</Typography>
</ListItemIcon>
<ListItemText>
<Typography>{command.data}</Typography>
</ListItemText>
</ListItem>
))}
</List>
</Box>
</Box>
);
}

109
src/nfc/nfcCommands.tsx Normal file
View file

@ -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 (
<Box>
<TextField
label="NFC Command entry"
fullWidth
value={textCommand}
onChange={e => {
setTextCommand(e.target.value);
}}
style={{ paddingTop: 5, paddingBottom: 5 }}
/>
<Grid container spacing={2}>
<Grid item>
<Button
variant="contained"
color="primary"
onClick={() => {
if (ndef) {
writeCommand(textCommand);
} else {
openSnack("Browser does not support web nfc");
}
}}>
Write to NFC Tag
</Button>
</Grid>
<Grid item>
<Button
variant="contained"
color="primary"
onClick={() => {
if (ndef) {
readCommand();
} else {
openSnack("Browser does not support web nfc");
}
}}>
Read NFC Tag
</Button>
</Grid>
</Grid>
</Box>
);
}

34
src/pages/Nfc.tsx Normal file
View file

@ -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<NfcCommand[]>([]);
return (
<PageContainer>
<React.Fragment>
<Box padding={2}>
<CommandHistory commands={executedCommands} />
<NfcCommands
onCommandRun={commands => {
//add to the list of executed commands
let c = cloneDeep(executedCommands);
c.push(...commands);
setExecutedCommands(c);
}}
/>
</Box>
</React.Fragment>
</PageContainer>
);
}

80
src/web-nfc.d.ts vendored Normal file
View file

@ -0,0 +1,80 @@
// Type definitions for Web NFC
// Project: https://github.com/w3c/web-nfc
// Definitions by: Takefumi Yoshii <https://github.com/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<NDEFRecord>;
}
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<void>;
write: (message: NDEFMessageSource, options?: NDEFWriteOptions) => Promise<void>;
makeReadOnly: (options?: NDEFMakeReadOnlyOptions) => Promise<void>;
}
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;
}