* linting and prettiyin

* configure eslint

* removing lint staged

* linted bus

* udpated spec

* linnting

* more linting

* disble tslint

* linting

* more linting

* more linting

* more linting

* configure prettier

* remove duplicated settings files

* linting

* more linting

* linting

* lint

* more linting

* add lint in readme

* fix build

* non-quiet

* linting

* address codeql warnings
This commit is contained in:
Peli de Halleux 2021-02-26 12:26:22 +01:00 коммит произвёл GitHub
Родитель dd0298a3b3
Коммит 3a6b9176e3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
108 изменённых файлов: 7104 добавлений и 3130 удалений

28
.eslintrc.json Normal file
Просмотреть файл

@ -0,0 +1,28 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-empty-function": "off",
"react/display-name": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}

7
.github/workflows/codeql-analysis.yml поставляемый
Просмотреть файл

@ -12,7 +12,7 @@ on:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
@ -22,11 +22,6 @@ jobs:
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1

1
.gitignore поставляемый
Просмотреть файл

@ -2,7 +2,6 @@ node_modules
.nyc_output
.DS_Store
*.log
.vscode
.idea
compiled
.awcache

5
.prettierrc Normal file
Просмотреть файл

@ -0,0 +1,5 @@
{
"arrowParens": "avoid",
"semi": false,
"tabWidth": 4
}

8
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"tslint.enable": false,
"eslint.alwaysShowStatus": true,
"eslint.format.enable": false,
"eslint.debug": true,
"eslint.lintTask.enable": true,
}

Просмотреть файл

