fixed grain colour, added nfc page
This commit is contained in:
parent
2ab02a0349
commit
dab1d04a81
7 changed files with 272 additions and 1 deletions
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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") && (
|
||||
<Route path="firmware" element={<Firmware />} />
|
||||
)}
|
||||
<Route path="nfc" element={<Nfc />} />
|
||||
|
||||
{/* Map pages */}
|
||||
<Route path="visualFarm" element={<FieldMap />} />
|
||||
|
|
|
|||
|
|
@ -396,6 +396,20 @@ export default function SideNavigator(props: Props) {
|
|||
</ListItemButton>
|
||||
</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">
|
||||
<ListItemButton
|
||||
id="tour-marketplace"
|
||||
|
|
|
|||
32
src/nfc/commandHistory.tsx
Normal file
32
src/nfc/commandHistory.tsx
Normal 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
109
src/nfc/nfcCommands.tsx
Normal 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
34
src/pages/Nfc.tsx
Normal 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
80
src/web-nfc.d.ts
vendored
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue