Skip to content

Commit

Permalink
feat: Adding nmea 0183 parser and instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
Tony Bentley authored and Tony Bentley committed Jan 3, 2024
1 parent f313e97 commit a6cdb6c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# signalk-multiplex-viewer
A plugin for tracing NMEA 0183 and 2000 multiplexed events from Signalk server

- Select from one or more event emitters
- Choose from a list of associated events
- Filter and search for targeted NMEA sentences/pgns
- Find duplicates or missing values and validate that Signalk is providing the correct values for your NMEA connections
### Instructions
- Check the Emitters associated with the NMEA data source
- Check the Events associated with the NMEA data source
- Supported NMEA 0183 sentences will display in the Capture Window: APB, BWC, DBT, DTM, GGA, GLL, GNS, GSA, GST, GSV, HDG, HDM, HDT, MTK, MWV, RDID, RMC, VHW, VTG, ZDA
- All Signalk converted NMEA 2000 PGNs will display in the Capture Window
- Cick a row to inspect the formatted data in the Capture Window
- Use the filters to reduce the rows to match specific criteria
- You can export the rows into a CSV file

NOTE: Depends on signalk-server branch [ws-events](https://github.com/SignalK/signalk-server/tree/ws-events) until merged.

Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@types/node": "^16.18.68",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"nmea-simple": "^3.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.3",
Expand Down
65 changes: 58 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
GridToolbarExport,
GridToolbarFilterButton
} from '@mui/x-data-grid';
import DialogTitle from '@mui/material/DialogTitle';
import Dialog from '@mui/material/Dialog';

import {
Grid,
Expand All @@ -14,18 +16,20 @@ import {
Accordion,
AccordionSummary,
Typography,
AccordionDetails
AccordionDetails,
} from '@mui/material';

import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import InfoRoundedIcon from '@mui/icons-material/InfoRounded';
import { useState, useEffect, useCallback, useRef } from 'react';
import { Data, Event, EventData, EEListItem, EListItem, EventItem } from './interfaces';
import { v4 as uuid } from 'uuid';
import * as nmea from 'nmea-simple';
import './App.css';

const location = `${window.location.host.split(':')[0]}:3000`;

const getData = async () => {
const res = await fetch(`http://${location}/skServer/eventsRoutingData`);
const res = await fetch(`${window.location.host}/skServer/eventsRoutingData`);
return await res.json();
}

Expand All @@ -39,20 +43,41 @@ const CustomToolbar = () => {
);
}


function App() {

const [events, setEvents] = useState(Array<EEListItem> ());
const [emitters, setEmitters] = useState(Array<EListItem> ());
const [eventItems, setEventItems] = useState(Array<EventItem> ());
const socketConnection = useRef<WebSocket | null>(null);
const [styled, setStyled] = useState()

const [dialog, setDialog] = useState(false);

const columnHeader = [
{ field: 'identifier', headerName: 'ID', description:'PGN/Sentence Unique Identifier', width: 80 },
{ field: 'event', headerName: 'Event', description:'Associated emitter event', width: 150 },
{ field: 'data', headerName: 'Data', description:'NMEA 0183 string, or NMEA 2000 JSON', width: 650 },
];

const showJSONFormatted = (event: any) => {
let parsed: any
const isString = typeof event.row.identifier === 'string';
if(isString){
const replaced = event.row.data.replace(/"/g, '')
try{
parsed = nmea.parseNmeaSentence(replaced)
} catch (err) {
parsed = {error: `Unable to parse sentence for ${event.row.identifier}`}
console.log(err)
}

} else {
parsed = JSON.parse(event.row.data)
}

setStyled(parsed)
}

const initData = (eventData: Data) => {
let eventList: Array<EEListItem> = []
let emittersList: Array<EListItem> = []
Expand Down Expand Up @@ -146,7 +171,7 @@ function App() {
const updateSocketEvents = useCallback(() => {
const checkedVisibleEvents = events.filter((event: EEListItem) => event.checked === true && event.visible === true)
const eventList = checkedVisibleEvents.map(event => event.value).join(',');
const socketAddress = `ws:${location}/signalk/v1/stream?events=${eventList}&subscribe=none`;
const socketAddress = `ws:${window.location.host}/signalk/v1/stream?events=${eventList}&subscribe=none`;
if(checkedVisibleEvents.length > 0){
socketConnection.current?.close();
const socket = new WebSocket(socketAddress);
Expand Down Expand Up @@ -209,9 +234,10 @@ function App() {
}, [emitters]);

return (
<div className="App" style={{marginTop: 10}}>
<div className="App" style={{marginTop: 10, marginBottom: 20}}>
<Grid container spacing={2} mt={5} pl={0}>
<Grid xs={3} item p={0} style={{overflow:'scroll', height: 650}} mt={5}>
<InfoRoundedIcon color='info' style={{cursor: 'pointer', marginLeft:20}} onClick={()=>setDialog(true)}/>
<Accordion defaultExpanded={true} disableGutters style={{boxShadow:'none'}}>
<AccordionSummary sx={{pt:0, mt:0, mb:0, pb:0}}
expandIcon={<ExpandMoreIcon />}
Expand All @@ -238,7 +264,7 @@ function App() {
<Accordion defaultExpanded={true} style={{boxShadow:'none'}}>
<AccordionSummary sx={{pt:0, mt:0, mb:0, pb:0}}
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
aria-controls="panel1b-content"
aria-expanded={true}
id="events">
<Typography variant="body1">Events ({events.filter(item=>item.visible).length})</Typography>
Expand All @@ -260,9 +286,23 @@ function App() {
</List>
</AccordionDetails>
</Accordion>

<Accordion defaultExpanded={true} style={{boxShadow:'none'}}>
<AccordionSummary sx={{pt:0, mt:0, mb:0, pb:0}}
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1c-content"
aria-expanded={true}
id="capture">
<Typography variant="body1">Capture Window</Typography>
</AccordionSummary>
<AccordionDetails >
{ <pre style={{whiteSpace:'pre-wrap'}}> { JSON.stringify(styled, null, 2) } </pre> }
</AccordionDetails>
</Accordion>
</Grid>
<Grid xs={8} item style={{ height: 700, width: '100%', marginTop: 16 }}>
<DataGrid
onRowClick={showJSONFormatted}
slots={{ toolbar: CustomToolbar }}
hideFooter={true}
slotProps={{
Expand All @@ -276,6 +316,17 @@ function App() {
<Typography variant='body2' style={{float:'right', paddingTop:4}}>Total Rows: {eventItems.length}</Typography>
</Grid>
</Grid>
<Dialog open={dialog} onClose={()=>{setDialog(false)}}>
<DialogTitle>Instructions</DialogTitle>
<ul>
<li>Check the Emitters associated with the NMEA data source</li>
<li>Check the Events associated with the NMEA data source</li>
<li>Cick a row to inspect the formatted data in the Capture Window</li>
<li>Use the filters to reduce the rows to match specific criteria</li>
<li>You can export the rows into a CSV file</li>
<li>Currently only the following NMEA 0183 sentences can be parsed: APB, BWC, DBT, DTM, GGA, GLL, GNS, GSA, GST, GSV, HDG, HDM, HDT, MTK, MWV, RDID, RMC, VHW, VTG, ZDA</li>
</ul>
</Dialog>
</div>
);
}
Expand Down

0 comments on commit a6cdb6c

Please sign in to comment.