@ -73,6 +73,14 @@ We use [Mocha](https://mochajs.org/) to run the unit test suite from ``/tests``.
yarn test
```
## Linting
Run the following command to detect linting issues
```
yarn lint
```
### Docs build
Launch the gatsbdy develop mode and navigate to http://localhost:8000 . This build does not require to load dist as the library is compiled directly into the web site.

Просмотреть файл

@ -1,4 +0,0 @@
{
"arrowParens": "avoid",
"semi": false
}

2179
docs/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -16,7 +16,11 @@
"@microsoft/applicationinsights-web-basic": "^2.5.11",
"@octokit/core": "^3.2.5",
"@react-three/drei": "^3.8.6",
"@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.15.2",
"compare-versions": "^3.6.0",
"eslint": "^7.20.0",
"eslint-plugin-react": "^7.22.0",
"gatsby": "^2.32.3",
"gatsby-image": "^2.11.0",
"gatsby-plugin-manifest": "^2.12.0",
@ -50,6 +54,7 @@
"material-ui-dropzone": "^3.5.0",
"notistack": "^1.0.3",
"octokit-plugin-create-pull-request": "^3.9.3",
"prettier": "2.2.1",
"prism-react-renderer": "^1.2.0",
"prop-types": "^15.7.2",
"react": "^17.0.1",

Просмотреть файл

@ -1,6 +1,5 @@
import React, { useMemo } from 'react';
import { Menu, MenuItem, Typography } from '@material-ui/core';
import { SRV_CTRL } from '../../../src/jdom/constants';
import { isInfrastructure, serviceSpecificationFromClassIdentifier, serviceSpecifications } from '../../../src/jdom/spec';
// tslint:disable-next-line: match-default-export-name no-submodule-imports
import AddIcon from '@material-ui/icons/Add';

Просмотреть файл

@ -3,7 +3,7 @@ import useDbValue from "./useDbValue";
import useEffectAsync from "./useEffectAsync";
import Alert from "./ui/Alert"
import { AccordionActions, AccordionSummary, AccordionDetails, Accordion, Typography, TextField, Box } from '@material-ui/core';
import { Button, Link } from "gatsby-theme-material-ui";
import { Button } from "gatsby-theme-material-ui";
// tslint:disable-next-line: match-default-export-name no-submodule-imports
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
// tslint:disable-next-line: match-default-export-name no-submodule-imports

Просмотреть файл

@ -23,6 +23,7 @@ export interface AppProps {
setSearchQuery: (s: string) => void,
toolsMenu: boolean,
setToolsMenu: (visible: boolean) => void,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setError: (error: any) => void,
widgetMode: boolean,
showDeviceHostsDialog: boolean,
@ -32,21 +33,22 @@ export interface AppProps {
const AppContext = createContext<AppProps>({
drawerType: DrawerType.None,
setDrawerType: (type) => { },
setDrawerType: () => { },
searchQuery: undefined,
setSearchQuery: (s) => { },
setSearchQuery: () => { },
toolsMenu: false,
setToolsMenu: (v) => { },
setError: (error: any) => { },
setToolsMenu: () => { },
setError: () => { },
widgetMode: false,
showDeviceHostsDialog: false,
toggleShowDeviceHostsDialog: () => { },
showRenameDeviceDialog: (device) => { }
showRenameDeviceDialog: () => { }
});
AppContext.displayName = "app";
export default AppContext;
// eslint-disable-next-line react/prop-types
export const AppProvider = ({ children }) => {
const { bus } = useContext<JacdacContextProps>(JacdacContext)
const [type, setType] = useState(DrawerType.None)
@ -90,7 +92,7 @@ export const AppProvider = ({ children }) => {
useEffect(() => bus.subscribe(CONNECTION_STATE, cs => {
switch (cs) {
case BusState.Connected:
if (!!bus.transport)
if (bus.transport)
enqueueSnackbar("connected...", {
variant: "info"
})

Просмотреть файл

@ -15,7 +15,6 @@ import PacketRecorder from "./PacketRecorder";
import DrawerSearchInput from "./DrawerSearchInput";
import DrawerSearchResults from "./DrawerSearchResults";
import DrawerToolsButtonGroup from "./DrawerToolsButtonGroup";
import PacketStats from "./PacketStats";
const useStyles = makeStyles((theme) => createStyles({
drawer: {

Просмотреть файл

@ -87,9 +87,8 @@ export default function CmdButton(props: {
}
}
finally {
if (!mounted())
return;
setWorking(false)
if (mounted())
setWorking(false)
}
}

Просмотреть файл

@ -139,8 +139,8 @@ export class Db extends JDEventSource {
try {
const transaction = this.db.transaction([table], "readwrite");
const blobs = transaction.objectStore(table)
const request = data !== undefined ? blobs.put(data, id) : blobs.delete(id);;
request.onsuccess = (event) => {
const request = data !== undefined ? blobs.put(data, id) : blobs.delete(id);
request.onsuccess = () => {
this.emit(CHANGE)
resolve()
}
@ -191,6 +191,7 @@ DbContext.displayName = "db";
export default DbContext;
// eslint-disable-next-line react/prop-types
export const DbProvider = ({ children }) => {
const [db, setDb] = useState<Db>(undefined)
const [error, setError] = useState(undefined)

Просмотреть файл

@ -1,4 +1,4 @@
import React, { useEffect, useState, Fragment } from 'react';
import React from 'react';
// tslint:disable-next-line: no-submodule-imports
import { makeStyles } from '@material-ui/core/styles';
// tslint:disable-next-line: no-submodule-imports
@ -11,12 +11,12 @@ import ServiceButton from './ServiceButton';
import useChange from '../jacdac/useChange';
import { navigate } from "gatsby";
import { JDService } from '../../../src/jdom/service';
import { CardActions, createStyles, Theme } from '@material-ui/core';
import { CardActions, createStyles } from '@material-ui/core';
import DeviceCardHeader from './DeviceCardHeader';
import { useRegisterStringValue } from '../jacdac/useRegisterValue';
import { DeviceLostAlert } from './alert/DeviceLostAlert';
const useStyles = makeStyles((theme: Theme) => createStyles({
const useStyles = makeStyles(() => createStyles({
root: {
},
bullet: {

Просмотреть файл

@ -1,4 +1,4 @@
import { Typography, useTheme } from "@material-ui/core";
import { Typography } from "@material-ui/core";
import React from "react"
import { JDDevice } from "../../../src/jdom/device";
import useDeviceName from "./useDeviceName";

Просмотреть файл

@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useState } from 'react';
// tslint:disable-next-line: no-submodule-imports
import Tabs from '@material-ui/core/Tabs';
// tslint:disable-next-line: no-submodule-imports
@ -34,7 +34,7 @@ export default function DeviceSpecificationSource(props: {
const [tab, setTab] = useState(0);
const spec = deviceSpecification
const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => {
const handleTabChange = (event: React.ChangeEvent<unknown>, newValue: number) => {
setTab(newValue);
};

Просмотреть файл

@ -1,13 +1,5 @@
import { Link } from "gatsby-theme-material-ui";
import React from "react";
// tslint:disable-next-line: no-submodule-imports
import TreeView from '@material-ui/lab/TreeView';
// tslint:disable-next-line: no-submodule-imports match-default-export-name
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
// tslint:disable-next-line: no-submodule-imports match-default-export-name
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
// tslint:disable-next-line: no-submodule-imports
import TreeItem from '@material-ui/lab/TreeItem';
import Alert from "./ui/Alert"
import { useDrawerSearchResults } from "./useDrawerSearchResults";

Просмотреть файл

@ -1,5 +1,4 @@
import { Badge } from "@material-ui/core";
import React, { useContext, useEffect, useState } from "react";
import React, { useContext } from "react";
import AppContext, { DrawerType } from "./AppContext";
// tslint:disable-next-line: no-submodule-imports match-default-export-name
import HistoryIcon from '@material-ui/icons/History';
@ -9,12 +8,9 @@ import MenuIcon from '@material-ui/icons/Menu';
import AccountTreeIcon from '@material-ui/icons/AccountTree';
import IconButtonWithTooltip from "./ui/IconButtonWithTooltip";
import ConnectButton from "../jacdac/ConnectButton";
import JacdacContext, { JacdacContextProps } from "../jacdac/Context";
import { DEVICE_CHANGE } from "../../../src/jdom/constants";
import WebUSBSupported from "./WebUSBSupported";
export default function DrawerToolsButtonGroup(props: { className?: string, showToc?: boolean, showCurrent?: boolean, showConnect?: boolean }) {
const { bus } = useContext<JacdacContextProps>(JacdacContext)
const { className, showToc, showCurrent, showConnect } = props;
const { drawerType, setDrawerType } = useContext(AppContext)

Просмотреть файл

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React from "react";
import { JDEvent } from "../../../src/jdom/event";
import { Typography, Badge } from "@material-ui/core";
import KindIcon from "./KindIcon";

Просмотреть файл

@ -134,10 +134,10 @@ export default class FieldDataSet extends JDEventSource {
this.emit(CHANGE);
}
toCSV(sep: string = ",", options?: { units?: boolean }) {
toCSV(sep = ",", options?: { units?: boolean }) {
const allheaders = ["time", ...this.headers].join(sep)
const start = this.startTimestamp
let csv: string[] = [allheaders]
const csv: string[] = [allheaders]
if (options?.units)
csv.push(["ms", ...this.units].join(sep))
this.rows.forEach(row => csv.push(

Просмотреть файл

@ -7,7 +7,6 @@ import { DEVICE_CHANGE, DEVICE_FIRMWARE_INFO, FIRMWARE_BLOBS_CHANGE } from "../.
import useEventRaised from "../jacdac/useEventRaised";
import { computeUpdates } from "../../../src/jdom/flashing";
import IconButtonWithTooltip from "./ui/IconButtonWithTooltip";
import useDevices from "./hooks/useDevices";
export default function FlashButton(props: { className?: string }) {
const { bus } = useContext<JacdacContextProps>(JacdacContext)

Просмотреть файл

@ -7,7 +7,6 @@ import AppContext from "./AppContext";
import { GITHUB_API_KEY } from "./github";
import useDbValue from "./useDbValue";
import { useSnackbar } from "notistack";
import Alert from "./ui/Alert";
import GitHubIcon from '@material-ui/icons/GitHub';
import ApiKeyAccordion from "./ApiKeyAccordion";
@ -21,7 +20,7 @@ export default function GithubPullRequestButton(props: {
}) {
const { commit, files, label, title, body, head } = props;
const { value: token } = useDbValue(GITHUB_API_KEY, "")
const [response, setResponse] = useState(undefined);
const [, setResponse] = useState(undefined);
const [busy, setBusy] = useState(false)
const { setError: setAppError } = useContext(AppContext)
const { enqueueSnackbar } = useSnackbar();

Просмотреть файл

@ -1,6 +1,6 @@
import React, { } from 'react';
// tslint:disable-next-line: no-submodule-imports
import { Box, Card, CardActions, CardContent, CardHeader, CircularProgress, Typography } from '@material-ui/core';
import { Box, CardHeader, Typography } from '@material-ui/core';
import { useLatestRelease, useRepository } from './github';
import GitHubIcon from '@material-ui/icons/GitHub';
import { Link } from 'gatsby-theme-material-ui';
@ -12,7 +12,7 @@ export default function GithubRepositoryCardHeader(props: {
}) {
const { slug, showRelease } = props;
const { response: repo, loading: repoLoading } = useRepository(slug);
const { response: release, loading: releaseLoading } = useLatestRelease(showRelease && slug);
const { response: release } = useLatestRelease(showRelease && slug);
const iframe = inIFrame();
const target = iframe ? "_blank" : ""

Просмотреть файл

@ -84,7 +84,7 @@ function DeviceTreeItem(props: { device: JDDevice } & StyledTreeViewItemProps &
}
function ServiceTreeItem(props: { service: JDService } & StyledTreeViewItemProps & JDomTreeViewProps) {
const { service, checked, setChecked, checkboxes, dashboard, registerFilter, eventFilter, ...other } = props;
const { service, checked, setChecked, checkboxes, registerFilter, eventFilter, ...other } = props;
const specification = service.specification;
const showSpecificationAction = false;
const id = service.id
@ -226,12 +226,12 @@ export default function JDomTreeView(props: JDomTreeViewProps) {
const { bus } = useContext<JacdacContextProps>(JacdacContext)
const devices = useChange(bus, () => bus.devices().filter(dev => !deviceFilter || deviceFilter(dev)))
const handleToggle = (event: React.ChangeEvent<{}>, nodeIds: string[]) => {
const handleToggle = (event: React.ChangeEvent<unknown>, nodeIds: string[]) => {
setExpanded(nodeIds);
if (onToggle) onToggle(nodeIds)
};
const handleSelect = (event: React.ChangeEvent<{}>, nodeIds: string[]) => {
const handleSelect = (event: React.ChangeEvent<unknown>, nodeIds: string[]) => {
setSelected(nodeIds);
if (onSelect) onSelect(nodeIds)
};

Просмотреть файл

@ -47,16 +47,17 @@ const PacketsContext = createContext<PacketsProps>({
tracing: false,
toggleTracing: () => { },
paused: false,
setPaused: (p) => { },
setPaused: () => { },
progress: undefined,
timeRange: undefined,
toggleTimeRange: () => { },
setTimeRange: (range) => { }
setTimeRange: () => { }
});
PacketsContext.displayName = "packets";
export default PacketsContext;
// eslint-disable-next-line react/prop-types
export const PacketsProvider = ({ children }) => {
const { bus, disconnectAsync } = useContext<JacdacContextProps>(JacdacContext)
const { value: filter, setValue: _setFilter } = useDbValue("packetfilter", "repeated-announce:false")

Просмотреть файл

@ -1,5 +1,6 @@
import React, { Fragment } from "react";
// eslint-disable-next-line react/prop-types
const Page = ({ props, children }) => {
return <Fragment {...props}>{children}</Fragment>
}

Просмотреть файл

@ -1,4 +1,4 @@
import React, { createContext, useContext, useEffect, useMemo, useRef } from "react";
import React, { createContext, useContext, useEffect, useRef } from "react";
import { JSONTryParse, SMap } from "../../../src/jdom/utils";
import { BrowserFileStorage, HostedFileStorage, IFileStorage } from '../../../src/embed/filestorage'
import { IThemeMessage } from "../../../src/embed/protocol";
@ -64,11 +64,15 @@ const ServiceManagerContext = createContext<ServiceManagerContextProps>({
});
ServiceManagerContext.displayName = "Services";
export default ServiceManagerContext;
// eslint-disable-next-line react/prop-types
export const ServiceManagerProvider = ({ children }) => {
const { toggleDarkMode } = useContext(DarkModeContext)
const { bus } = useContext<JacdacContextProps>(JacdacContext)
const props = useRef<ServiceManagerContextProps>(createProps())
const propsRef = useRef<ServiceManagerContextProps>(createProps())
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleMessage = (ev: MessageEvent<any>) => {
const msg = ev.data;
if (msg?.source !== 'jacdac')
@ -91,14 +95,14 @@ export const ServiceManagerProvider = ({ children }) => {
return () => { };
}, [])
return <ServiceManagerContext.Provider value={props.current}>
return <ServiceManagerContext.Provider value={propsRef.current}>
{children}
</ServiceManagerContext.Provider>
function createProps(): ServiceManagerContextProps {
const isHosted = inIFrame();
let fileStorage: IFileStorage = new BrowserFileStorage()
let deviceNames = new LocalStorageDeviceNameSettings(
const deviceNames = new LocalStorageDeviceNameSettings(
bus,
new LocalStorageSettings("jacdac_device_names")
);
@ -120,5 +124,3 @@ export const ServiceManagerProvider = ({ children }) => {
}
}
}
export default ServiceManagerContext;

Просмотреть файл

@ -15,7 +15,7 @@ export default function ServiceRegisters(props: {
hideMissingValues?: boolean,
showTrends?: boolean
}) {
const { service, registerIdentifiers, filter, showRegisterName, hideMissingValues, showTrends, expanded } = props;
const { service, registerIdentifiers, filter, showRegisterName, hideMissingValues, showTrends } = props;
const specification = useChange(service, spec => spec.specification);
const registers = useMemo(() => {
const packets = specification?.packets;

Просмотреть файл

@ -1,8 +1,5 @@
import React, { useState } from 'react';
// tslint:disable-next-line: no-submodule-imports
import Tabs from '@material-ui/core/Tabs';
// tslint:disable-next-line: no-submodule-imports
import Tab from '@material-ui/core/Tab';
import { Tabs, Tab } from '@material-ui/core';
import { serviceSpecificationFromClassIdentifier } from '../../../src/jdom/spec';
import { Paper, createStyles, makeStyles, Theme } from '@material-ui/core';
import TabPanel, { a11yProps } from './ui/TabPanel';
@ -38,7 +35,7 @@ export default function ServiceSpecificationSource(props: {
const convs = converters();
const showDTDL = spec?.camelName !== "system"
const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => {
const handleTabChange = (event: React.ChangeEvent<unknown>, newValue: number) => {
setTab(newValue);
};
@ -61,7 +58,7 @@ export default function ServiceSpecificationSource(props: {
{showSpecification && <TabPanel key="spec" value={tab} index={index++}>
<ServiceSpecification service={spec} />
</TabPanel>}
{["sts", "ts", "c", "json"].map((lang, i) =>
{["sts", "ts", "c", "json"].map((lang) =>
<TabPanel key={`conv${lang}`} value={tab} index={index++}>
<Snippet value={() => convs[lang](spec)} mode={lang} />
</TabPanel>)}

Просмотреть файл

@ -4,7 +4,7 @@ import { Link } from 'gatsby-theme-material-ui';
import useGridBreakpoints from './useGridBreakpoints';
import JacdacContext, { JacdacContextProps } from "../jacdac/Context";
import useChange from '../jacdac/useChange';
import { Grid, Card, CardHeader, CardActions, Button, createStyles, makeStyles, Paper, Step, StepContent, StepLabel, Stepper, Theme, Typography } from '@material-ui/core';
import { Grid, Card, CardActions, Button, createStyles, makeStyles, Paper, Step, StepContent, StepLabel, Stepper, Theme, Typography } from '@material-ui/core';
// tslint:disable-next-line: no-submodule-imports
import Alert from "./ui/Alert";
import DeviceCardHeader from "./DeviceCardHeader"
@ -101,8 +101,8 @@ export default function ServiceTest(props: { serviceSpec: jdspec.ServiceSpec })
<StepLabel>Select a service to test</StepLabel>
<StepContent>
{!!services.length && <Grid container spacing={2}>
{services.map(service => <Grid item {...gridBreakpoints}>
<Card key={service.id}>
{services.map(service => <Grid item key={service.id} {...gridBreakpoints}>
<Card >
<DeviceCardHeader device={service.device} />
<CardActions>
<Button variant="contained" color="primary" onClick={handleSelect(service)}>Select</Button>

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useContext, useMemo } from "react"
import { makeStyles, createStyles, Theme, List, ListItem, Typography, useTheme, Box, useMediaQuery } from '@material-ui/core';
import { makeStyles, createStyles, Theme, List, ListItem, Typography, useTheme, Box } from '@material-ui/core';
import { Link } from 'gatsby-theme-material-ui';
// tslint:disable-next-line: no-submodule-imports
import ListItemText from '@material-ui/core/ListItemText';
@ -71,7 +71,6 @@ export default function Toc(props: { pagePath: string }) {
const { setDrawerType } = useContext(AppContext)
const theme = useTheme();
const classes = useStyles();
const mobile = useMediaQuery(theme.breakpoints.down("lg"));
const data = useStaticQuery(graphql`
query {
site {
@ -127,7 +126,7 @@ export default function Toc(props: { pagePath: string }) {
const tree = useMemo(() => {
// convert pages into tree
let toc: TocNode[] = [{
const toc: TocNode[] = [{
name: "Home",
path: "/",
order: 0
@ -189,7 +188,7 @@ export default function Toc(props: { pagePath: string }) {
return tree;
}, []);
const TocListItem = (props: { entry: TocNode, level: number }) => {
function TocListItem(props: { entry: TocNode, level: number }) {
const { entry, level } = props;
const { path, children, name } = entry;
const selected = pagePath === path;
@ -211,6 +210,6 @@ export default function Toc(props: { pagePath: string }) {
}
return <List dense className={classes.root}>
{tree.map(entry => <TocListItem key={'tocitem' + entry.path} entry={entry} level={0} />)}
{tree.map((entry, i) => <TocListItem key={i} entry={entry} level={0} />)}
</List>
}

Просмотреть файл

@ -43,7 +43,7 @@ function UnitTrendChart(props: {
const mint = Math.min.apply(null, times);
let minv = unit == "/" ? 0 : Math.min.apply(null, indexes.map(i => dataSet.mins[i]));
let maxv = unit == "/" ? 1 : Math.max.apply(null, indexes.map(i => dataSet.maxs[i]));
let opposite = unit != "/" && Math.sign(minv) != Math.sign(maxv)
const opposite = unit != "/" && Math.sign(minv) != Math.sign(maxv)
if (isNaN(minv) && isNaN(maxv)) {
minv = 0
maxv = 1

Просмотреть файл

@ -126,7 +126,7 @@ export default function WebDiagnostics() {
setV(v + 1);
}
const handleChange = (panel: string) => (event: React.ChangeEvent<{}>, isExpanded: boolean) => {
const handleChange = (panel: string) => (event: React.ChangeEvent<unknown>, isExpanded: boolean) => {
setExpanded(isExpanded ? panel : false);
};

Просмотреть файл

@ -30,8 +30,8 @@ function NoSsrConnectAlert(props: { serviceClass?: number }) {
connectionState === BusState.Disconnected)
return <Box displayPrint="none">
<Alert severity="info" closeable={true}>
{!spec && <span>Don't forget to connect!</span>}
{spec && <span>Don't forget to connect some {spec.name} devices!</span>}
{!spec && <span>Did you connect your device?</span>}
{spec && <span>Did you connect a {spec.name} device?</span>}
<ConnectButton className={classes.button} full={true} transparent={true} />
</Alert>
</Box>

Просмотреть файл

@ -105,18 +105,18 @@ const serviceViews: {
},
[SRV_CHARACTER_SCREEN]: {
component: DashboardCharacterScreen,
weight: (srv) => 3
weight: () => 3
},
[SRV_RAIN_GAUGE]: {
component: DashboardRainGauge,
},
[SRV_LED_MATRIX]: {
component: DashboardLEDMatrix,
weight: (srv) => 3
weight: () => 3
},
[SRV_ARCADE_GAMEPAD]: {
component: DashboardArcadeGamepad,
weight: (srv) => 3
weight: () => 3
},
[SRV_WIND_DIRECTION]: {
component: DashboardWindDirection,
@ -160,7 +160,7 @@ const serviceViews: {
},
[SRV_SOUND_PLAYER]: {
component: DashboardSoundPlayer,
weight: (srv) => 2
weight: ( ) => 2
},
[SRV_ANALOG_BUTTON]: {
component: DashboardAnalogButton,
@ -272,7 +272,7 @@ export default function DashboardServiceWidget(props: React.Attributes & Dashboa
const color = host ? "secondary" : "primary";
// no special support
if (!component)
return createElement(DefaultWidget, props);;
return createElement(DefaultWidget, props);
return <NoSsr>
<Suspense fallback={<CircularProgress color={color} disableShrink={true} variant={"indeterminate"} size={"3em"} />}>

Просмотреть файл

@ -4,9 +4,7 @@ import React, { useContext, useMemo, useState } from "react";
import { useId } from "react-use-id-hook";
import hosts, { addHost } from "../../../../src/hosts/hosts";
import { VIRTUAL_DEVICE_NODE_NAME } from "../../../../src/jdom/constants";
import JDDeviceHost from "../../../../src/jdom/devicehost";
import Flags from "../../../../src/jdom/flags";
import ServiceHost from "../../../../src/jdom/servicehost";
import { delay } from "../../../../src/jdom/utils";
import JacdacContext, { JacdacContextProps } from "../../jacdac/Context";
import KindIcon from "../KindIcon";
@ -18,7 +16,6 @@ export default function StartSimulatorDialog(props: { open: boolean, onClose: ()
const deviceHostDialogId = useId();
const deviceHostLabelId = useId();
const { } = props;
const [selected, setSelected] = useState("button");
const { enqueueSnackbar } = useSnackbar();
const hostDefinitions = useMemo(() => hosts(), []);

Просмотреть файл

@ -11,13 +11,14 @@ import GaugeWidget from "../widgets/GaugeWidget";
import useWidgetSize from "../widgets/useWidgetSize";
import ValueWithUnitWidget from "../widgets/ValueWithUnitWidget";
import useUnitIcon from "../hooks/useUnitIcon";
import { PackedSimpleValue } from "../../../../src/jdom/pack";
export default function MemberInput(props: {
specification: jdspec.PacketMember,
serviceSpecification: jdspec.ServiceSpec,
serviceMemberSpecification: jdspec.PacketInfo,
value: any,
setValue?: (v: any) => void,
value: PackedSimpleValue,
setValue?: (v: PackedSimpleValue) => void,
showDataType?: boolean,
color?: "primary" | "secondary",
variant?: RegisterInputVariant,
@ -46,7 +47,7 @@ export default function MemberInput(props: {
const minValue = pick(min, typicalMin, absoluteMin, /^u/.test(type) ? 0 : undefined)
const maxValue = pick(max, typicalMax, absoluteMax)
const errorValue = !!error ? ("±" + roundWithPrecision(error, 1 - Math.floor(Math.log10(error))).toLocaleString()) : undefined;
const errorValue = error ? ("±" + roundWithPrecision(error, 1 - Math.floor(Math.log10(error))).toLocaleString()) : undefined;
const unit = prettyUnit(specification.unit);
const helperText = errorText
|| [prettyMemberUnit(specification, showDataType), errorValue]
@ -72,8 +73,9 @@ export default function MemberInput(props: {
setValue(r.value)
setErrorText(r.error)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleEnumChange = (event: React.ChangeEvent<{ value: any }>) => {
const v = enumInfo.isFlags ? flagsToValue(event.target.value) : event.target.value
const v = enumInfo.isFlags ? flagsToValue(event.target.value as number[]) : event.target.value as number
setValue(v)
}
const handleSliderChange = (event: unknown, newValue: number | number[]) => {
@ -127,7 +129,7 @@ export default function MemberInput(props: {
aria-label={label}
disabled={disabled}
multiple={enumInfo.isFlags}
value={enumInfo.isFlags ? valueToFlags(enumInfo, value) : value}
value={enumInfo.isFlags ? valueToFlags(enumInfo, value as number) : value}
onChange={handleEnumChange}>
{Object.keys(enumInfo.members).map(n => <MenuItem key={n} value={enumInfo.members[n]}>{n}</MenuItem>)}
</Select>
@ -141,7 +143,7 @@ export default function MemberInput(props: {
return <GaugeWidget
tabIndex={0}
label={label}
value={value}
value={value as number}
color={color}
variant={signed ? "fountain" : undefined}
min={min} max={max} step={step}
@ -154,7 +156,7 @@ export default function MemberInput(props: {
return <Slider
aria-label={label}
color={color}
value={value}
value={value as number}
valueLabelFormat={percentValueFormat}
onChange={disabled ? undefined : handleSliderChange}
min={min} max={max} step={step}
@ -180,7 +182,7 @@ export default function MemberInput(props: {
return <ValueWithUnitWidget
tabIndex={0}
label={specification.unit}
value={value}
value={value as number}
min={minValue}
max={maxValue}
icon={unitIcon}
@ -190,7 +192,7 @@ export default function MemberInput(props: {
onChange={disabled ? undefined : handleSliderChange} />
return <Slider
value={value}
value={value as number}
color={color}
valueLabelFormat={off ? offFormat : valueLabelFormat}
onChange={disabled ? undefined : handleSliderChange}
@ -216,7 +218,7 @@ export default function MemberInput(props: {
if (isWidget) // we need min/max to support a slider
return <ValueWithUnitWidget
tabIndex={0}
value={roundWithPrecision(value, 1)}
value={roundWithPrecision(value as number, 1)}
label={specification.unit}
color={color}
size={widgetSize} />

Просмотреть файл

@ -53,7 +53,7 @@ export default function useFirmwareBlobs() {
console.log(`firmwares: change`)
const names = await fw?.list()
console.log(`import stored uf2`, names)
let uf2s: FirmwareBlob[] = [];
const uf2s: FirmwareBlob[] = [];
if (names?.length) {
for (const name of names) {
const blob = await fw.get(name)

Просмотреть файл

@ -3,10 +3,9 @@ import { deviceSpecificationFromFirmwareIdentifier, deviceSpecifications } from
import JacdacContext, { JacdacContextProps } from "../../jacdac/Context";
import useEffectAsync from "../useEffectAsync";
import { unique } from "../../../../src/jdom/utils";
import { BootloaderCmd, CMD_ADVERTISEMENT_DATA, ControlReg, DEVICE_CHANGE, SRV_BOOTLOADER, SRV_CTRL } from "../../../../src/jdom/constants";
import { BootloaderCmd, ControlReg, DEVICE_CHANGE, SRV_BOOTLOADER } from "../../../../src/jdom/constants";
import useEventRaised from "../../jacdac/useEventRaised";
import Packet from "../../../../src/jdom/packet";
import { jdunpack } from "../../../../src/jdom/pack";
export default function useFirmwareRepos(showAllRepos?: boolean) {
const { bus } = useContext<JacdacContextProps>(JacdacContext)
@ -24,7 +23,7 @@ export default function useFirmwareRepos(showAllRepos?: boolean) {
if (showAllRepos)
repos = deviceSpecifications().map(spec => spec.repo);
else {
let firmwares: number[] = [];
const firmwares: number[] = [];
// ask firmware registers
for (const register of registers) {
await register.refresh(true)

Просмотреть файл

@ -1,37 +1,37 @@
import useFetch from "./useFetch";
import useFetch from "./useFetch"
const ROOT = "https://api.github.com/"
export const GITHUB_API_KEY = "githubtoken"
export interface GithubRelease {
url: string,
html_url: string,
tag_name: string,
name: string,
body: string,
url: string
html_url: string
tag_name: string
name: string
body: string
assets: {
url: string,
browser_download_url: string,
url: string
browser_download_url: string
name: string
}[]
}
export interface GithubUser {
login: string;
avatar_url: string;
html_url: string;
login: string
avatar_url: string
html_url: string
}
export interface GithubRepository {
name: string;
full_name: string;
private: boolean;
owner: GithubUser;
description: string;
fork: boolean;
homepage: string;
default_branch: string;
organization: GithubUser;
html_url: string;
name: string
full_name: string
private: boolean
owner: GithubUser
description: string
fork: boolean
homepage: string
default_branch: string
organization: GithubUser
html_url: string
}
export function normalizeSlug(slug: string): string {
@ -39,46 +39,57 @@ export function normalizeSlug(slug: string): string {
}
export interface GitHubApiOptions {
ignoreThrottled?: boolean;
ignoreThrottled?: boolean
}
export function parseRepoUrl(url: string): { owner: string; name: string; } {
const m = /^https:\/\/github\.com\/([^\/ \t]+)\/([^\/ \t]+)\/?$/.exec(url || "")
if (m)
return { owner: m[1], name: m[2] }
return undefined;
export function parseRepoUrl(url: string): { owner: string; name: string } {
const m = /^https:\/\/github\.com\/([^/ \t]+)\/([^/ \t]+)\/?$/.exec(
url || ""
)
if (m) return { owner: m[1], name: m[2] }
return undefined
}
export async function fetchLatestRelease(slug: string, options?: GitHubApiOptions): Promise<GithubRelease> {
const uri = `${ROOT}repos/${normalizeSlug(slug)}/releases/latest`;
export async function fetchLatestRelease(
slug: string,
options?: GitHubApiOptions
): Promise<GithubRelease> {
const uri = `${ROOT}repos/${normalizeSlug(slug)}/releases/latest`
const resp = await fetch(uri)
// console.log(resp)
switch (resp.status) {
case 200:
case 204:
case 204: {
const release: GithubRelease = await resp.json()
return release;
return release
}
case 404:
// unknow repo or no access
return undefined;
return undefined
case 403:
// throttled
if (options?.ignoreThrottled)
return undefined;
throw new Error("Too many calls to GitHub, try again later");
if (options?.ignoreThrottled) return undefined
throw new Error("Too many calls to GitHub, try again later")
}
throw new Error(`unknown status code ${resp.status}`)
}
export async function fetchReleaseBinary(slug: string, tag: string): Promise<Blob> {
export async function fetchReleaseBinary(
slug: string,
tag: string
): Promise<Blob> {
// we are not using the release api because of CORS.
const downloadUrl = `https://raw.githubusercontent.com/${normalizeSlug(slug)}/${tag}/dist/firmware.uf2`
const req = await fetch(downloadUrl, { headers: { "Accept": "application/octet-stream" } })
const downloadUrl = `https://raw.githubusercontent.com/${normalizeSlug(
slug
)}/${tag}/dist/firmware.uf2`
const req = await fetch(downloadUrl, {
headers: { Accept: "application/octet-stream" },
})
if (req.status == 200) {
const firmware = await req.blob()
return firmware;
return firmware
}
return undefined;
return undefined
}
function useFetchApi<T>(path: string, options?: GitHubApiOptions) {
@ -90,45 +101,54 @@ function useFetchApi<T>(path: string, options?: GitHubApiOptions) {
case 202:
case 203:
case 204:
break;
break
case 404:
// unknow repo or no access
res.response = undefined;
break;
res.response = undefined
break
case 403:
// throttled
if (options?.ignoreThrottled) {
res.response = undefined;
res.response = undefined
return res
}
else
throw new Error("Too many calls to GitHub, try again later");
} else
throw new Error("Too many calls to GitHub, try again later")
default:
console.log(`unknown status`, res)
throw new Error(`Unknown response from GitHub ${res.status}`);
throw new Error(`Unknown response from GitHub ${res.status}`)
}
return res;
return res
}
export function useRepository(slug: string) {
const path = `repos/${normalizeSlug(slug)}`
const res = useFetchApi<GithubRepository>(path, { ignoreThrottled: true });
return res;
const res = useFetchApi<GithubRepository>(path, { ignoreThrottled: true })
return res
}
export function useLatestRelease(slug: string, options?: GitHubApiOptions) {
if (!slug)
return { response: undefined, loading: false, error: undefined, status: undefined }
const uri = `repos/${normalizeSlug(slug)}/releases/latest`;
const res = useFetchApi<GithubRelease>(uri, { ignoreThrottled: true });
return res;
return {
response: undefined,
loading: false,
error: undefined,
status: undefined,
}
const uri = `repos/${normalizeSlug(slug)}/releases/latest`
const res = useFetchApi<GithubRelease>(uri, { ...(options || {}), ignoreThrottled: true })
return res
}
export function useLatestReleases(slug: string, options?: GitHubApiOptions) {
if (!slug)
return { response: undefined, loading: false, error: undefined, status: undefined }
const uri = `repos/${normalizeSlug(slug)}/releases`;
const res = useFetchApi<GithubRelease[]>(uri, { ignoreThrottled: true });
return res;
}
return {
response: undefined,
loading: false,
error: undefined,
status: undefined,
}
const uri = `repos/${normalizeSlug(slug)}/releases`
const res = useFetchApi<GithubRelease[]>(uri, { ...(options || {}), ignoreThrottled: true })
return res
}

Просмотреть файл

@ -7,4 +7,4 @@ export default function useMounted(): () => boolean {
return () => { mounted.current = false; }
}, []);
return () => mounted.current;
};
}

Просмотреть файл

@ -1,9 +1,10 @@
import { SvgIcon, SvgIconProps } from "@material-ui/core";
import React from "react";
export default (props: SvgIconProps) =>
<SvgIcon {...props}>
export default function MicrobitIcon(props: SvgIconProps) {
return <SvgIcon {...props}>
<path fill="none" d="M0 18.461h195.474v34.211H0z" />
<path d="M37.363 29.377a3.04 3.04 0 01-3.035-3.042 3.035 3.035 0 013.035-3.038 3.035 3.035 0 013.039 3.038 3.038 3.038 0 01-3.039 3.042M15.052 23.3a3.04 3.04 0 00-3.042 3.035 3.044 3.044 0 003.042 3.042 3.042 3.042 0 003.036-3.042 3.037 3.037 0 00-3.036-3.035m-.003-5.99h22.576c4.979 0 9.027 4.047 9.027 9.027 0 4.979-4.049 9.031-9.027 9.031H15.049c-4.977 0-9.03-4.053-9.03-9.031-.001-4.98 4.053-9.027 9.03-9.027m22.576 24.076c8.299 0 15.047-6.75 15.047-15.049s-6.748-15.051-15.047-15.051H15.049C6.75 11.286 0 18.038 0 26.337s6.75 15.049 15.049 15.049h22.576" />
</SvgIcon>
}

Просмотреть файл

@ -1,10 +1,10 @@
import { Grid, TextField, useEventCallback } from "@material-ui/core";
import { Grid, TextField } from "@material-ui/core";
import React, { ChangeEvent, useEffect, useMemo, useState } from "react";
import { clone, JSONTryParse, SMap, uniqueName } from "../../../../src/jdom/utils";
import useChange from '../../jacdac/useChange';
// tslint:disable-next-line: no-submodule-imports match-default-export-name
import DeleteIcon from '@material-ui/icons/Delete';
import { resolveMakecodeService, resolveMakecodeServiceFromClassIdentifier, serviceSpecificationFromClassIdentifier, serviceSpecifications } from "../../../../src/jdom/spec";
import { resolveMakecodeService, resolveMakecodeServiceFromClassIdentifier, serviceSpecifications } from "../../../../src/jdom/spec";
import AddServiceIconButton from "../AddServiceIconButton";
import ServiceSpecificationSelect from "../ServiceSpecificationSelect"
import { escapeName } from "../../../../src/azure-iot/dtdl"
@ -88,9 +88,11 @@ function ClientRoleRow(props: { config: Configuration, component: ClientRole, on
</Grid>
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function validateClientRole(config: Configuration, role: ClientRole) {
let serviceError: string = undefined;
let nameError: string = undefined;
const serviceError: string = undefined;
const nameError: string = undefined;
// TODO
return { serviceError, nameError }
}

Просмотреть файл

@ -41,7 +41,7 @@ function MakeCodeSnippetNoSSR(props: { source: string }) {
const tabs = ["blocks", "typescript", "sim"]
const { editor, setEditor } = useContext(MakeCodeSnippetContext);
const [tab, setTab] = useState(tabs.indexOf(editor) || 0);
const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => {
const handleTabChange = (event: React.ChangeEvent<unknown>, newValue: number) => {
if (newValue < tabs.length - 1)
setEditor(tabs[newValue]);
setTab(newValue);

Просмотреть файл

@ -160,10 +160,10 @@ export function useMakeCodeRenderer() {
// listen for messages
const handleMessage = (ev: MessageEvent) => {
let msg = ev.data;
const msg = ev.data;
if (msg.source != "makecode") return;
switch (msg.type) {
case "renderready":
case "renderready": {
console.log(`mkcd: renderer ready, ${Object.keys(pendingRequests).length} pending`)
const iframe = typeof document !== "undefined" && document.getElementById(iframeId)
if (iframe) {
@ -173,17 +173,19 @@ export function useMakeCodeRenderer() {
.forEach(k => sendRequest(pendingRequests[k].req));
}
break;
case "renderblocks":
}
case "renderblocks": {
const id = msg.id; // this is the id you sent
const r = pendingRequests[id];
if (!r) return;
delete pendingRequests[id];
r.resolve(msg as RenderBlocksResponseMessage);
break;
}
}
}
useWindowEvent("message", handleMessage, false)
useWindowEvent("message", handleMessage, false, [])
return {
render

Просмотреть файл

@ -2,7 +2,10 @@ import React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
export default function PageTemplate({ data: { mdx } }) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function PageTemplate(props: { data: { mdx: { body: any } } }) {
const { data } = props;
const { mdx } = data;
return <MDXRenderer>{mdx.body}</MDXRenderer>
}

Просмотреть файл

@ -1,6 +1,6 @@
import { Grid, TextField } from "@material-ui/core";
import React, { ChangeEvent, useMemo } from "react";
import { clone, SMap, uniqueName } from "../../../../src/jdom/utils";
import { clone, uniqueName } from "../../../../src/jdom/utils";
import useLocalStorage from "../useLocalStorage";
// tslint:disable-next-line: no-submodule-imports match-default-export-name
import DeleteIcon from '@material-ui/icons/Delete';
@ -58,7 +58,7 @@ function ComponentRow(props: { twin: DigitalTwinSpec, component: DigitalTwinComp
function validateTwinComponent(twin: DigitalTwinSpec, component: DigitalTwinComponent) {
let serviceError: string = undefined;
let nameError: string = undefined;
const nameError: string = undefined;
const count = twin.components.filter(c => c.service.classIdentifier === component.service.classIdentifier).length
if (count > 1)
serviceError = `Multiple same service not supported.`
@ -110,7 +110,7 @@ export default function AzureDeviceTwinDesigner() {
variant={variant}
/>
</Grid>
{twin.components.map(c => <ComponentRow twin={twin} component={c} onUpdate={update} />)}
{twin.components.map((c, i) => <ComponentRow key={i} twin={twin} component={c} onUpdate={update} />)}
<Grid item xs={12}>
<AddServiceIconButton onAdd={handleAddService} />
</Grid>

Просмотреть файл

@ -34,7 +34,8 @@ interface ConnectionString {
}
function parseConnectionString(source: string): ConnectionString {
let r: any = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const r: any = {
source
};
source.split(';').map(fragment => fragment.split('=')).forEach(kv => r[kv[0]] = kv[1]);
@ -117,7 +118,7 @@ class AzureIotHubClient {
}
}
export default function AzureIoTHub(props: {}) {
export default function AzureIoTHub() {
return <>
<ConnectAlert />
<ApiKeyManager />

Просмотреть файл

@ -75,8 +75,7 @@ function createDataSet(bus: JDBus,
return set;
}
export default function Collector(props: {}) {
const { } = props;
export default function Collector() {
const { bus } = useContext<JacdacContextProps>(JacdacContext)
const classes = useStyles();
const { fileStorage } = useContext(ServiceManagerContext)

Просмотреть файл

@ -93,7 +93,7 @@ export default function DeviceDesigner() {
: deviceSpecifications().find(dev => dev.id == device.id)
? "identifer already used"
: "";
const servicesError = !!device.services?.length
const servicesError = device.services?.length
? ""
: "Select at least one service"
const imageError = !imageBase64 ? "missing image" : ""

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useContext, useEffect, useState } from "react"
import { Box, Button, Card, CardActions, CardContent, CardHeader, CardMedia, CircularProgress, Collapse, Grid, Switch, TextField, Typography, useEventCallback, useTheme } from '@material-ui/core';
import { Box, Button, Card, CardActions, CardContent, CardHeader, CardMedia, CircularProgress, Grid, Switch, Typography, useTheme } from '@material-ui/core';
import { Link } from 'gatsby-theme-material-ui';
import useDbValue from "../useDbValue";
import JacdacContext, { JacdacContextProps } from "../../jacdac/Context";
@ -10,7 +10,7 @@ import { JDClient } from "../../../../src/jdom/client";
import DeviceCardHeader from "../DeviceCardHeader";
import Alert from "../ui/Alert";
import useEffectAsync from "../useEffectAsync";
import { CHANGE, CONNECT, CONNECTING, CONNECTION_STATE, DISCONNECT, ERROR, PACKET_REPORT, PROGRESS, REPORT_RECEIVE, SensorAggregatorReg, SRV_MODEL_RUNNER, SRV_SENSOR_AGGREGATOR } from "../../../../src/jdom/constants";
import { CHANGE, CONNECT, CONNECTING, CONNECTION_STATE, DISCONNECT, ERROR, PROGRESS, REPORT_RECEIVE, SRV_MODEL_RUNNER, SRV_SENSOR_AGGREGATOR } from "../../../../src/jdom/constants";
import FieldDataSet from "../FieldDataSet";
import { deviceSpecificationFromFirmwareIdentifier, isSensor } from "../../../../src/jdom/spec";
import CircularProgressWithLabel from "../ui/CircularProgressWithLabel";
@ -48,6 +48,7 @@ interface EdgeImpulseResponse {
interface EdgeImpulseHello {
hello?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
err?: any;
}
@ -139,6 +140,7 @@ class EdgeImpulseClient extends JDClient {
public samplingState = IDLE;
private _hello: EdgeImpulseRemoteManagementInfo;
private _sample: EdgeImpulseSampling;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _pingInterval: any;
private pong: boolean;
private aggregatorClient: SensorAggregatorClient;
@ -182,6 +184,7 @@ class EdgeImpulseClient extends JDClient {
w.close();
}
catch (e) {
console.debug(e)
}
finally {
this.setConnectionState(DISCONNECT);
@ -206,7 +209,7 @@ class EdgeImpulseClient extends JDClient {
}
}
private send(msg: any) {
private send(msg: unknown) {
this._ws?.send(JSON.stringify(msg))
}
@ -241,6 +244,7 @@ class EdgeImpulseClient extends JDClient {
this.connect();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private handleMessage(msg: any) {
// response to ping?
if (msg.data === "pong") {
@ -485,7 +489,7 @@ class EdgeImpulseClient extends JDClient {
}
}
static async apiFetch<T extends EdgeImpulseResponse>(apiKey: string, path: string | number, body?: any): Promise<T> {
static async apiFetch<T extends EdgeImpulseResponse>(apiKey: string, path: string | number, body?: unknown): Promise<T> {
const API_ROOT = "https://studio.edgeimpulse.com/v1/api/"
const url = `${API_ROOT}${path}`
const options: RequestInit = {
@ -646,7 +650,7 @@ function Acquisition(props: {
const [connectionState, setConnectionState] = useState(DISCONNECT)
const [samplingState, setSamplingState] = useState(IDLE)
const [samplingProgress, setSamplingProgress] = useState(0)
const [deviceInfo, setDeviceInfo] = useState<EdgeImpulseDeviceInfo>(undefined);
const [, setDeviceInfo] = useState<EdgeImpulseDeviceInfo>(undefined);
const { deviceId } = device;
const deviceName = useDeviceName(device, false);
const projectId = info?.id;
@ -726,7 +730,7 @@ function Acquisition(props: {
</Box>
}
export default function EdgeImpulse(props: {}) {
export default function EdgeImpulse() {
const { value: apiKey } = useDbValue(EDGE_IMPULSE_API_KEY, "")
const { bus } = useContext<JacdacContextProps>(JacdacContext);
const [model, setModel] = useState<Uint8Array>(undefined)

Просмотреть файл

@ -9,7 +9,7 @@ import SafeBootAlert from "../firmware/SafeBootAlert";
export default function Flash() {
const [tab, setTab] = useState(0);
const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => {
const handleTabChange = (event: React.ChangeEvent<unknown>, newValue: number) => {
setTab(newValue);
}

Просмотреть файл

@ -72,7 +72,7 @@ export function ModelActions(props: {
</>
}
export default function ModelUploader(props: {}) {
export default function ModelUploader() {
const classes = useStyles()
const [importing, setImporting] = useState(false)
const { data: model, setBlob: setModel } = useDbUint8Array("model.tflite")
@ -154,7 +154,7 @@ export default function ModelUploader(props: {}) {
<p>Machine learning models are typically stored in a <code>.tflite</code> file.</p>
{model && <Alert severity={'success'}>Model loaded ({prettySize(model.byteLength)})</Alert>}
{model && <p />}
<ImportButton required={!model} disabled={importing} text={"Import model"} onFilesUploaded={handleTfmodelFiles} />
<ImportButton disabled={importing} text={"Import model"} onFilesUploaded={handleTfmodelFiles} />
<Button aria-label="clear model" disabled={importing} onClick={handleClearModel}>clear model</Button>
{models?.length && <List>
{models.map(model => <ListItem key={model.path} button onClick={handleLoadModel(model)}>
@ -166,7 +166,7 @@ export default function ModelUploader(props: {}) {
{sensorConfig && <Alert severity={'success'}>Sensor configuration loaded</Alert>}
{sensorConfig && <SensorAggregatorConfigView config={sensorConfig} />}
{sensorConfig && <p />}
<ImportButton required={!sensorConfig} disabled={importing} text={"Import configuration"} onFilesUploaded={handleSensorConfigFiles} />
<ImportButton disabled={importing} text={"Import configuration"} onFilesUploaded={handleSensorConfigFiles} />
<Button aria-label="clear configuration" disabled={importing} onClick={handleClearConfiguration}>clear configuration</Button>
{inputConfigurations?.length && <List>
{inputConfigurations.map(iconfig => <ListItem key={iconfig.path} button onClick={handleLoadInputConfiguration(iconfig)}>

Просмотреть файл

@ -1,8 +1,9 @@
import { PaletteType } from "@material-ui/core";
import React, { useEffect, useState } from "react";
import React, { ReactNode, useEffect, useState } from "react";
import DarkModeContext from './DarkModeContext'
const DarkModeProvider = ({ children }) => {
export default function DarkModeProvider(props: { children: ReactNode }) {
const { children } = props;
const KEY = 'darkMode'
const [darkMode, setDarkMode] = useState<PaletteType>("light");
const [darkModeMounted, setMounted] = useState(false);
@ -37,5 +38,3 @@ const DarkModeProvider = ({ children }) => {
{children}
</DarkModeContext.Provider>
}
export default DarkModeProvider

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react/jsx-key */
import React, { useContext, useRef } from 'react'
import Highlight, { defaultProps, Language, PrismTheme } from 'prism-react-renderer'
// tslint:disable-next-line: no-submodule-imports match-default-export-name

Просмотреть файл

@ -29,8 +29,9 @@ export default function IconButtonWithProgress(props: IconButtonWithProgressProp
progress={progress}
progressColor={progressColor}
progressStyle={progressStyle}
progressSize={progressSize}
children={badge} />}
progressSize={progressSize}>
{badge}
</CircularProgressBox>}
</IconButton></span>
</Tooltip>
}

Просмотреть файл

@ -1,42 +1,40 @@
import { useState } from "react";
import useEffectAsync from "./useEffectAsync";
import { useState } from "react"
import useEffectAsync from "./useEffectAsync"
export default function useFetch<T>(url: RequestInfo, options?: RequestInit) {
const [response, setResponse] = useState<T>(undefined);
const [error, setError] = useState<any>(undefined);
const [status, setStatus] = useState<number>(undefined);
const [loading, setLoading] = useState(true); // start in loading mode
const [response, setResponse] = useState<T>(undefined)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [error, setError] = useState<any>(undefined)
const [status, setStatus] = useState<number>(undefined)
const [loading, setLoading] = useState(true) // start in loading mode
useEffectAsync(async (mounted) => {
setLoading(true);
try {
const res = await fetch(url, options);
if (!mounted())
return;
const status = res.status;
setStatus(status);
if (status >= 200 && status <= 204) {
const json = await res.json();
if (!mounted())
return;
setResponse(json);
useEffectAsync(
async mounted => {
setLoading(true)
try {
const res = await fetch(url, options)
if (!mounted()) return
const status = res.status
setStatus(status)
if (status >= 200 && status <= 204) {
const json = await res.json()
if (!mounted()) return
setResponse(json)
}
} catch (error) {
if (!mounted()) return
setError(error)
} finally {
if (mounted()) setLoading(false)
}
} catch (error) {
if (!mounted())
return;
setError(error);
}
finally {
if (!mounted())
return;
setLoading(false)
}
}, [url]);
},
[url]
)
return {
response,
error,
status,
loading
};
};
loading,
}
}

Просмотреть файл

@ -7,7 +7,6 @@ import DeviceList from "./DeviceList"
import ServiceList from "./ServiceList"
import DeviceSpecificationList from "./DeviceSpecificationList"
import FilteredDeviceSpecificationList from "./FilteredDeviceSpecificationList"
import ServiceSpecificationList from "./ServiceSpecificationList"
import PacketsPreview from "./PacketsPreview"
import UpdateDeviceList from "./UpdateDeviceList";
import FlashButton from "./FlashButton";

Просмотреть файл

@ -17,7 +17,7 @@ export default function JoystickWidget(props: {
onUpdate?: (newx: number, newy: number) => void,
hostValues?: () => [number, number]
}) {
const { color, x, y, onUpdate, widgetSize, hostValues } = props;
const { color, x, y, onUpdate, hostValues } = props;
const { active, background, controlBackground } = useWidgetTheme(color);
const dragSurfaceRef = useRef<SVGCircleElement>();
const [grabbing, setGrabbing] = useState(false)

Просмотреть файл

@ -5,19 +5,18 @@ import useServiceHost from "../hooks/useServiceHost";
import LedPixelServiceHost from "../../../../src/hosts/ledpixelservicehost";
import SvgWidget from "../widgets/SvgWidget";
import useWidgetTheme from "../widgets/useWidgetTheme";
import useWidgetSize from "../widgets/useWidgetSize";
import { JDService } from "../../../../src/jdom/service";
import { useRegisterUnpackedValue } from "../../jacdac/useRegisterValue"
function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
const [r$, g$, b$] = [r / 255, g / 255, b / 255];
let cMin = Math.min(r$, g$, b$);
let cMax = Math.max(r$, g$, b$);
let cDelta = cMax - cMin;
const cMin = Math.min(r$, g$, b$);
const cMax = Math.max(r$, g$, b$);
const cDelta = cMax - cMin;
let h: number;
let s: number;
let l: number;
let maxAndMin = cMax + cMin;
const maxAndMin = cMax + cMin;
//lum
l = (maxAndMin / 2) * 100
@ -75,7 +74,7 @@ function LightStripWidget(props: {
numPixels: number,
actualBrightness: number,
host: LedPixelServiceHost,
widgetSize: string,
widgetSize?: string,
}) {
const { lightVariant, numPixels, actualBrightness, host, widgetSize } = props;
const { background, controlBackground } = useWidgetTheme()
@ -192,15 +191,14 @@ function LightMatrixWidget(props: {
lightVariant: LedPixelVariant,
actualBrightness: number,
host: LedPixelServiceHost,
widgetSize: string,
widgetSize?: string,
columns: number,
rows: number,
}) {
const { actualBrightness, columns, rows, host, widgetSize } = props;
const { columns, rows, host, widgetSize } = props;
const { background, controlBackground } = useWidgetTheme()
const widgetRef = useRef<SVGGElement>();
const clickeable = !!host;
// compute size
const pw = 8;
const ph = 8;
@ -255,7 +253,7 @@ function LightMatrixWidget(props: {
}
export default function LightWidget(props: { variant?: "icon" | "", service: JDService, widgetCount?: number }) {
const { service, widgetCount, variant } = props;
const { service } = props;
const [numPixels] = useRegisterUnpackedValue<[number]>(service.register(LedPixelReg.NumPixels));
const [lightVariant] = useRegisterUnpackedValue<[LedPixelVariant]>(service.register(LedPixelReg.Variant));
const [actualBrightness] = useRegisterUnpackedValue<[number]>(service.register(LedPixelReg.ActualBrightness));

Просмотреть файл

@ -2,7 +2,6 @@ import React from "react";
import { Grid, Slider, Typography } from "@material-ui/core";
import { isSet, roundWithPrecision } from "../../../../src/jdom/utils";
import { CSSProperties } from "@material-ui/core/styles/withStyles";
import useUnitIcons from "../hooks/useUnitIcon";
export default function ValueWithUnitWidget(props: {
value: number,
@ -23,7 +22,7 @@ export default function ValueWithUnitWidget(props: {
const hasValue = !isNaN(value);
const valueText = hasValue ? roundWithPrecision(value, precision).toLocaleString() : "--"
const valueTextLength = isSet(min) && isSet(max) ? [min, max, min + (min + max) / 3]
.map(v => roundWithPrecision(v, precision).toLocaleString().replace(/[,\.]/, '').length)
.map(v => roundWithPrecision(v, precision).toLocaleString().replace(/[,.]/, '').length)
.reduce((l, r) => Math.max(l, r), 0)
+ precision : valueText.length;

Просмотреть файл

@ -1,54 +1,83 @@
export function svgPointerPoint(svg: SVGSVGElement, event: React.PointerEvent): DOMPoint {
const point = svg.createSVGPoint();
point.x = event.clientX;
point.y = event.clientY;
const res = point.matrixTransform(svg.getScreenCTM().inverse());
return res;
import React from "react"
export function svgPointerPoint(
svg: SVGSVGElement,
event: React.PointerEvent
): DOMPoint {
const point = svg.createSVGPoint()
point.x = event.clientX
point.y = event.clientY
const res = point.matrixTransform(svg.getScreenCTM().inverse())
return res
}
export function closestPoint(pathNode: SVGPathElement, step: number, point: DOMPoint): number {
export function closestPoint(
pathNode: SVGPathElement,
step: number,
point: DOMPoint
): number {
const pathLength = pathNode.getTotalLength()
const distance2 = (p: DOMPoint) => {
const dx = p.x - point.x
const dy = p.y - point.y;
return dx * dx + dy * dy;
const dy = p.y - point.y
return dx * dx + dy * dy
}
let bestLength: number = 0;
let bestDistance = Infinity;
let bestLength = 0
let bestDistance = Infinity
for (let scanLength = 0; scanLength <= pathLength; scanLength += step) {
const scan = pathNode.getPointAtLength(scanLength);
const scanDistance = distance2(scan);
const scan = pathNode.getPointAtLength(scanLength)
const scanDistance = distance2(scan)
if (scanDistance < bestDistance) {
bestLength = scanLength;
bestDistance = scanDistance;
bestLength = scanLength
bestDistance = scanDistance
}
}
return bestLength / pathLength;
return bestLength / pathLength
}
export function polarToCartesian(centerX: number, centerY: number, radius: number, angleInDegrees: number) {
const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
export function polarToCartesian(
centerX: number,
centerY: number,
radius: number,
angleInDegrees: number
) {
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians),
}
}
export function describeArc(x: number, y: number, radius: number, startAngle: number, endAngle: number, large?: boolean) {
export function describeArc(
x: number,
y: number,
radius: number,
startAngle: number,
endAngle: number,
large?: boolean
) {
const start = polarToCartesian(x, y, radius, endAngle)
const end = polarToCartesian(x, y, radius, startAngle)
const start = polarToCartesian(x, y, radius, endAngle);
const end = polarToCartesian(x, y, radius, startAngle);
const largeArcFlag = large !== true && (endAngle - startAngle <= 180) ? "0" : "1";
const largeArcFlag =
large !== true && endAngle - startAngle <= 180 ? "0" : "1"
const d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0,
end.x, end.y
].join(" ");
"M",
start.x,
start.y,
"A",
radius,
radius,
0,
largeArcFlag,
0,
end.x,
end.y,
].join(" ")
return d;
}
return d
}

Просмотреть файл

@ -2,6 +2,7 @@ import React from "react";
// tslint:disable-next-line: no-submodule-imports
import ThemeTopLayout from "gatsby-theme-material-ui-top-layout/src/components/top-layout";
// eslint-disable-next-line react/prop-types
export default function TopLayout({ children, theme }) {
return <ThemeTopLayout theme={theme}>
{children}

Просмотреть файл

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, ReactNode } from "react";
import JacdacContext from "./Context";
import { BusState, JDBus } from "../../../src/jdom/bus";
import { createUSBBus } from "../../../src/jdom/usb";
@ -36,7 +36,8 @@ GamepadHostManager.start(bus);
if (typeof window !== "undefined")
new IFrameBridgeClient(bus, args.frameId); // start bridge
const JacdacProvider = ({ children }) => {
export default function JacdacProvider(props: { children: ReactNode }) {
const { children } = props;
const [firstConnect, setFirstConnect] = useState(false)
const [connectionState, setConnectionState] = useState(bus.connectionState);
@ -60,5 +61,3 @@ const JacdacProvider = ({ children }) => {
</JacdacContext.Provider>
)
}
export default JacdacProvider;

@ -1 +1 @@
Subproject commit ec18b3e91202f31c6a5165fe3acb11ce2eaad71b
Subproject commit 55577e7b798d70eca9704fca22950537779a0c96

4699
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -24,7 +24,7 @@
},
"scripts": {
"pullsubmodules": "git submodule update --init --recursive",
"lint": "node node_modules/tslint/bin/tslint --project tsconfig.json",
"lint": "node node_modules/eslint/bin/eslint.js src/**/*.ts docs/src/**/*.ts docs/src/**/*.tsx",
"predist": "rm -rf dist",
"dist": "node node_modules/rollup/dist/bin/rollup -c rollup.config.ts",
"predistdocs": "cd docs && node node_modules/gatsby/cli.js clean && cd .. && node ./tools/prepare.js",
@ -134,12 +134,16 @@
"@semantic-release/git": "^9.0.0",
"@types/expect": "^24.3.0",
"@types/mocha": "^8.2.1",
"@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.15.2",
"cli": "^1.0.1",
"colors": "^1.4.0",
"cross-env": "^7.0.3",
"lint-staged": "^10.5.4",
"eslint": "^7.20.0",
"eslint-plugin-react": "^7.22.0",
"lodash.camelcase": "^4.3.0",
"mocha": "^8.3.0",
"prettier": "2.2.1",
"prompt": "^1.1.0",
"replace-in-file": "^6.2.0",
"rollup": "^2.38.5",
@ -151,8 +155,6 @@
"semantic-release": "^17.3.8",
"shelljs": "^0.8.4",
"ts-node": "^7.0.1",
"tslint": "^6",
"tslint-microsoft-contrib": "^6",
"typescript": "^4.1.4",
"webusb": "^2.2.0"
},

Просмотреть файл

@ -1,31 +1,40 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/***
* Jacdac service/device specification to DTDL
*
*
* DTDL specification: https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md.
*/
import { serviceSpecificationFromClassIdentifier, serviceSpecificationFromName } from "../jdom/spec";
import { uniqueMap } from "../jdom/utils";
import {
serviceSpecificationFromClassIdentifier,
serviceSpecificationFromName,
} from "../jdom/spec"
import { uniqueMap } from "../jdom/utils"
export const DTDL_REFERENCE_URL = "https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md"
export const DTDL_REFERENCE_URL =
"https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md"
export const DTDL_NAME = "Digital Twins Definition Language"
export const DTDL_CONTEXT = "dtmi:dtdl:context;2";
export const DTDL_CONTEXT = "dtmi:dtdl:context;2"
// https://github.com/Azure/digital-twin-model-identifier
// ^dtmi:(?:_+[A-Za-z0-9]|[A-Za-z])(?:[A-Za-z0-9_]*[A-Za-z0-9])?(?::(?:_+[A-Za-z0-9]|[A-Za-z])(?:[A-Za-z0-9_]*[A-Za-z0-9])?)*;[1-9][0-9]{0,8}$
function toDTMI(segments: (string | number)[], version?: number) {
return `dtmi:jacdac:${[...segments]
.map(seg => seg === undefined ? "???" : typeof seg === "string" ? seg : `x${seg.toString(16)}`)
.map(seg => seg.replace(/(-|_)/g, ''))
.join(':')};${version !== undefined ? version : 1}`.toLowerCase();
.map(seg =>
seg === undefined
? "???"
: typeof seg === "string"
? seg
: `x${seg.toString(16)}`
)
.map(seg => seg.replace(/(-|_)/g, ""))
.join(":")};${version !== undefined ? version : 1}`.toLowerCase()
}
function toUnit(pkt: jdspec.PacketInfo) {
if (pkt.fields.length !== 1)
return undefined;
const field = pkt.fields[0];
if (!field.unit)
return undefined;
if (pkt.fields.length !== 1) return undefined
const field = pkt.fields[0]
if (!field.unit) return undefined
/**
* type Unit = "m" | "kg" | "g" | "s" | "A" | "K" | "cd" | "mol" | "Hz" | "rad" | "sr" | "N" | "Pa" | "J" | "W" | "C" | "V" | "F" | "Ohm"
@ -37,94 +46,92 @@ function toUnit(pkt: jdspec.PacketInfo) {
| "varh" | "kvarh" | "kVAh" | "Wh/km" | "KiB" | "GB" | "Mbit/s" | "B/s" | "MB/s" | "mV" | "mA" | "dBm" | "ug/m3"
| "mm/h" | "m/h" | "ppm" | "/100" | "/1000" | "hPa" | "mm" | "cm" | "km" | "km/h";
*/
const units: jdspec.SMap<{ semantic: string; unit: string; }> = {
const units: jdspec.SMap<{ semantic: string; unit: string }> = {
"m/s2": {
semantic: "Acceleration",
unit: "metrePerSecondSquared"
unit: "metrePerSecondSquared",
},
"rad": {
rad: {
semantic: "Angle",
unit: "radian"
unit: "radian",
},
"rad/s": {
semantic: "AngularVelocity",
unit: "radianPerSecond"
unit: "radianPerSecond",
},
"rad/s2": {
semantic: "AngularAcceleration",
unit: "radianPerSecondSquared"
unit: "radianPerSecondSquared",
},
"m": {
m: {
semantic: "Length",
unit: "metre"
unit: "metre",
},
"m2": {
m2: {
semantic: "Area",
unit: "squareMetre"
unit: "squareMetre",
},
"s": {
s: {
semantic: "TimeSpan",
unit: "second"
unit: "second",
},
"ms": {
ms: {
semantic: "TimeSpan",
unit: "millisecond"
unit: "millisecond",
},
"us": {
us: {
semantic: "TimeSpan",
unit: "microsecond"
unit: "microsecond",
},
"K": {
K: {
semantic: "Temperature",
unit: "kelvin"
unit: "kelvin",
},
"C": {
C: {
semantic: "Temperature",
unit: "degreeCelsius"
unit: "degreeCelsius",
},
"F": {
F: {
semantic: "Temperature",
unit: "degreeFahrenheit"
unit: "degreeFahrenheit",
},
"g": {
g: {
semantic: "Acceleration",
unit: "gForce"
unit: "gForce",
},
"mA": {
mA: {
semantic: "Current",
unit: "milliampere"
unit: "milliampere",
},
"uA": {
uA: {
semantic: "Current",
unit: "microampere"
unit: "microampere",
},
"A": {
A: {
semantic: "Current",
unit: "ampere"
unit: "ampere",
},
"mV": {
mV: {
semantic: "Voltage",
unit: "millivolt"
unit: "millivolt",
},
"uV": {
uV: {
semantic: "Voltage",
unit: "microvolt"
unit: "microvolt",
},
"V": {
V: {
semantic: "Voltage",
unit: "volt"
unit: "volt",
},
};
const unit = units[field.unit];
if (unit)
return unit;
}
const unit = units[field.unit]
if (unit) return unit
// ignoring some known units
if (["#", "/"].indexOf(field.unit) > -1)
return undefined;
if (["#", "/"].indexOf(field.unit) > -1) return undefined
//console.warn(`unsupported unit ${field.unit}`)
return undefined;
return undefined
}
// https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#primitive-schemas
@ -137,37 +144,36 @@ function enumSchema(srv: jdspec.ServiceSpec, en: jdspec.EnumInfo): DTDLSchema {
const dtdl = {
"@type": "Enum",
"@id": enumDTDI(srv, en),
"valueSchema": "integer",
"enumValues": Object.keys(en.members).map(k => ({
valueSchema: "integer",
enumValues: Object.keys(en.members).map(k => ({
name: escapeName(k),
displayName: k,
enumValue: en.members[k]
}))
enumValue: en.members[k],
})),
}
return dtdl;
return dtdl
}
function fieldType(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo, field: jdspec.PacketMember) {
let type: string;
if (field.type == "bool")
type = "boolean";
else if (field.isFloat)
type = "float";
function fieldType(
srv: jdspec.ServiceSpec,
pkt: jdspec.PacketInfo,
field: jdspec.PacketMember
) {
let type: string
if (field.type == "bool") type = "boolean"
else if (field.isFloat) type = "float"
else if (field.isSimpleType) {
if (/^(u|i)/.test(field.type))
type = "integer";
if (/^(u|i)/.test(field.type)) type = "integer"
else if (field.type === "B")
// base64 encoded binary data
type = "string";
}
else if (field.type === "string" || field.type == "string0")
type = "string";
else if (field.shift && /^(u|i)/.test(field.type))
type = "float"; // decimal type
type = "string"
} else if (field.type === "string" || field.type == "string0")
type = "string"
else if (field.shift && /^(u|i)/.test(field.type)) type = "float"
// decimal type
else {
const en = srv.enums[field.type];
if (en)
type = enumDTDI(srv, en);
const en = srv.enums[field.type]
if (en) type = enumDTDI(srv, en)
}
//if (!type)
@ -175,7 +181,7 @@ function fieldType(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo, field: jdspe
return {
name: field.name == "_" ? pkt.name : field.name,
type: type
type: type,
}
}
@ -183,7 +189,7 @@ function fieldType(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo, field: jdspe
function objectSchema(schemas: DTDLSchema[]): DTDLSchema {
return {
"@type": "Object",
"fields": schemas
fields: schemas,
}
}
@ -191,29 +197,31 @@ function objectSchema(schemas: DTDLSchema[]): DTDLSchema {
function arraySchema(schema: string | DTDLSchema): DTDLSchema {
return {
"@type": "Array",
"elementSchema": schema
elementSchema: schema,
}
}
// converts JADAC pkt data layout into a DTDL schema
function toSchema(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo, supportsArray?: boolean): string | DTDLSchema {
const fields = pkt.fields.map(field => fieldType(srv, pkt, field));
if (!fields.length)
return undefined;
function toSchema(
srv: jdspec.ServiceSpec,
pkt: jdspec.PacketInfo,
supportsArray?: boolean
): string | DTDLSchema {
const fields = pkt.fields.map(field => fieldType(srv, pkt, field))
if (!fields.length) return undefined
// a single data entry
if (fields.length === 1 && !pkt.fields[0].startRepeats)
return fields[0].type;
return fields[0].type
// map fields into schema
const schemas: DTDLSchema[] =
fields.map(field => ({
name: field.name,
schema: field.type
}))
const schemas: DTDLSchema[] = fields.map(field => ({
name: field.name,
schema: field.type,
}))
// is there an array?
const repeatIndex = pkt.fields.findIndex(field => field.startRepeats);
const repeatIndex = pkt.fields.findIndex(field => field.startRepeats)
if (repeatIndex < 0) {
// no array
@ -224,34 +232,39 @@ function toSchema(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo, supportsArray
// check if arrays are supported
if (!supportsArray) {
//console.warn(`arrays not supported in ${srv.shortName}.${pkt.name}`)
return undefined;
return undefined
}
if (repeatIndex == 0) {
// the whole structure is an array
return arraySchema(objectSchema(schemas))
}
else {
} else {
// split fields into prelude and array data
const nonRepeat = schemas.slice(0, repeatIndex);
const repeats = schemas.slice(repeatIndex);
const nonRepeat = schemas.slice(0, repeatIndex)
const repeats = schemas.slice(repeatIndex)
return objectSchema([
...nonRepeat,
{
name: "repeat",
schema: arraySchema(repeats.length > 1 ? objectSchema(repeats) : repeats[0])
}
]);
schema: arraySchema(
repeats.length > 1 ? objectSchema(repeats) : repeats[0]
),
},
])
}
}
function packetToDTDL(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo): DTDLContent {
function packetToDTDL(
srv: jdspec.ServiceSpec,
pkt: jdspec.PacketInfo
): DTDLContent {
const types: jdspec.SMap<string> = {
"const": "Property",
"rw": "Property",
"ro": "Telemetry",
"event": "Telemetry"
const: "Property",
rw: "Property",
ro: "Telemetry",
event: "Telemetry",
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dtdl: any = {
"@type": types[pkt.kind] || `Unsupported${pkt.kind}`,
name: pkt.name,
@ -262,133 +275,142 @@ function packetToDTDL(srv: jdspec.ServiceSpec, pkt: jdspec.PacketInfo): DTDLCont
case "report":
case "command":
// https://docs.microsoft.com/en-us/azure/digital-twins/concepts-models#azure-digital-twins-dtdl-implementation-specifics
return undefined;
return undefined
case "const":
case "rw":
case "ro":
case "event":
const unit = toUnit(pkt);
case "event": {
const unit = toUnit(pkt)
if (unit) {
dtdl.unit = unit.unit;
dtdl.unit = unit.unit
}
dtdl.schema = toSchema(srv, pkt, false)
if (pkt.kind === "rw")
dtdl.writable = true;
if (pkt.kind === "rw") dtdl.writable = true
if (!dtdl.schema && pkt.kind === "event") {
// keep a count of the events
dtdl["@type"] = [dtdl["@type"], "Event"]
dtdl.schema = toDTMI([srv.classIdentifier, "event"]);
}
else if (unit && unit.semantic)
dtdl.schema = toDTMI([srv.classIdentifier, "event"])
} else if (unit && unit.semantic)
dtdl["@type"] = [dtdl["@type"], unit.semantic]
break;
break
}
default:
//console.log(`unknown packet kind ${pkt.kind}`)
break;
break
}
if (!dtdl.schema) {
//console.log(`unknown schema for ${srv.name}.${pkt.name}`);
return undefined;
return undefined
}
return dtdl;
return dtdl
}
interface DTDLNode {
'@type'?: string;
'@id'?: string;
"@type"?: string
"@id"?: string
// 1-64 characters
// ^[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$
name?: string;
displayName?: string,
description?: string;
name?: string
displayName?: string
description?: string
}
interface DTDLSchema extends DTDLNode {
fields?: DTDLSchema[];
schema?: string | DTDLSchema;
elementSchema?: string | DTDLSchema;
fields?: DTDLSchema[]
schema?: string | DTDLSchema
elementSchema?: string | DTDLSchema
}
interface DTDLContent extends DTDLNode {
'@type': "Property" | "Command" | "Component" | "Interface";
unit?: string;
schema?: string | DTDLSchema;
"@type": "Property" | "Command" | "Component" | "Interface"
unit?: string
schema?: string | DTDLSchema
}
interface DTDLInterface extends DTDLContent {
contents: DTDLContent[];
extends?: string | string[];
schemas?: (DTDLSchema | DTDLInterface)[];
'@context'?: string;
contents: DTDLContent[]
extends?: string | string[]
schemas?: (DTDLSchema | DTDLInterface)[]
"@context"?: string
}
export function escapeName(name: string) {
name = name.trim().replace(/[^a-zA-Z0-9_]/g, '_');
if (!/^[a-zA-Z]/.test(name))
name = "a" + name;
name = name[0].toLowerCase() + name.slice(1);
return name.slice(0, 64);
name = name.trim().replace(/[^a-zA-Z0-9_]/g, "_")
if (!/^[a-zA-Z]/.test(name)) name = "a" + name
name = name[0].toLowerCase() + name.slice(1)
return name.slice(0, 64)
}
function escapeDisplayName(name: string) {
return name.slice(0, 64);
return name.slice(0, 64)
}
export function serviceSpecificationToDTDL(srv: jdspec.ServiceSpec): DTDLInterface {
export function serviceSpecificationToDTDL(
srv: jdspec.ServiceSpec
): DTDLInterface {
const dtdl: DTDLInterface = {
"@type": "Interface",
"@id": serviceSpecificationDTMI(srv),
"displayName": escapeDisplayName(srv.name),
"description": srv.notes["short"],
"contents": srv.packets
displayName: escapeDisplayName(srv.name),
description: srv.notes["short"],
contents: srv.packets
.filter(pkt => !pkt.derived && !pkt.internal)
.map(pkt => {
try {
return packetToDTDL(srv, pkt)
} catch (e) {
console.log(`failed to generate DTDL for ${srv.name}`, e);
return undefined;
console.log(`failed to generate DTDL for ${srv.name}`, e)
return undefined
}
}).filter(c => !!c)
})
.filter(c => !!c),
}
if (srv.extends.length)
dtdl.extends = srv.extends.map(id => serviceSpecificationDTMI(serviceSpecificationFromName(id)))
dtdl.extends = srv.extends.map(id =>
serviceSpecificationDTMI(serviceSpecificationFromName(id))
)
const hasEvents = srv.packets.find(pkt => pkt.kind === "event");
const hasEnums = Object.keys(srv.enums).length;
const hasEvents = srv.packets.find(pkt => pkt.kind === "event")
const hasEnums = Object.keys(srv.enums).length
if (hasEvents || hasEnums) {
dtdl.schemas = [];
dtdl.schemas = []
if (hasEvents)
dtdl.schemas.push({
"@id": toDTMI([srv.classIdentifier, "event"]),
"@type": "Object",
"fields": [{
"name": "count",
"schema": "integer"
}]
});
fields: [
{
name: "count",
schema: "integer",
},
],
})
if (hasEnums)
dtdl.schemas = dtdl.schemas.concat(Object.keys(srv.enums).map(en => enumSchema(srv, srv.enums[en])));
dtdl.schemas = dtdl.schemas.concat(
Object.keys(srv.enums).map(en => enumSchema(srv, srv.enums[en]))
)
}
dtdl["@context"] = DTDL_CONTEXT
return dtdl;
return dtdl
}
export function serviceSpecificationToComponent(srv: jdspec.ServiceSpec, name: string): any {
export function serviceSpecificationToComponent(
srv: jdspec.ServiceSpec,
name: string
): any {
const dtdl = {
"@type": "Component",
"name": name,
"displayName": escapeDisplayName(srv.name),
"schema": serviceSpecificationDTMI(srv)
name: name,
displayName: escapeDisplayName(srv.name),
schema: serviceSpecificationDTMI(srv),
}
return dtdl;
return dtdl
}
export interface DTDLGenerationOptions {
inlineServices?: boolean; // generate all services
inlineServices?: boolean // generate all services
}
export function serviceSpecificationDTMI(srv: jdspec.ServiceSpec) {
@ -396,44 +418,51 @@ export function serviceSpecificationDTMI(srv: jdspec.ServiceSpec) {
}
export function deviceSpecificationDTMI(dev: jdspec.DeviceSpec) {
return toDTMI(["devices", dev.id.replace(/-/g, ':')]);
return toDTMI(["devices", dev.id.replace(/-/g, ":")])
}
export function DTMIToRoute(dtmi: string) {
const route = dtmi.toLowerCase().replace(/;/, '-')
.replace(/:/g, "/") + ".json";
return route;
const route =
dtmi.toLowerCase().replace(/;/, "-").replace(/:/g, "/") + ".json"
return route
}
export function deviceSpecificationToDTDL(dev: jdspec.DeviceSpec, options?: DTDLGenerationOptions): any {
const services = dev.services.map(srv => serviceSpecificationFromClassIdentifier(srv));
const uniqueServices = uniqueMap(services, srv => srv.classIdentifier.toString(), srv => srv);
const schemas = uniqueServices.map(srv => serviceSpecificationToDTDL(srv));
export function deviceSpecificationToDTDL(
dev: jdspec.DeviceSpec,
options?: DTDLGenerationOptions
): any {
const services = dev.services.map(srv =>
serviceSpecificationFromClassIdentifier(srv)
)
const uniqueServices = uniqueMap(
services,
srv => srv.classIdentifier.toString(),
srv => srv
)
const schemas = uniqueServices.map(srv => serviceSpecificationToDTDL(srv))
// allocate names
let names: string[] = [];
services.forEach((srv, i) => {
let name = escapeName(srv.shortId || srv.shortName)
if (names.indexOf(name) < 0)
names.push(name)
const names: string[] = []
services.forEach((srv) => {
const name = escapeName(srv.shortId || srv.shortName)
if (names.indexOf(name) < 0) names.push(name)
else {
let count = 2;
while (names.indexOf(name + count) > -1)
count++;
names.push(name + count);
let count = 2
while (names.indexOf(name + count) > -1) count++
names.push(name + count)
}
})
const dtdl: DTDLInterface = {
"@type": "Interface",
"@id": deviceSpecificationDTMI(dev),
"displayName": escapeDisplayName(dev.name),
"description": dev.description,
"contents": services.map((srv, i) => serviceSpecificationToComponent(srv, names[i])),
"@context": DTDL_CONTEXT
displayName: escapeDisplayName(dev.name),
description: dev.description,
contents: services.map((srv, i) =>
serviceSpecificationToComponent(srv, names[i])
),
"@context": DTDL_CONTEXT,
}
if (options?.inlineServices)
return [dtdl, ...schemas]
else
return dtdl
if (options?.inlineServices) return [dtdl, ...schemas]
else return dtdl
}

Просмотреть файл

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const cli = require("cli")
const fs = require("fs-extra")
import { PACKET_PROCESS } from "../jdom/constants"

Просмотреть файл

@ -1,4 +1,4 @@
import { SystemReg, READING_SENT } from "../jdom/constants";
import { SystemReg } from "../jdom/constants";
import RegisterHost from "../jdom/registerhost";
import { LevelDetector } from "./leveldetector";
import SensorServiceHost, { SensorServiceOptions } from "./sensorservicehost";

Просмотреть файл

@ -47,6 +47,7 @@ function getStringOffset(packetType: number) {
}
}
/*
function getMaxStringLength(packetType: number) {
switch (packetType) {
case PACKET_TYPE_STRING:
@ -58,6 +59,7 @@ function getMaxStringLength(packetType: number) {
return undefined;
}
}
*/
function truncateString(str: string) {
// TODO

Просмотреть файл

@ -8,7 +8,7 @@ import {
SRV_DISTANCE, SRV_E_CO2, SRV_HUMIDITY, SRV_LED_PIXEL, SRV_MATRIX_KEYPAD,
SRV_MOTOR, SRV_POTENTIOMETER,
SRV_PROTO_TEST, SRV_RAIN_GAUGE, SRV_RELAY,
SRV_ROLE_MANAGER, SRV_JOYSTICK,
SRV_JOYSTICK,
SRV_ROTARY_ENCODER,
SRV_SERVO, SRV_SETTINGS, SRV_SWITCH, SRV_THERMOMETER, SRV_TRAFFIC_LIGHT,
SRV_VIBRATION_MOTOR, SRV_TVOC, SRV_WIND_DIRECTION, SRV_WIND_SPEED,
@ -22,7 +22,7 @@ import {
} from "../jdom/constants";
import DeviceHost from "../jdom/devicehost";
import ProtocolTestServiceHost from "../jdom/protocoltestservicehost";
import ServiceHost, { ServiceHostOptions } from "../jdom/servicehost";
import ServiceHost from "../jdom/servicehost";
import ArcadeGamepadServiceHost from "./arcadegamepadservicehost";
import ButtonServiceHost from "./buttonservicehost";
import BuzzerServiceHost from "./buzzerservicehost";
@ -607,7 +607,7 @@ const _hosts: {
{
name: "relay 4x (SSR/5A)",
serviceClasses: [SRV_RELAY],
services: () => Array(4).fill(0).map(_ => new ServiceHost(SRV_RELAY, {
services: () => Array(4).fill(0).map(() => new ServiceHost(SRV_RELAY, {
intensityValues: [false],
variant: RelayVariant.SolidState,
registerValues: [
@ -654,12 +654,12 @@ const _hosts: {
{
name: "servo x 2",
serviceClasses: [SRV_SERVO],
services: () => Array(2).fill(0).map((_, i) => new ServoServiceHost(microServoOptions))
services: () => Array(2).fill(0).map(() => new ServoServiceHost(microServoOptions))
},
{
name: "servo x 4",
serviceClasses: [SRV_SERVO],
services: () => Array(4).fill(0).map((_, i) => new ServoServiceHost(microServoOptions))
services: () => Array(4).fill(0).map(() => new ServoServiceHost(microServoOptions))
},
{
name: "servo x 6",

Просмотреть файл

@ -51,7 +51,7 @@ function hsv(hue: number, sat: number, val: number): RGB {
const rampup_adj_with_floor = (rampup_amp_adj + brightness_floor);
const rampdown_adj_with_floor = (rampdown_amp_adj + brightness_floor);
let r: number = 0, g: number = 0, b: number = 0;
let r = 0, g = 0, b = 0;
if (section) {
if (section == 1) {
// section 1: 0x40..0x7F
@ -107,20 +107,20 @@ export default class LedPixelServiceHost extends ServiceHost {
private pxbuffer: Uint8Array = new Uint8Array(0);
private prog_mode: number = 0;
private prog_tmpmode: number = 0;
private prog_mode = 0;
private prog_tmpmode = 0;
private range_start: number = 0;
private range_end: number = 0;
private range_len: number = 0;
private range_ptr: number = 0;
private range_start = 0;
private range_end = 0;
private range_len = 0;
private range_ptr = 0;
private prog_ptr: number = 0;
private prog_size: number = 0;
private prog_ptr = 0;
private prog_size = 0;
private prog_data = new Uint8Array(0);
private dirty: boolean = true;
private inited: boolean = false;
private dirty = true;
private inited = false;
power_enable = false;
@ -215,7 +215,7 @@ export default class LedPixelServiceHost extends ServiceHost {
return false;
const p = this.pxbuffer;
let pi = this.range_ptr++ * 3;
const pi = this.range_ptr++ * 3;
// fast path
if (this.prog_tmpmode == LIGHT_MODE_REPLACE) {
p[pi + 0] = c.r;
@ -267,7 +267,7 @@ export default class LedPixelServiceHost extends ServiceHost {
let current_prev = 0;
let di = 0;
while (n--) {
let v = pxbuffer[di++];
const v = pxbuffer[di++];
current += SCALE0(v, intensity);
current_prev += SCALE0(v, prev_intensity);
current_full += v;
@ -279,8 +279,8 @@ export default class LedPixelServiceHost extends ServiceHost {
current_full *= 46;
// 14mA is the chip at 48MHz, 930uA per LED is static
let base_current = 14000 + 930 * numpixels;
let current_limit = maxpower * 1000 - base_current;
const base_current = 14000 + 930 * numpixels;
const current_limit = maxpower * 1000 - base_current;
if (current <= current_limit) {
this.intensity = intensity;
@ -443,7 +443,7 @@ export default class LedPixelServiceHost extends ServiceHost {
let first = range_start * 3;
let middle = (range_start + shift) * 3;
let last = range_end * 3;
const last = range_end * 3;
let next = middle;
while (first != next) {

Просмотреть файл

@ -1,4 +1,4 @@
import { CHANGE, LedReg, LedVariant, REPORT_UPDATE, SRV_LED } from "../jdom/constants";
import { CHANGE, LedReg, LedVariant, SRV_LED } from "../jdom/constants";
import { JDEventSource } from "../jdom/eventsource";
import RegisterHost from "../jdom/registerhost";
import ServiceHost from "../jdom/servicehost";

Просмотреть файл

@ -22,7 +22,7 @@ export default class RealTimeClockServiceHost
extends SensorServiceHost<RealTimeClockReadingType> {
readonly error: RegisterHost<[number]>;
readonly precision: RegisterHost<[number]>;
private lastSecond: number = 0;
private lastSecond = 0;
constructor() {
super(SRV_REAL_TIME_CLOCK, {
@ -47,7 +47,7 @@ export default class RealTimeClockServiceHost
}
private handleSetTime(pkt: Packet) {
console.log(`set time`)
console.log(`set time`, { pkt })
}
private refreshTime() {

Просмотреть файл

@ -1,16 +1,16 @@
import { SystemReg } from "../../jacdac-spec/dist/specconstants";
import { CHANGE, READING_SENT, REFRESH, REPORT_UPDATE, SensorReg } from "../jdom/constants";
import { CHANGE, READING_SENT, REFRESH, SensorReg } from "../jdom/constants";
import { PackedValues } from "../jdom/pack";
import RegisterHost from "../jdom/registerhost";
import ServiceHost, { ServiceHostOptions } from "../jdom/servicehost";
import TrafficLightServiceHost from "./trafficlightservicehost";
export interface SensorServiceOptions<TReading extends any[]> extends ServiceHostOptions {
export interface SensorServiceOptions<TReading extends PackedValues> extends ServiceHostOptions {
readingValues?: TReading,
readingError?: TReading,
streamingInterval?: number,
};
}
export default class SensorServiceHost<TReading extends any[]> extends ServiceHost {
export default class SensorServiceHost<TReading extends PackedValues> extends ServiceHost {
readonly reading: RegisterHost<TReading>;
readonly readingError: RegisterHost<TReading>;
readonly streamingSamples: RegisterHost<[number]>;

Просмотреть файл

@ -1,4 +1,4 @@
import { read16, read32 } from "./utils";
import { read16, read32 } from "./utils"
export enum NumberFormat {
Int8LE = 1,
@ -25,28 +25,49 @@ export enum NumberFormat {
function fmtInfoCore(fmt: NumberFormat) {
switch (fmt) {
case NumberFormat.Int8LE: return -1;
case NumberFormat.UInt8LE: return 1;
case NumberFormat.Int16LE: return -2;
case NumberFormat.UInt16LE: return 2;
case NumberFormat.Int32LE: return -4;
case NumberFormat.UInt32LE: return 4;
case NumberFormat.Int64LE: return -8;
case NumberFormat.UInt64LE: return 8;
case NumberFormat.Int8BE: return -10;
case NumberFormat.UInt8BE: return 10;
case NumberFormat.Int16BE: return -20;
case NumberFormat.UInt16BE: return 20;
case NumberFormat.Int32BE: return -40;
case NumberFormat.UInt32BE: return 40;
case NumberFormat.Int64BE: return -80;
case NumberFormat.UInt64BE: return 80;
case NumberFormat.Int8LE:
return -1
case NumberFormat.UInt8LE:
return 1
case NumberFormat.Int16LE:
return -2
case NumberFormat.UInt16LE:
return 2
case NumberFormat.Int32LE:
return -4
case NumberFormat.UInt32LE:
return 4
case NumberFormat.Int64LE:
return -8
case NumberFormat.UInt64LE:
return 8
case NumberFormat.Int8BE:
return -10
case NumberFormat.UInt8BE:
return 10
case NumberFormat.Int16BE:
return -20
case NumberFormat.UInt16BE:
return 20
case NumberFormat.Int32BE:
return -40
case NumberFormat.UInt32BE:
return 40
case NumberFormat.Int64BE:
return -80
case NumberFormat.UInt64BE:
return 80
case NumberFormat.Float32LE: return 4;
case NumberFormat.Float32BE: return 40;
case NumberFormat.Float64LE: return 8;
case NumberFormat.Float64BE: return 80;
default: throw new Error("unknown format");
case NumberFormat.Float32LE:
return 4
case NumberFormat.Float32BE:
return 40
case NumberFormat.Float64LE:
return 8
case NumberFormat.Float64BE:
return 80
default:
throw new Error("unknown format")
}
}
@ -83,31 +104,35 @@ export function sizeOfNumberFormat(format: NumberFormat) {
case NumberFormat.UInt8LE:
case NumberFormat.Int8BE:
case NumberFormat.UInt8BE:
return 1;
return 1
case NumberFormat.Int16LE:
case NumberFormat.UInt16LE:
case NumberFormat.Int16BE:
case NumberFormat.UInt16BE:
return 2;
return 2
case NumberFormat.Int32LE:
case NumberFormat.Int32BE:
case NumberFormat.UInt32BE:
case NumberFormat.UInt32LE:
case NumberFormat.Float32BE:
case NumberFormat.Float32LE:
return 4;
return 4
case NumberFormat.UInt64BE:
case NumberFormat.Int64BE:
case NumberFormat.UInt64LE:
case NumberFormat.Int64LE:
case NumberFormat.Float64BE:
case NumberFormat.Float64LE:
return 8;
return 8
}
return 0;
return 0
}
export function getNumber(buf: ArrayLike<number>, fmt: NumberFormat, offset: number) {
export function getNumber(
buf: ArrayLike<number>,
fmt: NumberFormat,
offset: number
) {
switch (fmt) {
case NumberFormat.UInt8BE:
case NumberFormat.UInt8LE:
@ -126,35 +151,38 @@ export function getNumber(buf: ArrayLike<number>, fmt: NumberFormat, offset: num
case NumberFormat.UInt64LE:
return read32(buf, offset) + read32(buf, offset + 4) * 0x100000000
case NumberFormat.Int64LE:
return read32(buf, offset) + (read32(buf, offset + 4) >> 0) * 0x100000000
default:
return (
read32(buf, offset) +
(read32(buf, offset + 4) >> 0) * 0x100000000
)
default: {
const inf = fmtInfo(fmt)
if (inf.isFloat) {
let arr = new Uint8Array(inf.size)
const arr = new Uint8Array(inf.size)
for (let i = 0; i < inf.size; ++i) {
arr[i] = buf[offset + i]
}
if (inf.swap)
arr.reverse()
if (inf.size == 4)
return new Float32Array(arr.buffer)[0]
else
return new Float64Array(arr.buffer)[0]
if (inf.swap) arr.reverse()
if (inf.size == 4) return new Float32Array(arr.buffer)[0]
else return new Float64Array(arr.buffer)[0]
}
throw new Error("unsupported fmt:" + fmt)
}
}
}
export function setNumber(buf: Uint8Array, fmt: NumberFormat, offset: number, r: number) {
let inf = fmtInfo(fmt)
export function setNumber(
buf: Uint8Array,
fmt: NumberFormat,
offset: number,
r: number
) {
const inf = fmtInfo(fmt)
if (inf.isFloat) {
let arr = new Uint8Array(inf.size)
if (inf.size == 4)
new Float32Array(arr.buffer)[0] = r
else
new Float64Array(arr.buffer)[0] = r
if (inf.swap)
arr.reverse()
const arr = new Uint8Array(inf.size)
if (inf.size == 4) new Float32Array(arr.buffer)[0] = r
else new Float64Array(arr.buffer)[0] = r
if (inf.swap) arr.reverse()
for (let i = 0; i < inf.size; ++i) {
buf[offset + i] = arr[i]
}
@ -162,8 +190,8 @@ export function setNumber(buf: Uint8Array, fmt: NumberFormat, offset: number, r:
}
for (let i = 0; i < inf.size; ++i) {
let off = !inf.swap ? offset + i : offset + inf.size - i - 1
buf[off] = (r & 0xff)
const off = !inf.swap ? offset + i : offset + inf.size - i - 1
buf[off] = r & 0xff
r >>= 8
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -8,7 +8,7 @@ export type ArgType = number | boolean | string | Uint8Array
export function packArguments(info: jdspec.PacketInfo, args: ArgType[]) {
let repeatIdx = -1
let numReps = 0
let argIdx = 0
const argIdx = 0
let dst = 0
const buf = new Uint8Array(256)

Просмотреть файл

@ -168,14 +168,14 @@ export class JDDevice extends JDNode {
return this._shortId;
}
get firmwareInfo() {
return this._firmwareInfo;
}
get parent(): JDNode {
return this.bus
}
get firmwareInfo() {
return this._firmwareInfo;
}
set firmwareInfo(info: FirmwareInfo) {
const changed = JSON.stringify(this._firmwareInfo) !== JSON.stringify(info);
if (changed) {
@ -283,7 +283,7 @@ export class JDDevice extends JDNode {
assert(this.announced)
if (!this._services) {
const n = this.serviceLength;
let s = [];
const s = [];
for (let i = 0; i < n; ++i)
s.push(new JDService(this, i));
this._services = s;

Просмотреть файл

@ -8,7 +8,7 @@ import { JDServiceMemberNode } from "./servicemembernode";
export class JDEvent extends JDServiceMemberNode {
private _lastReportPkt: Packet;
private _count: number = 0;
private _count = 0;
constructor(
service: JDService,

Просмотреть файл

@ -3,6 +3,7 @@ import { NEW_LISTENER, REMOVE_LISTENER, ERROR, CHANGE } from "./constants";
import { Observable, Observer } from "./observable";
import Flags from "./flags";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type EventHandler = (...args: any[]) => void;
interface Listener {
@ -93,6 +94,7 @@ export class JDEventSource {
* @param eventName
* @param args
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
emit(eventName: string, ...args: any[]): boolean {
if (!eventName) return false;
@ -114,6 +116,7 @@ export class JDEventSource {
--i;
}
try {
// eslint-disable-next-line prefer-spread
handler.apply(null, args);
}
catch (e) {

Просмотреть файл

@ -86,7 +86,7 @@ class FlashClient {
private async startFlashAsync() {
this.sessionId = (Math.random() * 0x10000000) | 0
for (let d of this.classClients) {
for (const d of this.classClients) {
d.start()
log(`flashing ${d.device.shortId}; available flash=${d.flashSize / 1024}kb; page=${d.pageSize}b`)
}
@ -96,7 +96,7 @@ class FlashClient {
this.allPending()
for (let i = 0; i < BL_RETRIES; ++i) {
for (let d of this.classClients) {
for (const d of this.classClients) {
if (d.pending) {
if (d.lastStatus && d.lastStatus.getNumber(NumberFormat.UInt32LE, 0) == this.sessionId) {
d.pending = false
@ -118,14 +118,14 @@ class FlashClient {
}
private async endFlashAsync() {
for (let f of this.classClients) {
for (const f of this.classClients) {
await delay(10)
await f.device.sendCtrlCommand(ControlCmd.Reset)
}
}
private allPending() {
for (let c of this.classClients) {
for (const c of this.classClients) {
c.pending = true
c.lastStatus = null
}
@ -133,7 +133,7 @@ class FlashClient {
private numPending() {
let num = 0
for (let c of this.classClients)
for (const c of this.classClients)
if (c.pending) num++
return num
}
@ -156,7 +156,7 @@ class FlashClient {
if (page.data.length != this.pageSize)
throw new Error("invalid page size")
for (let f of this.classClients)
for (const f of this.classClients)
f.lastStatus = null
this.allPending()
@ -176,7 +176,7 @@ class FlashClient {
if (i == 0 || currSubpage < numSubpage)
await p.sendAsMultiCommandAsync(this.bus, SRV_BOOTLOADER)
else {
for (let f of this.classClients)
for (const f of this.classClients)
if (f.pending) {
f.lastStatus = null
await f.sendCommandAsync(p)
@ -187,7 +187,7 @@ class FlashClient {
await this.waitForStatusAsync()
for (let f of this.classClients) {
for (const f of this.classClients) {
if (f.pending) {
let err = ""
if (f.lastStatus) {
@ -248,7 +248,7 @@ class FlashClient {
}
} finally {
// even if resetting failed, unregister event listeners
for (let d of this.classClients) {
for (const d of this.classClients) {
d.stop()
}
}
@ -265,7 +265,7 @@ export function parseUF2(uf2: Uint8Array, store: string): FirmwareBlob[] {
let currBlob: FirmwareBlob
for (let off = 0; off < uf2.length; off += 512) {
const header = uf2.slice(off, off + 32)
let [magic0, magic1, flags, trgaddr, payloadSize, blkNo, numBlocks, familyID] =
const [magic0, magic1, flags, trgaddr, payloadSize, blkNo, numBlocks, familyID] =
bufferToArray(header, NumberFormat.UInt32LE)
if (magic0 != UF2_MAGIC_START0 ||
magic1 != UF2_MAGIC_START1 ||

Просмотреть файл

@ -1,14 +1,25 @@
import { CMSISProto } from "./microbit";
import { USBOptions } from "./usb";
import { CMSISProto } from "./microbit"
import { USBOptions } from "./usb"
import {
throwError, delay, assert, SMap, PromiseBuffer, PromiseQueue, memcpy, write32, write16, read16,
encodeU32LE, read32, bufferToString
} from "./utils";
throwError,
delay,
assert,
SMap,
PromiseBuffer,
PromiseQueue,
memcpy,
write32,
write16,
read16,
encodeU32LE,
read32,
bufferToString,
} from "./utils"
const controlTransferGetReport = 0x01;
const controlTransferSetReport = 0x09;
const controlTransferOutReport = 0x200;
const controlTransferInReport = 0x100;
const controlTransferGetReport = 0x01
const controlTransferSetReport = 0x09
const controlTransferOutReport = 0x200
const controlTransferInReport = 0x100
// see https://github.com/microsoft/uf2/blob/main/hf2.md for full spec
export const HF2_DEVICE_MAJOR = 42
@ -28,11 +39,11 @@ export const HF2_CMD_INFO = 0x0002
// no arguments
// results is utf8 character array
export const HF2_CMD_RESET_INTO_APP = 0x0003// no arguments, no result
export const HF2_CMD_RESET_INTO_APP = 0x0003 // no arguments, no result
export const HF2_CMD_RESET_INTO_BOOTLOADER = 0x0004 // no arguments, no result
export const HF2_CMD_RESET_INTO_BOOTLOADER = 0x0004 // no arguments, no result
export const HF2_CMD_START_FLASH = 0x0005 // no arguments, no result
export const HF2_CMD_START_FLASH = 0x0005 // no arguments, no result
export const HF2_CMD_WRITE_FLASH_PAGE = 0x0006
/*
@ -80,10 +91,10 @@ export const HF2_CMD_DMESG = 0x0010
// results is utf8 character array
export const HF2_FLAG_SERIAL_OUT = 0x80
export const HF2_FLAG_SERIAL_ERR = 0xC0
export const HF2_FLAG_SERIAL_ERR = 0xc0
export const HF2_FLAG_CMDPKT_LAST = 0x40
export const HF2_FLAG_CMDPKT_BODY = 0x00
export const HF2_FLAG_MASK = 0xC0
export const HF2_FLAG_MASK = 0xc0
export const HF2_SIZE_MASK = 63
export const HF2_STATUS_OK = 0x00
@ -100,27 +111,27 @@ export const HF2_CMD_JDS_SEND = 0x0021
export const HF2_EV_JDS_PACKET = 0x800020
export class Transport {
dev: USBDevice;
iface: USBInterface;
altIface: USBAlternateInterface;
epIn: USBEndpoint;
epOut: USBEndpoint;
readLoopStarted = false;
ready = false;
rawMode = false;
dev: USBDevice
iface: USBInterface
altIface: USBAlternateInterface
epIn: USBEndpoint
epOut: USBEndpoint
readLoopStarted = false
ready = false
rawMode = false
constructor(private usb: USBOptions) { }
constructor(private usb: USBOptions) {}
onData = (v: Uint8Array) => { };
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onData = (v: Uint8Array) => {}
onError = (e: Error) => {
console.error("USB error: " + (e ? e.stack : e))
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
log(msg: string, v?: any) {
if (v != undefined)
console.log("USB: " + msg, v)
else
console.log("USB: " + msg)
if (v != undefined) console.log("USB: " + msg, v)
else console.log("USB: " + msg)
}
private mkProto(): Proto {
@ -139,9 +150,11 @@ export class Transport {
this.ready = false
if (!this.dev) return Promise.resolve()
this.log("close device")
return this.dev.close()
return this.dev
.close()
.catch(e => {
// just ignore errors closing, most likely device just disconnected
console.debug(e)
})
.then(() => {
this.clearDev()
@ -150,49 +163,53 @@ export class Transport {
}
recvPacketAsync(): Promise<Uint8Array> {
if (!this.rawMode)
this.error("rawMode required")
if (!this.rawMode) this.error("rawMode required")
return this.recvPacketCoreAsync()
}
private recvPacketCoreAsync(): Promise<Uint8Array> {
let final = (res: USBInTransferResult) => {
if (res.status != "ok")
this.error("USB IN transfer failed")
let arr = new Uint8Array(res.data.buffer)
if (arr.length == 0)
return this.recvPacketCoreAsync()
const final = (res: USBInTransferResult) => {
if (res.status != "ok") this.error("USB IN transfer failed")
const arr = new Uint8Array(res.data.buffer)
if (arr.length == 0) return this.recvPacketCoreAsync()
return arr
}
if (!this.dev)
return Promise.reject(new Error("Disconnected"))
if (!this.dev) return Promise.reject(new Error("Disconnected"))
if (!this.epIn) {
return this.dev.controlTransferIn({
requestType: "class",
recipient: "interface",
request: controlTransferGetReport,
value: controlTransferInReport,
index: this.iface.interfaceNumber
}, 64).then(final)
return this.dev
.controlTransferIn(
{
requestType: "class",
recipient: "interface",
request: controlTransferGetReport,
value: controlTransferInReport,
index: this.iface.interfaceNumber,
},
64
)
.then(final)
}
return this.dev.transferIn(this.epIn.endpointNumber, 64)
.then(final)
return this.dev.transferIn(this.epIn.endpointNumber, 64).then(final)
}
error(msg: string) {
console.error(`USB error on device ${this.dev ? this.dev.productName : "n/a"} (${msg})`)
console.error(
`USB error on device ${
this.dev ? this.dev.productName : "n/a"
} (${msg})`
)
this.onError(new Error("USB error"))
}
private async readLoop() {
if (this.rawMode || this.readLoopStarted)
return
if (this.rawMode || this.readLoopStarted) return
this.readLoopStarted = true
this.log("start read loop")
// eslint-disable-next-line no-constant-condition
while (true) {
if (!this.ready) {
break
@ -221,60 +238,69 @@ export class Transport {
}
sendPacketAsync(pkt: Uint8Array) {
if (!this.dev)
return Promise.reject(new Error("Disconnected"))
if (!this.dev) return Promise.reject(new Error("Disconnected"))
assert(pkt.length <= 64)
if (!this.epOut) {
return this.dev.controlTransferOut({
requestType: "class",
recipient: "interface",
request: controlTransferSetReport,
value: controlTransferOutReport,
index: this.iface.interfaceNumber
}, pkt).then(res => {
if (res.status != "ok")
this.error("USB CTRL OUT transfer failed")
})
return this.dev
.controlTransferOut(
{
requestType: "class",
recipient: "interface",
request: controlTransferSetReport,
value: controlTransferOutReport,
index: this.iface.interfaceNumber,
},
pkt
)
.then(res => {
if (res.status != "ok")
this.error("USB CTRL OUT transfer failed")
})
}
return this.dev.transferOut(this.epOut.endpointNumber, pkt)
return this.dev
.transferOut(this.epOut.endpointNumber, pkt)
.then(res => {
if (res.status != "ok")
this.error("USB OUT transfer failed")
if (res.status != "ok") this.error("USB OUT transfer failed")
})
}
get isMicrobit() {
return this.dev && this.dev.productId == 516 && this.dev.vendorId == 3368
return (
this.dev && this.dev.productId == 516 && this.dev.vendorId == 3368
)
}
private checkDevice() {
this.iface = undefined;
this.altIface = undefined;
if (!this.dev)
return false;
this.log("connect device: " + this.dev.manufacturerName + " " + this.dev.productName)
this.iface = undefined
this.altIface = undefined
if (!this.dev) return false
this.log(
"connect device: " +
this.dev.manufacturerName +
" " +
this.dev.productName
)
// resolve interfaces
const subcl = this.isMicrobit ? 0 : HF2_DEVICE_MAJOR
for (const iface of this.dev.configuration.interfaces) {
const alt = iface.alternates[0]
if (alt.interfaceClass == 0xff && alt.interfaceSubclass == subcl) {
this.iface = iface;
this.altIface = alt;
break;
this.iface = iface
this.altIface = alt
break
}
}
if (this.isMicrobit)
this.rawMode = true
return !!this.iface;
if (this.isMicrobit) this.rawMode = true
return !!this.iface
}
private async tryReconnectAsync() {
try {
const devices = await this.usb.getDevices();
this.dev = devices[0];
const devices = await this.usb.getDevices()
this.dev = devices[0]
} catch (e) {
console.log(e)
this.dev = undefined;
this.dev = undefined
}
}
@ -286,29 +312,28 @@ export class Transport {
// hf2 devices (incl. arcade)
classCode: 255,
subclassCode: HF2_DEVICE_MAJOR,
}, {
},
{
// micro:bit v2
vendorId: 3368,
productId: 516
}
]
productId: 516,
},
],
})
} catch (e) {
console.log(e)
this.dev = undefined;
this.dev = undefined
}
}
async connectAsync(background: boolean) {
await this.tryReconnectAsync();
if (!this.dev && !background)
await this.requestDeviceAsync();
await this.tryReconnectAsync()
if (!this.dev && !background) await this.requestDeviceAsync()
// background call and no device, just give up for now
if (!this.dev && background)
throwError("device not paired", true);
if (!this.dev && background) throwError("device not paired", true)
// let's connect!
await this.openDeviceAsync();
await this.openDeviceAsync()
const proto = this.mkProto()
await proto.postConnectAsync()
@ -317,18 +342,20 @@ export class Transport {
}
private async openDeviceAsync() {
if (!this.dev)
throwError("device not found")
if (!this.checkDevice())
throwError("device does not support HF2")
if (!this.dev) throwError("device not found")
if (!this.checkDevice()) throwError("device does not support HF2")
await this.dev.open()
await this.dev.selectConfiguration(1)
if (this.altIface.endpoints.length) {
this.epIn = this.altIface.endpoints.filter(e => e.direction == "in")[0]
this.epOut = this.altIface.endpoints.filter(e => e.direction == "out")[0]
assert(this.epIn.packetSize == 64);
assert(this.epOut.packetSize == 64);
this.epIn = this.altIface.endpoints.filter(
e => e.direction == "in"
)[0]
this.epOut = this.altIface.endpoints.filter(
e => e.direction == "out"
)[0]
assert(this.epIn.packetSize == 64)
assert(this.epOut.packetSize == 64)
}
this.log("claim interface")
await this.dev.claimInterface(this.iface.interfaceNumber)
@ -348,17 +375,17 @@ export interface Proto {
class HF2Proto implements Proto {
eventHandlers: SMap<(buf: Uint8Array) => void> = {}
msgs = new PromiseBuffer<Uint8Array>()
cmdSeq = (Math.random() * 0xffff) | 0;
private lock = new PromiseQueue();
cmdSeq = (Math.random() * 0xffff) | 0
private lock = new PromiseQueue()
constructor(public io: Transport) {
let frames: Uint8Array[] = []
io.onData = buf => {
let tp = buf[0] & HF2_FLAG_MASK
let len = buf[0] & 63
const tp = buf[0] & HF2_FLAG_MASK
const len = buf[0] & 63
//console.log(`msg tp=${tp} len=${len}`)
let frame = new Uint8Array(len)
const frame = new Uint8Array(len)
memcpy(frame, 0, buf, 1, len)
if (tp & HF2_FLAG_SERIAL_OUT) {
this.onSerial(frame, tp == HF2_FLAG_SERIAL_ERR)
@ -370,10 +397,10 @@ class HF2Proto implements Proto {
} else {
assert(tp == HF2_FLAG_CMDPKT_LAST)
let total = 0
for (let f of frames) total += f.length
let r = new Uint8Array(total)
for (const f of frames) total += f.length
const r = new Uint8Array(total)
let ptr = 0
for (let f of frames) {
for (const f of frames) {
memcpy(r, ptr, f)
ptr += f.length
}
@ -388,7 +415,6 @@ class HF2Proto implements Proto {
}
}
error(m: string) {
return this.io.error(m)
}
@ -396,28 +422,32 @@ class HF2Proto implements Proto {
talkAsync(cmd: number, data?: Uint8Array) {
let len = 8
if (data) len += data.length
let pkt = new Uint8Array(len)
let seq = ++this.cmdSeq & 0xffff
write32(pkt, 0, cmd);
write16(pkt, 4, seq);
write16(pkt, 6, 0);
if (data)
memcpy(pkt, 8, data, 0, data.length)
const pkt = new Uint8Array(len)
const seq = ++this.cmdSeq & 0xffff
write32(pkt, 0, cmd)
write16(pkt, 4, seq)
write16(pkt, 6, 0)
if (data) memcpy(pkt, 8, data, 0, data.length)
let numSkipped = 0
let handleReturnAsync = (): Promise<Uint8Array> =>
this.msgs.shiftAsync(1000) // we wait up to a second
const handleReturnAsync = (): Promise<Uint8Array> =>
this.msgs
.shiftAsync(1000) // we wait up to a second
.then(res => {
if (read16(res, 0) != seq) {
if (numSkipped < 3) {
numSkipped++
this.io.log(`message out of sync, (${seq} vs ${read16(res, 0)}); will re-try`)
this.io.log(
`message out of sync, (${seq} vs ${read16(
res,
0
)}); will re-try`
)
return handleReturnAsync()
}
this.error("out of sync")
}
let info = ""
if (res[3])
info = "; info=" + res[3]
if (res[3]) info = "; info=" + res[3]
switch (res[2]) {
case HF2_STATUS_OK:
return res.slice(4)
@ -439,29 +469,28 @@ class HF2Proto implements Proto {
})
return this.lock.enqueue("talk", () =>
this.sendMsgAsync(pkt)
.then(handleReturnAsync))
this.sendMsgAsync(pkt).then(handleReturnAsync)
)
}
private sendMsgAsync(buf: Uint8Array, serial: number = 0) {
private sendMsgAsync(buf: Uint8Array, serial = 0) {
// Util.assert(buf.length <= this.maxMsgSize)
let frame = new Uint8Array(64)
let loop = (pos: number): Promise<void> => {
const frame = new Uint8Array(64)
const loop = (pos: number): Promise<void> => {
let len = buf.length - pos
if (len <= 0) return Promise.resolve()
if (len > 63) {
len = 63
frame[0] = HF2_FLAG_CMDPKT_BODY;
frame[0] = HF2_FLAG_CMDPKT_BODY
} else {
frame[0] = HF2_FLAG_CMDPKT_LAST;
frame[0] = HF2_FLAG_CMDPKT_LAST
}
if (serial) frame[0] = serial == 1 ? HF2_FLAG_SERIAL_OUT : HF2_FLAG_SERIAL_ERR;
frame[0] |= len;
for (let i = 0; i < len; ++i)
frame[i + 1] = buf[pos + i]
return this.io.sendPacketAsync(frame)
.then(() => loop(pos + len))
if (serial)
frame[0] =
serial == 1 ? HF2_FLAG_SERIAL_OUT : HF2_FLAG_SERIAL_ERR
frame[0] |= len
for (let i = 0; i < len; ++i) frame[i + 1] = buf[pos + i]
return this.io.sendPacketAsync(frame).then(() => loop(pos + len))
}
return loop(0)
}
@ -477,8 +506,7 @@ class HF2Proto implements Proto {
}
sendJDMessageAsync(buf: Uint8Array) {
return this.talkAsync(HF2_CMD_JDS_SEND, buf)
.then(() => { })
return this.talkAsync(HF2_CMD_JDS_SEND, buf).then(() => {})
}
handleEvent(buf: Uint8Array) {
@ -495,7 +523,9 @@ class HF2Proto implements Proto {
}
}
onSerial(data: Uint8Array, iserr: boolean) {
console.log("SERIAL:", bufferToString(data))
const msg = `hf2 serial: ${bufferToString(data)}`
if (iserr) console.error(msg)
else console.log(msg)
}
async postConnectAsync() {
@ -513,7 +543,9 @@ class HF2Proto implements Proto {
// all good
this.io.log(`device in user-space mode`)
} else if (mode == HF2_MODE_BOOTLOADER) {
this.io.log(`device in bootloader mode, reseting into user-space mode`)
this.io.log(
`device in bootloader mode, reseting into user-space mode`
)
await this.talkAsync(HF2_CMD_RESET_INTO_APP)
// and fail
throwError("Device in bootloader mode")
@ -524,6 +556,6 @@ class HF2Proto implements Proto {
}
disconnectAsync(): Promise<void> {
return this.io.disconnectAsync();
return this.io.disconnectAsync()
}
}

Просмотреть файл

@ -22,7 +22,7 @@ export default class IFrameBridgeClient extends JDIFrameClient {
readonly bridgeId = "bridge" + Math.random();
packetSent = 0;
packetProcessed = 0;
private _lastAspectRatio: number = 0;
private _lastAspectRatio = 0;
constructor(readonly bus: JDBus, readonly frameId: string) {
super(bus)

Просмотреть файл

@ -96,7 +96,7 @@ export function lightEncode(format: string, args: (number | number[])[]) {
outarr.push(colors.length)
}
}
for (let c of colors) {
for (const c of colors) {
outarr.push((c >> 16) & 0xff)
outarr.push((c >> 8) & 0xff)
outarr.push((c >> 0) & 0xff)
@ -152,7 +152,7 @@ export function lightEncode(format: string, args: (number | number[])[]) {
if (typeof v == "number")
colors.push(v)
else
for (let vv of v) colors.push(vv)
for (const vv of v) colors.push(vv)
} else {
if (token.length == 7) {
const b = fromHex(token.slice(1))

Просмотреть файл

@ -6,8 +6,8 @@ import Frame from "./frame"
import Trace from "./trace"
export function parseTrace(contents: string): Trace {
let description: string[] = [];
let packets: Packet[] = []
const description: string[] = [];
const packets: Packet[] = []
contents?.split(/\r?\n/).forEach(ln => {
// parse data
const m = /(\d+)\s+([a-f0-9]{12,})/i.exec(ln)
@ -34,7 +34,7 @@ export function parseLogicLog(logcontents: string): Frame[] {
const res: Frame[] = []
let frameBytes = []
let lastTime = 0
for (let ln of logcontents.split(/\r?\n/)) {
for (const ln of logcontents.split(/\r?\n/)) {
let m = /^JD (\d+) ([0-9a-f]+)/i.exec(ln)
if (m) {
res.push({
@ -78,7 +78,7 @@ Time [s],Value,Parity Error,Framing Error
0.043264800000000,0x00,,Error
0.063968960000000,0x00,,Error
*/
m = /^([\d\.]+),(?:Async Serial,)?.*(0x[A-F0-9][A-F0-9])/.exec(ln)
m = /^([\d.]+),(?:Async Serial,)?.*(0x[A-F0-9][A-F0-9])/.exec(ln)
if (!m)
continue
const tm = parseFloat(m[1])

Просмотреть файл

@ -352,6 +352,7 @@ export class CMSISProto implements Proto {
return 0
}
// eslint-disable-next-line no-constant-condition
while (true) {
const a0 = await check(p0)
if (a0) return a0

Просмотреть файл

@ -71,10 +71,10 @@ export class ModelRunnerClient extends JDServiceClient {
})
}
async deployModel(model: Uint8Array, progress: (p: number) => void = () => { }) {
progress(0)
async deployModel(model: Uint8Array, onProgress?: (p: number) => void) {
onProgress?.(0)
const resp = await this.service.sendCmdAwaitResponseAsync(Packet.jdpacked(ModelRunnerCmd.SetModel, "u32", [model.length]), 3000)
progress(0.05)
onProgress?.(0.05)
const [pipePort] = jdunpack<[number]>(resp.data, "u16")
if (!pipePort)
throw new Error("wrong port " + pipePort)
@ -82,14 +82,14 @@ export class ModelRunnerClient extends JDServiceClient {
const chunkSize = 224 // has to be divisible by 8
for (let i = 0; i < model.length; i += chunkSize) {
await pipe.send(model.slice(i, i + chunkSize))
progress(0.05 + (i / model.length) * 0.9)
onProgress?.(0.05 + (i / model.length) * 0.9)
}
try {
await pipe.close()
} catch {
// the device may restart before we manage to close
}
progress(1)
onProgress?.(1)
}
async autoInvoke(everySamples = 1) {

Просмотреть файл

@ -43,7 +43,7 @@ function packNumberCore(buf: Uint8Array, offset: number, num: number) {
}
}
}
let fmt = tagFormat(tag)
const fmt = tagFormat(tag)
if (buf) {
buf[offset] = tag
setNumber(buf, fmt, offset + 1, num)
@ -55,12 +55,12 @@ function packNumberCore(buf: Uint8Array, offset: number, num: number) {
* Unpacks a buffer into a number array.
*/
export function unpackNumberArray(buf: Uint8Array, offset = 0): number[] {
let res: number[] = []
const res: number[] = []
while (offset < buf.length) {
let fmt = tagFormat(buf[offset++])
const fmt = tagFormat(buf[offset++])
if (fmt === null) {
let v = getNumber(buf, NumberFormat.Int8BE, offset - 1)
const v = getNumber(buf, NumberFormat.Int8BE, offset - 1)
if (-31 <= v && v <= 127)
res.push(v)
else
@ -82,12 +82,12 @@ export function unpackNumberArray(buf: Uint8Array, offset = 0): number[] {
*/
export function packNumberArray(nums: number[]): Uint8Array {
let off = 0
for (let n of nums) {
for (const n of nums) {
off += packNumberCore(null, off, n)
}
let buf = new Uint8Array(off)
const buf = new Uint8Array(off)
off = 0
for (let n of nums) {
for (const n of nums) {
off += packNumberCore(buf, off, n)
}
return buf

Просмотреть файл

@ -66,7 +66,7 @@ export abstract class JDNode extends JDEventSource {
export function visitNodes(node: JDNode, vis: (node: JDNode) => void) {
let todo = [node];
const todo = [node];
while (todo.length) {
const node = todo.pop();
vis(node)

Просмотреть файл

@ -2,6 +2,10 @@ import { getNumber, NumberFormat, setNumber, sizeOfNumberFormat } from "./buffer
import { clampToStorage, numberFormatToStorageType } from "./spec"
import { bufferEq, bufferToString, stringToBuffer } from "./utils"
export type PackedSimpleValue = number | boolean | string | Uint8Array
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PackedValues = any[];
/*
## Format strings
@ -35,8 +39,8 @@ const ch_s = 115
const ch_u = 117
const ch_x = 120
const ch_z = 122
const ch_0 = 48
const ch_9 = 57
//const ch_0 = 48
//const ch_9 = 57
const ch_colon = 58
const ch_sq_open = 91
const ch_sq_close = 93
@ -212,7 +216,7 @@ function jdunpackCore(buf: Uint8Array, fmt: string, repeat: number) {
}
}
export function jdunpack<T extends any[]>(buf: Uint8Array, fmt: string): T {
export function jdunpack<T extends PackedValues>(buf: Uint8Array, fmt: string): T {
if (!buf || !fmt) return undefined;
// hot path for buffers
@ -230,7 +234,7 @@ export function jdunpack<T extends any[]>(buf: Uint8Array, fmt: string): T {
return jdunpackCore(buf, fmt, 0) as T
}
function jdpackCore(trg: Uint8Array, fmt: string, data: any[], off: number) {
function jdpackCore(trg: Uint8Array, fmt: string, data: PackedValues, off: number) {
//console.log({ fmt, data })
let idx = 0
const parser = new TokenParser(fmt)
@ -243,7 +247,7 @@ function jdpackCore(trg: Uint8Array, fmt: string, data: any[], off: number) {
continue
}
let dataItem = data[idx++]
const dataItem = data[idx++]
if (c0 == ch_r && dataItem) {
const fmt0 = fmt.slice(parser.fp)
@ -310,7 +314,7 @@ function jdpackCore(trg: Uint8Array, fmt: string, data: any[], off: number) {
return off
}
export function jdpack<T extends any[]>(fmt: string, data: T) {
export function jdpack<T extends PackedValues>(fmt: string, data: T) {
if (!fmt || !data)
return undefined;
@ -332,7 +336,7 @@ export function jdpack<T extends any[]>(fmt: string, data: T) {
return res
}
export function jdpackEqual<T extends any[]>(fmt: string, left: T, right: T) {
export function jdpackEqual<T extends PackedValues>(fmt: string, left: T, right: T) {
if ((!left) !== (!right))
return false;
if (!left) return true;

Просмотреть файл

@ -261,14 +261,14 @@ export class Packet {
if (stripped.length == 0)
return
let sz = -4
for (let s of stripped) {
for (const s of stripped) {
sz += s.length
}
const data = new Uint8Array(sz)
this._header.set(stripped[0], 12)
data.set(stripped[0].slice(4), 0)
sz = stripped[0].length - 4
for (let s of stripped.slice(1)) {
for (const s of stripped.slice(1)) {
data.set(s, sz)
sz += s.length
}

Просмотреть файл

@ -1,37 +1,37 @@
import { SRV_LOGGER } from "../../jacdac-spec/dist/specconstants";
import { JDBus } from "./bus";
import Packet from "./packet";
import { isCommand, isInstanceOf, serviceSpecificationFromName } from "./spec";
import { SMap } from "./utils";
import { SRV_LOGGER } from "../../jacdac-spec/dist/specconstants"
import { JDBus } from "./bus"
import Packet from "./packet"
import { isInstanceOf, serviceSpecificationFromName } from "./spec"
import { SMap } from "./utils"
export type CompiledPacketFilter = (pkt: Packet) => boolean;
export type CompiledPacketFilter = (pkt: Packet) => boolean
export interface PacketFilterProps {
announce?: boolean,
repeatedAnnounce?: boolean,
requiresAck?: boolean,
log?: boolean,
firmwareIdentifiers?: number[],
flags?: string[],
regGet?: boolean,
regSet?: boolean,
devices?: SMap<{ from?: boolean; to?: boolean }>,
serviceClasses?: number[],
pkts?: string[],
before?: number,
after?: number,
grouping?: boolean,
pipes?: boolean,
port?: number,
collapseAck?: boolean,
collapsePipes?: boolean,
announce?: boolean
repeatedAnnounce?: boolean
requiresAck?: boolean
log?: boolean
firmwareIdentifiers?: number[]
flags?: string[]
regGet?: boolean
regSet?: boolean
devices?: SMap<{ from?: boolean; to?: boolean }>
serviceClasses?: number[]
pkts?: string[]
before?: number
after?: number
grouping?: boolean
pipes?: boolean
port?: number
collapseAck?: boolean
collapsePipes?: boolean
collapseGets?: boolean
}
export interface PacketFilter {
source: string;
props: PacketFilterProps;
filter: CompiledPacketFilter;
source: string
props: PacketFilterProps
filter: CompiledPacketFilter
}
export function parsePacketFilter(bus: JDBus, text: string): PacketFilter {
@ -39,139 +39,143 @@ export function parsePacketFilter(bus: JDBus, text: string): PacketFilter {
return {
source: text,
props: {
grouping: true
grouping: true,
},
filter: (pkt) => true
filter: () => true,
}
}
let flags = new Set<string>()
let serviceClasses = new Set<number>();
let pkts = new Set<string>();
let firmwares = new Set<number>();
let repeatedAnnounce: boolean = undefined;
let announce: boolean = undefined;
let regGet: boolean = undefined;
let regSet: boolean = undefined;
let requiresAck: boolean = undefined;
let log: boolean = undefined;
let before: number = undefined;
let after: number = undefined;
let devices: SMap<{ from: boolean; to: boolean; }> = {};
let grouping: boolean = true;
let pipes: boolean = undefined;
let port: number = undefined;
let collapseAck: boolean = true;
let collapsePipes: boolean = true;
let collapseGets: boolean = true;
const flags = new Set<string>()
const serviceClasses = new Set<number>()
const pkts = new Set<string>()
const firmwares = new Set<number>()
let repeatedAnnounce: boolean = undefined
let announce: boolean = undefined
let regGet: boolean = undefined
let regSet: boolean = undefined
let requiresAck: boolean = undefined
let log: boolean = undefined
let before: number = undefined
let after: number = undefined
const devices: SMap<{ from: boolean; to: boolean }> = {}
let grouping = true
let pipes: boolean = undefined
let port: number = undefined
let collapseAck = true
let collapsePipes = true
let collapseGets = true
text.split(/\s+/g).forEach(part => {
const [match, prefix, _, value] = /([a-z\-_]+)([:=]([^\s]+))?/.exec(part) || [];
const [, prefix, , value] =
/([a-z\-_]+)([:=]([^\s]+))?/.exec(part) || []
switch (prefix || "") {
case "kind":
case "k":
if (!value)
break;
if (!value) break
flags.add(value.toLowerCase())
break;
break
case "service":
case "srv":
if (!value)
break;
case "srv": {
if (!value) break
const service = serviceSpecificationFromName(value)
const serviceClass = service?.classIdentifier || parseInt(value, 16);
const serviceClass =
service?.classIdentifier || parseInt(value, 16)
if (serviceClass !== undefined && !isNaN(serviceClass))
serviceClasses.add(serviceClass)
break;
break
}
case "announce":
case "a":
announce = parseBoolean(value);
break;
announce = parseBoolean(value)
break
case "repeated-announce":
case "ra":
repeatedAnnounce = parseBoolean(value);
break;
repeatedAnnounce = parseBoolean(value)
break
case "requires-ack":
case "ack":
requiresAck = parseBoolean(value);
break;
requiresAck = parseBoolean(value)
break
case "collapse-ack":
collapseAck = parseBoolean(value);
break;
collapseAck = parseBoolean(value)
break
case "device":
case "dev":
case "to":
case "from":
if (!value)
break;
case "from": {
if (!value) break
// resolve device by name
const deviceId = bus.devices().find(d => d.shortId === value || d.name === value)?.deviceId;
const deviceId = bus
.devices()
.find(d => d.shortId === value || d.name === value)
?.deviceId
if (deviceId) {
const data = devices[deviceId] || (devices[deviceId] = { from: false, to: false })
if (prefix === "from")
data.from = true;
else if (prefix === "to")
data.to = true;
const data =
devices[deviceId] ||
(devices[deviceId] = { from: false, to: false })
if (prefix === "from") data.from = true
else if (prefix === "to") data.to = true
}
break;
break
}
case "fw":
case "firmware-identifier":
if (!value) return;
case "firmware-identifier": {
if (!value) return
// find register
const fwid = parseInt(value.replace(/^0?x/, ''), 16);
if (!isNaN(fwid))
firmwares.add(fwid);
break;
const fwid = parseInt(value.replace(/^0?x/, ""), 16)
if (!isNaN(fwid)) firmwares.add(fwid)
break
}
case "pkt":
case "reg":
case "register":
case "cmd":
case "command":
case "ev":
case "event":
if (!value) return;
case "event": {
if (!value) return
// find register
const id = parseInt(value.replace(/^0?x/, ''), 16);
if (!isNaN(id))
pkts.add(id.toString(16));
const id = parseInt(value.replace(/^0?x/, ""), 16)
if (!isNaN(id)) pkts.add(id.toString(16))
// support name
pkts.add(value);
break;
pkts.add(value)
break
}
case "reg-get":
case "get":
regGet = parseBoolean(value);
break;
regGet = parseBoolean(value)
break
case "reg-set":
case "set":
regSet = parseBoolean(value);
break;
regSet = parseBoolean(value)
break
case "log":
log = parseBoolean(value);
break;
log = parseBoolean(value)
break
case "before":
before = parseTimestamp(value);
break;
before = parseTimestamp(value)
break
case "after":
after = parseTimestamp(value);
break;
after = parseTimestamp(value)
break
case "grouping":
grouping = parseBoolean(value);
break;
grouping = parseBoolean(value)
break
case "pipes":
pipes = parseBoolean(value);
break;
pipes = parseBoolean(value)
break
case "collapse-pipe":
case "collapse-pipes":
collapsePipes = parseBoolean(value);
break;
collapsePipes = parseBoolean(value)
break
case "collapse-get":
case "collapse-gets":
collapseGets = parseBoolean(value);
break;
collapseGets = parseBoolean(value)
break
case "port":
port = parseInt(value);
break;
port = parseInt(value)
break
}
});
})
const props = {
announce,
@ -184,7 +188,8 @@ export function parsePacketFilter(bus: JDBus, text: string): PacketFilter {
regGet,
regSet,
devices,
serviceClasses: !!serviceClasses.size && Array.from(serviceClasses.keys()),
serviceClasses:
!!serviceClasses.size && Array.from(serviceClasses.keys()),
pkts: !!pkts.size && Array.from(pkts.keys()),
before,
after,
@ -192,25 +197,22 @@ export function parsePacketFilter(bus: JDBus, text: string): PacketFilter {
pipes,
collapsePipes,
collapseGets,
port
port,
}
const filter = compileFilter(props)
return {
source: text,
props,
filter,
};
}
function parseBoolean(value: string) {
if (value === "false" || value === "no")
return false;
else if (value === "true" || value === "yes" || !value)
return true;
else
return undefined;
if (value === "false" || value === "no") return false
else if (value === "true" || value === "yes" || !value) return true
else return undefined
}
function parseTimestamp(value: string) {
const t = parseInt(value);
return isNaN(t) ? undefined : t;
const t = parseInt(value)
return isNaN(t) ? undefined : t
}
}
@ -230,60 +232,71 @@ export function compileFilter(props: PacketFilterProps) {
before,
after,
pipes,
port
} = props;
port,
} = props
let filters: CompiledPacketFilter[] = [];
if (before !== undefined)
filters.push(pkt => pkt.timestamp <= before)
if (after !== undefined)
filters.push(pkt => pkt.timestamp >= after)
if (announce !== undefined)
filters.push(pkt => pkt.isAnnounce === announce)
const filters: CompiledPacketFilter[] = []
if (before !== undefined) filters.push(pkt => pkt.timestamp <= before)
if (after !== undefined) filters.push(pkt => pkt.timestamp >= after)
if (announce !== undefined) filters.push(pkt => pkt.isAnnounce === announce)
if (repeatedAnnounce !== undefined)
filters.push(pkt => !pkt.isAnnounce || (pkt.isRepeatedAnnounce === repeatedAnnounce))
filters.push(
pkt =>
!pkt.isAnnounce || pkt.isRepeatedAnnounce === repeatedAnnounce
)
if (requiresAck !== undefined)
filters.push(pkt => pkt.requiresAck === requiresAck);
if (flags)
filters.push(pkt => hasAnyFlag(pkt))
if (pipes !== undefined)
filters.push(pkt => pkt.isPipe)
if (port !== undefined)
filters.push(pkt => pkt.pipePort === port);
filters.push(pkt => pkt.requiresAck === requiresAck)
if (flags) filters.push(pkt => hasAnyFlag(pkt))
if (pipes !== undefined) filters.push(pkt => pkt.isPipe)
if (port !== undefined) filters.push(pkt => pkt.pipePort === port)
if (regGet !== undefined && regSet !== undefined)
filters.push(pkt => (pkt.isRegisterGet === regGet) && (pkt.isRegisterSet === regSet))
filters.push(
pkt => pkt.isRegisterGet === regGet && pkt.isRegisterSet === regSet
)
else if (regGet !== undefined)
filters.push(pkt => pkt.isRegisterGet === regGet)
else if (regSet !== undefined)
filters.push(pkt => pkt.isRegisterSet === regSet)
if (log !== undefined)
filters.push(pkt => (pkt.serviceClass === SRV_LOGGER && pkt.isReport) === log);
filters.push(
pkt => (pkt.serviceClass === SRV_LOGGER && pkt.isReport) === log
)
if (Object.keys(devices).length)
filters.push(pkt => {
if (!pkt.device) return false;
const f = devices[pkt.device.deviceId];
return !!f && (!f.from || !pkt.isCommand) && (!f.to || pkt.isCommand);
if (!pkt.device) return false
const f = devices[pkt.device.deviceId]
return (
!!f && (!f.from || !pkt.isCommand) && (!f.to || pkt.isCommand)
)
})
if (serviceClasses) {
filters.push(pkt => serviceClasses.some(serviceClass => isInstanceOf(pkt.serviceClass, serviceClass)));
filters.push(pkt =>
serviceClasses.some(serviceClass =>
isInstanceOf(pkt.serviceClass, serviceClass)
)
)
}
if (pkts) {
filters.push(pkt => pkts.indexOf(pkt.decoded?.info.identifier.toString(16)) > -1
|| pkts.indexOf(pkt.decoded?.info.name) > -1);
filters.push(
pkt =>
pkts.indexOf(pkt.decoded?.info.identifier.toString(16)) > -1 ||
pkts.indexOf(pkt.decoded?.info.name) > -1
)
}
if (firmwareIdentifiers)
filters.push(pkt => {
const fwid = pkt.device?.firmwareIdentifier;
return fwid === undefined || firmwareIdentifiers.indexOf(fwid) > -1;
const fwid = pkt.device?.firmwareIdentifier
return fwid === undefined || firmwareIdentifiers.indexOf(fwid) > -1
})
const filter: CompiledPacketFilter = (pkt: Packet) => filters.every(filter => filter(pkt));
return filter;
const filter: CompiledPacketFilter = (pkt: Packet) =>
filters.every(filter => filter(pkt))
return filter
function hasAnyFlag(pkt: Packet) {
const k = pkt.decoded?.info.kind;
return !!k && flags.indexOf(k) > -1;
const k = pkt.decoded?.info.kind
return !!k && flags.indexOf(k) > -1
}
}

Просмотреть файл

@ -57,7 +57,7 @@ export class OutPipe {
const cmd = (this.port << PIPE_PORT_SHIFT) | flags | (this._count & PIPE_COUNTER_MASK)
const pkt = Packet.from(cmd, buf)
pkt.serviceIndex = JD_SERVICE_INDEX_PIPE
const p = this.device.sendPktWithAck(pkt)
this.device.sendPktWithAck(pkt)
.then(
() => { },
err => {
@ -107,6 +107,7 @@ export class InPipe extends JDClient {
}
private allocPort() {
// eslint-disable-next-line no-constant-condition
while (true) {
this._port = 1 + randomUInt(511)
const info = this.bus.selfDevice.port(this._port)

Просмотреть файл

@ -227,8 +227,8 @@ export function decodeMember(
}
export function valueToFlags(enumInfo: jdspec.EnumInfo, value: number) {
let r = [];
let curr = value
const r = [];
const curr = value
for (const key of Object.keys(enumInfo.members)) {
const val = enumInfo.members[key]
if (curr & val) {
@ -336,7 +336,7 @@ function decodeEvent(service: jdspec.ServiceSpec, pkt: Packet): DecodedPacket {
|| syntheticPktInfo("event", evCode)
const decoded = decodeMembers(service, evInfo, pkt)
let description = `EVENT[${pkt.eventCounter}] ${evInfo.name}` + wrapDecodedMembers(decoded)
const description = `EVENT[${pkt.eventCounter}] ${evInfo.name}` + wrapDecodedMembers(decoded)
return {
service,
@ -418,7 +418,7 @@ export function decodePacketData(pkt: Packet): DecodedPacket {
}
function reverseLookup(map: SMap<number>, n: number) {
for (let k of Object.keys(map)) {
for (const k of Object.keys(map)) {
if (map[k] == n)
return k
}
@ -546,7 +546,7 @@ export function printPacket(pkt: Packet, opts: PrintPacketOptions = {}): string
if (decoded) {
pdesc += "; " + decoded.description
} else if (0 < d.length && d.length <= 4) {
let v0 = pkt.uintData, v1 = pkt.intData
const v0 = pkt.uintData, v1 = pkt.intData
pdesc += "; " + num2str(v0)
if (v0 != v1)
pdesc += "; signed: " + num2str(v1)

Просмотреть файл

@ -12,15 +12,15 @@ import { isRegister, isReading } from "./spec";
import { JDField } from "./field";
import { JDServiceMemberNode } from "./servicemembernode";
import { JDNode } from "./node";
import { jdpack, jdunpack } from "./pack";
import { jdpack, jdunpack, PackedValues } from "./pack";
export class JDRegister extends JDServiceMemberNode {
private _lastReportPkt: Packet;
private _fields: JDField[];
private _lastSetTimestamp: number = -Infinity;
private _lastGetTimestamp: number = -Infinity;
private _lastGetAttempts: number = 0;
private _lastSetTimestamp = -Infinity;
private _lastGetTimestamp = -Infinity;
private _lastGetAttempts = 0;
constructor(
service: JDService,
@ -77,7 +77,7 @@ export class JDRegister extends JDServiceMemberNode {
.then(() => { this.emit(GET_ATTEMPT) });
}
sendSetPackedAsync(fmt: string, values: any[], autoRefresh?: boolean): Promise<void> {
sendSetPackedAsync(fmt: string, values: PackedValues, autoRefresh?: boolean): Promise<void> {
return this.sendSetAsync(jdpack(fmt, values), autoRefresh)
}
@ -105,7 +105,7 @@ export class JDRegister extends JDServiceMemberNode {
return this._lastReportPkt?.timestamp
}
get unpackedValue(): any[] {
get unpackedValue(): PackedValues {
const d = this.data;
const fmt = this.specification?.packFormat;
return d && fmt && jdunpack(this.data, fmt);
@ -189,9 +189,8 @@ export class JDRegister extends JDServiceMemberNode {
}
compareTo(b: JDRegister) {
const a = this;
return a.code - b.code ||
a.service.compareTo(b.service);
return this.code - b.code ||
this.service.compareTo(b.service);
}
}

Просмотреть файл

@ -116,6 +116,7 @@ export class SensorAggregatorClient extends JDServiceClient {
}
async stats(): Promise<SensorAggregatorStats> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const info: any = {
"numSamples": this.getReg(SensorAggregatorReg.NumSamples, r => r.intValue),
"sampleSize": this.getReg(SensorAggregatorReg.SampleSize, r => r.intValue),

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше