implement driver add update remove and improve rename tree experience
This commit is contained in:
@@ -3,10 +3,9 @@ import axios from 'axios';
|
||||
|
||||
const host = window.location.hostname;
|
||||
const port = 8100;
|
||||
const baseURL = `http://${host}:${port}`;
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: baseURL,
|
||||
baseURL: `http://${host}:${port}`,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
@@ -4,11 +4,8 @@ import { initWebSocket } from '../vueLib/services/websocket';
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const $q = app.config.globalProperties.$q as QVueGlobals;
|
||||
const host = window.location.hostname;
|
||||
const port = 8100;
|
||||
|
||||
const randomId = Math.floor(Math.random() * 10001); // random number from 0 to 10000
|
||||
const ws = initWebSocket(`ws://${host}:${port}/ws?id=q${randomId}`, $q);
|
||||
const ws = initWebSocket(window.location.hostname, 8100, $q);
|
||||
|
||||
app.config.globalProperties.$socket = ws;
|
||||
ws.connect();
|
||||
|
@@ -66,18 +66,14 @@ const internalShowDialog = ref(props.showDialog);
|
||||
watch(
|
||||
() => props.showDialog,
|
||||
(newValue) => {
|
||||
console.log('watch showDialog', newValue);
|
||||
internalShowDialog.value = newValue;
|
||||
},
|
||||
);
|
||||
watch(internalShowDialog, (newValue) => {
|
||||
console.log('watch internalShowDialog', newValue);
|
||||
emit('update:showDialog', newValue);
|
||||
if (!newValue) {
|
||||
console.log('emit cancel');
|
||||
emit('cancel');
|
||||
} else {
|
||||
console.log('emit confirmed');
|
||||
emit('confirmed');
|
||||
}
|
||||
});
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<div
|
||||
:class="[
|
||||
'text-left',
|
||||
!props.row.path.includes('System') && props.row.path !== 'DBM'
|
||||
props.row.path?.split(':')[0] !== 'System' && props.row.path !== 'DBM'
|
||||
? 'cursor-pointer'
|
||||
: '',
|
||||
'q-mx-sm',
|
||||
@@ -32,7 +32,7 @@
|
||||
<div
|
||||
:class="[
|
||||
'text-center',
|
||||
!props.row.path.includes('System') && props.row.path !== 'DBM'
|
||||
props.row.path?.split(':')[0] !== 'System' && props.row.path !== 'DBM'
|
||||
? 'cursor-pointer'
|
||||
: '',
|
||||
'q-mx-sm',
|
||||
@@ -43,15 +43,18 @@
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-value="props">
|
||||
<q-td :props="props" @click="openDialog(props.row)">
|
||||
<q-td :props="props" @click="openDialog(props.row, 'value')">
|
||||
<div :class="['text-center', 'cursor-pointer', 'q-mx-sm']">
|
||||
{{ props.row.value }}
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-drivers="props">
|
||||
<q-td :props="props" @click="openDialog(props.row, 'driver')">
|
||||
<div v-if="props.row.type !== 'none'" :class="['cursor-pointer']">
|
||||
<q-td :props="props" @click="openDialog(props.row, 'drivers')">
|
||||
<div
|
||||
v-if="props.row.type !== 'NONE' || props.row.path?.split(':')[0] !== 'System'"
|
||||
:class="['cursor-pointer']"
|
||||
>
|
||||
<q-icon size="sm" name="cell_tower" :color="props.row.drivers ? 'blue-5' : 'grey-4'" />
|
||||
</div>
|
||||
</q-td>
|
||||
@@ -59,6 +62,7 @@
|
||||
</q-table>
|
||||
<RenameDialog width="400px" button-ok-label="Rename" ref="renameDialog" />
|
||||
<UpdateDialog width="400px" button-ok-label="Write" ref="updateDialog" />
|
||||
<UpdateDriver width="400px" ref="updateDriverDialog" />
|
||||
<UpdateDatatype
|
||||
width="400px"
|
||||
button-ok-label="Update"
|
||||
@@ -72,6 +76,7 @@
|
||||
import UpdateDialog from './dialog/UpdateValueDialog.vue';
|
||||
import RenameDialog from './dialog/RenameDatapoint.vue';
|
||||
import UpdateDatatype from './dialog/UpdateDatatype.vue';
|
||||
import UpdateDriver from './dialog/UpdateDriverDialog.vue';
|
||||
import type { QTableProps } from 'quasar';
|
||||
import type { Subscribe } from '../models/Subscribe';
|
||||
import { computed, ref } from 'vue';
|
||||
@@ -80,10 +85,12 @@ import { convertFromType } from './Datapoint';
|
||||
|
||||
const renameDialog = ref();
|
||||
const updateDialog = ref();
|
||||
const updateDriverDialog = ref();
|
||||
const updateDatatype = ref();
|
||||
|
||||
const openDialog = (sub: Subscribe, type?: string) => {
|
||||
if (sub.path?.includes('System') || sub.path === 'DBM') return;
|
||||
console.log(11, sub);
|
||||
if (sub.path?.split(':')[0] === 'System' && sub.path !== 'DBM') return;
|
||||
switch (type) {
|
||||
case 'type':
|
||||
updateDatatype.value.open(sub.uuid);
|
||||
@@ -91,10 +98,14 @@ const openDialog = (sub: Subscribe, type?: string) => {
|
||||
case 'rename':
|
||||
renameDialog.value.open(sub.uuid);
|
||||
break;
|
||||
default:
|
||||
if (sub.type === 'none') return;
|
||||
case 'value':
|
||||
if (sub.type === 'NONE') return;
|
||||
updateDialog.value?.open(ref(sub), type);
|
||||
break;
|
||||
case 'drivers':
|
||||
if (sub.type === 'NONE') return;
|
||||
updateDriverDialog.value?.open(sub);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<q-input
|
||||
class="q-mt-lg q-mb-none q-pl-lg q-pr-xl"
|
||||
filled
|
||||
v-model="path"
|
||||
v-model="addingForm.path"
|
||||
label=""
|
||||
:rules="[(val) => !!val || 'Path is required']"
|
||||
>
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="column">
|
||||
<span class="text-caption text-primary non-editable-prefix">Path *</span>
|
||||
<span class="text-body2 text-grey-6 non-editable-prefix"
|
||||
>{{ prefix }}{{ staticPrefix }}</span
|
||||
>{{ addingForm.prefix }}{{ addingForm.staticPrefix }}</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
@@ -26,8 +26,8 @@
|
||||
<DataTypes class="q-mt-lg q-pl-md q-pr-xl" flat v-model:datatype="datatype"></DataTypes>
|
||||
<div class="q-pl-lg">
|
||||
<div class="text-grey text-bold">Read Write Access</div>
|
||||
<q-checkbox v-model="read">Read</q-checkbox>
|
||||
<q-checkbox v-model="write">Write</q-checkbox>
|
||||
<q-checkbox v-model="addingForm.read">Read</q-checkbox>
|
||||
<q-checkbox v-model="addingForm.write">Write</q-checkbox>
|
||||
</div>
|
||||
<q-input
|
||||
:type="valueType"
|
||||
@@ -35,7 +35,7 @@
|
||||
label="Value"
|
||||
class="q-pl-md q-pr-xl"
|
||||
filled
|
||||
v-model="value"
|
||||
v-model="addingForm.value"
|
||||
></q-input>
|
||||
<q-btn no-caps class="q-mb-xl q-mx-xl q-px-lg" @click="onSubmit" color="primary">{{
|
||||
props.buttonOkLabel
|
||||
@@ -45,7 +45,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import DialogFrame from '../../dialog/DialogFrame.vue';
|
||||
import { useNotify } from '../../general/useNotify';
|
||||
import DataTypes from '../../buttons/DataTypes.vue';
|
||||
@@ -56,16 +56,21 @@ import { convertToType } from '../Datapoint';
|
||||
import { catchError } from 'src/vueLib/models/error';
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
|
||||
const addingForm = reactive({
|
||||
path: '',
|
||||
value: '',
|
||||
staticPrefix: '',
|
||||
read: true,
|
||||
write: true,
|
||||
prefix: 'DBM:',
|
||||
});
|
||||
const Dialog = ref();
|
||||
const path = ref('');
|
||||
const staticPrefix = ref('');
|
||||
const value = ref('');
|
||||
|
||||
const valueType = ref<'text' | 'number'>('text');
|
||||
const read = ref(true);
|
||||
const write = ref(true);
|
||||
|
||||
const datatype = ref('None');
|
||||
const addForm = ref();
|
||||
const prefix = 'DBM:';
|
||||
|
||||
const open = (uuid: string) => {
|
||||
Dialog.value?.open();
|
||||
@@ -84,15 +89,15 @@ function onSubmit() {
|
||||
if (success) {
|
||||
type = convertToType(datatype.value);
|
||||
|
||||
if (read.value) access = 'R';
|
||||
if (write.value) access += 'W';
|
||||
if (addingForm.read) access = 'R';
|
||||
if (addingForm.write) access += 'W';
|
||||
if (access == '') access = 'R';
|
||||
|
||||
setRequest(staticPrefix.value + path.value, type, value.value, access)
|
||||
setRequest(addingForm.staticPrefix + addingForm.path, type, addingForm.value, access)
|
||||
.then((respond) => {
|
||||
if (respond) {
|
||||
respond.forEach((set) => {
|
||||
NotifyResponse("Datapoint '" + prefix + set.path + "' added");
|
||||
NotifyResponse("Datapoint '" + addingForm.prefix + set.path + "' added");
|
||||
});
|
||||
addRawSubscription(respond[0]);
|
||||
UpdateTable();
|
||||
@@ -102,7 +107,7 @@ function onSubmit() {
|
||||
NotifyResponse(catchError(err), 'error');
|
||||
});
|
||||
} else {
|
||||
if (path.value === '') {
|
||||
if (addingForm.path === '') {
|
||||
NotifyResponse("Field 'Path' is requierd", 'error');
|
||||
return;
|
||||
} else NotifyResponse('Form not validated', 'error');
|
||||
@@ -133,8 +138,8 @@ function getDatapoint(uuid: string) {
|
||||
getRequest(uuid, '', 1)
|
||||
.then((resp) => {
|
||||
if (resp[0]) {
|
||||
staticPrefix.value = resp[0].path ?? '';
|
||||
if (staticPrefix.value !== '') staticPrefix.value += ':';
|
||||
addingForm.staticPrefix = resp[0].path ?? '';
|
||||
if (addingForm.staticPrefix !== '') addingForm.staticPrefix += ':';
|
||||
}
|
||||
})
|
||||
.catch((err) => NotifyResponse(catchError(err), 'error'));
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<q-input
|
||||
class="q-mt-lg q-mb-none q-pl-md q-mx-lg"
|
||||
filled
|
||||
v-model="path"
|
||||
v-model="copyData.path"
|
||||
label="Current Path"
|
||||
label-color="primary"
|
||||
readonly
|
||||
@@ -19,7 +19,7 @@
|
||||
<q-input
|
||||
class="q-mt-lg q-mt-none q-pl-md q-mx-lg"
|
||||
filled
|
||||
v-model="copyPath"
|
||||
v-model="copyData.copyPath"
|
||||
label="New Path *"
|
||||
label-color="primary"
|
||||
@keyup.enter="onSubmit"
|
||||
@@ -36,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import DialogFrame from '../../dialog/DialogFrame.vue';
|
||||
import { useNotify } from '../../general/useNotify';
|
||||
import { getRequest, setsRequest } from 'src/vueLib/models/Request';
|
||||
@@ -44,11 +44,14 @@ import { datapointRequestForCopy } from '../Datapoint';
|
||||
import { catchError } from 'src/vueLib/models/error';
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
const copyData = reactive({
|
||||
path: '',
|
||||
copyPath: '',
|
||||
prefix: 'DBM:',
|
||||
});
|
||||
|
||||
const Dialog = ref();
|
||||
const path = ref('');
|
||||
const copyPath = ref('');
|
||||
const copyForm = ref();
|
||||
const prefix = 'DBM:';
|
||||
|
||||
const open = (uuid: string) => {
|
||||
Dialog.value?.open();
|
||||
@@ -58,18 +61,18 @@ const open = (uuid: string) => {
|
||||
function onSubmit() {
|
||||
copyForm.value.validate().then((success: undefined) => {
|
||||
if (success) {
|
||||
if (copyPath.value === path.value) {
|
||||
if (copyData.copyPath === copyData.path) {
|
||||
NotifyResponse('copy path can not be the same as current path', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const absolutePath = path.value.slice(prefix.length);
|
||||
const absolutecopyPath = copyPath.value.slice(prefix.length);
|
||||
const absolutePath = copyData.path.slice(copyData.prefix.length);
|
||||
const absolutecopyPath = copyData.copyPath.slice(copyData.prefix.length);
|
||||
|
||||
getRequest('', absolutecopyPath, 1)
|
||||
.then((response) => {
|
||||
if (response?.length > 0) {
|
||||
NotifyResponse("path '" + copyPath.value + "' already exists", 'warning');
|
||||
NotifyResponse("path '" + copyData.copyPath + "' already exists", 'warning');
|
||||
return;
|
||||
}
|
||||
})
|
||||
@@ -80,7 +83,7 @@ function onSubmit() {
|
||||
setsRequest(
|
||||
datapointRequestForCopy(response, absolutePath, absolutecopyPath),
|
||||
).catch((err) => console.error(err));
|
||||
NotifyResponse(copyPath.value + ' copied');
|
||||
NotifyResponse(copyData.copyPath + ' copied');
|
||||
})
|
||||
.catch((err) => NotifyResponse(catchError(err), 'error'));
|
||||
} else {
|
||||
@@ -89,7 +92,7 @@ function onSubmit() {
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
if (copyPath.value === '') {
|
||||
if (copyData.copyPath === '') {
|
||||
NotifyResponse("Field 'New Path' is requierd", 'error');
|
||||
return;
|
||||
} else NotifyResponse('Form not validated', 'error');
|
||||
@@ -120,8 +123,8 @@ function getDatapoint(uuid: string) {
|
||||
getRequest(uuid)
|
||||
.then((resp) => {
|
||||
if (resp[0]) {
|
||||
path.value = prefix + resp[0].path;
|
||||
copyPath.value = prefix + resp[0].path;
|
||||
copyData.path = copyData.prefix + resp[0].path;
|
||||
copyData.copyPath = copyData.prefix + resp[0].path;
|
||||
}
|
||||
})
|
||||
.catch((err) => NotifyResponse(catchError(err), 'error'));
|
||||
|
226
src/vueLib/dbm/dialog/DriverDialog.vue
Normal file
226
src/vueLib/dbm/dialog/DriverDialog.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<DialogFrame ref="Dialog" :width="props.width" :header-title="localDialogLabel">
|
||||
<q-card-section
|
||||
v-if="props.dialogLabel || localDialogLabel"
|
||||
class="text-bold text-left q-mb-none q-pb-none"
|
||||
:class="'text-' + props.labelColor"
|
||||
>{{ props.dialogLabel || localDialogLabel }}</q-card-section
|
||||
>
|
||||
<q-card-section v-if="props.text" class="text-center" style="white-space: pre-line">{{
|
||||
props.text
|
||||
}}</q-card-section>
|
||||
<q-form ref="form">
|
||||
<q-card-section class="q-gutter-xs row q-col-gutter-xs">
|
||||
<div>
|
||||
<q-select
|
||||
class="col-8"
|
||||
filled
|
||||
label="Driver Name"
|
||||
type="text"
|
||||
name="Type"
|
||||
dense
|
||||
:options="options"
|
||||
:rules="[(val) => !!val || 'Name is required']"
|
||||
v-model="driverForm.type"
|
||||
/>
|
||||
<q-input
|
||||
class="col-8"
|
||||
filled
|
||||
label="Bus"
|
||||
type="text"
|
||||
name="Bus"
|
||||
dense
|
||||
:rules="[(val) => !!val || 'Bus is required']"
|
||||
v-model="driverForm.bus"
|
||||
/>
|
||||
<q-input
|
||||
v-if="driverForm.isAddress"
|
||||
class="col-8"
|
||||
filled
|
||||
dense
|
||||
label="Address"
|
||||
type="number"
|
||||
name="Address"
|
||||
v-model.number="driverForm.address"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!driverForm.isAddress" class="q-gutter-xs row q-col-gutter-xs">
|
||||
<q-input
|
||||
class="col-8"
|
||||
filled
|
||||
dense
|
||||
label="Subscribe"
|
||||
type="text"
|
||||
name="Address"
|
||||
v-model="driverForm.subscribe"
|
||||
/>
|
||||
<q-input
|
||||
class="col-8"
|
||||
filled
|
||||
dense
|
||||
label="Publish"
|
||||
type="text"
|
||||
name="Address"
|
||||
v-model="driverForm.publish"
|
||||
/>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-form>
|
||||
<q-card-actions class="text-primary">
|
||||
<q-btn v-if="props.buttonCancelLabel" flat :label="props.buttonCancelLabel" v-close-popup />
|
||||
<q-btn
|
||||
class="q-mb-xl q-ml-lg q-px-lg"
|
||||
v-if="props.buttonOkLabel"
|
||||
color="primary"
|
||||
no-caps
|
||||
:label="props.buttonOkLabel"
|
||||
@click="updateDriver"
|
||||
>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</DialogFrame>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import DialogFrame from '../../dialog/DialogFrame.vue';
|
||||
import type { Driver } from '../../models/Drivers';
|
||||
import { setRequest } from 'src/vueLib/models/Request';
|
||||
import { addRawSubscriptions } from 'src/vueLib/models/Subscriptions';
|
||||
import { convertToSubscribe, type RawSubs } from 'src/vueLib/models/Subscribe';
|
||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { UpdateTable } from '../updateTable';
|
||||
import { updateDriverTable, type DriverTableRow } from 'src/vueLib/models/driverTable';
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
|
||||
const Dialog = ref();
|
||||
const options = ['ArtNetDriver', 'OSCDriver'];
|
||||
const driverForm = reactive({
|
||||
type: 'ArtNetDriver',
|
||||
bus: '',
|
||||
isAddress: true,
|
||||
address: 0,
|
||||
subscribe: '',
|
||||
publish: '',
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
buttonOkLabel: {
|
||||
type: String,
|
||||
default: 'OK',
|
||||
},
|
||||
labelColor: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
dialogLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonCancelLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '300px',
|
||||
},
|
||||
});
|
||||
|
||||
const localDialogLabel = ref('');
|
||||
const driver = ref<Driver>();
|
||||
let dpUuid = '';
|
||||
const form = ref();
|
||||
const address = ref();
|
||||
const topic = ref();
|
||||
|
||||
watch(
|
||||
() => driverForm.type,
|
||||
(val) => {
|
||||
driverForm.isAddress = val === options[0];
|
||||
if (driverForm.isAddress) {
|
||||
driverForm.subscribe = '';
|
||||
driverForm.publish = '';
|
||||
topic.value = undefined;
|
||||
} else {
|
||||
driverForm.address = -1;
|
||||
address.value = undefined;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const open = (uuid: string, drvs: DriverTableRow, type: 'add' | 'edit') => {
|
||||
switch (type) {
|
||||
case 'add':
|
||||
localDialogLabel.value = 'Add Driver';
|
||||
driverForm.type = 'ArtNetDriver';
|
||||
driverForm.isAddress = true;
|
||||
break;
|
||||
case 'edit':
|
||||
localDialogLabel.value = 'Edit Driver';
|
||||
driverForm.type = drvs.type;
|
||||
driverForm.bus = drvs.bus;
|
||||
}
|
||||
|
||||
dpUuid = uuid;
|
||||
driver.value = drvs;
|
||||
|
||||
if (drvs.address) driverForm.address = drvs.address;
|
||||
if (drvs.subscribe) driverForm.subscribe = drvs.subscribe;
|
||||
if (drvs.publish) driverForm.publish = drvs.publish;
|
||||
|
||||
Dialog.value?.open();
|
||||
};
|
||||
|
||||
function updateDriver() {
|
||||
form.value?.validate();
|
||||
if (!driverForm.type || !driverForm.bus) {
|
||||
NotifyResponse('Please fill in all required fields', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (driverForm.address > -1) {
|
||||
address.value = [driverForm.address];
|
||||
}
|
||||
if (driverForm.subscribe !== '' || driverForm.publish !== '') {
|
||||
topic.value = { subscribe: [driverForm.subscribe], publish: [driverForm.publish] };
|
||||
}
|
||||
|
||||
setRequest('', undefined, undefined, undefined, dpUuid, {
|
||||
type: driverForm.type,
|
||||
buses: [
|
||||
{
|
||||
name: driverForm.bus,
|
||||
address: address.value,
|
||||
topic: topic.value,
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((resp) => {
|
||||
addRawSubscriptions(resp as RawSubs);
|
||||
|
||||
resp.forEach((set) => {
|
||||
updateDriverTable(convertToSubscribe(set));
|
||||
});
|
||||
|
||||
UpdateTable();
|
||||
Dialog.value.close();
|
||||
})
|
||||
.catch((err) => {
|
||||
NotifyResponse(err, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.outercard {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
@@ -10,7 +10,7 @@
|
||||
<q-input
|
||||
class="q-mt-lg q-mb-none q-pl-md q-mx-lg"
|
||||
filled
|
||||
v-model="path"
|
||||
v-model="removeData.path"
|
||||
label="Current Path"
|
||||
label-color="primary"
|
||||
readonly
|
||||
@@ -19,7 +19,7 @@
|
||||
<q-input
|
||||
class="q-mt-lg q-mt-none q-pl-md q-mx-lg"
|
||||
filled
|
||||
v-model="newPath"
|
||||
v-model="removeData.newPath"
|
||||
label="New Path *"
|
||||
label-color="primary"
|
||||
@keyup.enter="onSubmit"
|
||||
@@ -36,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import DialogFrame from '../../dialog/DialogFrame.vue';
|
||||
import { useNotify } from '../../general/useNotify';
|
||||
import { getRequest, setRequest } from 'src/vueLib/models/Request';
|
||||
@@ -49,10 +49,13 @@ import { buildTree } from '../dbmTree';
|
||||
const { NotifyResponse } = useNotify();
|
||||
const Dialog = ref();
|
||||
const datapoint = ref();
|
||||
const path = ref('');
|
||||
const newPath = ref('');
|
||||
const removeData = reactive({
|
||||
path: '',
|
||||
newPath: '',
|
||||
prefix: 'DBM:',
|
||||
});
|
||||
|
||||
const copyForm = ref();
|
||||
const prefix = 'DBM:';
|
||||
|
||||
const open = (uuid: string) => {
|
||||
Dialog.value?.open();
|
||||
@@ -62,14 +65,13 @@ const open = (uuid: string) => {
|
||||
function onSubmit() {
|
||||
copyForm.value.validate().then((success: undefined) => {
|
||||
if (success) {
|
||||
if (newPath.value === path.value) {
|
||||
if (removeData.newPath === removeData.path) {
|
||||
NotifyResponse('same name', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
getRequest('', newPath.value.slice(prefix.length), 1)
|
||||
getRequest('', removeData.newPath.slice(removeData.prefix.length), 1)
|
||||
.then((response) => {
|
||||
console.log(10, response);
|
||||
if (response?.length > 0) {
|
||||
NotifyResponse("path '" + response[0]?.path + "' already exists", 'warning');
|
||||
return;
|
||||
@@ -83,22 +85,24 @@ function onSubmit() {
|
||||
}
|
||||
|
||||
setRequest(
|
||||
newPath.value.slice(prefix.length),
|
||||
removeData.newPath.slice(removeData.prefix.length),
|
||||
datapoint.value.type,
|
||||
datapoint.value.value,
|
||||
datapoint.value.rights,
|
||||
datapoint.value.uuid,
|
||||
undefined,
|
||||
true,
|
||||
)
|
||||
.then((res) => {
|
||||
addRawSubscriptions(res as RawSubs);
|
||||
console.log(80, res);
|
||||
buildTree(convertToSubscribes(res as RawSubs));
|
||||
UpdateTable();
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
});
|
||||
} else {
|
||||
if (newPath.value === '') {
|
||||
if (removeData.newPath === '') {
|
||||
NotifyResponse("Field 'New Path' is requierd", 'error');
|
||||
return;
|
||||
} else NotifyResponse('Form not validated', 'error');
|
||||
@@ -130,8 +134,8 @@ function getDatapoint(uuid: string) {
|
||||
.then((resp) => {
|
||||
if (resp[0]) {
|
||||
datapoint.value = resp[0];
|
||||
path.value = prefix + resp[0].path;
|
||||
newPath.value = prefix + resp[0].path;
|
||||
removeData.path = removeData.prefix + resp[0].path;
|
||||
removeData.newPath = removeData.prefix + resp[0].path;
|
||||
}
|
||||
})
|
||||
.catch((err) => NotifyResponse(catchError(err), 'error'));
|
||||
|
199
src/vueLib/dbm/dialog/UpdateDriverDialog.vue
Normal file
199
src/vueLib/dbm/dialog/UpdateDriverDialog.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<DialogFrame ref="Dialog" :width="props.width" :header-title="datapoint?.path">
|
||||
<q-card-section
|
||||
v-if="props.dialogLabel || localDialogLabel"
|
||||
class="text-bold text-left q-mb-none q-pb-none"
|
||||
:class="'text-' + props.labelColor"
|
||||
>{{ props.dialogLabel || localDialogLabel }}</q-card-section
|
||||
>
|
||||
<q-card-section>
|
||||
<q-table
|
||||
flat
|
||||
dense
|
||||
virtual-scroll
|
||||
:rows-per-page-options="[0]"
|
||||
:rows="drivers"
|
||||
:columns="columns"
|
||||
row-key="type"
|
||||
>
|
||||
<!-- add symbol on top right of table-->
|
||||
<template v-slot:top-right>
|
||||
<q-btn
|
||||
size="sm"
|
||||
ripple
|
||||
rounded
|
||||
color="primary"
|
||||
icon="add"
|
||||
round
|
||||
dense
|
||||
@click="handleRow(driver, 'add')"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:body-cell-settings="props">
|
||||
<q-td :props="props" class="cursor-pointer">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
size="sm"
|
||||
icon="more_vert"
|
||||
@click="(evt) => openSubMenu(evt, props.row)"
|
||||
></q-btn>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
<q-menu ref="contextMenuRef" context-menu>
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section class="cursor-pointer" @click="handleRow(driver, 'edit')">
|
||||
Edit
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section class="text-negative cursor-pointer" @click="deleteDriver(driver)">
|
||||
Delete
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-card-section>
|
||||
<q-card-section v-if="props.text" class="text-center" style="white-space: pre-line">{{
|
||||
props.text
|
||||
}}</q-card-section>
|
||||
<q-card-actions align="left" class="text-primary">
|
||||
<q-btn v-if="props.buttonCancelLabel" flat :label="props.buttonCancelLabel" v-close-popup>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
class="q-mb-xl q-ml-lg q-mt-none"
|
||||
v-if="props.buttonOkLabel"
|
||||
color="primary"
|
||||
no-caps
|
||||
:label="props.buttonOkLabel"
|
||||
v-close-popup
|
||||
>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</DialogFrame>
|
||||
<DriverDialog :button-ok-label="driverOkLabel" ref="driverDialog"></DriverDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import DialogFrame from '../../dialog/DialogFrame.vue';
|
||||
import DriverDialog from './DriverDialog.vue';
|
||||
import { convertToSubscribe, type Subscribe } from '../../models/Subscribe';
|
||||
import type { Driver } from '../../models/Drivers';
|
||||
import { driverDefault } from '../../models/Drivers';
|
||||
import type { DriverTableRow } from 'src/vueLib/models/driverTable';
|
||||
import { updateDriverTable, useDriverTable } from 'src/vueLib/models/driverTable';
|
||||
import { deleteRequest } from 'src/vueLib/models/Request';
|
||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import type { Bus } from 'src/vueLib/models/Bus';
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
const Dialog = ref();
|
||||
const driverDialog = ref();
|
||||
const writeValue = ref();
|
||||
const onlyRead = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
buttonOkLabel: {
|
||||
type: String,
|
||||
default: 'OK',
|
||||
},
|
||||
labelColor: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
dialogLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonCancelLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '300px',
|
||||
},
|
||||
});
|
||||
|
||||
const datapoint = ref();
|
||||
const localDialogLabel = ref('');
|
||||
const contextMenuRef = ref();
|
||||
const table = useDriverTable();
|
||||
const drivers = table.driverTable;
|
||||
const driver: Driver = { type: '' };
|
||||
const driverOkLabel = ref('');
|
||||
const columns = table.columns;
|
||||
|
||||
const open = (sub: Subscribe) => {
|
||||
datapoint.value = sub;
|
||||
if (datapoint.value.rights == 'R') onlyRead.value = true;
|
||||
table.emptyTable();
|
||||
|
||||
localDialogLabel.value = 'Update Drivers';
|
||||
updateDriverTable(sub);
|
||||
|
||||
writeValue.value = sub.drivers;
|
||||
Dialog.value?.open();
|
||||
};
|
||||
|
||||
function openSubMenu(evt: Event, d: DriverTableRow) {
|
||||
if (d) {
|
||||
const bus: Bus = {
|
||||
name: d.bus,
|
||||
};
|
||||
bus.name = d.bus;
|
||||
if (d.address) bus.address = d.address !== undefined ? [d.address] : [];
|
||||
if (d.subscribe || d.publish)
|
||||
bus.topic = {
|
||||
subscribe:
|
||||
typeof d.subscribe === 'string' ? d.subscribe.split(',').map((s) => s.trim()) : [],
|
||||
publish: typeof d.publish === 'string' ? d.publish.split(',').map((s) => s.trim()) : [],
|
||||
};
|
||||
|
||||
driver.type = d.type;
|
||||
driver.buses = [bus];
|
||||
}
|
||||
const mouseEvent = evt as MouseEvent;
|
||||
contextMenuRef.value?.show(mouseEvent);
|
||||
}
|
||||
|
||||
function handleRow(driver: Driver | undefined, type: 'add' | 'edit') {
|
||||
driverOkLabel.value = 'Add';
|
||||
switch (type) {
|
||||
case 'add':
|
||||
driver = driverDefault;
|
||||
break;
|
||||
case 'edit':
|
||||
driverOkLabel.value = 'Update';
|
||||
break;
|
||||
}
|
||||
driverDialog.value?.open(datapoint.value.uuid, driver, type);
|
||||
}
|
||||
|
||||
function deleteDriver(driver: Driver | undefined) {
|
||||
deleteRequest(datapoint.value.uuid, '', driver)
|
||||
.then((resp) => {
|
||||
resp.forEach((set) => {
|
||||
updateDriverTable(convertToSubscribe(set));
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
NotifyResponse(err, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.outercard {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
@@ -6,7 +6,7 @@
|
||||
:class="'text-' + props.labelColor"
|
||||
>{{ props.dialogLabel ? props.dialogLabel : localDialogLabel }}</q-card-section
|
||||
>
|
||||
<q-card-section v-if="drivers && drivers.length == 0">
|
||||
<q-card-section v-if="datapoint.type !== 'BIT'">
|
||||
<q-input
|
||||
class="q-px-md q-ma-sm"
|
||||
label="current value"
|
||||
@@ -27,15 +27,20 @@
|
||||
></q-input>
|
||||
</q-card-section>
|
||||
<q-card-section v-else>
|
||||
<q-table
|
||||
flat
|
||||
dense
|
||||
virtual-scroll
|
||||
:rows-per-page-options="[0]"
|
||||
:rows="drivers"
|
||||
:columns="columns"
|
||||
>
|
||||
</q-table>
|
||||
<div class="column q-pr-xs q-ma-sm">
|
||||
<div class="row items-center q-gutter-sm">
|
||||
<div>current value</div>
|
||||
<div class="row items-left">
|
||||
<q-toggle class="readonly-toggle" left-label v-model="inputValue"></q-toggle>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row items-center q-gutter-lg">
|
||||
<div>new value</div>
|
||||
<div class="row items-left">
|
||||
<q-toggle left-label v-model="writeValue"></q-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section v-if="props.text" class="text-center" style="white-space: pre-line">{{
|
||||
props.text
|
||||
@@ -63,15 +68,16 @@ import type { Subscribe } from '../../models/Subscribe';
|
||||
import type { Ref } from 'vue';
|
||||
import { setValues } from '../../services/websocket';
|
||||
import { useNotify } from '../../general/useNotify';
|
||||
import type { QTableProps } from 'quasar';
|
||||
import type { Driver } from '../../models/Drivers';
|
||||
import { catchError } from 'src/vueLib/models/error';
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
const Dialog = ref();
|
||||
const localDialogLabel = ref('');
|
||||
const writeValue = ref();
|
||||
const onlyRead = ref(false);
|
||||
const writeType = ref<'text' | 'number'>('text');
|
||||
const datapoint = ref();
|
||||
const inputValue = ref(datapoint?.value);
|
||||
|
||||
const props = defineProps({
|
||||
buttonOkLabel: {
|
||||
@@ -100,33 +106,14 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const datapoint = ref();
|
||||
const inputValue = ref(datapoint?.value);
|
||||
const localDialogLabel = ref('');
|
||||
const drivers = ref<Driver[]>([]);
|
||||
const columns = [
|
||||
{ name: 'type', label: 'Driver Name', field: 'type', align: 'left' },
|
||||
{ name: 'bus', label: 'Bus Name', field: 'bus', align: 'center' },
|
||||
{ name: 'address', label: 'Address', field: 'address', align: 'center' },
|
||||
] as QTableProps['columns'];
|
||||
|
||||
const open = (sub: Ref<Subscribe>, type?: string) => {
|
||||
const open = (sub: Ref<Subscribe>) => {
|
||||
datapoint.value = sub.value;
|
||||
if (datapoint.value.rights == 'R') onlyRead.value = true;
|
||||
drivers.value = [];
|
||||
switch (type) {
|
||||
case 'driver':
|
||||
localDialogLabel.value = 'Update Drivers';
|
||||
if (sub.value.drivers) drivers.value = Object.values(sub.value.drivers);
|
||||
writeValue.value = sub.value.drivers;
|
||||
break;
|
||||
default:
|
||||
localDialogLabel.value = 'Update Value';
|
||||
if (sub.value.type === 'STR') writeType.value = 'text';
|
||||
else if (sub.value.type === 'BIT') writeType.value = 'text';
|
||||
else writeType.value = 'number';
|
||||
writeValue.value = sub.value.value;
|
||||
}
|
||||
localDialogLabel.value = 'Update Value';
|
||||
if (sub.value.type === 'STR') writeType.value = 'text';
|
||||
else writeType.value = 'number';
|
||||
writeValue.value = sub.value.value;
|
||||
|
||||
Dialog.value?.open();
|
||||
};
|
||||
|
||||
@@ -156,4 +143,8 @@ defineExpose({ open });
|
||||
.outercard {
|
||||
border-radius: 10px;
|
||||
}
|
||||
.readonly-toggle {
|
||||
pointer-events: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
|
7
src/vueLib/models/Bus.ts
Normal file
7
src/vueLib/models/Bus.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Topic } from './Topic';
|
||||
|
||||
export interface Bus {
|
||||
name: string;
|
||||
address?: number[];
|
||||
topic?: Topic;
|
||||
}
|
@@ -1,5 +1,9 @@
|
||||
import type { Bus } from './Bus';
|
||||
export interface Driver {
|
||||
type: string;
|
||||
addess: number;
|
||||
value: number;
|
||||
buses?: Bus[];
|
||||
}
|
||||
|
||||
export const driverDefault = <Driver>{
|
||||
type: '',
|
||||
};
|
||||
|
@@ -1,14 +1,21 @@
|
||||
import type { Driver } from './Drivers';
|
||||
export type Publish = {
|
||||
event: string;
|
||||
uuid: string;
|
||||
path: string;
|
||||
type: string;
|
||||
drivers?: Record<string, Driver>;
|
||||
value: string | number | boolean | null;
|
||||
hasChild: boolean;
|
||||
};
|
||||
export type Pubs = Publish[];
|
||||
|
||||
import { updateSubscriptionValue, removeRawSubscriptions } from './Subscriptions';
|
||||
import {
|
||||
updateSubscriptionValue,
|
||||
removeRawSubscriptions,
|
||||
addRawSubscription,
|
||||
removeRawSubscription,
|
||||
} from './Subscriptions';
|
||||
import { buildTree, buildTreeWithRawSubs, removeNodes } from '../dbm/dbmTree';
|
||||
import type { RawSubs, RawSubscribe } from '../models/Subscribe';
|
||||
import { ref } from 'vue';
|
||||
@@ -33,6 +40,11 @@ export function publishToSubscriptions(pubs: Pubs) {
|
||||
rawSubs.value.push(pub as RawSubscribe);
|
||||
break;
|
||||
}
|
||||
if (pub.drivers) {
|
||||
removeRawSubscription(pub as RawSubscribe);
|
||||
addRawSubscription(pub as RawSubscribe);
|
||||
UpdateTable();
|
||||
}
|
||||
updateSubscriptionValue(pub.uuid, pub.value);
|
||||
});
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import type { Driver } from './Drivers';
|
||||
import type { Gets } from './Get';
|
||||
import type { Sets } from './Set';
|
||||
import type { Subs } from './Subscribe';
|
||||
@@ -61,10 +62,11 @@ export async function rawSetsRequest(sets: Sets): Promise<Sets> {
|
||||
|
||||
export async function setRequest(
|
||||
path: string,
|
||||
type: string,
|
||||
value: string | number | boolean,
|
||||
type?: string,
|
||||
value?: string | number | boolean,
|
||||
rights?: string,
|
||||
uuid?: string,
|
||||
driver?: Driver,
|
||||
rename?: boolean,
|
||||
): Promise<Sets> {
|
||||
const payload = {
|
||||
@@ -73,6 +75,7 @@ export async function setRequest(
|
||||
value: value,
|
||||
rights: rights,
|
||||
uuid: uuid,
|
||||
driver: driver,
|
||||
rename: rename,
|
||||
};
|
||||
|
||||
@@ -99,14 +102,18 @@ export async function setsRequest(sets: Sets): Promise<Sets> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRequest(uuid?: string, path?: string, rename?: boolean): Promise<Sets> {
|
||||
export async function deleteRequest(
|
||||
uuid?: string,
|
||||
path?: string,
|
||||
driver?: Driver,
|
||||
rename?: boolean,
|
||||
): Promise<Sets> {
|
||||
let payload = {};
|
||||
if (uuid) {
|
||||
payload = { uuid: uuid, rename: rename };
|
||||
payload = { uuid: uuid, driver: driver, rename: rename };
|
||||
} else if (path) {
|
||||
payload = { path: path };
|
||||
payload = { path: path, driver: driver };
|
||||
}
|
||||
|
||||
const resp = await api.delete('/json_data', {
|
||||
data: {
|
||||
set: [payload],
|
||||
|
@@ -20,6 +20,7 @@ export type RawSubscribe = {
|
||||
path?: string;
|
||||
depth?: number;
|
||||
value?: string | number | boolean | null;
|
||||
drivers?: Record<string, Driver>;
|
||||
rights?: string;
|
||||
hasChild?: boolean;
|
||||
};
|
||||
|
4
src/vueLib/models/Topic.ts
Normal file
4
src/vueLib/models/Topic.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface Topic {
|
||||
subscribe: string[];
|
||||
publish: string[];
|
||||
}
|
83
src/vueLib/models/driverTable.ts
Normal file
83
src/vueLib/models/driverTable.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { QTableColumn } from 'quasar';
|
||||
import type { Subscribe } from './Subscribe';
|
||||
|
||||
export type DriverTableRow = {
|
||||
type: string;
|
||||
bus: string;
|
||||
address?: number | undefined;
|
||||
subscribe?: string;
|
||||
publish?: string;
|
||||
};
|
||||
|
||||
const driverTable = reactive<DriverTableRow[]>([]);
|
||||
const columns = ref<QTableColumn[]>([]);
|
||||
const baseColumns: QTableColumn[] = [
|
||||
{ name: 'type', label: 'Driver Name', field: 'type', align: 'left' },
|
||||
{ name: 'bus', label: 'Bus Name', field: 'bus', align: 'center' },
|
||||
{ name: 'address', label: 'Address', field: 'address', align: 'center' },
|
||||
{ name: 'settings', label: '', field: 'settings', align: 'center' },
|
||||
];
|
||||
|
||||
export function updateDriverTable(sub: Subscribe) {
|
||||
driverTable.length = 0;
|
||||
let hasSubs = false;
|
||||
let hasPubs = false;
|
||||
|
||||
if (sub.drivers)
|
||||
Object.entries(sub.drivers).forEach(([driverName, driverData]) => {
|
||||
driverData.buses?.forEach((bus) => {
|
||||
hasSubs = bus.topic?.subscribe !== undefined || hasSubs;
|
||||
hasPubs = bus.topic?.publish !== undefined || hasPubs;
|
||||
|
||||
const subscribeList = bus.topic?.subscribe ?? [];
|
||||
const publishList = bus.topic?.publish ?? [];
|
||||
|
||||
const addresses = bus.address?.length ? bus.address : [undefined];
|
||||
|
||||
addresses.forEach((addr) => {
|
||||
driverTable.push({
|
||||
type: driverName,
|
||||
bus: bus.name,
|
||||
address: addr,
|
||||
subscribe: subscribeList.join(', '),
|
||||
publish: publishList.join(', '),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
reloadColumns(hasSubs, hasPubs);
|
||||
}
|
||||
export function useDriverTable() {
|
||||
function emptyTable() {
|
||||
driverTable.length = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
driverTable,
|
||||
emptyTable,
|
||||
columns,
|
||||
};
|
||||
}
|
||||
|
||||
function reloadColumns(hasSubs: boolean, hasPubs: boolean) {
|
||||
columns.value = [...baseColumns];
|
||||
const settingsIndex = columns?.value.findIndex((col) => col.name === 'settings');
|
||||
|
||||
if (hasSubs) {
|
||||
columns.value?.splice(settingsIndex ?? -1, 0, {
|
||||
name: 'subscribe',
|
||||
label: 'subscribe',
|
||||
field: 'subscribe',
|
||||
align: 'left',
|
||||
});
|
||||
}
|
||||
if (hasPubs) {
|
||||
columns.value?.splice(settingsIndex ?? -1, 0, {
|
||||
name: 'publish',
|
||||
label: 'publish',
|
||||
field: 'publish',
|
||||
align: 'left',
|
||||
});
|
||||
}
|
||||
}
|
@@ -12,9 +12,11 @@ export let socket: WebSocket | null = null;
|
||||
|
||||
const isConnected = ref(false);
|
||||
|
||||
export function initWebSocket(url: string, $q?: QVueGlobals) {
|
||||
export function initWebSocket(host: string, port: number = 8100, $q?: QVueGlobals) {
|
||||
const randomId = Math.floor(Math.random() * 10001); // random number from 0 to 10000
|
||||
|
||||
const connect = () => {
|
||||
socket = new WebSocket(url);
|
||||
socket = new WebSocket(`ws://${host}:${port}/ws?id=q${randomId}`);
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
|
Reference in New Issue
Block a user