Merge pull request #2366 from proddy/dev

show progress bar when automatically installing firmware, fix modbus headers
This commit is contained in:
Proddy
2025-01-21 22:07:00 +01:00
committed by GitHub
12 changed files with 5830 additions and 5444 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -24,8 +24,8 @@
"@alova/adapter-xhr": "2.1.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.4.0",
"@mui/material": "^6.4.0",
"@mui/icons-material": "^6.4.1",
"@mui/material": "^6.4.1",
"@table-library/react-table-library": "4.1.7",
"alova": "3.2.8",
"async-validator": "^4.2.5",
@@ -57,8 +57,8 @@
"prettier": "^3.4.2",
"rollup-plugin-visualizer": "^5.14.0",
"terser": "^5.37.0",
"typescript-eslint": "8.20.0",
"vite": "^6.0.9",
"typescript-eslint": "8.21.0",
"vite": "^6.0.11",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},

View File

@@ -1,14 +1,7 @@
import { useState } from 'react';
import CancelIcon from '@mui/icons-material/Cancel';
import {
Box,
Button,
CircularProgress,
Dialog,
DialogContent,
Typography
} from '@mui/material';
import { Box, Button, Dialog, DialogContent, Typography } from '@mui/material';
import { callAction } from 'api/app';
import { readSystemStatus } from 'api/system';
@@ -20,6 +13,8 @@ import { useI18nContext } from 'i18n/i18n-react';
import { SystemStatusCodes } from 'types';
import { useInterval } from 'utils';
import { LinearProgressWithLabel } from '../../components/upload/LinearProgressWithLabel';
const SystemMonitor = () => {
const [errorMessage, setErrorMessage] = useState<string>();
@@ -81,7 +76,7 @@ const SystemMonitor = () => {
fontWeight={400}
textAlign="center"
>
{data?.status === SystemStatusCodes.SYSTEM_STATUS_UPLOADING
{data?.status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING
? LL.WAIT_FIRMWARE()
: data?.status === SystemStatusCodes.SYSTEM_STATUS_RESTART_REQUESTED
? LL.APPLICATION_RESTARTING()
@@ -110,9 +105,15 @@ const SystemMonitor = () => {
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center">
{LL.PLEASE_WAIT()}&hellip;
</Typography>
<Box py={2}>
<CircularProgress size={32} />
</Box>
{data && data.status > SystemStatusCodes.SYSTEM_STATUS_UPLOADING && (
<Box width="100%" pl={2} pr={2} py={2}>
<LinearProgressWithLabel
value={Math.round(
data?.status - SystemStatusCodes.SYSTEM_STATUS_UPLOADING
)}
/>
</Box>
)}
</>
)}
</Box>

View File

@@ -0,0 +1,23 @@
import {
Box,
LinearProgress,
type LinearProgressProps,
Typography
} from '@mui/material';
export function LinearProgressWithLabel(
props: LinearProgressProps & { value: number }
) {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress variant="determinate" {...props} />
</Box>
<Box sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(
props.value
)}%`}</Typography>
</Box>
</Box>
);
}

View File

@@ -2,13 +2,7 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import {
Box,
Button,
LinearProgress,
type LinearProgressProps,
Typography
} from '@mui/material';
import { Box, Button, Typography } from '@mui/material';
import * as SystemApi from 'api/system';
@@ -16,21 +10,7 @@ import { useRequest } from 'alova/client';
import { useI18nContext } from 'i18n/i18n-react';
import DragNdrop from './DragNdrop';
function LinearProgressWithLabel(props: LinearProgressProps & { value: number }) {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress variant="determinate" {...props} />
</Box>
<Box sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(
props.value
)}%`}</Typography>
</Box>
</Box>
);
}
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
const SingleUpload = ({ doRestart }) => {
const [md5, setMd5] = useState<string>();

View File

@@ -5,7 +5,7 @@ import type { NetworkConnectionStatus } from './network';
export enum SystemStatusCodes {
SYSTEM_STATUS_NORMAL = 0,
SYSTEM_STATUS_PENDING_UPLOAD = 1,
SYSTEM_STATUS_UPLOADING = 2,
SYSTEM_STATUS_UPLOADING = 100,
SYSTEM_STATUS_ERROR_UPLOAD = 3,
SYSTEM_STATUS_PENDING_RESTART = 4,
SYSTEM_STATUS_RESTART_REQUESTED = 5

View File

@@ -783,38 +783,38 @@ __metadata:
languageName: node
linkType: hard
"@mui/core-downloads-tracker@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/core-downloads-tracker@npm:6.4.0"
checksum: 10c0/48112fb90df2fe58fdbc8ca9c8fac3487dc3aa0fbc31bd1e6b373f43f5bbb23d6fc673686b42cb46723e2d8c66ae914ecb9397c38e7d43a340ec6cbe07e1469d
"@mui/core-downloads-tracker@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/core-downloads-tracker@npm:6.4.1"
checksum: 10c0/5c35592eecb6b9b64b23003975a8ce20fadb9e9b712a78dd552b5ea835d06e8e4d7b7cb84f471f5658b5285de317a860b40597ad3c5e494e84f0ae0daf35fd6b
languageName: node
linkType: hard
"@mui/icons-material@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/icons-material@npm:6.4.0"
"@mui/icons-material@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/icons-material@npm:6.4.1"
dependencies:
"@babel/runtime": "npm:^7.26.0"
peerDependencies:
"@mui/material": ^6.4.0
"@mui/material": ^6.4.1
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/5e05dbb67e0f7e0c046d5665bebf41df487b31ce617987ac0e8da9c539221bad7e523bf77f0f0153b35aadb56f02146aded567e8032ba608696f70b708d505c0
checksum: 10c0/f551aea11f0c3a6ff0336333e1cf28b69d7965e5984643067e5f8563eabf1434dee910c9b4beb530f785f191b0ec14520d9c41fd8ee6649f4f8d26f831f02ba8
languageName: node
linkType: hard
"@mui/material@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/material@npm:6.4.0"
"@mui/material@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/material@npm:6.4.1"
dependencies:
"@babel/runtime": "npm:^7.26.0"
"@mui/core-downloads-tracker": "npm:^6.4.0"
"@mui/system": "npm:^6.4.0"
"@mui/core-downloads-tracker": "npm:^6.4.1"
"@mui/system": "npm:^6.4.1"
"@mui/types": "npm:^7.2.21"
"@mui/utils": "npm:^6.4.0"
"@mui/utils": "npm:^6.4.1"
"@popperjs/core": "npm:^2.11.8"
"@types/react-transition-group": "npm:^4.4.12"
clsx: "npm:^2.1.1"
@@ -825,7 +825,7 @@ __metadata:
peerDependencies:
"@emotion/react": ^11.5.0
"@emotion/styled": ^11.3.0
"@mui/material-pigment-css": ^6.4.0
"@mui/material-pigment-css": ^6.4.1
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -838,16 +838,16 @@ __metadata:
optional: true
"@types/react":
optional: true
checksum: 10c0/5bd9757b578827d841934725a7145ae1d295cbc979fec4fb1a099c439ae0db1a67ace47b2a94f289ac5289d16b0167ebba9a4e98b0499840555b60fb5b549923
checksum: 10c0/c5ad8ab4339e145cac815cd9fdd9c909d5e2e5edf4997b8afe5edc320f3a020ddb857b5939f6a4453a34d996e3f334804c3858c5dc5f0bfede087e7d6b5e574e
languageName: node
linkType: hard
"@mui/private-theming@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/private-theming@npm:6.4.0"
"@mui/private-theming@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/private-theming@npm:6.4.1"
dependencies:
"@babel/runtime": "npm:^7.26.0"
"@mui/utils": "npm:^6.4.0"
"@mui/utils": "npm:^6.4.1"
prop-types: "npm:^15.8.1"
peerDependencies:
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -855,7 +855,7 @@ __metadata:
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/3e89ff9e6baf85b1bdb0ed55e1ef50dadd95991ed498928e1764c1255fa174c0d134a46beebb1e4faabc22820b404923911c2bf6e9692fd269a77522c356eb6c
checksum: 10c0/5b6936e6dff6218373579f901aabba01eea1953e97a5b4b695736aeebca2d39fc7c2b4d0e54ab064d1ba99a6ffce0d907098791ecec28795f7da25c1bd0d33cb
languageName: node
linkType: hard
@@ -882,15 +882,15 @@ __metadata:
languageName: node
linkType: hard
"@mui/system@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/system@npm:6.4.0"
"@mui/system@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/system@npm:6.4.1"
dependencies:
"@babel/runtime": "npm:^7.26.0"
"@mui/private-theming": "npm:^6.4.0"
"@mui/private-theming": "npm:^6.4.1"
"@mui/styled-engine": "npm:^6.4.0"
"@mui/types": "npm:^7.2.21"
"@mui/utils": "npm:^6.4.0"
"@mui/utils": "npm:^6.4.1"
clsx: "npm:^2.1.1"
csstype: "npm:^3.1.3"
prop-types: "npm:^15.8.1"
@@ -906,7 +906,7 @@ __metadata:
optional: true
"@types/react":
optional: true
checksum: 10c0/5e8d82674693d747b947ce4e5c1a11fff148c5242b2af898d80575e780f0abd3857f234a47b1cec16ca02334ecc7450f0afb5e311ddee9e9cf342e5bf3c7c1fc
checksum: 10c0/34d731fb5faf1a6242c9faca5672a4b8722ac97cf5d1fd58cf85cce093cfbd2249ed57dd4c33e8f3b879c611e0fab56bb9e0e8fc1b7c8a49cbe4dd39ba32f98d
languageName: node
linkType: hard
@@ -922,9 +922,9 @@ __metadata:
languageName: node
linkType: hard
"@mui/utils@npm:^6.4.0":
version: 6.4.0
resolution: "@mui/utils@npm:6.4.0"
"@mui/utils@npm:^6.4.1":
version: 6.4.1
resolution: "@mui/utils@npm:6.4.1"
dependencies:
"@babel/runtime": "npm:^7.26.0"
"@mui/types": "npm:^7.2.21"
@@ -938,7 +938,7 @@ __metadata:
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/c72ab7685affb577f1b48a5f5be02d11254cddf858cc35d61e0483434249cc064f2d145e9e72be60c344f31ccc7d6e61c59a256413b7e68077e883c0ffc8882e
checksum: 10c0/c0cfb8737db7c5709ce35ebdf739c5014914a1344e1b305d13a75b061b970656777eaf01f6fbdb8f209da91c086ab36dd3f5ddc1c94237c5868a383cdd1aad81
languageName: node
linkType: hard
@@ -1467,15 +1467,15 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.20.0"
"@typescript-eslint/eslint-plugin@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.21.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.20.0"
"@typescript-eslint/type-utils": "npm:8.20.0"
"@typescript-eslint/utils": "npm:8.20.0"
"@typescript-eslint/visitor-keys": "npm:8.20.0"
"@typescript-eslint/scope-manager": "npm:8.21.0"
"@typescript-eslint/type-utils": "npm:8.21.0"
"@typescript-eslint/utils": "npm:8.21.0"
"@typescript-eslint/visitor-keys": "npm:8.21.0"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.3.1"
natural-compare: "npm:^1.4.0"
@@ -1484,64 +1484,64 @@ __metadata:
"@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/c68d0dc5419db93c38eea8adecac19e27f8b023d015a944ffded112d584e87fa7fe512070a6a1085899cab2e12e1c8db276e10412b74bf639ca6b04052bbfedc
checksum: 10c0/4601d21ec35b9fa5cfc1ad0330733ab40d6c6822c7fc15c3584a16f678c9a72e077a1725a950823fe0f499a15f3981795b1ea5d1e7a1be5c7b8296ea9ae6327c
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/parser@npm:8.20.0"
"@typescript-eslint/parser@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/parser@npm:8.21.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.20.0"
"@typescript-eslint/types": "npm:8.20.0"
"@typescript-eslint/typescript-estree": "npm:8.20.0"
"@typescript-eslint/visitor-keys": "npm:8.20.0"
"@typescript-eslint/scope-manager": "npm:8.21.0"
"@typescript-eslint/types": "npm:8.21.0"
"@typescript-eslint/typescript-estree": "npm:8.21.0"
"@typescript-eslint/visitor-keys": "npm:8.21.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/fff4a86be27f603ad8d6f7dd9758c46b04a254828f0c6d8a34869c1cf30b5828b60a1dc088f72680a7b65cc5fc696848df4605de19e59a18467306d7ca56c11d
checksum: 10c0/aadebd50ca7aa2d61ad85d890c0d7010f2c293ec4d50a7833ef9674f232f0bc7118faa93a898771fbea50f02d542d687cf3569421b23f72fe6fed6895d5506fc
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/scope-manager@npm:8.20.0"
"@typescript-eslint/scope-manager@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/scope-manager@npm:8.21.0"
dependencies:
"@typescript-eslint/types": "npm:8.20.0"
"@typescript-eslint/visitor-keys": "npm:8.20.0"
checksum: 10c0/a8074768d06c863169294116624a45c19377ff0b8635ad5fa4ae673b43cf704d1b9b79384ceef0ff0abb78b107d345cd90fe5572354daf6ad773fe462ee71e6a
"@typescript-eslint/types": "npm:8.21.0"
"@typescript-eslint/visitor-keys": "npm:8.21.0"
checksum: 10c0/ea405e79dc884ea1c76465604db52f9b0941d6cbb0bde6bce1af689ef212f782e214de69d46503c7c47bfc180d763369b7433f1965e3be3c442b417e8c9f8f75
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/type-utils@npm:8.20.0"
"@typescript-eslint/type-utils@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/type-utils@npm:8.21.0"
dependencies:
"@typescript-eslint/typescript-estree": "npm:8.20.0"
"@typescript-eslint/utils": "npm:8.20.0"
"@typescript-eslint/typescript-estree": "npm:8.21.0"
"@typescript-eslint/utils": "npm:8.21.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.0.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/7d46143f26ec606b71d20f0f5535b16abba2ba7a5a2daecd2584ddb61d1284dd8404f34265cc1fdfd541068b24b0211f7ad94801c94e4c60869d9f26bf3c0b9b
checksum: 10c0/617f5dfe83fd9a7c722b27fa4e7f0c84f29baa94f75a4e8e5ccfd5b0a373437f65724e21b9642870fb0960f204b1a7f516a038200a12f8118f21b1bf86315bf3
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/types@npm:8.20.0"
checksum: 10c0/21292d4ca089897015d2bf5ab99909a7b362902f63f4ba10696676823b50d00c7b4cd093b4b43fba01d12bc3feca3852d2c28528c06d8e45446b7477887dbee7
"@typescript-eslint/types@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/types@npm:8.21.0"
checksum: 10c0/67dfd300cc614d7b02e94d0dacfb228a7f4c3fd4eede29c43adb9e9fcc16365ae3df8d6165018da3c123dce65545bef03e3e8183f35e9b3a911ffc727e3274c2
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/typescript-estree@npm:8.20.0"
"@typescript-eslint/typescript-estree@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/typescript-estree@npm:8.21.0"
dependencies:
"@typescript-eslint/types": "npm:8.20.0"
"@typescript-eslint/visitor-keys": "npm:8.20.0"
"@typescript-eslint/types": "npm:8.21.0"
"@typescript-eslint/visitor-keys": "npm:8.21.0"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.2"
is-glob: "npm:^4.0.3"
@@ -1550,32 +1550,32 @@ __metadata:
ts-api-utils: "npm:^2.0.0"
peerDependencies:
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/54a2c1da7d1c5f7e865b941e8a3c98eb4b5f56ed8741664a84065173bde9602cdb8866b0984b26816d6af885c1528311c11e7286e869ed424483b74366514cbd
checksum: 10c0/0cf5b0382524f4af54fb5ec71ca7e939ec922711f2d77b383740b28dd4b21407b0ab5dded62df6819d01c12c0b354e95667e3c7025a5d27d05b805161ab94855
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/utils@npm:8.20.0"
"@typescript-eslint/utils@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/utils@npm:8.21.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0"
"@typescript-eslint/scope-manager": "npm:8.20.0"
"@typescript-eslint/types": "npm:8.20.0"
"@typescript-eslint/typescript-estree": "npm:8.20.0"
"@typescript-eslint/scope-manager": "npm:8.21.0"
"@typescript-eslint/types": "npm:8.21.0"
"@typescript-eslint/typescript-estree": "npm:8.21.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/dd36c3b22a2adde1e1462aed0c8b4720f61859b4ebb0c3ef935a786a6b1cb0ec21eb0689f5a8debe8db26d97ebb979bab68d6f8fe7b0098e6200a485cfe2991b
checksum: 10c0/d8347dbe9176417220aa62902cfc1b2007a9246bb7a8cccdf8590120903eb50ca14cb668efaab4646d086277f2367559985b62230e43ebd8b0723d237eeaa2f2
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.20.0":
version: 8.20.0
resolution: "@typescript-eslint/visitor-keys@npm:8.20.0"
"@typescript-eslint/visitor-keys@npm:8.21.0":
version: 8.21.0
resolution: "@typescript-eslint/visitor-keys@npm:8.21.0"
dependencies:
"@typescript-eslint/types": "npm:8.20.0"
"@typescript-eslint/types": "npm:8.21.0"
eslint-visitor-keys: "npm:^4.2.0"
checksum: 10c0/e95d8b2685e8beb6637bf2e9d06e4177a400d3a2b142ba749944690f969ee3186b750082fd9bf34ada82acf1c5dd5970201dfd97619029c8ecca85fb4b50dbd8
checksum: 10c0/b3f1412f550e35c0d7ae0410db616951116b365167539f9b85710d8bc2b36b322c5e637caee84cc1ae5df8f1d961880250d52ffdef352b31e5bdbef74ba6fea9
languageName: node
linkType: hard
@@ -1588,8 +1588,8 @@ __metadata:
"@emotion/react": "npm:^11.14.0"
"@emotion/styled": "npm:^11.14.0"
"@eslint/js": "npm:^9.18.0"
"@mui/icons-material": "npm:^6.4.0"
"@mui/material": "npm:^6.4.0"
"@mui/icons-material": "npm:^6.4.1"
"@mui/material": "npm:^6.4.1"
"@preact/compat": "npm:^18.3.1"
"@preact/preset-vite": "npm:^2.10.0"
"@table-library/react-table-library": "npm:4.1.7"
@@ -1617,8 +1617,8 @@ __metadata:
terser: "npm:^5.37.0"
typesafe-i18n: "npm:^5.26.2"
typescript: "npm:^5.7.3"
typescript-eslint: "npm:8.20.0"
vite: "npm:^6.0.9"
typescript-eslint: "npm:8.21.0"
vite: "npm:^6.0.11"
vite-plugin-imagemin: "npm:^0.6.1"
vite-tsconfig-paths: "npm:^5.1.4"
languageName: unknown
@@ -6646,17 +6646,17 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.20.0":
version: 8.20.0
resolution: "typescript-eslint@npm:8.20.0"
"typescript-eslint@npm:8.21.0":
version: 8.21.0
resolution: "typescript-eslint@npm:8.21.0"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.20.0"
"@typescript-eslint/parser": "npm:8.20.0"
"@typescript-eslint/utils": "npm:8.20.0"
"@typescript-eslint/eslint-plugin": "npm:8.21.0"
"@typescript-eslint/parser": "npm:8.21.0"
"@typescript-eslint/utils": "npm:8.21.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.8.0"
checksum: 10c0/049e0fa000657232c0fe26a062ef6a9cd16c5a58c814a74ac45971554c8b6bc67355821a66229f9537e819939a2ab065e7fcba9a70cd95c8283630dc58ac0144
checksum: 10c0/44e5c341ad7f0b41dce3b4ca7a4c0a399ebe51a5323d930750db1e308367b4813a620f4c2332a5774a1dccd0047ebbaf993a8b7effd67389e9069b29b5701520
languageName: node
linkType: hard
@@ -6858,9 +6858,9 @@ __metadata:
languageName: node
linkType: hard
"vite@npm:^6.0.9":
version: 6.0.9
resolution: "vite@npm:6.0.9"
"vite@npm:^6.0.11":
version: 6.0.11
resolution: "vite@npm:6.0.11"
dependencies:
esbuild: "npm:^0.24.2"
fsevents: "npm:~2.3.3"
@@ -6906,7 +6906,7 @@ __metadata:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/b36f0f51318d6c33184da31e280862f23ef89712884e088b864c9edf112d65f6cf6795f201916e370a4434575fd3e2868f07f3bd63c6bdd5c2ae2295fa7f6d8a
checksum: 10c0/a0537f9bf8d6ded740646a4aa44b8dbf442d3005e75f7b27e981ef6011f22d4759f5eb643a393c0ffb8d21e2f50fb5f774d3a53108fb96a10b0f83697e8efe84
languageName: node
linkType: hard

View File

@@ -31,26 +31,30 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
EOL
# Generate Modbus entity parameters
# One to build the modbus_entity_parameters.hpp header file
# And then run entity_dump test again to create the dump_entities.csv file with the correct modbus counts
# First generate Modbus entity parameters
# build the modbus_entity_parameters.hpp header file
make clean
make -s ARGS=-DEMSESP_MODBUS
rm -f ./src/core/modbus_entity_parameters.hpp
echo "test entity_dump" | ./emsesp | python3 ./scripts/strip_csv.py | python3 ./scripts/update_modbus_registers.py >./src/core/modbus_entity_parameters.hpp
ls -al ./src/core/modbus_entity_parameters.hpp
echo "test entity_dump" | ./emsesp | python3 ./scripts/strip_csv.py >./docs/dump_entities.csv
# dump_entities.csv
cat ./docs/dump_entities.csv | python3 ./scripts/update_modbus_registers.py >./src/core/modbus_entity_parameters.hpp
# generate Modbus doc - Modbus-Entity-Registers.md used in the emsesp.org documentation
rm -f ./docs/Modbus-Entity-Registers.md
cat ./docs/dump_entities.csv | python3 ./scripts/generate-modbus-register-doc.py >./docs/Modbus-Entity-Registers.md
# regenerate dump_entities.csv but without the Modbus entity parameters
make clean
make -s ARGS=-DEMSESP_STANDALONE
rm -f ./docs/dump_entities.csv
echo "test entity_dump" | ./emsesp | python3 ./scripts/strip_csv.py >./docs/dump_entities.csv
ls -al ./docs/dump_entities.csv
# dump_telegrams.csv
rm -f ./docs/dump_telegrams.csv
echo "test telegram_dump" | ./emsesp | python3 ./scripts/strip_csv.py >./docs/dump_telegrams.csv
ls -al ./docs/dump_telegrams.csv
# generate doc - Modbus-Entity-Registers.md used in the emsesp.org documentation
rm -f ./docs/Modbus-Entity-Registers.md
cat ./docs/dump_entities.csv | python3 ./scripts/generate-modbus-register-doc.py >./docs/Modbus-Entity-Registers.md
ls -al ./src/core/modbus_entity_parameters.hpp
ls -al ./docs/Modbus-Entity-Registers.md
ls -al ./docs/dump_entities.csv
ls -al ./docs/dump_telegrams.csv

373
scripts/otatool.py Normal file
View File

@@ -0,0 +1,373 @@
#!/usr/bin/env python
#
# otatool is used to perform ota-level operations - flashing ota partition
# erasing ota partition and switching ota partition
#
# SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from __future__ import division, print_function
import argparse
import binascii
import collections
import os
import struct
import sys
import tempfile
try:
from parttool import PARTITION_TABLE_OFFSET, PartitionName, PartitionType, ParttoolTarget
except ImportError:
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, 'partition_table')
sys.path.append(PARTTOOL_DIR)
from parttool import PARTITION_TABLE_OFFSET, PartitionName, PartitionType, ParttoolTarget
__version__ = '2.0'
SPI_FLASH_SEC_SIZE = 0x2000
quiet = False
def status(msg):
if not quiet:
print(msg)
class OtatoolTarget():
OTADATA_PARTITION = PartitionType('data', 'ota')
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None,
spi_flash_sec_size=SPI_FLASH_SEC_SIZE, esptool_args=[], esptool_write_args=[],
esptool_read_args=[], esptool_erase_args=[]):
self.target = ParttoolTarget(port, baud, partition_table_offset, partition_table_file, esptool_args,
esptool_write_args, esptool_read_args, esptool_erase_args)
self.spi_flash_sec_size = spi_flash_sec_size
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.close()
try:
self.target.read_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name)
with open(temp_file.name, 'rb') as f:
self.otadata = f.read()
finally:
os.unlink(temp_file.name)
def _check_otadata_partition(self):
if not self.otadata:
raise Exception('No otadata partition found')
def erase_otadata(self):
self._check_otadata_partition()
self.target.erase_partition(OtatoolTarget.OTADATA_PARTITION)
def _get_otadata_info(self):
info = []
otadata_info = collections.namedtuple('otadata_info', 'seq crc')
for i in range(2):
start = i * (self.spi_flash_sec_size >> 1)
seq = bytearray(self.otadata[start:start + 4])
crc = bytearray(self.otadata[start + 28:start + 32])
seq = struct.unpack('I', seq)
crc = struct.unpack('I', crc)
info.append(otadata_info(seq[0], crc[0]))
return info
def _get_partition_id_from_ota_id(self, ota_id):
if isinstance(ota_id, int):
return PartitionType('app', 'ota_' + str(ota_id))
else:
return PartitionName(ota_id)
def switch_ota_partition(self, ota_id):
self._check_otadata_partition()
import gen_esp32part as gen
def is_otadata_info_valid(status):
seq = status.seq % (1 << 32)
crc = binascii.crc32(struct.pack('I', seq), 0xFFFFFFFF) % (1 << 32)
return seq < (int('0xFFFFFFFF', 16) % (1 << 32)) and status.crc == crc
partition_table = self.target.partition_table
ota_partitions = list()
for i in range(gen.NUM_PARTITION_SUBTYPE_APP_OTA):
ota_partition = filter(lambda p: p.subtype == (gen.MIN_PARTITION_SUBTYPE_APP_OTA + i), partition_table)
try:
ota_partitions.append(list(ota_partition)[0])
except IndexError:
break
ota_partitions = sorted(ota_partitions, key=lambda p: p.subtype)
if not ota_partitions:
raise Exception('No ota app partitions found')
# Look for the app partition to switch to
ota_partition_next = None
try:
if isinstance(ota_id, int):
ota_partition_next = filter(lambda p: p.subtype - gen.MIN_PARTITION_SUBTYPE_APP_OTA == ota_id, ota_partitions)
else:
ota_partition_next = filter(lambda p: p.name == ota_id, ota_partitions)
ota_partition_next = list(ota_partition_next)[0]
except IndexError:
raise Exception('Partition to switch to not found')
otadata_info = self._get_otadata_info()
# Find the copy to base the computation for ota sequence number on
otadata_compute_base = -1
# Both are valid, take the max as computation base
if is_otadata_info_valid(otadata_info[0]) and is_otadata_info_valid(otadata_info[1]):
if otadata_info[0].seq >= otadata_info[1].seq:
otadata_compute_base = 0
else:
otadata_compute_base = 1
# Only one copy is valid, use that
elif is_otadata_info_valid(otadata_info[0]):
otadata_compute_base = 0
elif is_otadata_info_valid(otadata_info[1]):
otadata_compute_base = 1
# Both are invalid (could be initial state - all 0xFF's)
else:
pass
ota_seq_next = 0
ota_partitions_num = len(ota_partitions)
target_seq = (ota_partition_next.subtype & 0x0F) + 1
# Find the next ota sequence number
if otadata_compute_base == 0 or otadata_compute_base == 1:
base_seq = otadata_info[otadata_compute_base].seq % (1 << 32)
i = 0
while base_seq > target_seq % ota_partitions_num + i * ota_partitions_num:
i += 1
ota_seq_next = target_seq % ota_partitions_num + i * ota_partitions_num
else:
ota_seq_next = target_seq
# Create binary data from computed values
ota_seq_next = struct.pack('I', ota_seq_next)
ota_seq_crc_next = binascii.crc32(ota_seq_next, 0xFFFFFFFF) % (1 << 32)
ota_seq_crc_next = struct.pack('I', ota_seq_crc_next)
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.close()
try:
with open(temp_file.name, 'wb') as otadata_next_file:
start = (1 if otadata_compute_base == 0 else 0) * (self.spi_flash_sec_size >> 1)
otadata_next_file.write(self.otadata)
otadata_next_file.seek(start)
otadata_next_file.write(ota_seq_next)
otadata_next_file.seek(start + 28)
otadata_next_file.write(ota_seq_crc_next)
otadata_next_file.flush()
self.target.write_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name)
finally:
os.unlink(temp_file.name)
def read_ota_partition(self, ota_id, output):
self.target.read_partition(self._get_partition_id_from_ota_id(ota_id), output)
def write_ota_partition(self, ota_id, input):
self.target.write_partition(self._get_partition_id_from_ota_id(ota_id), input)
def erase_ota_partition(self, ota_id):
self.target.erase_partition(self._get_partition_id_from_ota_id(ota_id))
def _read_otadata(target):
target._check_otadata_partition()
otadata_info = target._get_otadata_info()
print(' {:8s} \t {:8s} | \t {:8s} \t {:8s}'.format('OTA_SEQ', 'CRC', 'OTA_SEQ', 'CRC'))
print('Firmware: 0x{:08x} \t0x{:08x} | \t0x{:08x} \t 0x{:08x}'.format(otadata_info[0].seq, otadata_info[0].crc,
otadata_info[1].seq, otadata_info[1].crc))
def _erase_otadata(target):
target.erase_otadata()
status('Erased ota_data partition contents')
def _switch_ota_partition(target, ota_id):
target.switch_ota_partition(ota_id)
def _read_ota_partition(target, ota_id, output):
target.read_ota_partition(ota_id, output)
status('Read ota partition contents to file {}'.format(output))
def _write_ota_partition(target, ota_id, input):
target.write_ota_partition(ota_id, input)
status('Written contents of file {} to ota partition'.format(input))
def _erase_ota_partition(target, ota_id):
target.erase_ota_partition(ota_id)
status('Erased contents of ota partition')
def main():
global quiet
parser = argparse.ArgumentParser('ESP-IDF OTA Partitions Tool')
parser.add_argument('--quiet', '-q', help='suppress stderr messages', action='store_true')
parser.add_argument('--esptool-args', help='additional main arguments for esptool', nargs='+')
parser.add_argument('--esptool-write-args', help='additional subcommand arguments for esptool write_flash', nargs='+')
parser.add_argument('--esptool-read-args', help='additional subcommand arguments for esptool read_flash', nargs='+')
parser.add_argument('--esptool-erase-args', help='additional subcommand arguments for esptool erase_region', nargs='+')
# There are two possible sources for the partition table: a device attached to the host
# or a partition table CSV/binary file. These sources are mutually exclusive.
parser.add_argument('--port', '-p', help='port where the device to read the partition table from is attached')
parser.add_argument('--baud', '-b', help='baudrate to use', type=int)
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
parser.add_argument('--partition-table-file', '-f', help='file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined')
subparsers = parser.add_subparsers(dest='operation', help='run otatool -h for additional help')
spi_flash_sec_size = argparse.ArgumentParser(add_help=False)
spi_flash_sec_size.add_argument('--spi-flash-sec-size', help='value of SPI_FLASH_SEC_SIZE macro', type=str)
# Specify the supported operations
subparsers.add_parser('read_otadata', help='read otadata partition', parents=[spi_flash_sec_size])
subparsers.add_parser('erase_otadata', help='erase otadata partition')
slot_or_name_parser = argparse.ArgumentParser(add_help=False)
slot_or_name_parser_args = slot_or_name_parser.add_mutually_exclusive_group()
slot_or_name_parser_args.add_argument('--slot', help='slot number of the ota partition', type=int)
slot_or_name_parser_args.add_argument('--name', help='name of the ota partition')
subparsers.add_parser('switch_ota_partition', help='switch otadata partition', parents=[slot_or_name_parser, spi_flash_sec_size])
read_ota_partition_subparser = subparsers.add_parser('read_ota_partition', help='read contents of an ota partition', parents=[slot_or_name_parser])
read_ota_partition_subparser.add_argument('--output', help='file to write the contents of the ota partition to', required=True)
write_ota_partition_subparser = subparsers.add_parser('write_ota_partition', help='write contents to an ota partition', parents=[slot_or_name_parser])
write_ota_partition_subparser.add_argument('--input', help='file whose contents to write to the ota partition')
subparsers.add_parser('erase_ota_partition', help='erase contents of an ota partition', parents=[slot_or_name_parser])
args = parser.parse_args()
quiet = args.quiet
# No operation specified, display help and exit
if args.operation is None:
if not quiet:
parser.print_help()
sys.exit(1)
target_args = {}
if args.port:
target_args['port'] = args.port
if args.partition_table_file:
target_args['partition_table_file'] = args.partition_table_file
if args.partition_table_offset:
target_args['partition_table_offset'] = int(args.partition_table_offset, 0)
try:
if args.spi_flash_sec_size:
target_args['spi_flash_sec_size'] = int(args.spi_flash_sec_size, 0)
except AttributeError:
pass
if args.esptool_args:
target_args['esptool_args'] = args.esptool_args
if args.esptool_write_args:
target_args['esptool_write_args'] = args.esptool_write_args
if args.esptool_read_args:
target_args['esptool_read_args'] = args.esptool_read_args
if args.esptool_erase_args:
target_args['esptool_erase_args'] = args.esptool_erase_args
if args.baud:
target_args['baud'] = args.baud
target = OtatoolTarget(**target_args)
# Create the operation table and execute the operation
common_args = {'target':target}
ota_id = []
try:
if args.name is not None:
ota_id = ['name']
else:
if args.slot is not None:
ota_id = ['slot']
except AttributeError:
pass
otatool_ops = {
'read_otadata':(_read_otadata, []),
'erase_otadata':(_erase_otadata, []),
'switch_ota_partition':(_switch_ota_partition, ota_id),
'read_ota_partition':(_read_ota_partition, ['output'] + ota_id),
'write_ota_partition':(_write_ota_partition, ['input'] + ota_id),
'erase_ota_partition':(_erase_ota_partition, ota_id)
}
(op, op_args) = otatool_ops[args.operation]
for op_arg in op_args:
common_args.update({op_arg:vars(args)[op_arg]})
try:
common_args['ota_id'] = common_args.pop('name')
except KeyError:
try:
common_args['ota_id'] = common_args.pop('slot')
except KeyError:
pass
if quiet:
# If exceptions occur, suppress and exit quietly
try:
op(**common_args)
except Exception:
sys.exit(2)
else:
op(**common_args)
if __name__ == '__main__':
main()

View File

@@ -2061,7 +2061,12 @@ bool System::uploadFirmwareURL(const char * url) {
// we're about to start the upload, set the status so the Web System Monitor spots it
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING);
// TODO do we need to stop the UART with EMSuart::stop() ?
// TODO do we need to stop the UART first with EMSuart::stop() ?
// set a callback so we can monitor progress in the WebUI
Update.onProgress([](size_t progress, size_t total) {
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING+(progress * 100 / total));
});
// get tcp stream and send it to Updater
WiFiClient * stream = http.getStreamPtr();

View File

@@ -62,7 +62,7 @@ enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 }
enum SYSTEM_STATUS : uint8_t {
SYSTEM_STATUS_NORMAL = 0,
SYSTEM_STATUS_PENDING_UPLOAD = 1,
SYSTEM_STATUS_UPLOADING = 2,
SYSTEM_STATUS_UPLOADING = 100,
SYSTEM_STATUS_ERROR_UPLOAD = 3,
SYSTEM_STATUS_PENDING_RESTART = 4,
SYSTEM_STATUS_RESTART_REQUESTED = 5

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.2-dev.13"
#define EMSESP_APP_VERSION "3.7.2-dev.14"