mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-03-23 01:56:32 +03:00
@@ -8,9 +8,19 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
|
|||||||
|
|
||||||
- change enum_heatingtype for remote control [#2268](https://github.com/emsesp/EMS-ESP32/issues/2268)
|
- change enum_heatingtype for remote control [#2268](https://github.com/emsesp/EMS-ESP32/issues/2268)
|
||||||
- system service commands [#2182](https://github.com/emsesp/EMS-ESP32/issues/2282)
|
- system service commands [#2182](https://github.com/emsesp/EMS-ESP32/issues/2282)
|
||||||
|
- read 0x02A5 for thermostat CT200 [#2277](https://github.com/emsesp/EMS-ESP32/issues/2277)
|
||||||
|
- Add "duplicate" option to Custom Entities [#2266](https://github.com/emsesp/EMS-ESP32/discussion/2266)
|
||||||
|
- Mask bits for bool custom entities
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
- long numbers of custom entities [#2267](https://github.com/emsesp/EMS-ESP32/issues/2267)
|
||||||
|
- modbus command path to `api/` [#2276](https://github.com/emsesp/EMS-ESP32/issues/2276)
|
||||||
|
- info command for devices without entity-commands [#2274](https://github.com/emsesp/EMS-ESP32/issues/2274)
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- show operation in pretty telegram between src and dst [#2263](https://github.com/emsesp/EMS-ESP32/discussions/2263)
|
- show operation in pretty telegram between src and dst [#2263](https://github.com/emsesp/EMS-ESP32/discussions/2263)
|
||||||
|
- update eModbus to 1.7.2 [#2254](https://github.com/emsesp/EMS-ESP32/issues/2254)
|
||||||
|
- modbus timeout default to 300 sec, change setting from ms to sec [#2254](https://github.com/emsesp/EMS-ESP32/issues/2254)
|
||||||
|
- update AsyncTCP to v3.2.14
|
||||||
|
|||||||
@@ -24,18 +24,18 @@
|
|||||||
"@alova/adapter-xhr": "2.0.11",
|
"@alova/adapter-xhr": "2.0.11",
|
||||||
"@emotion/react": "^11.13.5",
|
"@emotion/react": "^11.13.5",
|
||||||
"@emotion/styled": "^11.13.5",
|
"@emotion/styled": "^11.13.5",
|
||||||
"@mui/icons-material": "^6.1.9",
|
"@mui/icons-material": "^6.1.10",
|
||||||
"@mui/material": "^6.1.9",
|
"@mui/material": "^6.1.10",
|
||||||
"@table-library/react-table-library": "4.1.7",
|
"@table-library/react-table-library": "4.1.7",
|
||||||
"alova": "3.2.6",
|
"alova": "3.2.6",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"preact": "^10.25.1",
|
"preact": "^10.25.1",
|
||||||
"react": "^18.3.1",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^19.0.0",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.4.0",
|
||||||
"react-router": "^7.0.1",
|
"react-router": "^7.0.2",
|
||||||
"react-toastify": "^10.0.6",
|
"react-toastify": "^10.0.6",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.26.2",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.7.2"
|
||||||
@@ -45,20 +45,20 @@
|
|||||||
"@eslint/js": "^9.16.0",
|
"@eslint/js": "^9.16.0",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.1",
|
||||||
"@preact/preset-vite": "^2.9.2",
|
"@preact/preset-vite": "^2.9.2",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^5.0.1",
|
||||||
"@types/formidable": "^3",
|
"@types/formidable": "^3",
|
||||||
"@types/node": "^22.10.1",
|
"@types/node": "^22.10.1",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.13",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"concurrently": "^9.1.0",
|
"concurrently": "^9.1.0",
|
||||||
"eslint": "^9.16.0",
|
"eslint": "^9.16.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"formidable": "^3.5.2",
|
"formidable": "^3.5.2",
|
||||||
"prettier": "^3.4.1",
|
"prettier": "^3.4.2",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"terser": "^5.36.0",
|
"terser": "^5.37.0",
|
||||||
"typescript-eslint": "8.17.0",
|
"typescript-eslint": "8.17.0",
|
||||||
"vite": "^6.0.2",
|
"vite": "^6.0.3",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1",
|
||||||
"vite-tsconfig-paths": "^5.1.3"
|
"vite-tsconfig-paths": "^5.1.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const CustomEntities = () => {
|
|||||||
|
|
||||||
const entity_theme = useTheme({
|
const entity_theme = useTheme({
|
||||||
Table: `
|
Table: `
|
||||||
--data-table-library_grid-template-columns: repeat(1, minmax(60px, 1fr)) minmax(80px, auto) 80px 80px 80px 90px;
|
--data-table-library_grid-template-columns: repeat(1, minmax(60px, 1fr)) minmax(80px, auto) 80px 80px 80px 120px;
|
||||||
`,
|
`,
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -195,6 +195,25 @@ const CustomEntities = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDialogDup = (item: EntityItem) => {
|
||||||
|
setCreating(true);
|
||||||
|
setSelectedEntityItem({
|
||||||
|
id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100),
|
||||||
|
name: item.name + '_',
|
||||||
|
ram: item.ram,
|
||||||
|
device_id: item.device_id,
|
||||||
|
type_id: item.type_id,
|
||||||
|
offset: item.offset,
|
||||||
|
factor: item.factor,
|
||||||
|
uom: item.uom,
|
||||||
|
value_type: item.value_type,
|
||||||
|
writeable: item.writeable,
|
||||||
|
deleted: false,
|
||||||
|
value: item.value
|
||||||
|
});
|
||||||
|
setDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const addEntityItem = () => {
|
const addEntityItem = () => {
|
||||||
setCreating(true);
|
setCreating(true);
|
||||||
setSelectedEntityItem({
|
setSelectedEntityItem({
|
||||||
@@ -220,7 +239,7 @@ const CustomEntities = () => {
|
|||||||
: typeof value === 'number'
|
: typeof value === 'number'
|
||||||
? new Intl.NumberFormat().format(value) +
|
? new Intl.NumberFormat().format(value) +
|
||||||
(uom === 0 ? '' : ' ' + DeviceValueUOM_s[uom])
|
(uom === 0 ? '' : ' ' + DeviceValueUOM_s[uom])
|
||||||
: (value as string);
|
: (value as string) + (uom === 0 ? '' : ' ' + DeviceValueUOM_s[uom]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showHex(value: number, digit: number) {
|
function showHex(value: number, digit: number) {
|
||||||
@@ -296,6 +315,7 @@ const CustomEntities = () => {
|
|||||||
creating={creating}
|
creating={creating}
|
||||||
onClose={onDialogClose}
|
onClose={onDialogClose}
|
||||||
onSave={onDialogSave}
|
onSave={onDialogSave}
|
||||||
|
onDup={onDialogDup}
|
||||||
selectedItem={selectedEntityItem}
|
selectedItem={selectedEntityItem}
|
||||||
validator={entityItemValidation(entities, selectedEntityItem)}
|
validator={entityItemValidation(entities, selectedEntityItem)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ interface CustomEntitiesDialogProps {
|
|||||||
creating: boolean;
|
creating: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (ei: EntityItem) => void;
|
onSave: (ei: EntityItem) => void;
|
||||||
|
onDup: (ei: EntityItem) => void;
|
||||||
selectedItem: EntityItem;
|
selectedItem: EntityItem;
|
||||||
validator: Schema;
|
validator: Schema;
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,7 @@ const CustomEntitiesDialog = ({
|
|||||||
creating,
|
creating,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
|
onDup,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
validator
|
validator
|
||||||
}: CustomEntitiesDialogProps) => {
|
}: CustomEntitiesDialogProps) => {
|
||||||
@@ -91,6 +93,10 @@ const CustomEntitiesDialog = ({
|
|||||||
onSave(editItem);
|
onSave(editItem);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dup = () => {
|
||||||
|
onDup(editItem);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
@@ -128,6 +134,7 @@ const CustomEntitiesDialog = ({
|
|||||||
</TextField>
|
</TextField>
|
||||||
</Grid>
|
</Grid>
|
||||||
{editItem.ram === 1 && (
|
{editItem.ram === 1 && (
|
||||||
|
<>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextField
|
<TextField
|
||||||
name="value"
|
name="value"
|
||||||
@@ -140,6 +147,23 @@ const CustomEntitiesDialog = ({
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
name="uom"
|
||||||
|
label={LL.UNIT()}
|
||||||
|
value={editItem.uom}
|
||||||
|
margin="normal"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
select
|
||||||
|
>
|
||||||
|
{DeviceValueUOM_s.map((val, i) => (
|
||||||
|
<MenuItem key={val} value={i}>
|
||||||
|
{val}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{editItem.ram === 0 && (
|
{editItem.ram === 0 && (
|
||||||
<>
|
<>
|
||||||
@@ -255,7 +279,7 @@ const CustomEntitiesDialog = ({
|
|||||||
<TextField
|
<TextField
|
||||||
name="factor"
|
name="factor"
|
||||||
label={LL.FACTOR()}
|
label={LL.FACTOR()}
|
||||||
value={numberValue(editItem.factor)}
|
value={numberValue(editItem.factor as number)}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
@@ -292,12 +316,38 @@ const CustomEntitiesDialog = ({
|
|||||||
fieldErrors={fieldErrors}
|
fieldErrors={fieldErrors}
|
||||||
name="factor"
|
name="factor"
|
||||||
label="Bytes"
|
label="Bytes"
|
||||||
value={numberValue(editItem.factor)}
|
value={numberValue(editItem.factor as number)}
|
||||||
sx={{ width: '11ch' }}
|
sx={{ width: '11ch' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
type="number"
|
type="number"
|
||||||
|
slotProps={{
|
||||||
|
htmlInput: { step: '1', min: '1', max: '255' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
{editItem.value_type === DeviceValueType.BOOL && (
|
||||||
|
<Grid>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors}
|
||||||
|
name="factor"
|
||||||
|
label="Mask"
|
||||||
|
value={editItem.factor as string}
|
||||||
|
sx={{ width: '11ch' }}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
type="string"
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">0x</InputAdornment>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
htmlInput: { style: { textTransform: 'uppercase' } }
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
@@ -316,6 +366,15 @@ const CustomEntitiesDialog = ({
|
|||||||
>
|
>
|
||||||
{LL.REMOVE()}
|
{LL.REMOVE()}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
sx={{ ml: 1 }}
|
||||||
|
startIcon={<AddIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={dup}
|
||||||
|
>
|
||||||
|
{LL.DUPLICATE()}
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function formatValue(
|
|||||||
if (value === undefined || typeof value === 'boolean') {
|
if (value === undefined || typeof value === 'boolean') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return value as string;
|
return (value as string) + (uom === undefined || uom === 0 ? '' : ' ' + DeviceValueUOM_s[uom]);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (uom) {
|
switch (uom) {
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ export interface EntityItem {
|
|||||||
device_id: number | string;
|
device_id: number | string;
|
||||||
type_id: number | string;
|
type_id: number | string;
|
||||||
offset: number;
|
offset: number;
|
||||||
factor: number;
|
factor: number | string;
|
||||||
uom: number;
|
uom: number;
|
||||||
value_type: number;
|
value_type: number;
|
||||||
value?: unknown;
|
value?: unknown;
|
||||||
@@ -380,7 +380,7 @@ export interface EntityItem {
|
|||||||
o_device_id?: number | string;
|
o_device_id?: number | string;
|
||||||
o_type_id?: number | string;
|
o_type_id?: number | string;
|
||||||
o_offset?: number;
|
o_offset?: number;
|
||||||
o_factor?: number;
|
o_factor?: number | string;
|
||||||
o_uom?: number;
|
o_uom?: number;
|
||||||
o_value_type?: number;
|
o_value_type?: number;
|
||||||
o_deleted?: boolean;
|
o_deleted?: boolean;
|
||||||
|
|||||||
@@ -383,8 +383,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
|
|||||||
{ type: 'number', min: 0, max: 255, message: 'Must be between 0 and 255' }
|
{ type: 'number', min: 0, max: 255, message: 'Must be between 0 and 255' }
|
||||||
],
|
],
|
||||||
factor: [
|
factor: [
|
||||||
{ required: true, message: 'is required' },
|
{ required: true, message: 'is required' }
|
||||||
{ type: 'number', message: 'Must be a number' }
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ const ApplicationSettings = () => {
|
|||||||
name="modbus_timeout"
|
name="modbus_timeout"
|
||||||
label="Timeout"
|
label="Timeout"
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: MilliSecondsInputProps
|
input: SecondsInputProps
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={numberValue(data.modbus_timeout)}
|
value={numberValue(data.modbus_timeout)}
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const cz: Translation = {
|
|||||||
NO_DATA: 'Žádná data nejsou k dispozici',
|
NO_DATA: 'Žádná data nejsou k dispozici',
|
||||||
DASHBOARD_1: 'Přizpůsobte si dashboard označením EMS entit jako Oblíbené pomocí modulu Přizpůsobení',
|
DASHBOARD_1: 'Přizpůsobte si dashboard označením EMS entit jako Oblíbené pomocí modulu Přizpůsobení',
|
||||||
DEVELOPER_MODE: 'Režim vývojáře',
|
DEVELOPER_MODE: 'Režim vývojáře',
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,7 +343,8 @@ const de: Translation = {
|
|||||||
NO_DATA: 'Keine Daten verfügbar',
|
NO_DATA: 'Keine Daten verfügbar',
|
||||||
DASHBOARD_1: 'Passen Sie Ihr Dashboard an, indem Sie EMS-Entitäten mithilfe des Moduls „Anpassungen“ als Favorit markieren',
|
DASHBOARD_1: 'Passen Sie Ihr Dashboard an, indem Sie EMS-Entitäten mithilfe des Moduls „Anpassungen“ als Favorit markieren',
|
||||||
DEVELOPER_MODE: 'Entwicklermodus',
|
DEVELOPER_MODE: 'Entwicklermodus',
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
DUPLICATE: 'Kopieren',
|
||||||
|
UPGRADE: 'Aktualisieren'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const en: Translation = {
|
|||||||
NO_DATA: 'No data available',
|
NO_DATA: 'No data available',
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module',
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module',
|
||||||
DEVELOPER_MODE: 'Developer Mode',
|
DEVELOPER_MODE: 'Developer Mode',
|
||||||
|
DUPLICATE: 'Duplicate',
|
||||||
UPGRADE: 'Upgrade'
|
UPGRADE: 'Upgrade'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const fr: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const it: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const nl: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const no: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const pl: BaseTranslation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const sk: Translation = {
|
|||||||
NO_DATA: 'Nie sú k dispozícii žiadne údaje',
|
NO_DATA: 'Nie sú k dispozícii žiadne údaje',
|
||||||
DASHBOARD_1: 'Prispôsobte si svoj informačný panel tak, že označíte entity EMS ako Obľúbené pomocou modulu Prispôsobenia',
|
DASHBOARD_1: 'Prispôsobte si svoj informačný panel tak, že označíte entity EMS ako Obľúbené pomocou modulu Prispôsobenia',
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const sv: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ const tr: Translation = {
|
|||||||
NO_DATA: 'No data available', // TODO translate
|
NO_DATA: 'No data available', // TODO translate
|
||||||
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorite using the Customizations module', // TODO translate
|
||||||
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
DEVELOPER_MODE: 'Developer Mode', // TODO translate
|
||||||
|
DUPLICATE: 'Duplicate', // TODO translate
|
||||||
UPGRADE: 'Upgrade' // TODO translate
|
UPGRADE: 'Upgrade' // TODO translate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -74,18 +74,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/generator@npm:7.17.7":
|
"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.0":
|
||||||
version: 7.17.7
|
|
||||||
resolution: "@babel/generator@npm:7.17.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/types": "npm:^7.17.0"
|
|
||||||
jsesc: "npm:^2.5.1"
|
|
||||||
source-map: "npm:^0.5.0"
|
|
||||||
checksum: 10c0/8088453c4418e0ee6528506fbd5847bbdfd56327a0025ca9496a259261e162c594ffd08be0d63e74c32feced795616772f38acc5f5e493a86a45fd439fd9feb0
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.0":
|
|
||||||
version: 7.26.2
|
version: 7.26.2
|
||||||
resolution: "@babel/generator@npm:7.26.2"
|
resolution: "@babel/generator@npm:7.26.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -98,6 +87,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@babel/generator@npm:^7.26.2":
|
||||||
|
version: 7.26.3
|
||||||
|
resolution: "@babel/generator@npm:7.26.3"
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser": "npm:^7.26.3"
|
||||||
|
"@babel/types": "npm:^7.26.3"
|
||||||
|
"@jridgewell/gen-mapping": "npm:^0.3.5"
|
||||||
|
"@jridgewell/trace-mapping": "npm:^0.3.25"
|
||||||
|
jsesc: "npm:^3.0.2"
|
||||||
|
checksum: 10c0/54f260558e3e4ec8942da3cde607c35349bb983c3a7c5121243f96893fba3e8cd62e1f1773b2051f936f8c8a10987b758d5c7d76dbf2784e95bb63ab4843fa00
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-annotate-as-pure@npm:^7.25.9":
|
"@babel/helper-annotate-as-pure@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/helper-annotate-as-pure@npm:7.25.9"
|
resolution: "@babel/helper-annotate-as-pure@npm:7.25.9"
|
||||||
@@ -120,34 +122,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@npm:^7.22.20":
|
|
||||||
version: 7.24.7
|
|
||||||
resolution: "@babel/helper-environment-visitor@npm:7.24.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/types": "npm:^7.24.7"
|
|
||||||
checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/helper-function-name@npm:^7.23.0":
|
|
||||||
version: 7.24.7
|
|
||||||
resolution: "@babel/helper-function-name@npm:7.24.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/template": "npm:^7.24.7"
|
|
||||||
"@babel/types": "npm:^7.24.7"
|
|
||||||
checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@npm:^7.22.5":
|
|
||||||
version: 7.24.7
|
|
||||||
resolution: "@babel/helper-hoist-variables@npm:7.24.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/types": "npm:^7.24.7"
|
|
||||||
checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.25.9":
|
"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/helper-module-imports@npm:7.25.9"
|
resolution: "@babel/helper-module-imports@npm:7.25.9"
|
||||||
@@ -178,15 +152,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@npm:^7.22.6":
|
|
||||||
version: 7.24.7
|
|
||||||
resolution: "@babel/helper-split-export-declaration@npm:7.24.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/types": "npm:^7.24.7"
|
|
||||||
checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/helper-string-parser@npm:^7.25.9":
|
"@babel/helper-string-parser@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/helper-string-parser@npm:7.25.9"
|
resolution: "@babel/helper-string-parser@npm:7.25.9"
|
||||||
@@ -194,7 +159,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.25.9":
|
"@babel/helper-validator-identifier@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/helper-validator-identifier@npm:7.25.9"
|
resolution: "@babel/helper-validator-identifier@npm:7.25.9"
|
||||||
checksum: 10c0/4fc6f830177b7b7e887ad3277ddb3b91d81e6c4a24151540d9d1023e8dc6b1c0505f0f0628ae653601eb4388a8db45c1c14b2c07a9173837aef7e4116456259d
|
checksum: 10c0/4fc6f830177b7b7e887ad3277ddb3b91d81e6c4a24151540d9d1023e8dc6b1c0505f0f0628ae653601eb4388a8db45c1c14b2c07a9173837aef7e4116456259d
|
||||||
@@ -218,7 +183,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2":
|
"@babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2":
|
||||||
version: 7.26.2
|
version: 7.26.2
|
||||||
resolution: "@babel/parser@npm:7.26.2"
|
resolution: "@babel/parser@npm:7.26.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -229,6 +194,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@babel/parser@npm:^7.26.3":
|
||||||
|
version: 7.26.3
|
||||||
|
resolution: "@babel/parser@npm:7.26.3"
|
||||||
|
dependencies:
|
||||||
|
"@babel/types": "npm:^7.26.3"
|
||||||
|
bin:
|
||||||
|
parser: ./bin/babel-parser.js
|
||||||
|
checksum: 10c0/48f736374e61cfd10ddbf7b80678514ae1f16d0e88bc793d2b505d73d9b987ea786fc8c2f7ee8f8b8c467df062030eb07fd0eb2168f0f541ca1f542775852cad
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@npm:^7.25.9":
|
"@babel/plugin-syntax-jsx@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/plugin-syntax-jsx@npm:7.25.9"
|
resolution: "@babel/plugin-syntax-jsx@npm:7.25.9"
|
||||||
@@ -275,7 +251,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9":
|
"@babel/template@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/template@npm:7.25.9"
|
resolution: "@babel/template@npm:7.25.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -286,24 +262,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/traverse@npm:7.23.2":
|
|
||||||
version: 7.23.2
|
|
||||||
resolution: "@babel/traverse@npm:7.23.2"
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame": "npm:^7.22.13"
|
|
||||||
"@babel/generator": "npm:^7.23.0"
|
|
||||||
"@babel/helper-environment-visitor": "npm:^7.22.20"
|
|
||||||
"@babel/helper-function-name": "npm:^7.23.0"
|
|
||||||
"@babel/helper-hoist-variables": "npm:^7.22.5"
|
|
||||||
"@babel/helper-split-export-declaration": "npm:^7.22.6"
|
|
||||||
"@babel/parser": "npm:^7.23.0"
|
|
||||||
"@babel/types": "npm:^7.23.0"
|
|
||||||
debug: "npm:^4.1.0"
|
|
||||||
globals: "npm:^11.1.0"
|
|
||||||
checksum: 10c0/d096c7c4bab9262a2f658298a3c630ae4a15a10755bb257ae91d5ab3e3b2877438934859c8d34018b7727379fe6b26c4fa2efc81cf4c462a7fe00caf79fa02ff
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/traverse@npm:^7.25.9":
|
"@babel/traverse@npm:^7.25.9":
|
||||||
version: 7.25.9
|
version: 7.25.9
|
||||||
resolution: "@babel/traverse@npm:7.25.9"
|
resolution: "@babel/traverse@npm:7.25.9"
|
||||||
@@ -319,17 +277,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/types@npm:7.17.0":
|
"@babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0":
|
||||||
version: 7.17.0
|
|
||||||
resolution: "@babel/types@npm:7.17.0"
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-validator-identifier": "npm:^7.16.7"
|
|
||||||
to-fast-properties: "npm:^2.0.0"
|
|
||||||
checksum: 10c0/ad09224272b40fedb00b262677d12b6838f5b5df5c47d67059ba1181bd4805439993393a8de32459dae137b536d60ebfcaf39ae84d8b3873f1e81cc75f5aeae8
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@babel/types@npm:^7.17.0, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0":
|
|
||||||
version: 7.26.0
|
version: 7.26.0
|
||||||
resolution: "@babel/types@npm:7.26.0"
|
resolution: "@babel/types@npm:7.26.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -339,6 +287,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@babel/types@npm:^7.26.3":
|
||||||
|
version: 7.26.3
|
||||||
|
resolution: "@babel/types@npm:7.26.3"
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser": "npm:^7.25.9"
|
||||||
|
"@babel/helper-validator-identifier": "npm:^7.25.9"
|
||||||
|
checksum: 10c0/966c5242c5e55c8704bf7a7418e7be2703a0afa4d19a8480999d5a4ef13d095dd60686615fe5983cb7593b4b06ba3a7de8d6ca501c1d78bdd233a10d90be787b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/babel-plugin@npm:^11.13.5":
|
"@emotion/babel-plugin@npm:^11.13.5":
|
||||||
version: 11.13.5
|
version: 11.13.5
|
||||||
resolution: "@emotion/babel-plugin@npm:11.13.5"
|
resolution: "@emotion/babel-plugin@npm:11.13.5"
|
||||||
@@ -840,38 +798,38 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/core-downloads-tracker@npm:^6.1.9":
|
"@mui/core-downloads-tracker@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/core-downloads-tracker@npm:6.1.9"
|
resolution: "@mui/core-downloads-tracker@npm:6.1.10"
|
||||||
checksum: 10c0/f84c48291e6c85bafb60015f0464f041ca3168dcfd7c563cb584bf925495872f0a180ed52e985b544c6cb87297a1686b1d3a90f451d12594765d0811fc7514b0
|
checksum: 10c0/b94259c7cc8065dfed7ead4250e1bba4e65ec5625d86a11d118d1c22ec05f058d6f93b66d73bba9d787453058a2a05b5a1bbfd01ac53f3410e25358da3e4a571
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/icons-material@npm:^6.1.9":
|
"@mui/icons-material@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/icons-material@npm:6.1.9"
|
resolution: "@mui/icons-material@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@mui/material": ^6.1.9
|
"@mui/material": ^6.1.10
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/7efc0c4a4c668118252ec0a0bf7ed8e4d9af68d03d92e76d382109aaf8b018bd3512b47c444947c12df25be02615483a5e4889ddf745b42ab0446583b34d59f8
|
checksum: 10c0/970a402962b27219bdff4b7af32e758fea800c6879112887cdc71f0d6869c3ea66e24e320edbc39b704f174511a918a055b96d49b91bdeefb1afe8d753b42d4e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/material@npm:^6.1.9":
|
"@mui/material@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/material@npm:6.1.9"
|
resolution: "@mui/material@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
"@mui/core-downloads-tracker": "npm:^6.1.9"
|
"@mui/core-downloads-tracker": "npm:^6.1.10"
|
||||||
"@mui/system": "npm:^6.1.9"
|
"@mui/system": "npm:^6.1.10"
|
||||||
"@mui/types": "npm:^7.2.19"
|
"@mui/types": "npm:^7.2.19"
|
||||||
"@mui/utils": "npm:^6.1.9"
|
"@mui/utils": "npm:^6.1.10"
|
||||||
"@popperjs/core": "npm:^2.11.8"
|
"@popperjs/core": "npm:^2.11.8"
|
||||||
"@types/react-transition-group": "npm:^4.4.11"
|
"@types/react-transition-group": "npm:^4.4.11"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
@@ -882,7 +840,7 @@ __metadata:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@emotion/react": ^11.5.0
|
"@emotion/react": ^11.5.0
|
||||||
"@emotion/styled": ^11.3.0
|
"@emotion/styled": ^11.3.0
|
||||||
"@mui/material-pigment-css": ^6.1.9
|
"@mui/material-pigment-css": ^6.1.10
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
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
|
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
@@ -895,16 +853,16 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/934be9f17e66ac9944ff970c2a2c67292aadd339f5124e2ec82fbfd25fe713e7716c61005b918dd31836674fb8e327bd87f200276f2ef8930e9ba4e77727bad6
|
checksum: 10c0/501f434aa61b56806cd6d8bc27ddd0cfc6f423ee132bb0cc02ca36af182b251b8d15dd69ed8b90742813039d2bfe1e396f6ddf84f15f453d53bfbf355d18f99a
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/private-theming@npm:^6.1.9":
|
"@mui/private-theming@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/private-theming@npm:6.1.9"
|
resolution: "@mui/private-theming@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
"@mui/utils": "npm:^6.1.9"
|
"@mui/utils": "npm:^6.1.10"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
"@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
@@ -912,13 +870,13 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/d2ed09a50a30496905ba57735c370efa85b00ed0b29b2bd9e7193416d4825659d48abaf2bcf649a80dfaa035262487b81a3396b042fd97646f775ca4d20dd05c
|
checksum: 10c0/1e296d7582b276e7b9de8c9af252b4998ab8343a570ed7ead8c23b3275a218f0722f389d698c45cab9b4b219ad82411af6f71c7678818d3387925db01d1a9ead
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/styled-engine@npm:^6.1.9":
|
"@mui/styled-engine@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/styled-engine@npm:6.1.9"
|
resolution: "@mui/styled-engine@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
"@emotion/cache": "npm:^11.13.5"
|
"@emotion/cache": "npm:^11.13.5"
|
||||||
@@ -935,19 +893,19 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@emotion/styled":
|
"@emotion/styled":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/74d239b2d306b0aa2271d9943cea83ea0a05c16c55cd2239605b0f1cb91fc4aa508891173d1b9bb69e92074d1d4db14d8b85618c639667967a1f608607047e66
|
checksum: 10c0/2e6ad8f1c3de4ce4b6a246de976f304e433e8a5031465bc9614e51eeae2dd308c76dbdfc2b9f95bbcb971c1008bdbc9103d9418313934a9760f6555334997586
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/system@npm:^6.1.9":
|
"@mui/system@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/system@npm:6.1.9"
|
resolution: "@mui/system@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
"@mui/private-theming": "npm:^6.1.9"
|
"@mui/private-theming": "npm:^6.1.10"
|
||||||
"@mui/styled-engine": "npm:^6.1.9"
|
"@mui/styled-engine": "npm:^6.1.10"
|
||||||
"@mui/types": "npm:^7.2.19"
|
"@mui/types": "npm:^7.2.19"
|
||||||
"@mui/utils": "npm:^6.1.9"
|
"@mui/utils": "npm:^6.1.10"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
csstype: "npm:^3.1.3"
|
csstype: "npm:^3.1.3"
|
||||||
prop-types: "npm:^15.8.1"
|
prop-types: "npm:^15.8.1"
|
||||||
@@ -963,7 +921,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/0ae66dbbf183bf6ccefeaf3808699c6ef2dd72928134e3e902716e3f75d6b15ccc41c1478a23f20824b76ff3ca86b0923ebccb58ae11b9e04305988eae74d653
|
checksum: 10c0/ba228faa0a2c3f5b25770faa1484c04846889642eda1d0c3922b324c6de2946fc4debf7ddb994f1ef349bbe260243ab5e1117b624ea5062209ba1898f5dff1a3
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -979,9 +937,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@mui/utils@npm:^6.1.9":
|
"@mui/utils@npm:^6.1.10":
|
||||||
version: 6.1.9
|
version: 6.1.10
|
||||||
resolution: "@mui/utils@npm:6.1.9"
|
resolution: "@mui/utils@npm:6.1.10"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.26.0"
|
"@babel/runtime": "npm:^7.26.0"
|
||||||
"@mui/types": "npm:^7.2.19"
|
"@mui/types": "npm:^7.2.19"
|
||||||
@@ -995,7 +953,7 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/react":
|
"@types/react":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/2cd28cc44f9048b2c9b5511f074082da1f5b969c524f08f827aaa5e226392238bb91fbf6c5054290590b5d9a67d7f753a6ee9b7cc7613df7193635b8f63ca577
|
checksum: 10c0/0cecda147b026fbb6f397e54fc08719c5805a7e5fd4255c30a45a7155748345dbbc57fda9181986db222f93da0012f001b1f1f624084e5227ca7814ed1c3dd30
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1291,23 +1249,29 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@trivago/prettier-plugin-sort-imports@npm:^4.3.0":
|
"@trivago/prettier-plugin-sort-imports@npm:^5.0.1":
|
||||||
version: 4.3.0
|
version: 5.0.1
|
||||||
resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0"
|
resolution: "@trivago/prettier-plugin-sort-imports@npm:5.0.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/generator": "npm:7.17.7"
|
"@babel/generator": "npm:^7.26.2"
|
||||||
"@babel/parser": "npm:^7.20.5"
|
"@babel/parser": "npm:^7.26.2"
|
||||||
"@babel/traverse": "npm:7.23.2"
|
"@babel/traverse": "npm:^7.25.9"
|
||||||
"@babel/types": "npm:7.17.0"
|
"@babel/types": "npm:^7.26.0"
|
||||||
javascript-natural-sort: "npm:0.7.1"
|
javascript-natural-sort: "npm:^0.7.1"
|
||||||
lodash: "npm:^4.17.21"
|
lodash: "npm:^4.17.21"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@vue/compiler-sfc": 3.x
|
"@vue/compiler-sfc": 3.x
|
||||||
prettier: 2.x - 3.x
|
prettier: 2.x - 3.x
|
||||||
|
prettier-plugin-svelte: 3.x
|
||||||
|
svelte: 4.x
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@vue/compiler-sfc":
|
"@vue/compiler-sfc":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/42270fb9c89e54a3f8b6ac8c43e6d0e03350e2857e902cdad4de22c78ef1864da600525595311bc7e94e51c16c7dd3882c2e048a162fdab59761ffa893756aa2
|
prettier-plugin-svelte:
|
||||||
|
optional: true
|
||||||
|
svelte:
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/2256a2ff06f1809f52e37d609ed04cdb5253f3ee83e959dfff301f4e6f3295cdd4adb5015d5a1ac40ff934f6bde9c0c2bd1e84ee7c0d65e5b755c4ce739bfe08
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1488,7 +1452,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react@npm:*, @types/react@npm:^18.3.12":
|
"@types/react@npm:*":
|
||||||
version: 18.3.12
|
version: 18.3.12
|
||||||
resolution: "@types/react@npm:18.3.12"
|
resolution: "@types/react@npm:18.3.12"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1498,6 +1462,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/react@npm:^18.3.13":
|
||||||
|
version: 18.3.13
|
||||||
|
resolution: "@types/react@npm:18.3.13"
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types": "npm:*"
|
||||||
|
csstype: "npm:^3.0.2"
|
||||||
|
checksum: 10c0/91815e00157deb179fa670aa2dfc491952698b7743ffddca0e3e0f16e7a18454f3f5ef72321a07386c49e721563b9d280dbbdfae039face764e2fdd8ad949d4b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/responselike@npm:^1.0.0":
|
"@types/responselike@npm:^1.0.0":
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
resolution: "@types/responselike@npm:1.0.3"
|
resolution: "@types/responselike@npm:1.0.3"
|
||||||
@@ -1646,15 +1620,15 @@ __metadata:
|
|||||||
"@emotion/react": "npm:^11.13.5"
|
"@emotion/react": "npm:^11.13.5"
|
||||||
"@emotion/styled": "npm:^11.13.5"
|
"@emotion/styled": "npm:^11.13.5"
|
||||||
"@eslint/js": "npm:^9.16.0"
|
"@eslint/js": "npm:^9.16.0"
|
||||||
"@mui/icons-material": "npm:^6.1.9"
|
"@mui/icons-material": "npm:^6.1.10"
|
||||||
"@mui/material": "npm:^6.1.9"
|
"@mui/material": "npm:^6.1.10"
|
||||||
"@preact/compat": "npm:^18.3.1"
|
"@preact/compat": "npm:^18.3.1"
|
||||||
"@preact/preset-vite": "npm:^2.9.2"
|
"@preact/preset-vite": "npm:^2.9.2"
|
||||||
"@table-library/react-table-library": "npm:4.1.7"
|
"@table-library/react-table-library": "npm:4.1.7"
|
||||||
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
|
"@trivago/prettier-plugin-sort-imports": "npm:^5.0.1"
|
||||||
"@types/formidable": "npm:^3"
|
"@types/formidable": "npm:^3"
|
||||||
"@types/node": "npm:^22.10.1"
|
"@types/node": "npm:^22.10.1"
|
||||||
"@types/react": "npm:^18.3.12"
|
"@types/react": "npm:^18.3.13"
|
||||||
"@types/react-dom": "npm:^18.3.1"
|
"@types/react-dom": "npm:^18.3.1"
|
||||||
alova: "npm:3.2.6"
|
alova: "npm:3.2.6"
|
||||||
async-validator: "npm:^4.2.5"
|
async-validator: "npm:^4.2.5"
|
||||||
@@ -1665,18 +1639,18 @@ __metadata:
|
|||||||
jwt-decode: "npm:^4.0.0"
|
jwt-decode: "npm:^4.0.0"
|
||||||
mime-types: "npm:^2.1.35"
|
mime-types: "npm:^2.1.35"
|
||||||
preact: "npm:^10.25.1"
|
preact: "npm:^10.25.1"
|
||||||
prettier: "npm:^3.4.1"
|
prettier: "npm:^3.4.2"
|
||||||
react: "npm:^18.3.1"
|
react: "npm:^19.0.0"
|
||||||
react-dom: "npm:^18.3.1"
|
react-dom: "npm:^19.0.0"
|
||||||
react-icons: "npm:^5.3.0"
|
react-icons: "npm:^5.4.0"
|
||||||
react-router: "npm:^7.0.1"
|
react-router: "npm:^7.0.2"
|
||||||
react-toastify: "npm:^10.0.6"
|
react-toastify: "npm:^10.0.6"
|
||||||
rollup-plugin-visualizer: "npm:^5.12.0"
|
rollup-plugin-visualizer: "npm:^5.12.0"
|
||||||
terser: "npm:^5.36.0"
|
terser: "npm:^5.37.0"
|
||||||
typesafe-i18n: "npm:^5.26.2"
|
typesafe-i18n: "npm:^5.26.2"
|
||||||
typescript: "npm:^5.7.2"
|
typescript: "npm:^5.7.2"
|
||||||
typescript-eslint: "npm:8.17.0"
|
typescript-eslint: "npm:8.17.0"
|
||||||
vite: "npm:^6.0.2"
|
vite: "npm:^6.0.3"
|
||||||
vite-plugin-imagemin: "npm:^0.6.1"
|
vite-plugin-imagemin: "npm:^0.6.1"
|
||||||
vite-tsconfig-paths: "npm:^5.1.3"
|
vite-tsconfig-paths: "npm:^5.1.3"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
@@ -4420,7 +4394,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"javascript-natural-sort@npm:0.7.1":
|
"javascript-natural-sort@npm:^0.7.1":
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
resolution: "javascript-natural-sort@npm:0.7.1"
|
resolution: "javascript-natural-sort@npm:0.7.1"
|
||||||
checksum: 10c0/340f8ffc5d30fb516e06dc540e8fa9e0b93c865cf49d791fed3eac3bdc5fc71f0066fc81d44ec1433edc87caecaf9f13eec4a1fce8c5beafc709a71eaedae6fe
|
checksum: 10c0/340f8ffc5d30fb516e06dc540e8fa9e0b93c865cf49d791fed3eac3bdc5fc71f0066fc81d44ec1433edc87caecaf9f13eec4a1fce8c5beafc709a71eaedae6fe
|
||||||
@@ -4477,15 +4451,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"jsesc@npm:^2.5.1":
|
|
||||||
version: 2.5.2
|
|
||||||
resolution: "jsesc@npm:2.5.2"
|
|
||||||
bin:
|
|
||||||
jsesc: bin/jsesc
|
|
||||||
checksum: 10c0/dbf59312e0ebf2b4405ef413ec2b25abb5f8f4d9bc5fb8d9f90381622ebca5f2af6a6aa9a8578f65903f9e33990a6dc798edd0ce5586894bf0e9e31803a1de88
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"jsesc@npm:^3.0.2":
|
"jsesc@npm:^3.0.2":
|
||||||
version: 3.0.2
|
version: 3.0.2
|
||||||
resolution: "jsesc@npm:3.0.2"
|
resolution: "jsesc@npm:3.0.2"
|
||||||
@@ -4661,7 +4626,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
|
"loose-envify@npm:^1.4.0":
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
resolution: "loose-envify@npm:1.4.0"
|
resolution: "loose-envify@npm:1.4.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5578,12 +5543,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"prettier@npm:^3.4.1":
|
"prettier@npm:^3.4.2":
|
||||||
version: 3.4.1
|
version: 3.4.2
|
||||||
resolution: "prettier@npm:3.4.1"
|
resolution: "prettier@npm:3.4.2"
|
||||||
bin:
|
bin:
|
||||||
prettier: bin/prettier.cjs
|
prettier: bin/prettier.cjs
|
||||||
checksum: 10c0/2d6cc3101ad9de72b49c59339480b0983e6ff6742143da0c43f476bf3b5ef88ede42ebd9956d7a0a8fa59f7a5990e8ef03c9ad4c37f7e4c9e5db43ee0853156c
|
checksum: 10c0/99e076a26ed0aba4ebc043880d0f08bbb8c59a4c6641cdee6cdadf2205bdd87aa1d7823f50c3aea41e015e99878d37c58d7b5f0e663bba0ef047f94e36b96446
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5678,24 +5643,23 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-dom@npm:^18.3.1":
|
"react-dom@npm:^19.0.0":
|
||||||
version: 18.3.1
|
version: 19.0.0
|
||||||
resolution: "react-dom@npm:18.3.1"
|
resolution: "react-dom@npm:19.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: "npm:^1.1.0"
|
scheduler: "npm:^0.25.0"
|
||||||
scheduler: "npm:^0.23.2"
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.3.1
|
react: ^19.0.0
|
||||||
checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85
|
checksum: 10c0/a36ce7ab507b237ae2759c984cdaad4af4096d8199fb65b3815c16825e5cfeb7293da790a3fc2184b52bfba7ba3ff31c058c01947aff6fd1a3701632aabaa6a9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-icons@npm:^5.3.0":
|
"react-icons@npm:^5.4.0":
|
||||||
version: 5.3.0
|
version: 5.4.0
|
||||||
resolution: "react-icons@npm:5.3.0"
|
resolution: "react-icons@npm:5.4.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: "*"
|
react: "*"
|
||||||
checksum: 10c0/a53af0de558f0e2bacfbafc973aa8ca1e6a429006a2f83d70becad5be431726a691e096336c95949bb0d967587b145264c5046d2bc1518969dbae731855d0712
|
checksum: 10c0/698b0c4616870152d86de2585fb709bf3eeb9292d971d689965dade5f0ee71cdc92d2125b728ab220cc39a4c6d6bad1ed526b9b934ab39a7b6001bc23b73ee16
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5713,9 +5677,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-router@npm:^7.0.1":
|
"react-router@npm:^7.0.2":
|
||||||
version: 7.0.1
|
version: 7.0.2
|
||||||
resolution: "react-router@npm:7.0.1"
|
resolution: "react-router@npm:7.0.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/cookie": "npm:^0.6.0"
|
"@types/cookie": "npm:^0.6.0"
|
||||||
cookie: "npm:^1.0.1"
|
cookie: "npm:^1.0.1"
|
||||||
@@ -5727,7 +5691,7 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
react-dom:
|
react-dom:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/aac4c9989ae6b9cf989b5ddcda88f505ba0704a4e4b37ae04c819c2bd02f080361f9eb1793695e3ecf41080d91b79aee454c3163b586d1b19ceca13f6eacec0e
|
checksum: 10c0/f6c04939218a3d7f2b03b215c2299eab4dbb0dea4a16e0acfd8bf181ec69ff42d66abdba10a25cc3297c514f052a0d03bfb80431225eb763bb27e4e5b0b4a106
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5781,12 +5745,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react@npm:^18.3.1":
|
"react@npm:^19.0.0":
|
||||||
version: 18.3.1
|
version: 19.0.0
|
||||||
resolution: "react@npm:18.3.1"
|
resolution: "react@npm:19.0.0"
|
||||||
dependencies:
|
checksum: 10c0/9cad8f103e8e3a16d15cb18a0d8115d8bd9f9e1ce3420310aea381eb42aa0a4f812cf047bb5441349257a05fba8a291515691e3cb51267279b2d2c3253f38471
|
||||||
loose-envify: "npm:^1.1.0"
|
|
||||||
checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -6060,12 +6022,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"scheduler@npm:^0.23.2":
|
"scheduler@npm:^0.25.0":
|
||||||
version: 0.23.2
|
version: 0.25.0
|
||||||
resolution: "scheduler@npm:0.23.2"
|
resolution: "scheduler@npm:0.25.0"
|
||||||
dependencies:
|
checksum: 10c0/a4bb1da406b613ce72c1299db43759526058fdcc413999c3c3e0db8956df7633acf395cb20eb2303b6a65d658d66b6585d344460abaee8080b4aa931f10eaafe
|
||||||
loose-envify: "npm:^1.1.0"
|
|
||||||
checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -6263,7 +6223,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"source-map@npm:^0.5.0, source-map@npm:^0.5.7":
|
"source-map@npm:^0.5.7":
|
||||||
version: 0.5.7
|
version: 0.5.7
|
||||||
resolution: "source-map@npm:0.5.7"
|
resolution: "source-map@npm:0.5.7"
|
||||||
checksum: 10c0/904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599
|
checksum: 10c0/904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599
|
||||||
@@ -6592,9 +6552,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"terser@npm:^5.36.0":
|
"terser@npm:^5.37.0":
|
||||||
version: 5.36.0
|
version: 5.37.0
|
||||||
resolution: "terser@npm:5.36.0"
|
resolution: "terser@npm:5.37.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/source-map": "npm:^0.3.3"
|
"@jridgewell/source-map": "npm:^0.3.3"
|
||||||
acorn: "npm:^8.8.2"
|
acorn: "npm:^8.8.2"
|
||||||
@@ -6602,7 +6562,7 @@ __metadata:
|
|||||||
source-map-support: "npm:~0.5.20"
|
source-map-support: "npm:~0.5.20"
|
||||||
bin:
|
bin:
|
||||||
terser: bin/terser
|
terser: bin/terser
|
||||||
checksum: 10c0/f4ed2bead19f64789ddcfb85b7cef78f3942f967b8890c54f57d1e35bc7d547d551c6a4c32210bce6ba45b1c738314bbfac6acbc6c762a45cd171777d0c120d9
|
checksum: 10c0/ff0dc79b0a0da821e7f5bf7a047eab6d04e70e88b62339a0f1d71117db3310e255f5c00738fa3b391f56c3571f800a00047720261ba04ced0241c1f9922199f4
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -6627,13 +6587,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"to-fast-properties@npm:^2.0.0":
|
|
||||||
version: 2.0.0
|
|
||||||
resolution: "to-fast-properties@npm:2.0.0"
|
|
||||||
checksum: 10c0/b214d21dbfb4bce3452b6244b336806ffea9c05297148d32ebb428d5c43ce7545bdfc65a1ceb58c9ef4376a65c0cb2854d645f33961658b3e3b4f84910ddcdd7
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"to-regex-range@npm:^5.0.1":
|
"to-regex-range@npm:^5.0.1":
|
||||||
version: 5.0.1
|
version: 5.0.1
|
||||||
resolution: "to-regex-range@npm:5.0.1"
|
resolution: "to-regex-range@npm:5.0.1"
|
||||||
@@ -6942,9 +6895,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vite@npm:^6.0.2":
|
"vite@npm:^6.0.3":
|
||||||
version: 6.0.2
|
version: 6.0.3
|
||||||
resolution: "vite@npm:6.0.2"
|
resolution: "vite@npm:6.0.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: "npm:^0.24.0"
|
esbuild: "npm:^0.24.0"
|
||||||
fsevents: "npm:~2.3.3"
|
fsevents: "npm:~2.3.3"
|
||||||
@@ -6990,7 +6943,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
vite: bin/vite.js
|
vite: bin/vite.js
|
||||||
checksum: 10c0/45fc609f2bc5fb5beb5a8e2cad9ad6c2edce229a922f6fc1270ea2a9d75819482edcc0f77c85b4a7abdad7eb69ce6a4f26131925d47cdc0778fc15d1bbc3b6a2
|
checksum: 10c0/764ebed14770426a638575b23a51127c630ace873999ab896b0184484d8107e7255cdf64cfb36c65c1ef1d583e44b70a1d14c0f05b89612e834a5806e3964475
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,32 @@ extern "C" {
|
|||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Required for:
|
||||||
|
// https://github.com/espressif/arduino-esp32/blob/3.0.3/libraries/Network/src/NetworkInterface.cpp#L37-L47
|
||||||
|
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||||
|
// #include <NetworkInterface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TAG "AsyncTCP"
|
||||||
|
|
||||||
|
// https://github.com/espressif/arduino-esp32/issues/10526
|
||||||
|
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||||
|
#define TCP_MUTEX_LOCK() \
|
||||||
|
if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \
|
||||||
|
LOCK_TCPIP_CORE(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TCP_MUTEX_UNLOCK() \
|
||||||
|
if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \
|
||||||
|
UNLOCK_TCPIP_CORE(); \
|
||||||
|
}
|
||||||
|
#else // CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||||
|
#define TCP_MUTEX_LOCK()
|
||||||
|
#define TCP_MUTEX_UNLOCK()
|
||||||
|
#endif // CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||||
|
|
||||||
|
#define INVALID_CLOSED_SLOT -1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TCP/IP Event Task
|
* TCP/IP Event Task
|
||||||
* */
|
* */
|
||||||
@@ -54,7 +80,7 @@ typedef struct {
|
|||||||
void * arg;
|
void * arg;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
void * pcb;
|
tcp_pcb * pcb;
|
||||||
int8_t err;
|
int8_t err;
|
||||||
} connected;
|
} connected;
|
||||||
struct {
|
struct {
|
||||||
@@ -105,8 +131,7 @@ static uint32_t _closed_index = []() {
|
|||||||
|
|
||||||
static inline bool _init_async_event_queue() {
|
static inline bool _init_async_event_queue() {
|
||||||
if (!_async_queue) {
|
if (!_async_queue) {
|
||||||
_async_queue =
|
_async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE_SIZE, sizeof(lwip_event_packet_t *));
|
||||||
xQueueCreate(CONFIG_ASYNC_TCP_QUEUE, sizeof(lwip_event_packet_t *)); // double queue to 128 see https://github.com/emsesp/EMS-ESP32/issues/177
|
|
||||||
if (!_async_queue) {
|
if (!_async_queue) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -252,7 +277,7 @@ static bool _start_async_task() {
|
|||||||
"async_tcp",
|
"async_tcp",
|
||||||
CONFIG_ASYNC_TCP_STACK_SIZE,
|
CONFIG_ASYNC_TCP_STACK_SIZE,
|
||||||
NULL,
|
NULL,
|
||||||
CONFIG_ASYNC_TCP_TASK_PRIORITY,
|
CONFIG_ASYNC_TCP_PRIORITY,
|
||||||
&_async_service_task_handle,
|
&_async_service_task_handle,
|
||||||
CONFIG_ASYNC_TCP_RUNNING_CORE);
|
CONFIG_ASYNC_TCP_RUNNING_CORE);
|
||||||
if (!_async_service_task_handle) {
|
if (!_async_service_task_handle) {
|
||||||
@@ -410,7 +435,7 @@ typedef struct {
|
|||||||
static err_t _tcp_output_api(struct tcpip_api_call_data * api_call_msg) {
|
static err_t _tcp_output_api(struct tcpip_api_call_data * api_call_msg) {
|
||||||
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
||||||
msg->err = ERR_CONN;
|
msg->err = ERR_CONN;
|
||||||
if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
|
if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) {
|
||||||
msg->err = tcp_output(msg->pcb);
|
msg->err = tcp_output(msg->pcb);
|
||||||
}
|
}
|
||||||
return msg->err;
|
return msg->err;
|
||||||
@@ -430,7 +455,7 @@ static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) {
|
|||||||
static err_t _tcp_write_api(struct tcpip_api_call_data * api_call_msg) {
|
static err_t _tcp_write_api(struct tcpip_api_call_data * api_call_msg) {
|
||||||
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
||||||
msg->err = ERR_CONN;
|
msg->err = ERR_CONN;
|
||||||
if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
|
if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) {
|
||||||
msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
|
msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
|
||||||
}
|
}
|
||||||
return msg->err;
|
return msg->err;
|
||||||
@@ -453,7 +478,9 @@ static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char * data
|
|||||||
static err_t _tcp_recved_api(struct tcpip_api_call_data * api_call_msg) {
|
static err_t _tcp_recved_api(struct tcpip_api_call_data * api_call_msg) {
|
||||||
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
||||||
msg->err = ERR_CONN;
|
msg->err = ERR_CONN;
|
||||||
if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
|
if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) {
|
||||||
|
// if(msg->closed_slot != INVALID_CLOSED_SLOT && !_closed_slots[msg->closed_slot]) {
|
||||||
|
// if(msg->closed_slot != INVALID_CLOSED_SLOT) {
|
||||||
msg->err = 0;
|
msg->err = 0;
|
||||||
tcp_recved(msg->pcb, msg->received);
|
tcp_recved(msg->pcb, msg->received);
|
||||||
}
|
}
|
||||||
@@ -475,7 +502,7 @@ static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) {
|
|||||||
static err_t _tcp_close_api(struct tcpip_api_call_data * api_call_msg) {
|
static err_t _tcp_close_api(struct tcpip_api_call_data * api_call_msg) {
|
||||||
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
||||||
msg->err = ERR_CONN;
|
msg->err = ERR_CONN;
|
||||||
if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
|
if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) {
|
||||||
msg->err = tcp_close(msg->pcb);
|
msg->err = tcp_close(msg->pcb);
|
||||||
}
|
}
|
||||||
return msg->err;
|
return msg->err;
|
||||||
@@ -495,7 +522,7 @@ static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) {
|
|||||||
static err_t _tcp_abort_api(struct tcpip_api_call_data * api_call_msg) {
|
static err_t _tcp_abort_api(struct tcpip_api_call_data * api_call_msg) {
|
||||||
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
|
||||||
msg->err = ERR_CONN;
|
msg->err = ERR_CONN;
|
||||||
if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) {
|
if (msg->closed_slot == INVALID_CLOSED_SLOT || !_closed_slots[msg->closed_slot]) {
|
||||||
tcp_abort(msg->pcb);
|
tcp_abort(msg->pcb);
|
||||||
}
|
}
|
||||||
return msg->err;
|
return msg->err;
|
||||||
@@ -595,20 +622,22 @@ AsyncClient::AsyncClient(tcp_pcb * pcb)
|
|||||||
, _tx_last_packet(0)
|
, _tx_last_packet(0)
|
||||||
, _rx_timeout(0)
|
, _rx_timeout(0)
|
||||||
, _rx_last_ack(0)
|
, _rx_last_ack(0)
|
||||||
, _ack_timeout(ASYNC_MAX_ACK_TIME)
|
, _ack_timeout(CONFIG_ASYNC_TCP_MAX_ACK_TIME)
|
||||||
, _connect_port(0)
|
, _connect_port(0)
|
||||||
, prev(NULL)
|
, prev(NULL)
|
||||||
, next(NULL) {
|
, next(NULL) {
|
||||||
_pcb = pcb;
|
_pcb = pcb;
|
||||||
_closed_slot = -1;
|
_closed_slot = INVALID_CLOSED_SLOT;
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
_allocate_closed_slot();
|
|
||||||
_rx_last_packet = millis();
|
_rx_last_packet = millis();
|
||||||
tcp_arg(_pcb, this);
|
tcp_arg(_pcb, this);
|
||||||
tcp_recv(_pcb, &_tcp_recv);
|
tcp_recv(_pcb, &_tcp_recv);
|
||||||
tcp_sent(_pcb, &_tcp_sent);
|
tcp_sent(_pcb, &_tcp_sent);
|
||||||
tcp_err(_pcb, &_tcp_error);
|
tcp_err(_pcb, &_tcp_error);
|
||||||
tcp_poll(_pcb, &_tcp_poll, 1);
|
tcp_poll(_pcb, &_tcp_poll, 1);
|
||||||
|
if (!_allocate_closed_slot()) {
|
||||||
|
_close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +739,7 @@ void AsyncClient::onPoll(AcConnectHandler cb, void * arg) {
|
|||||||
|
|
||||||
bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) {
|
bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) {
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
log_w("already connected, state %d", _pcb->state);
|
log_d("already connected, state %d", _pcb->state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!_start_async_task()) {
|
if (!_start_async_task()) {
|
||||||
@@ -718,28 +747,34 @@ bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_pcb * pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
|
if (!_allocate_closed_slot()) {
|
||||||
if (!pcb) {
|
log_e("failed to allocate: closed slot full");
|
||||||
log_e("pcb == NULL");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TCP_MUTEX_LOCK();
|
||||||
|
tcp_pcb * pcb = tcp_new_ip_type(addr.type);
|
||||||
|
if (!pcb) {
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
|
log_e("pcb == NULL");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
tcp_arg(pcb, this);
|
tcp_arg(pcb, this);
|
||||||
tcp_err(pcb, &_tcp_error);
|
tcp_err(pcb, &_tcp_error);
|
||||||
tcp_recv(pcb, &_tcp_recv);
|
tcp_recv(pcb, &_tcp_recv);
|
||||||
tcp_sent(pcb, &_tcp_sent);
|
tcp_sent(pcb, &_tcp_sent);
|
||||||
tcp_poll(pcb, &_tcp_poll, 1);
|
tcp_poll(pcb, &_tcp_poll, 1);
|
||||||
_tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected);
|
TCP_MUTEX_UNLOCK();
|
||||||
return true;
|
|
||||||
|
esp_err_t err = _tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected);
|
||||||
|
return err == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::connect(IPAddress ip, uint16_t port) {
|
bool AsyncClient::connect(const IPAddress & ip, uint16_t port) {
|
||||||
ip_addr_t addr;
|
ip_addr_t addr;
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
// ip_addr_set_ip4_u32(&addr, ip);
|
|
||||||
addr.u_addr.ip4.addr = ip;
|
addr.u_addr.ip4.addr = ip;
|
||||||
addr.type = IPADDR_TYPE_V4;
|
addr.type = IPADDR_TYPE_V4;
|
||||||
ip_clear_no4(&addr);
|
|
||||||
#else
|
#else
|
||||||
ip.to_ip_addr_t(&addr);
|
ip.to_ip_addr_t(&addr);
|
||||||
#endif
|
#endif
|
||||||
@@ -748,10 +783,9 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5
|
#if LWIP_IPV6 && ESP_IDF_VERSION_MAJOR < 5
|
||||||
bool AsyncClient::connect(IPv6Address ip, uint16_t port) {
|
bool AsyncClient::connect(const IPv6Address & ip, uint16_t port) {
|
||||||
ip_addr_t addr;
|
auto ipaddr = static_cast<const uint32_t *>(ip);
|
||||||
addr.type = IPADDR_TYPE_V6;
|
ip_addr_t addr = IPADDR6_INIT(ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
|
||||||
memcpy(addr.u_addr.ip6.addr, static_cast<const uint32_t *>(ip), sizeof(uint32_t) * 4);
|
|
||||||
|
|
||||||
return _connect(addr, port);
|
return _connect(addr, port);
|
||||||
}
|
}
|
||||||
@@ -783,7 +817,7 @@ bool AsyncClient::connect(const char * host, uint16_t port) {
|
|||||||
_connect_port = port;
|
_connect_port = port;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
log_e("error: %d", err);
|
log_d("error: %d", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,7 +837,7 @@ int8_t AsyncClient::abort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncClient::space() {
|
size_t AsyncClient::space() {
|
||||||
if ((_pcb != NULL) && (_pcb->state == 4)) {
|
if ((_pcb != NULL) && (_pcb->state == ESTABLISHED)) {
|
||||||
return tcp_sndbuf(_pcb);
|
return tcp_sndbuf(_pcb);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -862,17 +896,19 @@ int8_t AsyncClient::_close() {
|
|||||||
//ets_printf("X: 0x%08x\n", (uint32_t)this);
|
//ets_printf("X: 0x%08x\n", (uint32_t)this);
|
||||||
int8_t err = ERR_OK;
|
int8_t err = ERR_OK;
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
//log_i("");
|
TCP_MUTEX_LOCK();
|
||||||
tcp_arg(_pcb, NULL);
|
tcp_arg(_pcb, NULL);
|
||||||
tcp_sent(_pcb, NULL);
|
tcp_sent(_pcb, NULL);
|
||||||
tcp_recv(_pcb, NULL);
|
tcp_recv(_pcb, NULL);
|
||||||
tcp_err(_pcb, NULL);
|
tcp_err(_pcb, NULL);
|
||||||
tcp_poll(_pcb, NULL, 0);
|
tcp_poll(_pcb, NULL, 0);
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
_tcp_clear_events(this);
|
_tcp_clear_events(this);
|
||||||
err = _tcp_close(_pcb, _closed_slot);
|
err = _tcp_close(_pcb, _closed_slot);
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
err = abort();
|
err = abort();
|
||||||
}
|
}
|
||||||
|
_free_closed_slot();
|
||||||
_pcb = NULL;
|
_pcb = NULL;
|
||||||
if (_discard_cb) {
|
if (_discard_cb) {
|
||||||
_discard_cb(_discard_cb_arg, this);
|
_discard_cb(_discard_cb_arg, this);
|
||||||
@@ -881,40 +917,43 @@ int8_t AsyncClient::_close() {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncClient::_allocate_closed_slot() {
|
bool AsyncClient::_allocate_closed_slot() {
|
||||||
|
if (_closed_slot != INVALID_CLOSED_SLOT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
xSemaphoreTake(_slots_lock, portMAX_DELAY);
|
xSemaphoreTake(_slots_lock, portMAX_DELAY);
|
||||||
uint32_t closed_slot_min_index = 0;
|
uint32_t closed_slot_min_index = 0;
|
||||||
for (int i = 0; i < _number_of_closed_slots; ++i) {
|
for (int i = 0; i < _number_of_closed_slots; ++i) {
|
||||||
if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) {
|
if ((_closed_slot == INVALID_CLOSED_SLOT || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) {
|
||||||
closed_slot_min_index = _closed_slots[i];
|
closed_slot_min_index = _closed_slots[i];
|
||||||
_closed_slot = i;
|
_closed_slot = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_closed_slot != -1) {
|
if (_closed_slot != INVALID_CLOSED_SLOT) {
|
||||||
_closed_slots[_closed_slot] = 0;
|
_closed_slots[_closed_slot] = 0;
|
||||||
}
|
}
|
||||||
xSemaphoreGive(_slots_lock);
|
xSemaphoreGive(_slots_lock);
|
||||||
|
return (_closed_slot != INVALID_CLOSED_SLOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncClient::_free_closed_slot() {
|
void AsyncClient::_free_closed_slot() {
|
||||||
if (_closed_slot != -1) {
|
xSemaphoreTake(_slots_lock, portMAX_DELAY);
|
||||||
|
if (_closed_slot != INVALID_CLOSED_SLOT) {
|
||||||
_closed_slots[_closed_slot] = _closed_index;
|
_closed_slots[_closed_slot] = _closed_index;
|
||||||
_closed_slot = -1;
|
_closed_slot = INVALID_CLOSED_SLOT;
|
||||||
++_closed_index;
|
++_closed_index;
|
||||||
}
|
}
|
||||||
|
xSemaphoreGive(_slots_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Private Callbacks
|
* Private Callbacks
|
||||||
* */
|
* */
|
||||||
|
|
||||||
int8_t AsyncClient::_connected(void * pcb, int8_t err) {
|
int8_t AsyncClient::_connected(tcp_pcb * pcb, int8_t err) {
|
||||||
_pcb = reinterpret_cast<tcp_pcb *>(pcb);
|
_pcb = reinterpret_cast<tcp_pcb *>(pcb);
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
_rx_last_packet = millis();
|
_rx_last_packet = millis();
|
||||||
// tcp_recv(_pcb, &_tcp_recv);
|
|
||||||
// tcp_sent(_pcb, &_tcp_sent);
|
|
||||||
// tcp_poll(_pcb, &_tcp_poll, 1);
|
|
||||||
}
|
}
|
||||||
if (_connect_cb) {
|
if (_connect_cb) {
|
||||||
_connect_cb(_connect_cb_arg, this);
|
_connect_cb(_connect_cb_arg, this);
|
||||||
@@ -924,6 +963,7 @@ int8_t AsyncClient::_connected(void * pcb, int8_t err) {
|
|||||||
|
|
||||||
void AsyncClient::_error(int8_t err) {
|
void AsyncClient::_error(int8_t err) {
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
|
TCP_MUTEX_LOCK();
|
||||||
tcp_arg(_pcb, NULL);
|
tcp_arg(_pcb, NULL);
|
||||||
if (_pcb->state == LISTEN) {
|
if (_pcb->state == LISTEN) {
|
||||||
tcp_sent(_pcb, NULL);
|
tcp_sent(_pcb, NULL);
|
||||||
@@ -931,6 +971,8 @@ void AsyncClient::_error(int8_t err) {
|
|||||||
tcp_err(_pcb, NULL);
|
tcp_err(_pcb, NULL);
|
||||||
tcp_poll(_pcb, NULL, 0);
|
tcp_poll(_pcb, NULL, 0);
|
||||||
}
|
}
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
|
_free_closed_slot();
|
||||||
_pcb = NULL;
|
_pcb = NULL;
|
||||||
}
|
}
|
||||||
if (_error_cb) {
|
if (_error_cb) {
|
||||||
@@ -944,7 +986,7 @@ void AsyncClient::_error(int8_t err) {
|
|||||||
//In LwIP Thread
|
//In LwIP Thread
|
||||||
int8_t AsyncClient::_lwip_fin(tcp_pcb * pcb, int8_t err) {
|
int8_t AsyncClient::_lwip_fin(tcp_pcb * pcb, int8_t err) {
|
||||||
if (!_pcb || pcb != _pcb) {
|
if (!_pcb || pcb != _pcb) {
|
||||||
log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
|
log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
tcp_arg(_pcb, NULL);
|
tcp_arg(_pcb, NULL);
|
||||||
@@ -972,11 +1014,9 @@ int8_t AsyncClient::_fin(tcp_pcb * pcb, int8_t err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int8_t AsyncClient::_sent(tcp_pcb * pcb, uint16_t len) {
|
int8_t AsyncClient::_sent(tcp_pcb * pcb, uint16_t len) {
|
||||||
_rx_last_packet = millis();
|
_rx_last_ack = _rx_last_packet = millis();
|
||||||
_rx_last_ack = millis();
|
|
||||||
//log_i("%u", len);
|
|
||||||
if (_sent_cb) {
|
if (_sent_cb) {
|
||||||
_sent_cb(_sent_cb_arg, this, len, (millis() - _tx_last_packet));
|
_sent_cb(_sent_cb_arg, this, len, (_rx_last_packet - _tx_last_packet));
|
||||||
}
|
}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@@ -1000,19 +1040,19 @@ int8_t AsyncClient::_recv(tcp_pcb * pcb, pbuf * pb, int8_t err) {
|
|||||||
} else if (_pcb) {
|
} else if (_pcb) {
|
||||||
_tcp_recved(_pcb, _closed_slot, b->len);
|
_tcp_recved(_pcb, _closed_slot, b->len);
|
||||||
}
|
}
|
||||||
pbuf_free(b);
|
|
||||||
}
|
}
|
||||||
|
pbuf_free(b);
|
||||||
}
|
}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t AsyncClient::_poll(tcp_pcb * pcb) {
|
int8_t AsyncClient::_poll(tcp_pcb * pcb) {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
log_w("pcb is NULL");
|
// log_d("pcb is NULL");
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
if (pcb != _pcb) {
|
if (pcb != _pcb) {
|
||||||
log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
|
log_d("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1023,7 +1063,7 @@ int8_t AsyncClient::_poll(tcp_pcb * pcb) {
|
|||||||
const uint32_t one_day = 86400000;
|
const uint32_t one_day = 86400000;
|
||||||
bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day;
|
bool last_tx_is_after_last_ack = (_rx_last_ack - _tx_last_packet + one_day) < one_day;
|
||||||
if (last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) {
|
if (last_tx_is_after_last_ack && (now - _tx_last_packet) >= _ack_timeout) {
|
||||||
log_w("ack timeout %d", pcb->state);
|
log_d("ack timeout %d", pcb->state);
|
||||||
if (_timeout_cb)
|
if (_timeout_cb)
|
||||||
_timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet));
|
_timeout_cb(_timeout_cb_arg, this, (now - _tx_last_packet));
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
@@ -1031,7 +1071,7 @@ int8_t AsyncClient::_poll(tcp_pcb * pcb) {
|
|||||||
}
|
}
|
||||||
// RX Timeout
|
// RX Timeout
|
||||||
if (_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) {
|
if (_rx_timeout && (now - _rx_last_packet) >= (_rx_timeout * 1000)) {
|
||||||
log_w("rx timeout %d", pcb->state);
|
log_d("rx timeout %d", pcb->state);
|
||||||
_close();
|
_close();
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@@ -1052,7 +1092,9 @@ void AsyncClient::_dns_found(struct ip_addr * ipaddr) {
|
|||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
if (ipaddr) {
|
if (ipaddr) {
|
||||||
connect(IPAddress(ipaddr), _connect_port);
|
IPAddress ip;
|
||||||
|
ip.from_ip_addr_t(ipaddr);
|
||||||
|
connect(ip, _connect_port);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
if (_error_cb) {
|
if (_error_cb) {
|
||||||
@@ -1076,7 +1118,7 @@ bool AsyncClient::free() {
|
|||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_pcb->state == 0 || _pcb->state > 4) {
|
if (_pcb->state == CLOSED || _pcb->state > ESTABLISHED) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1131,6 +1173,18 @@ bool AsyncClient::getNoDelay() {
|
|||||||
return tcp_nagle_disabled(_pcb);
|
return tcp_nagle_disabled(_pcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt) {
|
||||||
|
if (ms != 0) {
|
||||||
|
_pcb->so_options |= SOF_KEEPALIVE; //Turn on TCP Keepalive for the given pcb
|
||||||
|
// Set the time between keepalive messages in milli-seconds
|
||||||
|
_pcb->keep_idle = ms;
|
||||||
|
_pcb->keep_intvl = ms;
|
||||||
|
_pcb->keep_cnt = cnt; //The number of unanswered probes required to force closure of the socket
|
||||||
|
} else {
|
||||||
|
_pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t AsyncClient::getMss() {
|
uint16_t AsyncClient::getMss() {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1177,11 +1231,21 @@ IPv6Address AsyncClient::localIP6() {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
IPAddress AsyncClient::remoteIP6() {
|
IPAddress AsyncClient::remoteIP6() {
|
||||||
return _pcb ? IPAddress(dynamic_cast<const ip_addr_t *>(&_pcb->remote_ip)) : IPAddress(IPType::IPv6);
|
if (!_pcb) {
|
||||||
|
return IPAddress(IPType::IPv6);
|
||||||
|
}
|
||||||
|
IPAddress ip;
|
||||||
|
ip.from_ip_addr_t(&(_pcb->remote_ip));
|
||||||
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPAddress AsyncClient::localIP6() {
|
IPAddress AsyncClient::localIP6() {
|
||||||
return _pcb ? IPAddress(dynamic_cast<const ip_addr_t *>(&_pcb->local_ip)) : IPAddress(IPType::IPv6);
|
if (!_pcb) {
|
||||||
|
return IPAddress(IPType::IPv6);
|
||||||
|
}
|
||||||
|
IPAddress ip;
|
||||||
|
ip.from_ip_addr_t(&(_pcb->local_ip));
|
||||||
|
return ip;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -1215,7 +1279,12 @@ IPAddress AsyncClient::remoteIP() {
|
|||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
return IPAddress(getRemoteAddress());
|
return IPAddress(getRemoteAddress());
|
||||||
#else
|
#else
|
||||||
return _pcb ? IPAddress(dynamic_cast<const ip_addr_t *>(&_pcb->remote_ip)) : IPAddress();
|
if (!_pcb) {
|
||||||
|
return IPAddress();
|
||||||
|
}
|
||||||
|
IPAddress ip;
|
||||||
|
ip.from_ip_addr_t(&(_pcb->remote_ip));
|
||||||
|
return ip;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1227,7 +1296,12 @@ IPAddress AsyncClient::localIP() {
|
|||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
return IPAddress(getLocalAddress());
|
return IPAddress(getLocalAddress());
|
||||||
#else
|
#else
|
||||||
return _pcb ? IPAddress(dynamic_cast<const ip_addr_t *>(&_pcb->local_ip)) : IPAddress();
|
if (!_pcb) {
|
||||||
|
return IPAddress();
|
||||||
|
}
|
||||||
|
IPAddress ip;
|
||||||
|
ip.from_ip_addr_t(&(_pcb->local_ip));
|
||||||
|
return ip;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1247,35 +1321,35 @@ bool AsyncClient::connected() {
|
|||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _pcb->state == 4;
|
return _pcb->state == ESTABLISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::connecting() {
|
bool AsyncClient::connecting() {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _pcb->state > 0 && _pcb->state < 4;
|
return _pcb->state > CLOSED && _pcb->state < ESTABLISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::disconnecting() {
|
bool AsyncClient::disconnecting() {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _pcb->state > 4 && _pcb->state < 10;
|
return _pcb->state > ESTABLISHED && _pcb->state < TIME_WAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::disconnected() {
|
bool AsyncClient::disconnected() {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return _pcb->state == 0 || _pcb->state == 10;
|
return _pcb->state == CLOSED || _pcb->state == TIME_WAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::freeable() {
|
bool AsyncClient::freeable() {
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return _pcb->state == 0 || _pcb->state > 4;
|
return _pcb->state == CLOSED || _pcb->state > ESTABLISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncClient::canSend() {
|
bool AsyncClient::canSend() {
|
||||||
@@ -1384,7 +1458,7 @@ void AsyncClient::_s_error(void * arg, int8_t err) {
|
|||||||
reinterpret_cast<AsyncClient *>(arg)->_error(err);
|
reinterpret_cast<AsyncClient *>(arg)->_error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err) {
|
int8_t AsyncClient::_s_connected(void * arg, struct tcp_pcb * pcb, int8_t err) {
|
||||||
return reinterpret_cast<AsyncClient *>(arg)->_connected(pcb, err);
|
return reinterpret_cast<AsyncClient *>(arg)->_connected(pcb, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1396,6 +1470,7 @@ AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
|
|||||||
: _port(port)
|
: _port(port)
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
, _bind4(true)
|
, _bind4(true)
|
||||||
|
, _bind6(false)
|
||||||
#else
|
#else
|
||||||
, _bind4(addr.type() != IPType::IPv6)
|
, _bind4(addr.type() != IPType::IPv6)
|
||||||
, _bind6(addr.type() == IPType::IPv6)
|
, _bind6(addr.type() == IPType::IPv6)
|
||||||
@@ -1410,6 +1485,7 @@ AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
|
|||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
|
, _bind4(false)
|
||||||
, _bind6(true)
|
, _bind6(true)
|
||||||
, _addr6(addr)
|
, _addr6(addr)
|
||||||
, _noDelay(false)
|
, _noDelay(false)
|
||||||
@@ -1422,7 +1498,7 @@ AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
|||||||
AsyncServer::AsyncServer(uint16_t port)
|
AsyncServer::AsyncServer(uint16_t port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
, _bind4(true)
|
, _bind4(true)
|
||||||
, _bind6(true)
|
, _bind6(false)
|
||||||
, _addr((uint32_t)IPADDR_ANY)
|
, _addr((uint32_t)IPADDR_ANY)
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
, _addr6()
|
, _addr6()
|
||||||
@@ -1451,17 +1527,10 @@ void AsyncServer::begin() {
|
|||||||
log_e("failed to start task");
|
log_e("failed to start task");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int8_t err, bind_type;
|
int8_t err;
|
||||||
|
TCP_MUTEX_LOCK();
|
||||||
if (_bind4 && _bind6) {
|
_pcb = tcp_new_ip_type(_bind4 && _bind6 ? IPADDR_TYPE_ANY : (_bind6 ? IPADDR_TYPE_V6 : IPADDR_TYPE_V4));
|
||||||
bind_type = IPADDR_TYPE_ANY;
|
TCP_MUTEX_UNLOCK();
|
||||||
} else if (_bind6) {
|
|
||||||
bind_type = IPADDR_TYPE_V6;
|
|
||||||
} else {
|
|
||||||
bind_type = IPADDR_TYPE_V4;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pcb = tcp_new_ip_type(bind_type);
|
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
log_e("_pcb == NULL");
|
log_e("_pcb == NULL");
|
||||||
return;
|
return;
|
||||||
@@ -1469,13 +1538,13 @@ void AsyncServer::begin() {
|
|||||||
|
|
||||||
ip_addr_t local_addr;
|
ip_addr_t local_addr;
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
// ip_addr_set_ip4_u32(&local_addr, _addr);
|
if (_bind6) { // _bind6 && _bind4 both at the same time is not supported on Arduino 2 in this lib API
|
||||||
local_addr.u_addr.ip4.addr = _addr;
|
local_addr.type = IPADDR_TYPE_V6;
|
||||||
|
memcpy(local_addr.u_addr.ip6.addr, static_cast<const uint32_t *>(_addr6), sizeof(uint32_t) * 4);
|
||||||
|
} else {
|
||||||
local_addr.type = IPADDR_TYPE_V4;
|
local_addr.type = IPADDR_TYPE_V4;
|
||||||
ip_clear_no4(&local_addr);
|
local_addr.u_addr.ip4.addr = _addr;
|
||||||
/* local_addr.type = bind_type;
|
}
|
||||||
local_addr.u_addr.ip4.addr = (uint32_t) _addr;
|
|
||||||
memcpy(local_addr.u_addr.ip6.addr, static_cast<const uint32_t*>(_addr6), sizeof(uint32_t) * 4); */
|
|
||||||
#else
|
#else
|
||||||
_addr.to_ip_addr_t(&local_addr);
|
_addr.to_ip_addr_t(&local_addr);
|
||||||
#endif
|
#endif
|
||||||
@@ -1493,16 +1562,22 @@ void AsyncServer::begin() {
|
|||||||
log_e("listen_pcb == NULL");
|
log_e("listen_pcb == NULL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
TCP_MUTEX_LOCK();
|
||||||
tcp_arg(_pcb, (void *)this);
|
tcp_arg(_pcb, (void *)this);
|
||||||
tcp_accept(_pcb, &_s_accept);
|
tcp_accept(_pcb, &_s_accept);
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncServer::end() {
|
void AsyncServer::end() {
|
||||||
if (_pcb) {
|
if (_pcb) {
|
||||||
|
TCP_MUTEX_LOCK();
|
||||||
tcp_arg(_pcb, NULL);
|
tcp_arg(_pcb, NULL);
|
||||||
tcp_accept(_pcb, NULL);
|
tcp_accept(_pcb, NULL);
|
||||||
if (tcp_close(_pcb) != ERR_OK) {
|
if (tcp_close(_pcb) != ERR_OK) {
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
_tcp_abort(_pcb, -1);
|
_tcp_abort(_pcb, -1);
|
||||||
|
} else {
|
||||||
|
TCP_MUTEX_UNLOCK();
|
||||||
}
|
}
|
||||||
_pcb = NULL;
|
_pcb = NULL;
|
||||||
}
|
}
|
||||||
@@ -1521,7 +1596,7 @@ int8_t AsyncServer::_accept(tcp_pcb * pcb, int8_t err) {
|
|||||||
if (tcp_close(pcb) != ERR_OK) {
|
if (tcp_close(pcb) != ERR_OK) {
|
||||||
tcp_abort(pcb);
|
tcp_abort(pcb);
|
||||||
}
|
}
|
||||||
log_e("FAIL");
|
log_d("FAIL");
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,12 @@
|
|||||||
#ifndef ASYNCTCP_H_
|
#ifndef ASYNCTCP_H_
|
||||||
#define ASYNCTCP_H_
|
#define ASYNCTCP_H_
|
||||||
|
|
||||||
|
#define ASYNCTCP_VERSION "3.2.14"
|
||||||
|
#define ASYNCTCP_VERSION_MAJOR 3
|
||||||
|
#define ASYNCTCP_VERSION_MINOR 2
|
||||||
|
#define ASYNCTCP_VERSION_REVISION 14
|
||||||
|
#define ASYNCTCP_FORK_mathieucarbou
|
||||||
|
|
||||||
#include "IPAddress.h"
|
#include "IPAddress.h"
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
#include "IPv6Address.h"
|
#include "IPv6Address.h"
|
||||||
@@ -52,24 +58,26 @@ extern "C" {
|
|||||||
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
|
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_ASYNC_TCP_TASK_PRIORITY
|
|
||||||
#define CONFIG_ASYNC_TCP_TASK_PRIORITY 5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// EMS-ESP: stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k
|
// EMS-ESP: stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k
|
||||||
#ifndef CONFIG_ASYNC_TCP_STACK_SIZE
|
#ifndef CONFIG_ASYNC_TCP_STACK_SIZE
|
||||||
#define CONFIG_ASYNC_TCP_STACK_SIZE 5120
|
#define CONFIG_ASYNC_TCP_STACK_SIZE 6144
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_ASYNC_TCP_PRIORITY
|
||||||
|
#define CONFIG_ASYNC_TCP_PRIORITY 5
|
||||||
|
#endif
|
||||||
|
|
||||||
// EMS-ESP: maybe enlarge queue to 64 or 128 see https://github.com/emsesp/EMS-ESP32/issues/177
|
// EMS-ESP: maybe enlarge queue to 64 or 128 see https://github.com/emsesp/EMS-ESP32/issues/177
|
||||||
#ifndef CONFIG_ASYNC_TCP_QUEUE
|
#ifndef CONFIG_ASYNC_TCP_QUEUE_SIZE
|
||||||
#define CONFIG_ASYNC_TCP_QUEUE 32
|
#define CONFIG_ASYNC_TCP_QUEUE_SIZE 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_ASYNC_TCP_MAX_ACK_TIME
|
||||||
|
#define CONFIG_ASYNC_TCP_MAX_ACK_TIME 5000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AsyncClient;
|
class AsyncClient;
|
||||||
|
|
||||||
#define ASYNC_MAX_ACK_TIME 5000
|
|
||||||
#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
|
#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
|
||||||
#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.
|
#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.
|
||||||
|
|
||||||
@@ -96,9 +104,9 @@ class AsyncClient {
|
|||||||
bool operator!=(const AsyncClient & other) {
|
bool operator!=(const AsyncClient & other) {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
bool connect(IPAddress ip, uint16_t port);
|
bool connect(const IPAddress & ip, uint16_t port);
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
#if ESP_IDF_VERSION_MAJOR < 5
|
||||||
bool connect(IPv6Address ip, uint16_t port);
|
bool connect(const IPv6Address & ip, uint16_t port);
|
||||||
#endif
|
#endif
|
||||||
bool connect(const char * host, uint16_t port);
|
bool connect(const char * host, uint16_t port);
|
||||||
void close(bool now = false);
|
void close(bool now = false);
|
||||||
@@ -133,6 +141,8 @@ class AsyncClient {
|
|||||||
void setNoDelay(bool nodelay);
|
void setNoDelay(bool nodelay);
|
||||||
bool getNoDelay();
|
bool getNoDelay();
|
||||||
|
|
||||||
|
void setKeepAlive(uint32_t ms, uint8_t cnt);
|
||||||
|
|
||||||
uint32_t getRemoteAddress();
|
uint32_t getRemoteAddress();
|
||||||
uint16_t getRemotePort();
|
uint16_t getRemotePort();
|
||||||
uint32_t getLocalAddress();
|
uint32_t getLocalAddress();
|
||||||
@@ -180,7 +190,7 @@ class AsyncClient {
|
|||||||
static int8_t _s_lwip_fin(void * arg, struct tcp_pcb * tpcb, int8_t err);
|
static int8_t _s_lwip_fin(void * arg, struct tcp_pcb * tpcb, int8_t err);
|
||||||
static void _s_error(void * arg, int8_t err);
|
static void _s_error(void * arg, int8_t err);
|
||||||
static int8_t _s_sent(void * arg, struct tcp_pcb * tpcb, uint16_t len);
|
static int8_t _s_sent(void * arg, struct tcp_pcb * tpcb, uint16_t len);
|
||||||
static int8_t _s_connected(void * arg, void * tpcb, int8_t err);
|
static int8_t _s_connected(void * arg, struct tcp_pcb * tpcb, int8_t err);
|
||||||
static void _s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg);
|
static void _s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg);
|
||||||
|
|
||||||
int8_t _recv(tcp_pcb * pcb, pbuf * pb, int8_t err);
|
int8_t _recv(tcp_pcb * pcb, pbuf * pb, int8_t err);
|
||||||
@@ -222,8 +232,8 @@ class AsyncClient {
|
|||||||
|
|
||||||
int8_t _close();
|
int8_t _close();
|
||||||
void _free_closed_slot();
|
void _free_closed_slot();
|
||||||
void _allocate_closed_slot();
|
bool _allocate_closed_slot();
|
||||||
int8_t _connected(void * pcb, int8_t err);
|
int8_t _connected(tcp_pcb * pcb, int8_t err);
|
||||||
void _error(int8_t err);
|
void _error(int8_t err);
|
||||||
int8_t _poll(tcp_pcb * pcb);
|
int8_t _poll(tcp_pcb * pcb);
|
||||||
int8_t _sent(tcp_pcb * pcb, uint16_t len);
|
int8_t _sent(tcp_pcb * pcb, uint16_t len);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eModbus",
|
"name": "eModbus",
|
||||||
"version": "1.7.0",
|
"version": "1.7.2",
|
||||||
"keywords": "Arduino, ESP32, Modbus, RTU, ASCII, ModbusASCII, ModbusRTU, ModbusTCP",
|
"keywords": "Arduino, ESP32, Modbus, RTU, ASCII, ModbusASCII, ModbusRTU, ModbusTCP",
|
||||||
"description": "ModbusRTU, ModbusASCII and ModbusTCP functions for ESP32",
|
"description": "ModbusRTU, ModbusASCII and ModbusTCP functions for ESP32",
|
||||||
"homepage": "https://emodbus.github.io",
|
"homepage": "https://emodbus.github.io",
|
||||||
@@ -23,6 +23,25 @@
|
|||||||
"url": "https://github.com/eModbus/eModbus",
|
"url": "https://github.com/eModbus/eModbus",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"owner": "me-no-dev",
|
||||||
|
"name": "AsyncTCP",
|
||||||
|
"version": "*",
|
||||||
|
"platforms": ["espressif32"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "me-no-dev",
|
||||||
|
"name": "ESPAsyncTCP",
|
||||||
|
"version": "*",
|
||||||
|
"platforms": ["espressif8266"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ethernet",
|
||||||
|
"version": "https://github.com/arduino-libraries/Ethernet.git",
|
||||||
|
"platforms": ["espressif32"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"export": {
|
"export": {
|
||||||
"include":
|
"include":
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name=eModbus
|
name=eModbus
|
||||||
version=1.7.0
|
version=1.7.2
|
||||||
author=bertmelis,Miq1 <miq1@gmx.de>
|
author=bertmelis,Miq1 <miq1@gmx.de>
|
||||||
maintainer=Miq1 <miq1@gmx.de>
|
maintainer=Miq1 <miq1@gmx.de>
|
||||||
sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32.
|
sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32.
|
||||||
|
|||||||
21
lib/eModbus/src/ModbusBridgeETH.h
Normal file
21
lib/eModbus/src/ModbusBridgeETH.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// eModbus: Copyright 2024 by Michael Harwerth, Bert Melis and the contributors to eModbus
|
||||||
|
// MIT license - see license.md for details
|
||||||
|
// =================================================================================================
|
||||||
|
#ifndef _MODBUS_BRIDGE_ETHERNET_H
|
||||||
|
#define _MODBUS_BRIDGE_ETHERNET_H
|
||||||
|
#include "options.h"
|
||||||
|
#if HAS_ETHERNET == 1
|
||||||
|
#include <ETH.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#undef SERVER_END
|
||||||
|
#define SERVER_END // NIL for Ethernet
|
||||||
|
|
||||||
|
#include "ModbusServerTCPtemp.h"
|
||||||
|
#include "ModbusBridgeTemp.h"
|
||||||
|
|
||||||
|
using ModbusBridgeEthernet = ModbusBridge<ModbusServerTCP<WiFiServer, WiFiClient>>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -29,8 +29,8 @@ public:
|
|||||||
ModbusBridge();
|
ModbusBridge();
|
||||||
|
|
||||||
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
||||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin = -1);
|
ModbusBridge(uint32_t timeout, int rtsPin = -1);
|
||||||
ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts);
|
ModbusBridge(uint32_t timeout, RTScallback rts);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~ModbusBridge();
|
~ModbusBridge();
|
||||||
@@ -44,6 +44,12 @@ public:
|
|||||||
// Block a function code (respond with ILLEGAL_FUNCTION error)
|
// Block a function code (respond with ILLEGAL_FUNCTION error)
|
||||||
bool denyFunctionCode(uint8_t aliasID, uint8_t functionCode);
|
bool denyFunctionCode(uint8_t aliasID, uint8_t functionCode);
|
||||||
|
|
||||||
|
// Add/remove request/response filters
|
||||||
|
bool addRequestFilter(uint8_t aliasID, MBSworker rF);
|
||||||
|
bool removeRequestFilter(uint8_t aliasID);
|
||||||
|
bool addResponseFilter(uint8_t aliasID, MBSworker rF);
|
||||||
|
bool removeResponseFilter(uint8_t aliasID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ServerData holds all data necessary to address a single server
|
// ServerData holds all data necessary to address a single server
|
||||||
struct ServerData {
|
struct ServerData {
|
||||||
@@ -52,6 +58,8 @@ protected:
|
|||||||
ServerType serverType; // TCP_SERVER or RTU_SERVER
|
ServerType serverType; // TCP_SERVER or RTU_SERVER
|
||||||
IPAddress host; // TCP: host IP address, else 0.0.0.0
|
IPAddress host; // TCP: host IP address, else 0.0.0.0
|
||||||
uint16_t port; // TCP: host port number, else 0
|
uint16_t port; // TCP: host port number, else 0
|
||||||
|
MBSworker requestFilter; // optional filter requests before forwarding them
|
||||||
|
MBSworker responseFilter; // optional filter responses before forwarding them
|
||||||
|
|
||||||
// RTU constructor
|
// RTU constructor
|
||||||
ServerData(uint8_t sid, ModbusClient *c) :
|
ServerData(uint8_t sid, ModbusClient *c) :
|
||||||
@@ -59,7 +67,9 @@ protected:
|
|||||||
client(c),
|
client(c),
|
||||||
serverType(RTU_SERVER),
|
serverType(RTU_SERVER),
|
||||||
host(IPAddress(0, 0, 0, 0)),
|
host(IPAddress(0, 0, 0, 0)),
|
||||||
port(0) {}
|
port(0),
|
||||||
|
requestFilter(nullptr),
|
||||||
|
responseFilter(nullptr) {}
|
||||||
|
|
||||||
// TCP constructor
|
// TCP constructor
|
||||||
ServerData(uint8_t sid, ModbusClient *c, IPAddress h, uint16_t p) :
|
ServerData(uint8_t sid, ModbusClient *c, IPAddress h, uint16_t p) :
|
||||||
@@ -67,7 +77,9 @@ protected:
|
|||||||
client(c),
|
client(c),
|
||||||
serverType(TCP_SERVER),
|
serverType(TCP_SERVER),
|
||||||
host(h),
|
host(h),
|
||||||
port(p) {}
|
port(p),
|
||||||
|
requestFilter(nullptr),
|
||||||
|
responseFilter(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default worker functions
|
// Default worker functions
|
||||||
@@ -85,13 +97,13 @@ ModbusBridge<SERVERCLASS>::ModbusBridge() :
|
|||||||
|
|
||||||
// Constructors for RTU variant
|
// Constructors for RTU variant
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, int rtsPin) :
|
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, int rtsPin) :
|
||||||
SERVERCLASS(serial, timeout, rtsPin) { }
|
SERVERCLASS(timeout, rtsPin) { }
|
||||||
|
|
||||||
// Alternate constructors for RTU variant
|
// Alternate constructors for RTU variant
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
ModbusBridge<SERVERCLASS>::ModbusBridge(HardwareSerial& serial, uint32_t timeout, RTScallback rts) :
|
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, RTScallback rts) :
|
||||||
SERVERCLASS(serial, timeout, rts) { }
|
SERVERCLASS(timeout, rts) { }
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
@@ -156,6 +168,62 @@ bool ModbusBridge<SERVERCLASS>::denyFunctionCode(uint8_t aliasID, uint8_t functi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename SERVERCLASS>
|
||||||
|
bool ModbusBridge<SERVERCLASS>::addRequestFilter(uint8_t aliasID, MBSworker rF) {
|
||||||
|
// Is there already an entry for the aliasID?
|
||||||
|
if (servers.find(aliasID) != servers.end()) {
|
||||||
|
// Yes. Chain in filter function
|
||||||
|
servers[aliasID]->requestFilter = rF;
|
||||||
|
LOG_D("Request filter added for server %02X\n", aliasID);
|
||||||
|
} else {
|
||||||
|
LOG_E("Server %d not attached to bridge, no request filter set!\n", aliasID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SERVERCLASS>
|
||||||
|
bool ModbusBridge<SERVERCLASS>::removeRequestFilter(uint8_t aliasID) {
|
||||||
|
// Is there already an entry for the aliasID?
|
||||||
|
if (servers.find(aliasID) != servers.end()) {
|
||||||
|
// Yes. Chain in filter function
|
||||||
|
servers[aliasID]->requestFilter = nullptr;
|
||||||
|
LOG_D("Request filter removed for server %02X\n", aliasID);
|
||||||
|
} else {
|
||||||
|
LOG_E("Server %d not attached to bridge, no request filter set!\n", aliasID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SERVERCLASS>
|
||||||
|
bool ModbusBridge<SERVERCLASS>::addResponseFilter(uint8_t aliasID, MBSworker rF) {
|
||||||
|
// Is there already an entry for the aliasID?
|
||||||
|
if (servers.find(aliasID) != servers.end()) {
|
||||||
|
// Yes. Chain in filter function
|
||||||
|
servers[aliasID]->responseFilter = rF;
|
||||||
|
LOG_D("Response filter added for server %02X\n", aliasID);
|
||||||
|
} else {
|
||||||
|
LOG_E("Server %d not attached to bridge, no response filter set!\n", aliasID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SERVERCLASS>
|
||||||
|
bool ModbusBridge<SERVERCLASS>::removeResponseFilter(uint8_t aliasID) {
|
||||||
|
// Is there already an entry for the aliasID?
|
||||||
|
if (servers.find(aliasID) != servers.end()) {
|
||||||
|
// Yes. Chain in filter function
|
||||||
|
servers[aliasID]->responseFilter = nullptr;
|
||||||
|
LOG_D("Response filter removed for server %02X\n", aliasID);
|
||||||
|
} else {
|
||||||
|
LOG_E("Server %d not attached to bridge, no response filter set!\n", aliasID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// bridgeWorker: default worker function to process bridge requests
|
// bridgeWorker: default worker function to process bridge requests
|
||||||
template<typename SERVERCLASS>
|
template<typename SERVERCLASS>
|
||||||
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
||||||
@@ -167,11 +235,17 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
|||||||
if (servers.find(aliasID) != servers.end()) {
|
if (servers.find(aliasID) != servers.end()) {
|
||||||
// Found it. We may use servers[aliasID] now without allocating a new map slot
|
// Found it. We may use servers[aliasID] now without allocating a new map slot
|
||||||
|
|
||||||
|
// Request filter hook to be called here
|
||||||
|
if (servers[aliasID]->requestFilter) {
|
||||||
|
LOG_D("Calling request filter\n");
|
||||||
|
msg = servers[aliasID]->requestFilter(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// Set real target server ID
|
// Set real target server ID
|
||||||
msg.setServerID(servers[aliasID]->serverID);
|
msg.setServerID(servers[aliasID]->serverID);
|
||||||
|
|
||||||
// Issue the request
|
// Issue the request
|
||||||
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, functionCode);
|
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, msg.getFunctionCode());
|
||||||
// TCP servers have a target host/port that needs to be set in the client
|
// TCP servers have a target host/port that needs to be set in the client
|
||||||
if (servers[aliasID]->serverType == TCP_SERVER) {
|
if (servers[aliasID]->serverType == TCP_SERVER) {
|
||||||
response = reinterpret_cast<ModbusClientTCP *>(servers[aliasID]->client)->syncRequestMT(msg, (uint32_t)millis(), servers[aliasID]->host, servers[aliasID]->port);
|
response = reinterpret_cast<ModbusClientTCP *>(servers[aliasID]->client)->syncRequestMT(msg, (uint32_t)millis(), servers[aliasID]->host, servers[aliasID]->port);
|
||||||
@@ -179,8 +253,19 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
|||||||
response = servers[aliasID]->client->syncRequestM(msg, (uint32_t)millis());
|
response = servers[aliasID]->client->syncRequestM(msg, (uint32_t)millis());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-set the requested server ID
|
// Response filter hook to be called here
|
||||||
|
if (servers[aliasID]->responseFilter) {
|
||||||
|
LOG_D("Calling response filter\n");
|
||||||
|
response = servers[aliasID]->responseFilter(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-set the requested server ID and function code (may have been modified by filters)
|
||||||
response.setServerID(aliasID);
|
response.setServerID(aliasID);
|
||||||
|
if (response.getError() != SUCCESS) {
|
||||||
|
response.setFunctionCode(functionCode | 0x80);
|
||||||
|
} else {
|
||||||
|
response.setFunctionCode(functionCode);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
||||||
response.setError(aliasID, functionCode, INVALID_SERVER);
|
response.setError(aliasID, functionCode, INVALID_SERVER);
|
||||||
|
|||||||
@@ -54,20 +54,20 @@ ModbusClientRTU::~ModbusClientRTU() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// begin: start worker task - general version
|
// begin: start worker task - general version
|
||||||
void ModbusClientRTU::begin(Stream& serial, uint32_t baudRate, int coreID) {
|
void ModbusClientRTU::begin(Stream& serial, uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
MR_serial = &serial;
|
MR_serial = &serial;
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin: start worker task - HardwareSerial version
|
// begin: start worker task - HardwareSerial version
|
||||||
void ModbusClientRTU::begin(HardwareSerial& serial, int coreID) {
|
void ModbusClientRTU::begin(HardwareSerial& serial, int coreID, uint32_t userInterval) {
|
||||||
MR_serial = &serial;
|
MR_serial = &serial;
|
||||||
uint32_t baudRate = serial.baudRate();
|
uint32_t baudRate = serial.baudRate();
|
||||||
serial.setRxFIFOFull(1);
|
serial.setRxFIFOFull(1);
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID) {
|
void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
// Task already running? End it in case
|
// Task already running? End it in case
|
||||||
end();
|
end();
|
||||||
|
|
||||||
@@ -77,6 +77,11 @@ void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID) {
|
|||||||
// Set minimum interval time
|
// Set minimum interval time
|
||||||
MR_interval = RTUutils::calculateInterval(baudRate);
|
MR_interval = RTUutils::calculateInterval(baudRate);
|
||||||
|
|
||||||
|
// If user defined interval is longer, use that
|
||||||
|
if (MR_interval < userInterval) {
|
||||||
|
MR_interval = userInterval;
|
||||||
|
}
|
||||||
|
|
||||||
// Create unique task name
|
// Create unique task name
|
||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ public:
|
|||||||
~ModbusClientRTU();
|
~ModbusClientRTU();
|
||||||
|
|
||||||
// begin: start worker task
|
// begin: start worker task
|
||||||
void begin(Stream& serial, uint32_t baudrate, int coreID = -1);
|
void begin(Stream& serial, uint32_t baudrate, int coreID = -1, uint32_t userInterval = 0);
|
||||||
// Special variant for HardwareSerial
|
// Special variant for HardwareSerial
|
||||||
void begin(HardwareSerial& serial, int coreID = -1);
|
void begin(HardwareSerial& serial, int coreID = -1, uint32_t userInterval = 0);
|
||||||
|
|
||||||
// end: stop the worker
|
// end: stop the worker
|
||||||
void end();
|
void end();
|
||||||
@@ -87,7 +87,7 @@ protected:
|
|||||||
ModbusMessage receive(const ModbusMessage request);
|
ModbusMessage receive(const ModbusMessage request);
|
||||||
|
|
||||||
// start background task
|
// start background task
|
||||||
void doBegin(uint32_t baudRate, int coreID);
|
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
||||||
|
|
||||||
void isInstance() { return; } // make class instantiable
|
void isInstance() { return; } // make class instantiable
|
||||||
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
||||||
|
|||||||
@@ -7,35 +7,32 @@
|
|||||||
// #undef LOCAL_LOG_LEVEL
|
// #undef LOCAL_LOG_LEVEL
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
ModbusClientTCPasync::ModbusClientTCPasync(IPAddress address, uint16_t port, uint16_t queueLimit)
|
ModbusClientTCPasync::ModbusClientTCPasync(IPAddress address, uint16_t port, uint16_t queueLimit) :
|
||||||
: ModbusClient()
|
ModbusClient(),
|
||||||
, txQueue()
|
txQueue(),
|
||||||
, rxQueue()
|
rxQueue(),
|
||||||
, MTA_client()
|
MTA_client(),
|
||||||
, MTA_timeout(DEFAULTTIMEOUT)
|
MTA_timeout(DEFAULTTIMEOUT),
|
||||||
, MTA_idleTimeout(DEFAULTIDLETIME)
|
MTA_idleTimeout(DEFAULTIDLETIME),
|
||||||
, MTA_qLimit(queueLimit)
|
MTA_qLimit(queueLimit),
|
||||||
, MTA_maxInflightRequests(queueLimit)
|
MTA_maxInflightRequests(queueLimit),
|
||||||
, MTA_lastActivity(0)
|
MTA_lastActivity(0),
|
||||||
, MTA_state(DISCONNECTED)
|
MTA_state(DISCONNECTED),
|
||||||
, MTA_host(address)
|
MTA_host(address),
|
||||||
, MTA_port(port) {
|
MTA_port(port)
|
||||||
|
{
|
||||||
// attach all handlers on async tcp events
|
// attach all handlers on async tcp events
|
||||||
MTA_client.onConnect([](void * i, AsyncClient * c) { (static_cast<ModbusClientTCPasync *>(i))->onConnected(); }, this);
|
MTA_client.onConnect([](void* i, AsyncClient* c) { (static_cast<ModbusClientTCPasync*>(i))->onConnected(); }, this);
|
||||||
MTA_client.onDisconnect([](void * i, AsyncClient * c) { (static_cast<ModbusClientTCPasync *>(i))->onDisconnected(); }, this);
|
MTA_client.onDisconnect([](void* i, AsyncClient* c) { (static_cast<ModbusClientTCPasync*>(i))->onDisconnected(); }, this);
|
||||||
MTA_client.onError([](void * i, AsyncClient * c, int8_t error) { (static_cast<ModbusClientTCPasync *>(i))->onACError(c, error); }, this);
|
MTA_client.onError([](void* i, AsyncClient* c, int8_t error) { (static_cast<ModbusClientTCPasync*>(i))->onACError(c, error); }, this);
|
||||||
// MTA_client.onTimeout([](void* i, AsyncClient* c, uint32_t time) { (static_cast<ModbusClientTCPasync*>(i))->onTimeout(time); }, this);
|
// MTA_client.onTimeout([](void* i, AsyncClient* c, uint32_t time) { (static_cast<ModbusClientTCPasync*>(i))->onTimeout(time); }, this);
|
||||||
// MTA_client.onAck([](void* i, AsyncClient* c, size_t len, uint32_t time) { (static_cast<ModbusClientTCPasync*>(i))->onAck(len, time); }, this);
|
// MTA_client.onAck([](void* i, AsyncClient* c, size_t len, uint32_t time) { (static_cast<ModbusClientTCPasync*>(i))->onAck(len, time); }, this);
|
||||||
MTA_client.onData([](void * i,
|
MTA_client.onData([](void* i, AsyncClient* c, void* data, size_t len) { (static_cast<ModbusClientTCPasync*>(i))->onPacket(static_cast<uint8_t*>(data), len); }, this);
|
||||||
AsyncClient * c,
|
MTA_client.onPoll([](void* i, AsyncClient* c) { (static_cast<ModbusClientTCPasync*>(i))->onPoll(); }, this);
|
||||||
void * data,
|
|
||||||
size_t len) { (static_cast<ModbusClientTCPasync *>(i))->onPacket(static_cast<uint8_t *>(data), len); },
|
|
||||||
this);
|
|
||||||
MTA_client.onPoll([](void * i, AsyncClient * c) { (static_cast<ModbusClientTCPasync *>(i))->onPoll(); }, this);
|
|
||||||
|
|
||||||
// disable nagle algorithm ref Modbus spec
|
// disable nagle algorithm ref Modbus spec
|
||||||
MTA_client.setNoDelay(true);
|
MTA_client.setNoDelay(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor: clean up queue, task etc.
|
// Destructor: clean up queue, task etc.
|
||||||
ModbusClientTCPasync::~ModbusClientTCPasync() {
|
ModbusClientTCPasync::~ModbusClientTCPasync() {
|
||||||
@@ -49,7 +46,7 @@ ModbusClientTCPasync::~ModbusClientTCPasync() {
|
|||||||
delete txQueue.front();
|
delete txQueue.front();
|
||||||
txQueue.pop_front();
|
txQueue.pop_front();
|
||||||
}
|
}
|
||||||
for (auto it = rxQueue.cbegin(); it != rxQueue.cend(); /* no increment */) {
|
for (auto it = rxQueue.cbegin(); it != rxQueue.cend();/* no increment */) {
|
||||||
delete it->second;
|
delete it->second;
|
||||||
it = rxQueue.erase(it);
|
it = rxQueue.erase(it);
|
||||||
}
|
}
|
||||||
@@ -100,7 +97,8 @@ void ModbusClientTCPasync::setMaxInflightRequests(uint32_t maxInflightRequests)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove all pending request from queue
|
// Remove all pending request from queue
|
||||||
void ModbusClientTCPasync::clearQueue() {
|
void ModbusClientTCPasync::clearQueue()
|
||||||
|
{
|
||||||
LOCK_GUARD(lock1, qLock);
|
LOCK_GUARD(lock1, qLock);
|
||||||
LOCK_GUARD(lock2, sLock);
|
LOCK_GUARD(lock2, sLock);
|
||||||
// Delete all elements from queues
|
// Delete all elements from queues
|
||||||
@@ -153,9 +151,8 @@ bool ModbusClientTCPasync::addToQueue(int32_t token, ModbusMessage request, bool
|
|||||||
LOCK_GUARD(lock1, qLock);
|
LOCK_GUARD(lock1, qLock);
|
||||||
if (txQueue.size() + rxQueue.size() < MTA_qLimit) {
|
if (txQueue.size() + rxQueue.size() < MTA_qLimit) {
|
||||||
HEXDUMP_V("Enqueue", request.data(), request.size());
|
HEXDUMP_V("Enqueue", request.data(), request.size());
|
||||||
RequestEntry * re = new RequestEntry(token, request, syncReq);
|
RequestEntry *re = new RequestEntry(token, request, syncReq);
|
||||||
if (!re)
|
if (!re) return false; //TODO: proper error returning in case allocation fails
|
||||||
return false; // TODO proper error returning in case allocation fails
|
|
||||||
// inject proper transactionID
|
// inject proper transactionID
|
||||||
re->head.transactionID = messageCount++;
|
re->head.transactionID = messageCount++;
|
||||||
re->head.len = request.size();
|
re->head.len = request.size();
|
||||||
@@ -193,7 +190,7 @@ void ModbusClientTCPasync::onDisconnected() {
|
|||||||
// empty queue on disconnect, calling errorcode on every waiting request
|
// empty queue on disconnect, calling errorcode on every waiting request
|
||||||
LOCK_GUARD(lock2, qLock);
|
LOCK_GUARD(lock2, qLock);
|
||||||
while (!txQueue.empty()) {
|
while (!txQueue.empty()) {
|
||||||
RequestEntry * r = txQueue.front();
|
RequestEntry* r = txQueue.front();
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(IP_CONNECTION_FAILED, r->token);
|
onError(IP_CONNECTION_FAILED, r->token);
|
||||||
}
|
}
|
||||||
@@ -201,7 +198,7 @@ void ModbusClientTCPasync::onDisconnected() {
|
|||||||
txQueue.pop_front();
|
txQueue.pop_front();
|
||||||
}
|
}
|
||||||
while (!rxQueue.empty()) {
|
while (!rxQueue.empty()) {
|
||||||
RequestEntry * r = rxQueue.begin()->second;
|
RequestEntry *r = rxQueue.begin()->second;
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(IP_CONNECTION_FAILED, r->token);
|
onError(IP_CONNECTION_FAILED, r->token);
|
||||||
}
|
}
|
||||||
@@ -211,7 +208,7 @@ void ModbusClientTCPasync::onDisconnected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ModbusClientTCPasync::onACError(AsyncClient * c, int8_t error) {
|
void ModbusClientTCPasync::onACError(AsyncClient* c, int8_t error) {
|
||||||
// onDisconnect will alse be called, so nothing to do here
|
// onDisconnect will alse be called, so nothing to do here
|
||||||
LOG_W("TCP error: %s\n", c->errorToString(error));
|
LOG_W("TCP error: %s\n", c->errorToString(error));
|
||||||
}
|
}
|
||||||
@@ -225,7 +222,7 @@ void onAck(size_t len, uint32_t time) {
|
|||||||
// assuming we don't need this
|
// assuming we don't need this
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
void ModbusClientTCPasync::onPacket(uint8_t * data, size_t length) {
|
void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
|
||||||
LOG_D("packet received (len:%d)\n", length);
|
LOG_D("packet received (len:%d)\n", length);
|
||||||
// reset idle timeout
|
// reset idle timeout
|
||||||
MTA_lastActivity = millis();
|
MTA_lastActivity = millis();
|
||||||
@@ -234,8 +231,8 @@ void ModbusClientTCPasync::onPacket(uint8_t * data, size_t length) {
|
|||||||
LOG_D("parsing (len:%d)\n", length + 1);
|
LOG_D("parsing (len:%d)\n", length + 1);
|
||||||
}
|
}
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
RequestEntry * request = nullptr;
|
RequestEntry* request = nullptr;
|
||||||
ModbusMessage * response = nullptr;
|
ModbusMessage* response = nullptr;
|
||||||
uint16_t transactionID = 0;
|
uint16_t transactionID = 0;
|
||||||
uint16_t protocolID = 0;
|
uint16_t protocolID = 0;
|
||||||
uint16_t messageLength = 0;
|
uint16_t messageLength = 0;
|
||||||
@@ -249,7 +246,9 @@ void ModbusClientTCPasync::onPacket(uint8_t * data, size_t length) {
|
|||||||
transactionID = (data[0] << 8) | data[1];
|
transactionID = (data[0] << 8) | data[1];
|
||||||
protocolID = (data[2] << 8) | data[3];
|
protocolID = (data[2] << 8) | data[3];
|
||||||
messageLength = (data[4] << 8) | data[5];
|
messageLength = (data[4] << 8) | data[5];
|
||||||
if (protocolID == 0 && length >= (uint32_t)messageLength + 6 && messageLength < 256) {
|
if (protocolID == 0 &&
|
||||||
|
length >= (uint32_t)messageLength + 6 &&
|
||||||
|
messageLength < 256) {
|
||||||
response = new ModbusMessage(messageLength);
|
response = new ModbusMessage(messageLength);
|
||||||
response->add(&data[6], messageLength);
|
response->add(&data[6], messageLength);
|
||||||
LOG_D("packet validated (len:%d)\n", messageLength);
|
LOG_D("packet validated (len:%d)\n", messageLength);
|
||||||
@@ -300,7 +299,7 @@ void ModbusClientTCPasync::onPacket(uint8_t * data, size_t length) {
|
|||||||
|
|
||||||
if (request->isSyncRequest) {
|
if (request->isSyncRequest) {
|
||||||
{
|
{
|
||||||
LOCK_GUARD(sL, syncRespM);
|
LOCK_GUARD(sL ,syncRespM);
|
||||||
syncResponse[request->token] = *response;
|
syncResponse[request->token] = *response;
|
||||||
}
|
}
|
||||||
} else if (onResponse) {
|
} else if (onResponse) {
|
||||||
@@ -336,7 +335,7 @@ void ModbusClientTCPasync::onPoll() {
|
|||||||
|
|
||||||
// next check if timeout has struck for oldest request
|
// next check if timeout has struck for oldest request
|
||||||
if (!rxQueue.empty()) {
|
if (!rxQueue.empty()) {
|
||||||
RequestEntry * request = rxQueue.begin()->second;
|
RequestEntry* request = rxQueue.begin()->second;
|
||||||
if (millis() - request->sentTime > MTA_timeout) {
|
if (millis() - request->sentTime > MTA_timeout) {
|
||||||
LOG_D("request timeouts (now:%lu-sent:%u)\n", millis(), request->sentTime);
|
LOG_D("request timeouts (now:%lu-sent:%u)\n", millis(), request->sentTime);
|
||||||
// oldest element timeouts, call onError and clean up
|
// oldest element timeouts, call onError and clean up
|
||||||
@@ -363,7 +362,7 @@ void ModbusClientTCPasync::handleSendingQueue() {
|
|||||||
// by mutex.
|
// by mutex.
|
||||||
|
|
||||||
// try to send everything we have waiting
|
// try to send everything we have waiting
|
||||||
std::list<RequestEntry *>::iterator it = txQueue.begin();
|
std::list<RequestEntry*>::iterator it = txQueue.begin();
|
||||||
while (it != txQueue.end()) {
|
while (it != txQueue.end()) {
|
||||||
// get the actual element
|
// get the actual element
|
||||||
if (send(*it)) {
|
if (send(*it)) {
|
||||||
@@ -378,7 +377,7 @@ void ModbusClientTCPasync::handleSendingQueue() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModbusClientTCPasync::send(RequestEntry * re) {
|
bool ModbusClientTCPasync::send(RequestEntry* re) {
|
||||||
// ATTENTION: This method does not have a lock guard.
|
// ATTENTION: This method does not have a lock guard.
|
||||||
// Calling sites must assure shared resources are protected
|
// Calling sites must assure shared resources are protected
|
||||||
// by mutex.
|
// by mutex.
|
||||||
@@ -392,7 +391,7 @@ bool ModbusClientTCPasync::send(RequestEntry * re) {
|
|||||||
// Write TCP header first
|
// Write TCP header first
|
||||||
MTA_client.add(reinterpret_cast<const char *>((const uint8_t *)(re->head)), 6, ASYNC_WRITE_FLAG_COPY);
|
MTA_client.add(reinterpret_cast<const char *>((const uint8_t *)(re->head)), 6, ASYNC_WRITE_FLAG_COPY);
|
||||||
// Request comes next
|
// Request comes next
|
||||||
MTA_client.add(reinterpret_cast<const char *>(re->msg.data()), re->msg.size(), ASYNC_WRITE_FLAG_COPY);
|
MTA_client.add(reinterpret_cast<const char*>(re->msg.data()), re->msg.size(), ASYNC_WRITE_FLAG_COPY);
|
||||||
// done
|
// done
|
||||||
MTA_client.send();
|
MTA_client.send();
|
||||||
LOG_D("request sent (msgid:%d)\n", re->head.transactionID);
|
LOG_D("request sent (msgid:%d)\n", re->head.transactionID);
|
||||||
|
|||||||
@@ -18,30 +18,48 @@ void ModbusServer::registerWorker(uint8_t serverID, uint8_t functionCode, MBSwor
|
|||||||
|
|
||||||
// getWorker: if a worker function is registered, return its address, nullptr otherwise
|
// getWorker: if a worker function is registered, return its address, nullptr otherwise
|
||||||
MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
||||||
|
bool serverFound = false;
|
||||||
|
LOG_D("Need worker for %02X-%02X : ", serverID, functionCode);
|
||||||
// Search the FC map associated with the serverID
|
// Search the FC map associated with the serverID
|
||||||
auto svmap = workerMap.find(serverID);
|
auto svmap = workerMap.find(serverID);
|
||||||
// Is there one?
|
// Is there one?
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
|
serverFound = true;
|
||||||
|
// No explicit serverID entry found, but we may have one for ANY_SERVER
|
||||||
|
} else {
|
||||||
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
serverFound = true;
|
||||||
|
serverID = ANY_SERVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Did we find a serverID?
|
||||||
|
if (serverFound) {
|
||||||
// Yes. Now look for the function code in the inner map
|
// Yes. Now look for the function code in the inner map
|
||||||
|
bool functionCodeFound = false;
|
||||||
auto fcmap = svmap->second.find(functionCode);;
|
auto fcmap = svmap->second.find(functionCode);;
|
||||||
// Found it?
|
// Found it?
|
||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
LOG_D("Worker found for %02X/%02X\n", serverID, functionCode);
|
functionCodeFound = true;
|
||||||
return fcmap->second;
|
|
||||||
// No, no explicit worker found, but may be there is one for ANY_FUNCTION_CODE?
|
// No, no explicit worker found, but may be there is one for ANY_FUNCTION_CODE?
|
||||||
} else {
|
} else {
|
||||||
fcmap = svmap->second.find(ANY_FUNCTION_CODE);;
|
fcmap = svmap->second.find(ANY_FUNCTION_CODE);;
|
||||||
// Found it?
|
// Found it?
|
||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
LOG_D("Worker found for %02X/ANY\n", serverID);
|
functionCodeFound = true;
|
||||||
|
functionCode = ANY_FUNCTION_CODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (functionCodeFound) {
|
||||||
|
// Yes. Return the function pointer for it.
|
||||||
|
LOGRAW_D("Worker found for %02X/%02X\n", serverID, functionCode);
|
||||||
return fcmap->second;
|
return fcmap->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// No matching function pointer found
|
// No matching function pointer found
|
||||||
LOG_D("No matching worker found\n");
|
LOGRAW_D("No matching worker found\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,16 +86,29 @@ bool ModbusServer::unregisterWorker(uint8_t serverID, uint8_t functionCode) {
|
|||||||
return (numEntries ? true : false);
|
return (numEntries ? true : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// isServerFor: if any worker function is registered for the given serverID, return true
|
// isServerFor: if a worker function is registered for the given serverID, return true
|
||||||
bool ModbusServer::isServerFor(uint8_t serverID) {
|
// functionCode defaults to ANY_FUNCTION_CODE and will yield true for any function code,
|
||||||
// Search the FC map for the serverID
|
// including ANY_FUNCTION_CODE :D
|
||||||
auto svmap = workerMap.find(serverID);
|
bool ModbusServer::isServerFor(uint8_t serverID, uint8_t functionCode) {
|
||||||
// Is it there? Then return true
|
// Check if there is a non-nullptr function for the given combination
|
||||||
if (svmap != workerMap.end()) return true;
|
if (getWorker(serverID, functionCode)) {
|
||||||
// No, serverID was not found. Return false
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isServerFor: short version to look up if the server is known at all
|
||||||
|
bool ModbusServer::isServerFor(uint8_t serverID) {
|
||||||
|
// Check if there is a non-nullptr function for the given combination
|
||||||
|
auto svmap = workerMap.find(serverID);
|
||||||
|
// Is there one?
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// getMessageCount: read number of messages processed
|
// getMessageCount: read number of messages processed
|
||||||
uint32_t ModbusServer::getMessageCount() {
|
uint32_t ModbusServer::getMessageCount() {
|
||||||
return messageCount;
|
return messageCount;
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ public:
|
|||||||
// Returns true if the worker was found and removed
|
// Returns true if the worker was found and removed
|
||||||
bool unregisterWorker(uint8_t serverID, uint8_t functionCode = 0);
|
bool unregisterWorker(uint8_t serverID, uint8_t functionCode = 0);
|
||||||
|
|
||||||
// isServerFor: if any worker function is registered for the given serverID, return true
|
// isServerFor: if a worker function is registered for the given serverID, return true
|
||||||
|
bool isServerFor(uint8_t serverID, uint8_t functionCode);
|
||||||
|
|
||||||
|
// isServerFor: short version to look up if the server is known at all
|
||||||
bool isServerFor(uint8_t serverID);
|
bool isServerFor(uint8_t serverID);
|
||||||
|
|
||||||
// getMessageCount: read number of messages processed
|
// getMessageCount: read number of messages processed
|
||||||
|
|||||||
19
lib/eModbus/src/ModbusServerETH.h
Normal file
19
lib/eModbus/src/ModbusServerETH.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// =================================================================================================
|
||||||
|
// eModbus: Copyright 2024 by Michael Harwerth, Bert Melis and the contributors to eModbus
|
||||||
|
// MIT license - see license.md for details
|
||||||
|
// =================================================================================================
|
||||||
|
#ifndef _MODBUS_SERVER_ETH_H
|
||||||
|
#define _MODBUS_SERVER_ETH_H
|
||||||
|
#include "options.h"
|
||||||
|
#if HAS_ETHERNET == 1
|
||||||
|
#include <ETH.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#undef SERVER_END
|
||||||
|
#define SERVER_END // NIL for Ethernet
|
||||||
|
|
||||||
|
#include "ModbusServerTCPtemp.h"
|
||||||
|
using ModbusServerEthernet = ModbusServerTCP<WiFiServer, WiFiClient>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -12,8 +12,15 @@
|
|||||||
#undef SERVER_END
|
#undef SERVER_END
|
||||||
#define SERVER_END // NIL for Ethernet
|
#define SERVER_END // NIL for Ethernet
|
||||||
|
|
||||||
|
// Create own non-virtual EthernetServer class
|
||||||
|
class EthernetServerEM : public EthernetServer {
|
||||||
|
public:
|
||||||
|
EthernetServerEM(uint16_t port) : EthernetServer(port) { }
|
||||||
|
void begin(uint16_t port = 0) { }
|
||||||
|
};
|
||||||
|
|
||||||
#include "ModbusServerTCPtemp.h"
|
#include "ModbusServerTCPtemp.h"
|
||||||
using ModbusServerEthernet = ModbusServerTCP<EthernetServer, EthernetClient>;
|
using ModbusServerEthernet = ModbusServerTCP<EthernetServerEM, EthernetClient>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -64,26 +64,31 @@ ModbusServerRTU::~ModbusServerRTU() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start: create task with RTU server - general version
|
// start: create task with RTU server - general version
|
||||||
void ModbusServerRTU::begin(Stream& serial, uint32_t baudRate, int coreID) {
|
void ModbusServerRTU::begin(Stream& serial, uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
MSRserial = &serial;
|
MSRserial = &serial;
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start: create task with RTU server - HardwareSerial versions
|
// start: create task with RTU server - HardwareSerial versions
|
||||||
void ModbusServerRTU::begin(HardwareSerial& serial, int coreID) {
|
void ModbusServerRTU::begin(HardwareSerial& serial, int coreID, uint32_t userInterval) {
|
||||||
MSRserial = &serial;
|
MSRserial = &serial;
|
||||||
uint32_t baudRate = serial.baudRate();
|
uint32_t baudRate = serial.baudRate();
|
||||||
serial.setRxFIFOFull(1);
|
serial.setRxFIFOFull(1);
|
||||||
doBegin(baudRate, coreID);
|
doBegin(baudRate, coreID, userInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID) {
|
void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterval) {
|
||||||
// Task already running? Stop it in case.
|
// Task already running? Stop it in case.
|
||||||
end();
|
end();
|
||||||
|
|
||||||
// Set minimum interval time
|
// Set minimum interval time
|
||||||
MSRinterval = RTUutils::calculateInterval(baudRate);
|
MSRinterval = RTUutils::calculateInterval(baudRate);
|
||||||
|
|
||||||
|
// If user defined interval is longer, use that
|
||||||
|
if (MSRinterval < userInterval) {
|
||||||
|
MSRinterval = userInterval;
|
||||||
|
}
|
||||||
|
|
||||||
// Create unique task name
|
// Create unique task name
|
||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
||||||
@@ -180,10 +185,12 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
|
|||||||
}
|
}
|
||||||
// Is it a broadcast?
|
// Is it a broadcast?
|
||||||
if (request[0] == 0) {
|
if (request[0] == 0) {
|
||||||
|
LOG_D("Broadcast!\n");
|
||||||
// Yes. Do we have a listener?
|
// Yes. Do we have a listener?
|
||||||
if (myServer->listener) {
|
if (myServer->listener) {
|
||||||
// Yes. call it
|
// Yes. call it
|
||||||
myServer->listener(request);
|
myServer->listener(request);
|
||||||
|
LOG_D("Broadcast served.\n");
|
||||||
}
|
}
|
||||||
// else we simply ignore it
|
// else we simply ignore it
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ public:
|
|||||||
~ModbusServerRTU();
|
~ModbusServerRTU();
|
||||||
|
|
||||||
// begin: create task with RTU server to accept requests
|
// begin: create task with RTU server to accept requests
|
||||||
void begin(Stream& serial, uint32_t baudRate, int coreID = -1);
|
void begin(Stream& serial, uint32_t baudRate, int coreID = -1, uint32_t userInterval = 0);
|
||||||
void begin(HardwareSerial& serial, int coreID = -1);
|
void begin(HardwareSerial& serial, int coreID = -1, uint32_t userInterval = 0);
|
||||||
|
|
||||||
// end: kill server task
|
// end: kill server task
|
||||||
void end();
|
void end();
|
||||||
@@ -64,7 +64,7 @@ protected:
|
|||||||
inline void isInstance() { } // Make class instantiable
|
inline void isInstance() { } // Make class instantiable
|
||||||
|
|
||||||
// internal common begin function
|
// internal common begin function
|
||||||
void doBegin(uint32_t baudRate, int coreID);
|
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
||||||
|
|
||||||
static uint8_t instanceCounter; // Number of RTU servers created (for task names)
|
static uint8_t instanceCounter; // Number of RTU servers created (for task names)
|
||||||
TaskHandle_t serverTask; // task of the started server
|
TaskHandle_t serverTask; // task of the started server
|
||||||
|
|||||||
@@ -8,16 +8,16 @@
|
|||||||
// #undef LOCAL_LOG_LEVEL
|
// #undef LOCAL_LOG_LEVEL
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
ModbusServerTCPasync::mb_client::mb_client(ModbusServerTCPasync * s, AsyncClient * c)
|
ModbusServerTCPasync::mb_client::mb_client(ModbusServerTCPasync* s, AsyncClient* c) :
|
||||||
: server(s)
|
server(s),
|
||||||
, client(c)
|
client(c),
|
||||||
, lastActiveTime(millis())
|
lastActiveTime(millis()),
|
||||||
, message(nullptr)
|
message(nullptr),
|
||||||
, error(SUCCESS)
|
error(SUCCESS),
|
||||||
, outbox() {
|
outbox() {
|
||||||
client->onData([](void * i, AsyncClient * c, void * data, size_t len) { (static_cast<mb_client *>(i))->onData(static_cast<uint8_t *>(data), len); }, this);
|
client->onData([](void* i, AsyncClient* c, void* data, size_t len) { (static_cast<mb_client*>(i))->onData(static_cast<uint8_t*>(data), len); }, this);
|
||||||
client->onPoll([](void * i, AsyncClient * c) { (static_cast<mb_client *>(i))->onPoll(); }, this);
|
client->onPoll([](void* i, AsyncClient* c) { (static_cast<mb_client*>(i))->onPoll(); }, this);
|
||||||
client->onDisconnect([](void * i, AsyncClient * c) { (static_cast<mb_client *>(i))->onDisconnect(); }, this);
|
client->onDisconnect([](void* i, AsyncClient* c) { (static_cast<mb_client*>(i))->onDisconnect(); }, this);
|
||||||
client->setNoDelay(true);
|
client->setNoDelay(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ ModbusServerTCPasync::mb_client::~mb_client() {
|
|||||||
delete client; // will also close connection, if any
|
delete client; // will also close connection, if any
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerTCPasync::mb_client::onData(uint8_t * data, size_t len) {
|
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
|
||||||
lastActiveTime = millis();
|
lastActiveTime = millis();
|
||||||
LOG_D("data len %d\n", len);
|
LOG_D("data len %d\n", len);
|
||||||
|
|
||||||
@@ -101,7 +101,8 @@ void ModbusServerTCPasync::mb_client::onData(uint8_t * data, size_t len) {
|
|||||||
break;
|
break;
|
||||||
case 0xF1: // ECHO
|
case 0xF1: // ECHO
|
||||||
userData = request;
|
userData = request;
|
||||||
if (request.getFunctionCode() == WRITE_MULT_REGISTERS || request.getFunctionCode() == WRITE_MULT_COILS) {
|
if (request.getFunctionCode() == WRITE_MULT_REGISTERS ||
|
||||||
|
request.getFunctionCode() == WRITE_MULT_COILS) {
|
||||||
userData.resize(6);
|
userData.resize(6);
|
||||||
}
|
}
|
||||||
LOG_D("ECHO response\n");
|
LOG_D("ECHO response\n");
|
||||||
@@ -138,7 +139,8 @@ void ModbusServerTCPasync::mb_client::onData(uint8_t * data, size_t len) {
|
|||||||
void ModbusServerTCPasync::mb_client::onPoll() {
|
void ModbusServerTCPasync::mb_client::onPoll() {
|
||||||
LOCK_GUARD(lock1, obLock);
|
LOCK_GUARD(lock1, obLock);
|
||||||
handleOutbox();
|
handleOutbox();
|
||||||
if (server->idle_timeout > 0 && millis() - lastActiveTime > server->idle_timeout) {
|
if (server->idle_timeout > 0 &&
|
||||||
|
millis() - lastActiveTime > server->idle_timeout) {
|
||||||
LOG_D("client idle, closing\n");
|
LOG_D("client idle, closing\n");
|
||||||
client->close();
|
client->close();
|
||||||
}
|
}
|
||||||
@@ -149,7 +151,7 @@ void ModbusServerTCPasync::mb_client::onDisconnect() {
|
|||||||
server->onClientDisconnect(this);
|
server->onClientDisconnect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerTCPasync::mb_client::addResponseToOutbox(ModbusMessage * response) {
|
void ModbusServerTCPasync::mb_client::addResponseToOutbox(ModbusMessage* response) {
|
||||||
if (response->size() > 0) {
|
if (response->size() > 0) {
|
||||||
LOCK_GUARD(lock1, obLock);
|
LOCK_GUARD(lock1, obLock);
|
||||||
outbox.push(response);
|
outbox.push(response);
|
||||||
@@ -159,10 +161,10 @@ void ModbusServerTCPasync::mb_client::addResponseToOutbox(ModbusMessage * respon
|
|||||||
|
|
||||||
void ModbusServerTCPasync::mb_client::handleOutbox() {
|
void ModbusServerTCPasync::mb_client::handleOutbox() {
|
||||||
while (!outbox.empty()) {
|
while (!outbox.empty()) {
|
||||||
ModbusMessage * m = outbox.front();
|
ModbusMessage* m = outbox.front();
|
||||||
if (m->size() <= client->space()) {
|
if (m->size() <= client->space()) {
|
||||||
LOG_D("sending (%d)\n", m->size());
|
LOG_D("sending (%d)\n", m->size());
|
||||||
client->add(reinterpret_cast<const char *>(m->data()), m->size(), ASYNC_WRITE_FLAG_COPY);
|
client->add(reinterpret_cast<const char*>(m->data()), m->size(), ASYNC_WRITE_FLAG_COPY);
|
||||||
client->send();
|
client->send();
|
||||||
delete m;
|
delete m;
|
||||||
outbox.pop();
|
outbox.pop();
|
||||||
@@ -172,11 +174,11 @@ void ModbusServerTCPasync::mb_client::handleOutbox() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusServerTCPasync::ModbusServerTCPasync()
|
ModbusServerTCPasync::ModbusServerTCPasync() :
|
||||||
: server(nullptr)
|
server(nullptr),
|
||||||
, clients()
|
clients(),
|
||||||
, maxNoClients(5)
|
maxNoClients(5),
|
||||||
, idle_timeout(60000) {
|
idle_timeout(60000) {
|
||||||
// setup will be done in 'start'
|
// setup will be done in 'start'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,19 +195,19 @@ uint16_t ModbusServerTCPasync::activeClients() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ModbusServerTCPasync::start(uint16_t port, uint8_t max_clients, uint32_t timeout, int coreID) {
|
bool ModbusServerTCPasync::start(uint16_t port, uint8_t maxClients, uint32_t timeout, int coreID) {
|
||||||
// don't restart if already running
|
// don't restart if already running
|
||||||
if (server) {
|
if (server) {
|
||||||
LOG_W("Server already running.\n");
|
LOG_W("Server already running.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxNoClients = max_clients;
|
maxNoClients = maxClients;
|
||||||
idle_timeout = timeout;
|
idle_timeout = timeout;
|
||||||
server = new AsyncServer(port);
|
server = new AsyncServer(port);
|
||||||
if (server) {
|
if (server) {
|
||||||
server->setNoDelay(true);
|
server->setNoDelay(true);
|
||||||
server->onClient([](void * i, AsyncClient * c) { (static_cast<ModbusServerTCPasync *>(i))->onClientConnect(c); }, this);
|
server->onClient([](void* i, AsyncClient* c) { (static_cast<ModbusServerTCPasync*>(i))->onClientConnect(c); }, this);
|
||||||
server->begin();
|
server->begin();
|
||||||
LOG_D("Modbus server started\n");
|
LOG_D("Modbus server started\n");
|
||||||
return true;
|
return true;
|
||||||
@@ -215,6 +217,7 @@ bool ModbusServerTCPasync::start(uint16_t port, uint8_t max_clients, uint32_t ti
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ModbusServerTCPasync::stop() {
|
bool ModbusServerTCPasync::stop() {
|
||||||
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
LOG_W("Server not running.\n");
|
LOG_W("Server not running.\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -238,13 +241,11 @@ bool ModbusServerTCPasync::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ModbusServerTCPasync::isRunning() {
|
bool ModbusServerTCPasync::isRunning() {
|
||||||
if (server)
|
if (server) return true;
|
||||||
return true;
|
else return false;
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerTCPasync::onClientConnect(AsyncClient * client) {
|
void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
|
||||||
LOG_D("new client\n");
|
LOG_D("new client\n");
|
||||||
LOCK_GUARD(lock1, cListLock);
|
LOCK_GUARD(lock1, cListLock);
|
||||||
if (clients.size() < maxNoClients) {
|
if (clients.size() < maxNoClients) {
|
||||||
@@ -257,10 +258,10 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient * client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusServerTCPasync::onClientDisconnect(mb_client * client) {
|
void ModbusServerTCPasync::onClientDisconnect(mb_client* client) {
|
||||||
LOCK_GUARD(lock1, cListLock);
|
LOCK_GUARD(lock1, cListLock);
|
||||||
// delete mb_client from list
|
// delete mb_client from list
|
||||||
clients.remove_if([client](mb_client * i) { return i->client == client->client; });
|
clients.remove_if([client](mb_client* i) { return i->client == client->client; });
|
||||||
// delete client itself
|
// delete client itself
|
||||||
delete client;
|
delete client;
|
||||||
LOG_D("nr clients: %d\n", clients.size());
|
LOG_D("nr clients: %d\n", clients.size());
|
||||||
|
|||||||
@@ -29,29 +29,30 @@ using std::lock_guard;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
class ModbusServerTCPasync : public ModbusServer {
|
class ModbusServerTCPasync : public ModbusServer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class mb_client {
|
class mb_client {
|
||||||
friend class ModbusServerTCPasync;
|
friend class ModbusServerTCPasync;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mb_client(ModbusServerTCPasync * s, AsyncClient * c);
|
mb_client(ModbusServerTCPasync* s, AsyncClient* c);
|
||||||
~mb_client();
|
~mb_client();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onData(uint8_t * data, size_t len);
|
void onData(uint8_t* data, size_t len);
|
||||||
void onPoll();
|
void onPoll();
|
||||||
void onDisconnect();
|
void onDisconnect();
|
||||||
void addResponseToOutbox(ModbusMessage * response);
|
void addResponseToOutbox(ModbusMessage* response);
|
||||||
void handleOutbox();
|
void handleOutbox();
|
||||||
ModbusServerTCPasync * server;
|
ModbusServerTCPasync* server;
|
||||||
AsyncClient * client;
|
AsyncClient* client;
|
||||||
uint32_t lastActiveTime;
|
uint32_t lastActiveTime;
|
||||||
ModbusMessage * message;
|
ModbusMessage* message;
|
||||||
Modbus::Error error;
|
Modbus::Error error;
|
||||||
std::queue<ModbusMessage *> outbox;
|
std::queue<ModbusMessage*> outbox;
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
std::mutex obLock; // outbox protection
|
std::mutex obLock; // outbox protection
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class ModbusServerTCPasync : public ModbusServer {
|
|||||||
uint16_t activeClients();
|
uint16_t activeClients();
|
||||||
|
|
||||||
// start: create task with TCP server to accept requests
|
// start: create task with TCP server to accept requests
|
||||||
bool start(uint16_t port, uint8_t max_clients, uint32_t timeout, int coreID = -1);
|
bool start(uint16_t port, uint8_t maxClients, uint32_t timeout, int coreID = -1);
|
||||||
|
|
||||||
// stop: drop all connections and kill server task
|
// stop: drop all connections and kill server task
|
||||||
bool stop();
|
bool stop();
|
||||||
@@ -75,18 +76,17 @@ class ModbusServerTCPasync : public ModbusServer {
|
|||||||
bool isRunning();
|
bool isRunning();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void isInstance() {
|
inline void isInstance() { }
|
||||||
}
|
void onClientConnect(AsyncClient* client);
|
||||||
void onClientConnect(AsyncClient * client);
|
void onClientDisconnect(mb_client* client);
|
||||||
void onClientDisconnect(mb_client * client);
|
|
||||||
|
|
||||||
AsyncServer * server;
|
AsyncServer* server;
|
||||||
std::list<mb_client *> clients;
|
std::list<mb_client*> clients;
|
||||||
uint8_t maxNoClients;
|
uint8_t maxNoClients;
|
||||||
uint32_t idle_timeout;
|
uint32_t idle_timeout;
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
std::mutex cListLock; // client list protection
|
std::mutex cListLock; // client list protection
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ extern "C" {
|
|||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
using std::lock_guard;
|
|
||||||
using std::mutex;
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
using std::mutex;
|
||||||
|
using std::lock_guard;
|
||||||
|
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
class ModbusServerTCP : public ModbusServer {
|
class ModbusServerTCP : public ModbusServer {
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
ModbusServerTCP();
|
ModbusServerTCP();
|
||||||
|
|
||||||
@@ -34,18 +34,17 @@ class ModbusServerTCP : public ModbusServer {
|
|||||||
uint16_t activeClients();
|
uint16_t activeClients();
|
||||||
|
|
||||||
// start: create task with TCP server to accept requests
|
// start: create task with TCP server to accept requests
|
||||||
bool start(uint16_t port, uint8_t max_clients, uint32_t timeout, int coreID = -1);
|
bool start(uint16_t port, uint8_t maxClients, uint32_t timeout, int coreID = -1);
|
||||||
|
|
||||||
// stop: drop all connections and kill server task
|
// stop: drop all connections and kill server task
|
||||||
bool stop();
|
bool stop();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Prevent copy construction and assignment
|
// Prevent copy construction and assignment
|
||||||
ModbusServerTCP(ModbusServerTCP & m) = delete;
|
ModbusServerTCP(ModbusServerTCP& m) = delete;
|
||||||
ModbusServerTCP & operator=(ModbusServerTCP & m) = delete;
|
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
|
||||||
|
|
||||||
inline void isInstance() {
|
inline void isInstance() { }
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t numClients;
|
uint8_t numClients;
|
||||||
TaskHandle_t serverTask;
|
TaskHandle_t serverTask;
|
||||||
@@ -55,18 +54,9 @@ class ModbusServerTCP : public ModbusServer {
|
|||||||
mutex clientLock;
|
mutex clientLock;
|
||||||
|
|
||||||
struct ClientData {
|
struct ClientData {
|
||||||
ClientData()
|
ClientData() : task(nullptr), client(0), timeout(0), parent(nullptr) {}
|
||||||
: task(nullptr)
|
ClientData(TaskHandle_t t, CT& c, uint32_t to, ModbusServerTCP<ST, CT> *p) :
|
||||||
, client(0)
|
task(t), client(c), timeout(to), parent(p) {}
|
||||||
, timeout(0)
|
|
||||||
, parent(nullptr) {
|
|
||||||
}
|
|
||||||
ClientData(TaskHandle_t t, CT & c, uint32_t to, ModbusServerTCP<ST, CT> * p)
|
|
||||||
: task(t)
|
|
||||||
, client(c)
|
|
||||||
, timeout(to)
|
|
||||||
, parent(p) {
|
|
||||||
}
|
|
||||||
~ClientData() {
|
~ClientData() {
|
||||||
if (client) {
|
if (client) {
|
||||||
client.stop();
|
client.stop();
|
||||||
@@ -79,39 +69,37 @@ class ModbusServerTCP : public ModbusServer {
|
|||||||
TaskHandle_t task;
|
TaskHandle_t task;
|
||||||
CT client;
|
CT client;
|
||||||
uint32_t timeout;
|
uint32_t timeout;
|
||||||
ModbusServerTCP<ST, CT> * parent;
|
ModbusServerTCP<ST, CT> *parent;
|
||||||
};
|
};
|
||||||
ClientData ** clients;
|
ClientData **clients;
|
||||||
|
|
||||||
// serve: loop function for server task
|
// serve: loop function for server task
|
||||||
static void serve(ModbusServerTCP<ST, CT> * myself);
|
static void serve(ModbusServerTCP<ST, CT> *myself);
|
||||||
|
|
||||||
// worker: loop function for client tasks
|
// worker: loop function for client tasks
|
||||||
static void worker(ClientData * myData);
|
static void worker(ClientData *myData);
|
||||||
|
|
||||||
// receive: read data from TCP
|
// receive: read data from TCP
|
||||||
ModbusMessage receive(CT & client, uint32_t timeWait);
|
ModbusMessage receive(CT& client, uint32_t timeWait);
|
||||||
|
|
||||||
// accept: start a task to receive requests and respond to a given client
|
// accept: start a task to receive requests and respond to a given client
|
||||||
bool accept(CT & client, uint32_t timeout, int coreID = -1);
|
bool accept(CT& client, uint32_t timeout, int coreID = -1);
|
||||||
|
|
||||||
// clientAvailable: return true,. if a client slot is currently unused
|
// clientAvailable: return true,. if a client slot is currently unused
|
||||||
bool clientAvailable() {
|
bool clientAvailable() { return (numClients - activeClients()) > 0; }
|
||||||
return (numClients - activeClients()) > 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
ModbusServerTCP<ST, CT>::ModbusServerTCP()
|
ModbusServerTCP<ST, CT>::ModbusServerTCP() :
|
||||||
: ModbusServer()
|
ModbusServer(),
|
||||||
, numClients(0)
|
numClients(0),
|
||||||
, serverTask(nullptr)
|
serverTask(nullptr),
|
||||||
, serverPort(502)
|
serverPort(502),
|
||||||
, serverTimeout(20000)
|
serverTimeout(20000),
|
||||||
, serverGoDown(false) {
|
serverGoDown(false) {
|
||||||
clients = new ClientData *[numClients]();
|
clients = new ClientData*[numClients]();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor: closes the connections
|
// Destructor: closes the connections
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
@@ -141,27 +129,26 @@ uint16_t ModbusServerTCP<ST, CT>::activeClients() {
|
|||||||
clients[i] = nullptr;
|
clients[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clients[i] != nullptr)
|
if (clients[i] != nullptr) cnt++;
|
||||||
cnt++;
|
|
||||||
}
|
}
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start: create task with TCP server to accept requests
|
// start: create task with TCP server to accept requests
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
bool ModbusServerTCP<ST, CT>::start(uint16_t port, uint8_t max_clients, uint32_t timeout, int coreID) {
|
bool ModbusServerTCP<ST, CT>::start(uint16_t port, uint8_t maxClients, uint32_t timeout, int coreID) {
|
||||||
// Task already running?
|
// Task already running?
|
||||||
if (serverTask != nullptr) {
|
if (serverTask != nullptr) {
|
||||||
// Yes. stop it first
|
// Yes. stop it first
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
// Does the required number of slots fit?
|
// Does the required number of slots fit?
|
||||||
if (numClients != max_clients) {
|
if (numClients != maxClients) {
|
||||||
// No. Drop array and allocate a new one
|
// No. Drop array and allocate a new one
|
||||||
delete[] clients;
|
delete[] clients;
|
||||||
// Now allocate a new one
|
// Now allocate a new one
|
||||||
numClients = max_clients;
|
numClients = maxClients;
|
||||||
clients = new ClientData *[numClients]();
|
clients = new ClientData*[numClients]();
|
||||||
}
|
}
|
||||||
serverPort = port;
|
serverPort = port;
|
||||||
serverTimeout = timeout;
|
serverTimeout = timeout;
|
||||||
@@ -179,11 +166,11 @@ bool ModbusServerTCP<ST, CT>::start(uint16_t port, uint8_t max_clients, uint32_t
|
|||||||
delay(2000);
|
delay(2000);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop: drop all connections and kill server task
|
// stop: drop all connections and kill server task
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
bool ModbusServerTCP<ST, CT>::stop() {
|
bool ModbusServerTCP<ST, CT>::stop() {
|
||||||
// Check for clients still connected
|
// Check for clients still connected
|
||||||
for (uint8_t i = 0; i < numClients; ++i) {
|
for (uint8_t i = 0; i < numClients; ++i) {
|
||||||
// Client is alive?
|
// Client is alive?
|
||||||
@@ -202,11 +189,11 @@ bool ModbusServerTCP<ST, CT>::stop() {
|
|||||||
serverGoDown = false;
|
serverGoDown = false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept: start a task to receive requests and respond to a given client
|
// accept: start a task to receive requests and respond to a given client
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
bool ModbusServerTCP<ST, CT>::accept(CT & client, uint32_t timeout, int coreID) {
|
bool ModbusServerTCP<ST, CT>::accept(CT& client, uint32_t timeout, int coreID) {
|
||||||
// Look for an empty client slot
|
// Look for an empty client slot
|
||||||
for (uint8_t i = 0; i < numClients; ++i) {
|
for (uint8_t i = 0; i < numClients; ++i) {
|
||||||
// Empty slot?
|
// Empty slot?
|
||||||
@@ -230,7 +217,7 @@ bool ModbusServerTCP<ST, CT>::accept(CT & client, uint32_t timeout, int coreID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
void ModbusServerTCP<ST, CT>::serve(ModbusServerTCP<ST, CT> * myself) {
|
void ModbusServerTCP<ST, CT>::serve(ModbusServerTCP<ST, CT> *myself) {
|
||||||
// need a local scope here to delete the server at termination time
|
// need a local scope here to delete the server at termination time
|
||||||
if (1) {
|
if (1) {
|
||||||
// Set up server with given port
|
// Set up server with given port
|
||||||
@@ -263,12 +250,12 @@ void ModbusServerTCP<ST, CT>::serve(ModbusServerTCP<ST, CT> * myself) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
void ModbusServerTCP<ST, CT>::worker(ClientData * myData) {
|
void ModbusServerTCP<ST, CT>::worker(ClientData *myData) {
|
||||||
// Get own reference data in handier form
|
// Get own reference data in handier form
|
||||||
CT myClient = myData->client;
|
CT myClient = myData->client;
|
||||||
uint32_t myTimeOut = myData->timeout;
|
uint32_t myTimeOut = myData->timeout;
|
||||||
// TaskHandle_t myTask = myData->task;
|
// TaskHandle_t myTask = myData->task;
|
||||||
ModbusServerTCP<ST, CT> * myParent = myData->parent;
|
ModbusServerTCP<ST, CT> *myParent = myData->parent;
|
||||||
unsigned long myLastMessage = millis();
|
unsigned long myLastMessage = millis();
|
||||||
|
|
||||||
LOG_D("Worker started, timeout=%d\n", myTimeOut);
|
LOG_D("Worker started, timeout=%d\n", myTimeOut);
|
||||||
@@ -312,7 +299,8 @@ void ModbusServerTCP<ST, CT>::worker(ClientData * myData) {
|
|||||||
break;
|
break;
|
||||||
case 0xF1: // ECHO
|
case 0xF1: // ECHO
|
||||||
response = request;
|
response = request;
|
||||||
if (request.getFunctionCode() == WRITE_MULT_REGISTERS || request.getFunctionCode() == WRITE_MULT_COILS) {
|
if (request.getFunctionCode() == WRITE_MULT_REGISTERS ||
|
||||||
|
request.getFunctionCode() == WRITE_MULT_COILS) {
|
||||||
response.resize(6);
|
response.resize(6);
|
||||||
}
|
}
|
||||||
LOG_D("ECHO response\n");
|
LOG_D("ECHO response\n");
|
||||||
@@ -370,8 +358,7 @@ void ModbusServerTCP<ST, CT>::worker(ClientData * myData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read away all that may still hang in the buffer
|
// Read away all that may still hang in the buffer
|
||||||
while (myClient.read() != -1) {
|
while (myClient.read() != -1) {}
|
||||||
}
|
|
||||||
// Now stop the client
|
// Now stop the client
|
||||||
myClient.stop();
|
myClient.stop();
|
||||||
|
|
||||||
@@ -386,7 +373,7 @@ void ModbusServerTCP<ST, CT>::worker(ClientData * myData) {
|
|||||||
|
|
||||||
// receive: get request via Client connection
|
// receive: get request via Client connection
|
||||||
template <typename ST, typename CT>
|
template <typename ST, typename CT>
|
||||||
ModbusMessage ModbusServerTCP<ST, CT>::receive(CT & client, uint32_t timeWait) {
|
ModbusMessage ModbusServerTCP<ST, CT>::receive(CT& client, uint32_t timeWait) {
|
||||||
unsigned long lastMillis = millis(); // Timer to check for timeout
|
unsigned long lastMillis = millis(); // Timer to check for timeout
|
||||||
ModbusMessage m; // to take read data
|
ModbusMessage m; // to take read data
|
||||||
uint16_t lengthVal = 0;
|
uint16_t lengthVal = 0;
|
||||||
@@ -395,13 +382,13 @@ ModbusMessage ModbusServerTCP<ST, CT>::receive(CT & client, uint32_t timeWait) {
|
|||||||
uint8_t buffer[BUFFERSIZE];
|
uint8_t buffer[BUFFERSIZE];
|
||||||
|
|
||||||
// wait for sufficient packet data or timeout
|
// wait for sufficient packet data or timeout
|
||||||
while ((millis() - lastMillis < timeWait) && ((cnt < 6) || (cnt < lengthVal)) && (cnt < BUFFERSIZE)) {
|
while ((millis() - lastMillis < timeWait) && ((cnt < 6) || (cnt < lengthVal)) && (cnt < BUFFERSIZE))
|
||||||
|
{
|
||||||
// Is there data waiting?
|
// Is there data waiting?
|
||||||
if (client.available()) {
|
if (client.available()) {
|
||||||
buffer[cnt] = client.read();
|
buffer[cnt] = client.read();
|
||||||
// Are we at the TCP header length field byte #1?
|
// Are we at the TCP header length field byte #1?
|
||||||
if (cnt == 4)
|
if (cnt == 4) lengthVal = buffer[cnt] << 8;
|
||||||
lengthVal = buffer[cnt] << 8;
|
|
||||||
// Are we at the TCP header length field byte #2?
|
// Are we at the TCP header length field byte #2?
|
||||||
if (cnt == 5) {
|
if (cnt == 5) {
|
||||||
lengthVal |= buffer[cnt];
|
lengthVal |= buffer[cnt];
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ FCType FCT::getType(uint8_t functionCode) {
|
|||||||
return table[functionCode & 0x7F];
|
return table[functionCode & 0x7F];
|
||||||
}
|
}
|
||||||
|
|
||||||
// setType: change the type of a function code.
|
// redefineType: change the type of a function code.
|
||||||
// This is possible only for the codes undefined yet and will return
|
// This is possible only for the codes undefined yet and will return
|
||||||
// the effective type
|
// the effective type
|
||||||
FCType FCT::redefineType(uint8_t functionCode, const FCType type) {
|
FCType FCT::redefineType(uint8_t functionCode, const FCType type) {
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ enum Error : uint8_t {
|
|||||||
UNDEFINED_ERROR = 0xFF // otherwise uncovered communication error
|
UNDEFINED_ERROR = 0xFF // otherwise uncovered communication error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Readable expression for the "illegal" server ID of 0
|
||||||
|
#define ANY_SERVER 0x00
|
||||||
|
|
||||||
#ifndef MINIMAL
|
#ifndef MINIMAL
|
||||||
|
|
||||||
// Constants for float and double re-ordering
|
// Constants for float and double re-ordering
|
||||||
|
|||||||
@@ -670,13 +670,9 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
|||||||
|
|
||||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||||
for (const auto & cf : cmdfunctions_) {
|
|
||||||
if (cf.device_type_ == device_type) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_MODBUS_TIMEOUT
|
#ifndef EMSESP_DEFAULT_MODBUS_TIMEOUT
|
||||||
#define EMSESP_DEFAULT_MODBUS_TIMEOUT 10000
|
#define EMSESP_DEFAULT_MODBUS_TIMEOUT 300
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
monitor_typeids = {0x0A};
|
monitor_typeids = {0x0A};
|
||||||
set_typeids = {};
|
set_typeids = {};
|
||||||
register_telegram_type(monitor_typeids[0], "EasyMonitor", true, MAKE_PF_CB(process_EasyMonitor));
|
register_telegram_type(monitor_typeids[0], "EasyMonitor", true, MAKE_PF_CB(process_EasyMonitor));
|
||||||
|
register_telegram_type(0x02A5, "EasyMonitor", false, MAKE_PF_CB(process_EasyMonitor));
|
||||||
|
|
||||||
// CRF
|
// CRF
|
||||||
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
|
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
|
||||||
@@ -840,13 +841,30 @@ void Thermostat::process_RC20Monitor(std::shared_ptr<const Telegram> telegram) {
|
|||||||
|
|
||||||
// type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
|
// type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
|
||||||
void Thermostat::process_EasyMonitor(std::shared_ptr<const Telegram> telegram) {
|
void Thermostat::process_EasyMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
monitor_typeids[0] = telegram->type_id;
|
||||||
auto hc = heating_circuit(telegram);
|
auto hc = heating_circuit(telegram);
|
||||||
if (hc == nullptr) {
|
if (hc == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (telegram->type_id == 0x0A) {
|
||||||
|
int16_t temp = hc->roomTemp;
|
||||||
|
if (telegram->read_value(temp, 8) && temp != 0) {
|
||||||
has_update(telegram, hc->roomTemp, 8); // is * 100
|
has_update(telegram, hc->roomTemp, 8); // is * 100
|
||||||
has_update(telegram, hc->selTemp, 10); // is * 100
|
has_update(telegram, hc->selTemp, 10); // is * 100
|
||||||
|
toggle_fetch(0x0A, true);
|
||||||
|
}
|
||||||
|
} else if (telegram->type_id == 0x02A5) { // see #2277
|
||||||
|
int16_t temp = hc->roomTemp / 10;
|
||||||
|
if (telegram->read_value(temp, 0)) { // is * 10
|
||||||
|
has_update(hc->roomTemp, temp * 10); // * 100
|
||||||
|
toggle_fetch(0x0A, false);
|
||||||
|
}
|
||||||
|
int16_t sel = hc->selTemp / 50;
|
||||||
|
if (telegram->read_value(sel, 6, 1)) { // is * 2
|
||||||
|
has_update(hc->selTemp, sel * 50); // * 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_ha_climate(hc);
|
add_ha_climate(hc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1547,7 +1547,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
|||||||
json["circuit"] = tag_to_mqtt(dv.tag);
|
json["circuit"] = tag_to_mqtt(dv.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
char val[10];
|
char val[20];
|
||||||
switch (dv.type) {
|
switch (dv.type) {
|
||||||
case DeviceValueType::ENUM: {
|
case DeviceValueType::ENUM: {
|
||||||
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
|
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
|
||||||
@@ -1785,7 +1785,7 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons
|
|||||||
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
|
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
|
||||||
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
|
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
|
||||||
: 0;
|
: 0;
|
||||||
char val[10] = {'\0'};
|
char val[20] = {'\0'};
|
||||||
if (dv.type == DeviceValueType::INT8) {
|
if (dv.type == DeviceValueType::INT8) {
|
||||||
json[name] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
json[name] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||||
} else if (dv.type == DeviceValueType::UINT8) {
|
} else if (dv.type == DeviceValueType::UINT8) {
|
||||||
|
|||||||
@@ -174,6 +174,14 @@ class EMSdevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void has_update(int16_t & value, int16_t newvalue) {
|
||||||
|
if (value != newvalue) {
|
||||||
|
value = newvalue;
|
||||||
|
has_update_ = true;
|
||||||
|
publish_value((void *)&value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void has_update(uint32_t & value, uint32_t newvalue) {
|
void has_update(uint32_t & value, uint32_t newvalue) {
|
||||||
if (value != newvalue) {
|
if (value != newvalue) {
|
||||||
value = newvalue;
|
value = newvalue;
|
||||||
|
|||||||
@@ -1689,7 +1689,7 @@ void EMSESP::start() {
|
|||||||
// start services
|
// start services
|
||||||
if (system_.modbus_enabled()) {
|
if (system_.modbus_enabled()) {
|
||||||
modbus_ = new Modbus;
|
modbus_ = new Modbus;
|
||||||
modbus_->start(1, system_.modbus_port(), system_.modbus_max_clients(), system_.modbus_timeout());
|
modbus_->start(1, system_.modbus_port(), system_.modbus_max_clients(), system_.modbus_timeout() * 1000);
|
||||||
}
|
}
|
||||||
mqtt_.start(); // mqtt init
|
mqtt_.start(); // mqtt init
|
||||||
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
||||||
|
|||||||
@@ -254,13 +254,19 @@ char * Helpers::render_value(char * result, const double value, const int8_t for
|
|||||||
|
|
||||||
char * ret = result;
|
char * ret = result;
|
||||||
double v = value < 0 ? value - 1.0 / (2 * p[format]) : value + 1.0 / (2 * p[format]);
|
double v = value < 0 ? value - 1.0 / (2 * p[format]) : value + 1.0 / (2 * p[format]);
|
||||||
auto whole = (int32_t)v;
|
auto whole = (long long)v;
|
||||||
|
|
||||||
if (whole == 0 && v < 0) {
|
if (whole <= 0 && v < 0) {
|
||||||
result[0] = '-';
|
result[0] = '-';
|
||||||
result++;
|
result++;
|
||||||
|
whole = -whole;
|
||||||
|
v = -v;
|
||||||
}
|
}
|
||||||
itoa(whole, result, 10);
|
#ifndef EMSESP_STANDALONE
|
||||||
|
lltoa(whole, result, 10);
|
||||||
|
#else
|
||||||
|
ultostr(result, whole, 10);
|
||||||
|
#endif
|
||||||
|
|
||||||
while (*result != '\0') {
|
while (*result != '\0') {
|
||||||
result++;
|
result++;
|
||||||
@@ -354,16 +360,16 @@ char * Helpers::render_value(char * result, const uint32_t value, const int8_t f
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (!format) {
|
if (!format) {
|
||||||
strlcpy(result, ltoa(new_value, s, 10), sizeof(s)); // format is 0
|
strlcpy(result, lltoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||||
} else if (format > 0) {
|
} else if (format > 0) {
|
||||||
strlcpy(result, ltoa(new_value / format, s, 10), sizeof(s));
|
strlcpy(result, lltoa(new_value / format, s, 10), sizeof(s));
|
||||||
strlcat(result, ".", sizeof(s));
|
strlcat(result, ".", sizeof(s));
|
||||||
strlcat(result, itoa(((new_value % format) * 10) / format, s, 10), sizeof(s));
|
strlcat(result, itoa(((new_value % format) * 10) / format, s, 10), sizeof(s));
|
||||||
if (format == 100) {
|
if (format == 100) {
|
||||||
strlcat(result, itoa(new_value % 10, s, 10), sizeof(s));
|
strlcat(result, itoa(new_value % 10, s, 10), sizeof(s));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strlcpy(result, ltoa(new_value * format * -1, s, 10), sizeof(s));
|
strlcpy(result, lltoa(new_value * format * -1, s, 10), sizeof(s));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!format) {
|
if (!format) {
|
||||||
@@ -458,26 +464,22 @@ int Helpers::atoint(const char * value) {
|
|||||||
// fahrenheit=0 - off, no conversion
|
// fahrenheit=0 - off, no conversion
|
||||||
// fahrenheit=1 - relative, 1.8t
|
// fahrenheit=1 - relative, 1.8t
|
||||||
// fahrenheit=2 - absolute, 1.8t + 32(fahrenheit-1)
|
// fahrenheit=2 - absolute, 1.8t + 32(fahrenheit-1)
|
||||||
float Helpers::transformNumFloat(float value, const int8_t numeric_operator, const uint8_t fahrenheit) {
|
double Helpers::transformNumFloat(double value, const int8_t numeric_operator, const uint8_t fahrenheit) {
|
||||||
float val;
|
double val;
|
||||||
|
|
||||||
if (numeric_operator == 0) { // DV_NUMOP_NONE
|
if (numeric_operator == 0) { // DV_NUMOP_NONE
|
||||||
val = value * 100 + 0.5;
|
val = value * 100;
|
||||||
} else if (numeric_operator > 0) { // DV_NUMOP_DIVxx
|
} else if (numeric_operator > 0) { // DV_NUMOP_DIVxx
|
||||||
val = value * 100 / numeric_operator + 0.5;
|
val = value * 100 / numeric_operator;
|
||||||
} else { // DV_NUMOP_MULxx
|
} else { // DV_NUMOP_MULxx
|
||||||
val = value * -100 * numeric_operator;
|
val = value * -100 * numeric_operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < 0) { // negative rounding
|
|
||||||
val = val - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fahrenheit) {
|
if (fahrenheit) {
|
||||||
val = val * 1.8 + 3200 * (fahrenheit - 1);
|
val = val * 1.8 + 3200 * (fahrenheit - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((int32_t)val) / 100.0;
|
return (round(val)) / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// abs of a signed 32-bit integer
|
// abs of a signed 32-bit integer
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Helpers {
|
|||||||
static uint16_t string2minutes(const std::string & str);
|
static uint16_t string2minutes(const std::string & str);
|
||||||
static float numericoperator2scalefactor(int8_t numeric_operator);
|
static float numericoperator2scalefactor(int8_t numeric_operator);
|
||||||
|
|
||||||
static float transformNumFloat(float value, const int8_t numeric_operator, const uint8_t fahrenheit = 0);
|
static double transformNumFloat(double value, const int8_t numeric_operator, const uint8_t fahrenheit = 0);
|
||||||
|
|
||||||
static std::string toLower(std::string const & s);
|
static std::string toLower(std::string const & s);
|
||||||
static std::string toUpper(std::string const & s);
|
static std::string toUpper(std::string const & s);
|
||||||
|
|||||||
@@ -414,9 +414,9 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
|
|||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
if (tag < DeviceValueTAG::TAG_HC1) {
|
if (tag < DeviceValueTAG::TAG_HC1) {
|
||||||
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + modbusInfo->short_name;
|
path = std::string("api/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + modbusInfo->short_name;
|
||||||
} else {
|
} else {
|
||||||
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + EMSdevice::tag_to_mqtt(tag) + "/"
|
path = std::string("api/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + EMSdevice::tag_to_mqtt(tag) + "/"
|
||||||
+ modbusInfo->short_name;
|
+ modbusInfo->short_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.7.2-dev.2"
|
#define EMSESP_APP_VERSION "3.7.2-dev.4"
|
||||||
@@ -56,7 +56,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) {
|
|||||||
ei["offset"] = entityItem.offset;
|
ei["offset"] = entityItem.offset;
|
||||||
ei["factor"] = entityItem.factor;
|
ei["factor"] = entityItem.factor;
|
||||||
ei["name"] = entityItem.name;
|
ei["name"] = entityItem.name;
|
||||||
ei["uom"] = entityItem.uom;
|
ei["uom"] = entityItem.value_type == DeviceValueType::BOOL ? 0 : entityItem.uom;
|
||||||
ei["value_type"] = entityItem.value_type;
|
ei["value_type"] = entityItem.value_type;
|
||||||
ei["writeable"] = entityItem.writeable;
|
ei["writeable"] = entityItem.writeable;
|
||||||
EMSESP::webCustomEntityService.render_value(ei, entityItem, true, true);
|
EMSESP::webCustomEntityService.render_value(ei, entityItem, true, true);
|
||||||
@@ -97,7 +97,6 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
|||||||
if (entityItem.ram == 1) {
|
if (entityItem.ram == 1) {
|
||||||
entityItem.device_id = 0;
|
entityItem.device_id = 0;
|
||||||
entityItem.type_id = 0;
|
entityItem.type_id = 0;
|
||||||
entityItem.uom = 0;
|
|
||||||
entityItem.value_type = DeviceValueType::STRING;
|
entityItem.value_type = DeviceValueType::STRING;
|
||||||
entityItem.writeable = true;
|
entityItem.writeable = true;
|
||||||
}
|
}
|
||||||
@@ -107,6 +106,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
|||||||
entityItem.data = "";
|
entityItem.data = "";
|
||||||
} else if (entityItem.value_type == DeviceValueType::BOOL) {
|
} else if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||||
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
|
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
|
||||||
|
entityItem.uom = 0;
|
||||||
} else if (entityItem.value_type == DeviceValueType::INT8) {
|
} else if (entityItem.value_type == DeviceValueType::INT8) {
|
||||||
entityItem.value = EMS_VALUE_DEFAULT_INT8;
|
entityItem.value = EMS_VALUE_DEFAULT_INT8;
|
||||||
} else if (entityItem.value_type == DeviceValueType::UINT8) {
|
} else if (entityItem.value_type == DeviceValueType::UINT8) {
|
||||||
@@ -180,7 +180,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
|||||||
if (!Helpers::value2bool(value, v)) {
|
if (!Helpers::value2bool(value, v)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v ? 0xFF : 0, 0);
|
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v ? (uint8_t)entityItem.factor : 0, 0);
|
||||||
} else {
|
} else {
|
||||||
float f;
|
float f;
|
||||||
if (!Helpers::value2float(value, f)) {
|
if (!Helpers::value2float(value, f)) {
|
||||||
@@ -214,7 +214,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
|||||||
// output of a single value
|
// output of a single value
|
||||||
// if add_uom is true it will add the UOM string to the value
|
// if add_uom is true it will add the UOM string to the value
|
||||||
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem & entity, const bool useVal, const bool web, const bool add_uom) {
|
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem & entity, const bool useVal, const bool web, const bool add_uom) {
|
||||||
char payload[12];
|
char payload[20];
|
||||||
std::string name = useVal ? "value" : entity.name;
|
std::string name = useVal ? "value" : entity.name;
|
||||||
switch (entity.value_type) {
|
switch (entity.value_type) {
|
||||||
case DeviceValueType::BOOL:
|
case DeviceValueType::BOOL:
|
||||||
@@ -266,7 +266,7 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem &
|
|||||||
default:
|
default:
|
||||||
// if no type treat it as a string
|
// if no type treat it as a string
|
||||||
if (entity.data.length() > 0) {
|
if (entity.data.length() > 0) {
|
||||||
output[name] = entity.data;
|
output[name] = add_uom ? entity.data + ' ' + EMSdevice::uom_to_string(entity.uom) : entity.data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -490,13 +490,15 @@ void WebCustomEntityService::generate_value_web(JsonObject output, const bool is
|
|||||||
}
|
}
|
||||||
|
|
||||||
obj["id"] = "00" + entity.name;
|
obj["id"] = "00" + entity.name;
|
||||||
|
if (entity.value_type != DeviceValueType::BOOL) {
|
||||||
obj["u"] = entity.uom;
|
obj["u"] = entity.uom;
|
||||||
|
}
|
||||||
|
|
||||||
if (entity.writeable) {
|
if (entity.writeable) {
|
||||||
obj["c"] = entity.name;
|
obj["c"] = entity.name;
|
||||||
include = true;
|
include = true;
|
||||||
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
|
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
|
||||||
char s[10];
|
char s[20];
|
||||||
obj["s"] = Helpers::render_value(s, entity.factor, 1);
|
obj["s"] = Helpers::render_value(s, entity.factor, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,6 +632,10 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
|
|||||||
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
|
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
|
||||||
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
||||||
}
|
}
|
||||||
|
// mask bits for bool values
|
||||||
|
if (entity.value_type == DeviceValueType::BOOL && entity.factor > 0) {
|
||||||
|
value = (value & (uint8_t)entity.factor) ? 1 : 0;
|
||||||
|
}
|
||||||
if (value != entity.value) {
|
if (value != entity.value) {
|
||||||
entity.value = value;
|
entity.value = value;
|
||||||
if (Mqtt::publish_single()) {
|
if (Mqtt::publish_single()) {
|
||||||
|
|||||||
@@ -267,10 +267,10 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
|||||||
if (data.is<const char *>()) {
|
if (data.is<const char *>()) {
|
||||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
||||||
} else if (data.is<int>()) {
|
} else if (data.is<int>()) {
|
||||||
char s[10];
|
char s[20];
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int32_t>(), 0), true, id, output);
|
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int32_t>(), 0), true, id, output);
|
||||||
} else if (data.is<float>()) {
|
} else if (data.is<float>()) {
|
||||||
char s[10];
|
char s[20];
|
||||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
||||||
} else if (data.is<bool>()) {
|
} else if (data.is<bool>()) {
|
||||||
return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output);
|
return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output);
|
||||||
|
|||||||
Reference in New Issue
Block a user