fix/refactor: modbus form (#28226)
Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
parent
62eb3be00a
commit
452a1301b1
4 changed files with 192 additions and 75 deletions
|
|
@ -46,7 +46,7 @@
|
|||
class="me-2"
|
||||
required
|
||||
:model-value="id || defaultId"
|
||||
@input="$emit('update:id', $event.target.value)"
|
||||
@update:model-value="(v) => $emit('update:id', v)"
|
||||
/>
|
||||
</FormRow>
|
||||
<div v-if="connection === MODBUS_CONNECTION.TCPIP">
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
class="me-2"
|
||||
required
|
||||
:model-value="host"
|
||||
@input="$emit('update:host', $event.target.value)"
|
||||
@update:model-value="(v) => $emit('update:host', v)"
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow :id="formId('modbusPort')" :label="$t('config.modbus.port')">
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
class="me-2 w-50"
|
||||
required
|
||||
:model-value="port || defaultPort"
|
||||
@input="$emit('update:port', $event.target.value)"
|
||||
@update:model-value="(v) => $emit('update:port', v)"
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow
|
||||
|
|
@ -142,7 +142,7 @@
|
|||
:choice="baudrateOptions"
|
||||
required
|
||||
:model-value="baudrate || defaultBaudrate"
|
||||
@input="$emit('update:baudrate', parseInt($event.target.value))"
|
||||
@update:model-value="(v) => $emit('update:baudrate', parseInt(v))"
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow :id="formId('modbusComset')" :label="$t('config.modbus.comset')">
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
:choice="comsetOptions"
|
||||
required
|
||||
:model-value="comset || defaultComset"
|
||||
@input="$emit('update:comset', $event.target.value)"
|
||||
@update:model-value="(v) => $emit('update:comset', v)"
|
||||
/>
|
||||
</FormRow>
|
||||
</div>
|
||||
|
|
@ -263,9 +263,9 @@ export default defineComponent({
|
|||
this.setProtocolByCapabilities(newValue);
|
||||
this.$emit("update:modbus", this.selectedModbus);
|
||||
},
|
||||
modbus(newValue: MODBUS_TYPE) {
|
||||
modbus(newValue: MODBUS_TYPE, oldValue: MODBUS_TYPE) {
|
||||
if (newValue) {
|
||||
this.setConnectionAndProtocolByModbus(newValue);
|
||||
this.setConnectionAndProtocolByModbus(newValue, oldValue);
|
||||
}
|
||||
},
|
||||
connection(newValue: MODBUS_CONNECTION, oldValue: MODBUS_CONNECTION) {
|
||||
|
|
@ -298,8 +298,8 @@ export default defineComponent({
|
|||
? MODBUS_PROTOCOL.TCP
|
||||
: MODBUS_PROTOCOL.RTU;
|
||||
},
|
||||
setConnectionAndProtocolByModbus(modbus?: MODBUS_TYPE) {
|
||||
switch (modbus) {
|
||||
setConnectionAndProtocolByModbus(newModbus?: MODBUS_TYPE, oldModbus?: MODBUS_TYPE) {
|
||||
switch (newModbus) {
|
||||
case MODBUS_TYPE.RS485_SERIAL:
|
||||
this.connection = MODBUS_CONNECTION.SERIAL;
|
||||
this.protocol = MODBUS_PROTOCOL.RTU;
|
||||
|
|
@ -313,6 +313,11 @@ export default defineComponent({
|
|||
this.protocol = MODBUS_PROTOCOL.TCP;
|
||||
break;
|
||||
}
|
||||
|
||||
// when switching from serial to TCP/IP, default protocol to Modbus-TCP (rtu=false)
|
||||
if (oldModbus === MODBUS_TYPE.RS485_SERIAL && newModbus === MODBUS_TYPE.RS485_TCPIP) {
|
||||
this.protocol = MODBUS_PROTOCOL.TCP;
|
||||
}
|
||||
},
|
||||
formId(name: string): string {
|
||||
return `${name}-${this.componentId}`;
|
||||
|
|
|
|||
126
assets/js/components/Config/ModbusProxyConnection.vue
Normal file
126
assets/js/components/Config/ModbusProxyConnection.vue
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<Modbus
|
||||
v-model:baudrate="localConnection.settings.baudrate"
|
||||
v-model:comset="localConnection.settings.comset"
|
||||
v-model:device="localConnection.settings.device"
|
||||
:component-id="`proxy-${index}`"
|
||||
:host="getHost(localConnection.settings.uri)"
|
||||
:port="getPort(localConnection.settings.uri)"
|
||||
:capabilities="['rs485', 'tcpip']"
|
||||
hide-modbus-id
|
||||
:default-baudrate="DEFAULT_BAUDRATE"
|
||||
:default-comset="DEFAULT_COMSET"
|
||||
:default-port="DEFAULT_PORT"
|
||||
:modbus="initialModbusType"
|
||||
@update:host="(host) => updateHost(host)"
|
||||
@update:port="(port) => updatePort(port)"
|
||||
@update:modbus="(modbus) => updateModbus(modbus)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import Modbus from "./DeviceModal/Modbus.vue";
|
||||
import {
|
||||
MODBUS_BAUDRATE,
|
||||
MODBUS_COMSET,
|
||||
MODBUS_TYPE,
|
||||
type ModbusProxy,
|
||||
type ModbusProxySettings,
|
||||
} from "@/types/evcc";
|
||||
import deepClone from "@/utils/deepClone";
|
||||
|
||||
export const DEFAULT_BAUDRATE = MODBUS_BAUDRATE._9600;
|
||||
export const DEFAULT_COMSET = MODBUS_COMSET._8N1;
|
||||
export const DEFAULT_PORT = 502;
|
||||
|
||||
function getModbusType(s: ModbusProxySettings) {
|
||||
if (s.device) {
|
||||
return MODBUS_TYPE.RS485_SERIAL;
|
||||
}
|
||||
return s.rtu ? MODBUS_TYPE.RS485_TCPIP : MODBUS_TYPE.TCPIP;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "ModbusProxyConnection",
|
||||
components: { Modbus },
|
||||
props: {
|
||||
connection: {
|
||||
type: Object as () => ModbusProxy,
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:connection"],
|
||||
data() {
|
||||
return {
|
||||
DEFAULT_BAUDRATE,
|
||||
DEFAULT_COMSET,
|
||||
DEFAULT_PORT,
|
||||
localConnection: deepClone(this.connection),
|
||||
initialModbusType: getModbusType(this.connection.settings),
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
localConnection: {
|
||||
handler(newVal: ModbusProxy) {
|
||||
if (newVal) {
|
||||
this.$emit("update:connection", newVal);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getHost(uri?: string) {
|
||||
return uri?.split(":")[0] || "";
|
||||
},
|
||||
getPort(uri?: string) {
|
||||
return uri?.split(":")[1] || "";
|
||||
},
|
||||
updateHost(newHost?: string) {
|
||||
const port = this.getPort(this.localConnection.settings.uri);
|
||||
|
||||
if (port === "" && newHost === undefined) {
|
||||
this.localConnection.settings.uri = undefined;
|
||||
} else {
|
||||
this.localConnection.settings.uri = `${newHost === undefined ? "" : newHost}:${port}`;
|
||||
}
|
||||
},
|
||||
updatePort(newPort?: string) {
|
||||
const host = this.getHost(this.localConnection.settings.uri);
|
||||
if (host === "" && newPort === undefined) {
|
||||
this.localConnection.settings.uri = undefined;
|
||||
} else {
|
||||
this.localConnection.settings.uri = `${host}:${newPort === undefined ? "" : newPort}`;
|
||||
}
|
||||
},
|
||||
updateModbus(modbus: MODBUS_TYPE) {
|
||||
this.initialModbusType = modbus;
|
||||
|
||||
switch (modbus) {
|
||||
case MODBUS_TYPE.RS485_SERIAL:
|
||||
this.localConnection.settings.uri = undefined;
|
||||
this.localConnection.settings.rtu = undefined;
|
||||
if (!this.localConnection.settings.baudrate) {
|
||||
this.localConnection.settings.baudrate = DEFAULT_BAUDRATE;
|
||||
}
|
||||
if (!this.localConnection.settings.comset) {
|
||||
this.localConnection.settings.comset = DEFAULT_COMSET;
|
||||
}
|
||||
break;
|
||||
case MODBUS_TYPE.RS485_TCPIP:
|
||||
case MODBUS_TYPE.TCPIP:
|
||||
this.localConnection.settings.device = undefined;
|
||||
this.localConnection.settings.baudrate = undefined;
|
||||
this.localConnection.settings.comset = undefined;
|
||||
this.localConnection.settings.rtu = modbus === MODBUS_TYPE.RS485_TCPIP;
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
@ -85,19 +85,12 @@
|
|||
</div>
|
||||
</h5>
|
||||
</div>
|
||||
<Modbus
|
||||
v-model:baudrate="c.settings.baudrate"
|
||||
v-model:comset="c.settings.comset"
|
||||
v-model:device="c.settings.device"
|
||||
:modbus="getModbus(c.settings)"
|
||||
:component-id="`proxy-${index}`"
|
||||
:host="getHost(c.settings.uri)"
|
||||
:port="getPort(c.settings.uri)"
|
||||
:capabilities="['rs485', 'tcpip']"
|
||||
hide-modbus-id
|
||||
@update:host="(host) => updateHost(host, c.settings)"
|
||||
@update:port="(port) => updatePort(port, c.settings)"
|
||||
@update:modbus="(modbus) => updateModbus(c.settings, modbus)"
|
||||
<ModbusProxyConnection
|
||||
:connection="c"
|
||||
:index="index"
|
||||
@update:connection="
|
||||
(connection) => (values[index] = connection)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -143,17 +136,13 @@ import "@h2d2/shopicons/es/regular/arrowdown";
|
|||
import "@h2d2/shopicons/es/regular/plus";
|
||||
import "@h2d2/shopicons/es/regular/trash";
|
||||
import JsonModal from "./JsonModal.vue";
|
||||
import {
|
||||
MODBUS_COMSET,
|
||||
MODBUS_CONNECTION,
|
||||
MODBUS_PROTOCOL,
|
||||
MODBUS_PROXY_READONLY,
|
||||
MODBUS_TYPE,
|
||||
type ModbusProxy,
|
||||
type ModbusProxySettings,
|
||||
} from "@/types/evcc";
|
||||
import { MODBUS_PROXY_READONLY, type ModbusProxy } from "@/types/evcc";
|
||||
import ASCII_DIAGRAM from "./modbus-diagram.txt?raw";
|
||||
import Modbus from "./DeviceModal/Modbus.vue";
|
||||
import ModbusProxyConnection, {
|
||||
DEFAULT_BAUDRATE,
|
||||
DEFAULT_COMSET,
|
||||
DEFAULT_PORT,
|
||||
} from "./ModbusProxyConnection.vue";
|
||||
import PropertyField from "./PropertyField.vue";
|
||||
import FormRow from "./FormRow.vue";
|
||||
import SponsorTokenRequired from "./DeviceModal/SponsorTokenRequired.vue";
|
||||
|
|
@ -162,7 +151,14 @@ import { defineComponent } from "vue";
|
|||
|
||||
export default defineComponent({
|
||||
name: "ModbusProxyModal",
|
||||
components: { JsonModal, Modbus, FormRow, PropertyField, SponsorTokenRequired, SelectGroup },
|
||||
components: {
|
||||
JsonModal,
|
||||
ModbusProxyConnection,
|
||||
FormRow,
|
||||
PropertyField,
|
||||
SponsorTokenRequired,
|
||||
SelectGroup,
|
||||
},
|
||||
props: {
|
||||
isSponsor: Boolean,
|
||||
},
|
||||
|
|
@ -170,10 +166,6 @@ export default defineComponent({
|
|||
data() {
|
||||
return {
|
||||
ASCII_DIAGRAM,
|
||||
MODBUS_PROXY_READONLY,
|
||||
MODBUS_CONNECTION,
|
||||
MODBUS_PROTOCOL,
|
||||
MODBUS_TYPE,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -188,12 +180,6 @@ export default defineComponent({
|
|||
formId(index: number, name: string) {
|
||||
return `modbusproxy-connection-${index}-${name}`;
|
||||
},
|
||||
getModbus(s: ModbusProxySettings) {
|
||||
if (s.device) {
|
||||
return MODBUS_TYPE.RS485_SERIAL;
|
||||
}
|
||||
return s.rtu ? MODBUS_TYPE.RS485_TCPIP : MODBUS_TYPE.TCPIP;
|
||||
},
|
||||
getReadonlyHelp(readonly = MODBUS_PROXY_READONLY.FALSE): string {
|
||||
return this.$t(`config.modbusproxy.readonly.help.${readonly}`);
|
||||
},
|
||||
|
|
@ -203,41 +189,12 @@ export default defineComponent({
|
|||
port: highestPort + 1,
|
||||
readonly: MODBUS_PROXY_READONLY.FALSE,
|
||||
settings: {
|
||||
uri: ":502",
|
||||
baudrate: 9600,
|
||||
comset: "8N1" as MODBUS_COMSET,
|
||||
uri: `:${DEFAULT_PORT}`,
|
||||
baudrate: DEFAULT_BAUDRATE,
|
||||
comset: DEFAULT_COMSET,
|
||||
},
|
||||
});
|
||||
},
|
||||
getHost(uri?: string) {
|
||||
return uri?.split(":")[0] || "";
|
||||
},
|
||||
getPort(uri?: string) {
|
||||
return uri?.split(":")[1] || "";
|
||||
},
|
||||
updateHost(newHost: string, settings: ModbusProxySettings) {
|
||||
const port = this.getPort(settings.uri);
|
||||
settings.uri = `${newHost}:${port}`;
|
||||
},
|
||||
updatePort(newPort: string | number, settings: ModbusProxySettings) {
|
||||
const host = this.getHost(settings.uri);
|
||||
settings.uri = `${host}:${newPort}`;
|
||||
},
|
||||
updateModbus(settings: ModbusProxySettings, modbus: MODBUS_TYPE) {
|
||||
switch (modbus) {
|
||||
case MODBUS_TYPE.RS485_SERIAL:
|
||||
settings.uri = undefined;
|
||||
settings.rtu = undefined;
|
||||
break;
|
||||
case MODBUS_TYPE.RS485_TCPIP:
|
||||
case MODBUS_TYPE.TCPIP:
|
||||
settings.device = undefined;
|
||||
settings.baudrate = undefined;
|
||||
settings.comset = undefined;
|
||||
settings.rtu = modbus === MODBUS_TYPE.RS485_TCPIP;
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -87,6 +87,35 @@ test.describe("modbusproxy", async () => {
|
|||
await expect(modal).not.toContainText("Connection #1");
|
||||
});
|
||||
|
||||
test("modbusproxy serial defaults", async ({ page }) => {
|
||||
await start();
|
||||
await page.goto("/#/config");
|
||||
|
||||
const modbusproxyCard = page.getByTestId("modbusproxy");
|
||||
await modbusproxyCard.getByRole("button", { name: "edit" }).click();
|
||||
const modal = page.getByTestId("modbusproxy-modal");
|
||||
await expectModalVisible(modal);
|
||||
|
||||
await modal.getByRole("button", { name: "Add proxy connection" }).click();
|
||||
const deviceBox = modal.getByTestId("device-box");
|
||||
|
||||
await expect(deviceBox.getByLabel("Network")).toBeChecked();
|
||||
await expect(deviceBox.getByLabel("Port")).toHaveValue("502");
|
||||
|
||||
// switch to RS485
|
||||
await deviceBox.getByText("RS485", { exact: true }).click();
|
||||
await expect(deviceBox.getByLabel("Baud rate")).toHaveValue("9600");
|
||||
await expect(deviceBox.getByLabel("ComSet")).toHaveValue("8N1");
|
||||
|
||||
await deviceBox.getByLabel("Baud rate").selectOption("19200");
|
||||
|
||||
// switch to Network and back: defaults restored
|
||||
await deviceBox.getByText("Network").click();
|
||||
await deviceBox.getByText("RS485", { exact: true }).click();
|
||||
await expect(deviceBox.getByLabel("Baud rate")).toHaveValue("9600");
|
||||
await expect(deviceBox.getByLabel("ComSet")).toHaveValue("8N1");
|
||||
});
|
||||
|
||||
test("modbusproxy via db (yaml to json migration)", async ({ page }) => {
|
||||
await start(undefined, CONFIG_MODBUSPROXY_MIGRATE);
|
||||
await page.goto("/#/config");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue