Compare commits

...

21 Commits

Author SHA1 Message Date
Jordan Hsu 9c45ab8caa Merge branch 'testing/trigger' into testing/security 2022-01-10 16:18:31 +08:00
Jordan Hsu 8c53573857 [add] trigger will save param when start 2022-01-06 16:42:18 +08:00
Jordan Hsu 9a77e6dcff [modify] neulive 3.5 sti constraint 2022-01-06 13:09:52 +08:00
Jordan Hsu c8644f320a [fix] display bug when F5 2022-01-05 16:42:35 +08:00
Jordan Hsu 8b6348e77e bug fix and modify trigger mode option default to output 2022-01-05 16:04:37 +08:00
Jordan Hsu 48555dd793 [modify] trigger will automatically open chart when starting 2022-01-05 12:51:48 +08:00
Jordan Hsu 02617bbb86 [fix] trigger display order bug 2022-01-05 12:50:19 +08:00
peterlu14 5328bd76ba [fix] sd data from api 2022-01-04 14:13:38 +08:00
Jordan Hsu 9d19fd3fc9 add neu 3.1 sample rate option 2022-01-04 11:14:46 +08:00
Alan Lin 1e2bf182bf neu31 update adc_clk ratio formula 2022-01-04 11:03:23 +08:00
Alan Lin 288f9b325c update download SD function 2021-12-27 18:22:03 +08:00
Jordan Hsu e4a8f1e8d3 bug fix 2021-12-27 17:41:29 +08:00
Jordan Hsu 48ce6e6342 [add] chart hide initially and adjust x axis 2021-12-24 16:39:28 +08:00
TommyXin 7f0564a3ca Merge remote-tracking branch 'origin/neulive_hide_gimer' into testing/trigger 2021-12-24 12:05:05 +08:00
Jordan Hsu aa9c8c21b7 [modify] trigger mqtt route 2021-12-24 11:40:23 +08:00
TommyXin 0924c20e9e Merge remote-tracking branch 'origin/neulive_hide_gimer' into testing/trigger 2021-12-22 18:21:59 +08:00
Jordan Hsu a239223361 [add] trigger chart 2021-12-22 17:33:03 +08:00
Jordan Hsu e0341356c8 [add] trigger list ui with mqtt 2021-12-21 16:26:45 +08:00
Jordan Hsu b56c753063 [add] trigger list ui layout 2021-12-21 10:12:39 +08:00
Jordan Hsu 04741d8a77 [modify] triggerList to vuex 2021-12-21 10:10:34 +08:00
Jordan Hsu 3721e4a515 [add] device overview with trigger 2021-12-20 16:11:01 +08:00
29 changed files with 1259 additions and 87 deletions
@@ -46,58 +46,6 @@ export default {
description: '800',
},
],
neuliveSampleRateOptions: [
{
id: 0,
value: 1000,
description: '1k',
},
{
id: 1,
value: 2000,
description: '2k',
},
{
id: 2,
value: 4000,
description: '4k',
},
{
id: 3,
value: 5000,
description: '5k',
},
{
id: 4,
value: 8000,
description: '8k',
},
{
id: 5,
value: 10000,
description: '10k',
},
{
id: 6,
value: 20000,
description: '20k',
},
{
id: 7,
value: 40000,
description: '40k',
},
{
id: 8,
value: 80000,
description: '80k',
},
{
id: 9,
value: 100000,
description: '100k',
},
],
stimulateElectrodeOptions: [
{
label: 'Monopolar',
@@ -351,11 +299,7 @@ export default {
}
} else if (key === 'SAMPLE_RATE') {
showKey = 'Sample Rate'
for (let i = 0; i < this.neuliveSampleRateOptions.length; i++) {
if (this.neuliveSampleRateOptions[i].value === value) {
showValue = this.neuliveSampleRateOptions[i].description
}
}
showValue = value
}
this.content += ' ' + showKey + ': ' + showValue + '<br/>'
}
+79 -1
View File
@@ -38,7 +38,7 @@
<div class="row flex">
<div
class="flex sm12 xl6 md12 xs12 ma-0"
class="flex sm12 xl12 md12 xs12 ma-0"
v-for="device in controller.devices" :key="device.addr"
>
<div v-if="!controller.isScan || device.status == 'connected'">
@@ -94,6 +94,35 @@
</va-card>
</div>
</div>
<div
class="flex sm12 xl6 md12 xs12 ma-0"
v-for="trigger in triggerList" :key="trigger.mac"
>
<div v-if="!controller.isScan">
<va-card
style="background-color: #f8f8f8; box-shadow: 5px 5px 0 0 #f1f1f1;"
class="ma-0 pa-0"
>
<va-item :key="trigger.mac" class="pa-0 ma-0"
>
<!-- <va-item-section avatar class="ml-1">
<va-avatar style="border-radius: 0;">
<img :src="trigger.pic" :alt="trigger.name">
</va-avatar>
</va-item-section> -->
<va-item-section class="ml-3">
<va-item-label>
{{ trigger.name }}
<va-badge class="px-1 py-0 ml-2 mt-0" color="success">connected</va-badge>
</va-item-label>
<va-item-label caption>
{{ trigger.mac }}
</va-item-label>
</va-item-section>
</va-item>
</va-card>
</div>
</div>
</div>
</div>
@@ -140,12 +169,18 @@
import api from '@/data/api/index'
import { taskInfo } from '@/data/task/TaskInfo'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
import { mapFields } from 'vuex-map-fields'
export default {
name: 'devices-overview',
components: {
FulfillingBouncingCircleSpinner,
},
computed: {
...mapFields('device', [
'triggerList',
]),
},
created () {
},
data () {
@@ -248,6 +283,44 @@ export default {
})
}
},
'+/data-server/trigger_manager/#' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const topicSplit = topic.split('/')
const baseIndex = topicSplit.indexOf('trigger_manager')
const actionIndex = baseIndex + 1
const action = topicSplit[actionIndex]
const macIndex = baseIndex + 2
let _mac
if (topicSplit.length - 1 >= macIndex) {
_mac = topicSplit[macIndex]
}
let foundIndex
let mesJson
switch (action) {
case 'info_all': // control_ID/data-server/trigger_manager/info_all
this.triggerList = JSON.parse(mes)
break
case 'register': // control_ID/data-server/trigger_manager/register
mesJson = JSON.parse(mes)
foundIndex = this.triggerList.findIndex(o => o.mac === mesJson.mac)
if (foundIndex < 0) { // -1: not found
this.triggerList.push(mesJson)
} else {
this.triggerList[foundIndex] = mesJson
}
break
case 'remove': // control_ID/data-server/trigger_manager/remove/device_mac
foundIndex = this.triggerList.findIndex(o => o.mac === _mac)
this.triggerList.splice(foundIndex, 1)
break
case 'info': // control_ID/data-server/trigger_manager/info/device_mac
foundIndex = this.triggerList.findIndex(o => o.mac === _mac)
this.triggerList[foundIndex] = mesJson
break
default:
break
}
},
async '+/broadcast' (data, topic) {
const topicSplit = topic.split('/')
const mes = String.fromCharCode.apply(null, data).split(':')
@@ -379,6 +452,7 @@ export default {
this.mqttSub(controller.id + '/hardware_device_connect/+')
this.mqttSub(controller.id + '/hardware_scan/+')
this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/data-server/trigger_manager/#')
})
},
pageMqttUnSub: function () {
@@ -389,6 +463,7 @@ export default {
this.mqttUnSub(controller.id + '/hardware_device_connect/+')
this.mqttUnSub(controller.id + '/hardware_scan/+')
this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/data-server/trigger_manager/#')
})
},
pageInitPub: function () {
@@ -403,6 +478,8 @@ export default {
this.mqttPub(controller.id + '_user', JSON.stringify({
header: 'hardware_info/0',
}))
this.mqttPub(controller.id + '/client/trigger_manager/scan', '{}')
this.mqttPub(controller.id + '/client/trigger_manager/info_all', '{}')
// this.mqttPub(controller.id + '_user', JSON.stringify({
// header: 'device_update_usb/0',
// }))
@@ -427,6 +504,7 @@ export default {
this.mqttPub(controller.id + '_user', JSON.stringify({
header: 'hardware_scan/0',
}))
this.mqttPub(controller.id + '/client/trigger_manager/scan', '{}')
}
}
})
+1 -1
View File
@@ -701,7 +701,7 @@ export default {
if (this.controllerList[0].sd_uploading === false) {
const ret = await api.sdcard.getuuidList()
if (ret.status === 200) {
this.SDuuidList = ret.data.data
this.SDuuidList = ret.data
}
}
}
+67 -2
View File
@@ -78,6 +78,15 @@
@remove="removeChartCheck(chart.chartID)"
/>
</div>
<div
v-for="trigger in triggerList"
:key="trigger.id"
>
<trigger-chart
v-show="trigger.display === true"
:mac="trigger.mac"
/>
</div>
</div>
<div style="height: 150px;">
@@ -100,8 +109,11 @@ import { chartData } from '../../../data/task/ChartsManagement'
import { taskInfo } from '../../../data/task/TaskInfo'
import CanvasChart from './chart/CanvasChart'
import TaskDevices from './controller/TaskDevices'
import TriggerChart from './chart/TriggerChart'
// import api from '@/data/api'
import { mapFields } from 'vuex-map-fields'
import { mapActions } from 'vuex'
import deviceTypes from '@/store/modules/device/types'
// import TaskFormula from './formula/TaskFormula'
// TODO change chart data tag format
@@ -111,12 +123,17 @@ export default {
components: {
CanvasChart,
TaskDevices,
TriggerChart,
// TaskFormula,
},
computed: {
...mapFields('taskContent', [
'deviceList',
]),
...mapFields('device', [
'triggerList',
'chartKey',
]),
},
data () {
return {
@@ -127,8 +144,6 @@ export default {
dataRefreshTimer: null,
// charts data
chartDataAll: chartData.getAllChartsData(),
// chart refresh key
chartKey: 0,
chartNumber: taskInfo.getTaskInfo().chartList.length,
selectChartID: null,
showDeviceSetting: true,
@@ -230,6 +245,50 @@ export default {
await taskInfo.updateSdCardStatus(String.fromCharCode.apply(null, data))
this.refreshDevicesWindow()
},
'+/data-server/trigger_manager/#' (data, topic) {
let foundIndex
let mesJson
const mes = new TextDecoder('utf-8').decode(data)
const topicSplit = topic.split('/')
const baseIndex = topicSplit.indexOf('trigger_manager')
const actionIndex = baseIndex + 1
const action = topicSplit[actionIndex]
const macIndex = baseIndex + 2
let _mac
if (topicSplit.length - 1 >= macIndex) {
_mac = topicSplit[macIndex]
foundIndex = this.triggerList.findIndex(o => o.mac === _mac)
}
switch (action) {
case 'info_all': // control_ID/data-server/trigger_manager/info_all
this.triggerList = JSON.parse(mes)
this.deviceTriggerChartSetDisplay()
break
case 'register': // control_ID/data-server/trigger_manager/register
mesJson = JSON.parse(mes)
foundIndex = this.triggerList.findIndex(o => o.mac === mesJson.mac)
if (foundIndex < 0) { // -1: not found
this.triggerList.push(mesJson)
} else {
this.triggerList[foundIndex] = mesJson
}
break
case 'remove': // control_ID/data-server/trigger_manager/remove/device_mac
this.triggerList.splice(foundIndex, 1)
break
case 'info': // control_ID/data-server/trigger_manager/info/device_mac
this.triggerList[foundIndex] = JSON.parse(mes)
this.deviceTriggerChartSetDisplay()
break
case 'set_parameter': // control_ID/data-server/trigger_manager/set_parameter/device_mac
this.triggerList[foundIndex].parameter = JSON.parse(mes)
break
case 'runtime': // control_ID/data-server/trigger_manager/runtime/device_mac
break
default:
break
}
},
async '+/broadcast' (data, topic) {
const mes = String.fromCharCode.apply(null, data).split(':')
// let content
@@ -310,6 +369,7 @@ export default {
this.mqttSub(id + '/device_parameter/+')
this.mqttSub(id + '/device_parameter_value/+')
this.mqttSub(id + '/device_sd_card_status/+')
this.mqttSub(id + '/data-server/trigger_manager/#')
},
pageMqttUnSub: function (id) {
for (const device in taskInfo.getTaskInfo().deviceList) {
@@ -322,6 +382,7 @@ export default {
this.mqttUnSub(id + '/device_parameter/+')
this.mqttUnSub(id + '/device_parameter_value/+')
this.mqttUnSub(id + '/device_sd_card_status/+')
this.mqttUnSub(id + '/data-server/trigger_manager/#')
},
pageInit: async function () {
if (await this.$refs.devices_ref != null) {
@@ -340,6 +401,8 @@ export default {
header: 'hardware_device/0',
}))
await taskInfo.updateChartInfo()
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/scan', '{}')
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/info_all', '{}')
this.chartNumber = taskInfo.getTaskInfo().chartNumber
},
pageToast: function (mes) {
@@ -595,6 +658,8 @@ export default {
path: '/',
})
},
...mapActions('device', [deviceTypes.triggerChart.setDisplay]),
},
watch: {
},
@@ -0,0 +1,225 @@
<template>
<div class="flex" :class="cardclass">
<va-card>
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="sketchmapData" :auto-resize="true"></chart>
</va-card>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { taskInfo } from '@/data/task/TaskInfo'
import { mapFields } from 'vuex-map-fields'
import deviceTypes from '@/store/modules/device/types'
export default {
name: 'TriggerChart',
components: {
},
computed: {
...mapFields('device', [
'triggerList',
'allChannels',
]),
},
props: {
mac: {
type: String,
required: true,
},
},
created () {
},
data () {
return {
timer: null,
cardclass: ['xs12', 'md12', 'xl12', 'sm12'],
cardheight: '400px',
cardwidth: 'auto',
sketchmapData: {},
activeChannels: [],
}
},
mqtt: {
async '+/data-server/trigger_manager/#' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const topicSplit = topic.split('/')
const baseIndex = topicSplit.indexOf('trigger_manager')
const actionIndex = baseIndex + 1
const action = topicSplit[actionIndex]
const macIndex = baseIndex + 2
let _mac
if (topicSplit.length - 1 >= macIndex) {
_mac = topicSplit[macIndex]
}
if (_mac === this.mac) {
switch (action) {
case 'set_parameter': // control_ID/data-server/trigger_manager/set_parameter/device_mac
await this.initActiveChannels()
this.initChart()
break
case 'runtime': // control_ID/data-server/trigger_manager/runtime/device_mac
this.updateTimer(mes)
break
default:
break
}
}
},
'+/data-server/trigger_runtime/#' (data, topic) { // control_ID/data-server/trigger_runtime/pin_event/device_mac/channel
const mes = new TextDecoder('utf-8').decode(data)
const topicSplit = topic.split('/')
const baseIndex = topicSplit.indexOf('trigger_runtime')
const actionIndex = baseIndex + 1
const action = topicSplit[actionIndex]
const macIndex = baseIndex + 2
const channelIndex = baseIndex + 3
let _mac
if (topicSplit.length - 1 >= macIndex) {
_mac = topicSplit[macIndex]
}
let channel
if (topicSplit.length - 1 >= channelIndex) {
channel = parseInt(topicSplit[channelIndex])
}
let mesJson
if (_mac === this.mac) {
switch (action) {
case 'pin_event':
mesJson = JSON.parse(mes)
this.addSketchMapData(mesJson.timestamp, mesJson.status === 'high' ? 3.3 : 0.0, channel)
break
default:
break
}
}
},
},
methods: {
addSketchMapData (timestamp, val, channel) {
const gridIndex = this.activeChannels.indexOf(channel)
this.sketchmapData.series[gridIndex].data.push([parseInt(timestamp), val])
if (this.sketchmapData.series[gridIndex].data.length > 1000) {
this.sketchmapData.series[gridIndex].data.shift()
}
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
makeGrid (index, length) {
const _height = (100 / length) - 2 + '%'
const _top = index * (100 / length) + 2 + '%'
const _left = '0%'
const _right = '3%'
return {
index: index,
top: _top,
height: _height,
left: _left,
right: _right,
containLabel: true,
}
},
makeSeries (xAxisIndex, yAxisIndex) {
return {
type: 'line',
step: 'start',
data: [],
animation: false,
xAxisIndex: xAxisIndex,
yAxisIndex: yAxisIndex,
}
},
makeXAxis (gridIndex) {
return {
show: false,
type: 'time',
gridIndex: gridIndex,
min: 'dataMin',
max: 'dataMax',
}
},
makeYAxis (gridIndex) {
return {
type: 'value',
gridIndex: gridIndex,
splitNumber: 1,
splitLine: {
show: false,
},
axisLabel: {
formatter: function (value) {
return value + 'V'
},
},
min: 'dataMin',
max: 'dataMax',
}
},
async initActiveChannels () {
this.activeChannels = await this.deviceTriggerGetActiveChannels({ mac: this.mac })
},
initChart () {
this.clearChart()
const currImplementedChannels = this.activeChannels.filter(ch => ch < 4) // [TODO] now only open 4 channels (2022/01/05)
const gridLength = currImplementedChannels.length
for (let gridIndex = 0; gridIndex < gridLength; gridIndex++) {
this.sketchmapData.grid.push(this.makeGrid(gridIndex, gridLength))
this.sketchmapData.xAxis.push(this.makeXAxis(gridIndex))
this.sketchmapData.yAxis.push(this.makeYAxis(gridIndex))
this.sketchmapData.series.push(this.makeSeries(gridIndex, gridIndex))
if (gridIndex === gridLength - 1) {
this.sketchmapData.xAxis[gridLength - 1].show = true
}
}
},
clearChart () {
this.sketchmapData = {
xAxis: [],
yAxis: [],
tooltip: {
trigger: 'axis',
},
series: [],
grid: [],
}
},
updateTimer (status) {
if (status === 'start') {
if (this.timer === null) {
this.timer = setInterval(this.updateCurrentTime, 1000)
}
} else {
clearInterval(this.timer)
this.timer = null
}
},
updateCurrentTime () {
const currTime = Date.now()
this.sketchmapData.xAxis.forEach(axis => {
axis.min = currTime - 10000 // 10000ms = 10s
axis.max = currTime + 1000
})
},
...mapActions('device', [deviceTypes.trigger.getActiveChannels]),
},
async mounted () {
this.mqttSub(taskInfo.getTaskInfo().controllerID + '/data-server/trigger_manager/#')
this.mqttSub(taskInfo.getTaskInfo().controllerID + '/data-server/trigger_runtime/#')
await this.initActiveChannels()
this.initChart()
this.updateTimer('start')
},
destroyed () {
this.mqttUnSub(taskInfo.getTaskInfo().controllerID + '/data-server/trigger_manager/#')
this.mqttUnSub(taskInfo.getTaskInfo().controllerID + '/data-server/trigger_runtime/#')
this.clearChart()
this.updateTimer('stop')
},
}
</script>
@@ -48,12 +48,35 @@
</div>
</va-card>
<va-card class="mt-2">
<template slot="header">
<va-icon name="fa fa-bluetooth-b mr-3" color="primary"/>
<h5 class="mt-0 mb-0">TRIGGERS</h5>
</template>
<div v-if="state.LOADING" class="flex-center spinner-box mt-4 mb-5">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="this.$themes.primary"
:size="60"
>
</fulfilling-bouncing-circle-spinner>
</div>
<div class="flex row align--center">
<trigger-list
@start="startTrigger($event)"
@stop="stopTrigger($event)"
/>
</div>
</va-card>
</div>
</template>
<script>
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
import TaskDeviceList from './TaskDeviceList'
import TriggerList from './TriggerList'
// import TaskDeviceListGM from './neulive-gimer/TaskDeviceListGM'
import { taskInfo } from '../../../../data/task/TaskInfo'
import { chartData } from '../../../../data/task/ChartsManagement'
@@ -67,6 +90,7 @@ export default {
// TaskDeviceListGM,
FulfillingBouncingCircleSpinner,
TaskDeviceList,
TriggerList,
},
props: {
cardclass: {
@@ -336,6 +360,26 @@ export default {
device: taskInfo.getRawDeviceID(device.id),
}))
},
startTrigger (trigger) {
// save automatically when start
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/set_parameter', JSON.stringify({
mac: trigger.mac,
parameter_set: trigger.parameter,
}),
)
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/runtime', JSON.stringify({
mac: trigger.mac,
instruction: 'start',
}),
)
},
stopTrigger (trigger) {
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/runtime', JSON.stringify({
mac: trigger.mac,
instruction: 'stop',
}),
)
},
addDevice: async function (selectID) {
if (selectID == null) {
return false
@@ -0,0 +1,85 @@
<template>
<div class="flex sm12 xl12 md12 xs12">
<va-accordion v-for="trigger in triggerList" :key="trigger.id">
<va-collapse class="mb-2" :isOpenDefault='true'>
<span slot="header" >
<va-item :key="trigger.id" class="pa-0 ma-0"
>
<va-item-section class="ml-3" caption>
<va-item-label>
{{ trigger.name }}
</va-item-label>
<va-item-label caption>
{{ trigger.mac }}
</va-item-label>
</va-item-section>
</va-item>
<va-item class="pa-0 ma-0 mt-2">
<va-item-section class="ml-3">
<va-item-label>
<va-button @click.stop="start(trigger)" v-if="trigger.status !== 'RUNNING'" class="px-2 py-0 mr-0" color="success" small>START</va-button>
<va-button @click.stop="stop(trigger)" v-if="trigger.status === 'RUNNING'" class="px-2 py-0 mr-0" color="danger" small>STOP</va-button>
</va-item-label>
</va-item-section>
</va-item>
</span>
<div slot="body">
<div class="mt-0 mb-0" style="height: 350px; overflow-y: scroll; overflow-x: hidden;" :key="'detail' + trigger.id">
<div>
<trigger-control-window :mac="trigger.mac" />
</div>
</div>
</div>
</va-collapse>
</va-accordion>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import TriggerControlWindow from '../trigger/TriggerControlWindow'
export default {
name: 'TriggerList',
components: {
TriggerControlWindow,
},
data () {
return {
}
},
computed: {
...mapFields('device', [
'triggerList',
'chartKey',
]),
},
methods: {
start: async function (trigger) {
this.$emit('start', trigger)
this.triggerList.find(o => o.mac === trigger.mac).display = true
this.triggerList.find(o => o.mac === trigger.mac).status = 'RUNNING'
this.chartKey++
this.pageToast('The trigger ' + trigger.name + ' has been started.')
},
stop: function (trigger) {
this.$emit('stop', trigger)
this.triggerList.find(o => o.mac === trigger.mac).status = 'CONNTECTED'
this.pageToast('The trigger ' + trigger.name + ' has been stopped.')
},
pageToast: function (mes) {
this.showToast(
mes,
{
icon: 'fa-bell',
position: 'bottom-right',
duration: 4000,
},
)
},
},
destroyed () {
},
}
</script>
@@ -205,6 +205,16 @@ export default {
value: 20000,
description: '20k',
},
{
id: 7,
value: 25000,
description: '25k',
},
{
id: 8,
value: 31250,
description: '31.25k',
},
],
acceleratorSensitivitySelect: null,
acceleratorSensitivityOptions: [
@@ -333,7 +343,7 @@ export default {
},
sampleRateSelectChange () {
if (this.sampleRateSelect != null && this.sampleRateSelect.value !== ((taskInfo.getDeviceInfo(this.deviceID).parameterSet.SAMPLE_RATE))) {
const adcClock = 8e5 / this.sampleRateSelect.value
const adcClock = 1e6 / this.sampleRateSelect.value
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
header: 'device_parameter/0',
device: taskInfo.getRawDeviceID(this.deviceID),
@@ -202,43 +202,43 @@ export default {
},
{
id: 6,
value: 16000,
description: '16k',
},
{
id: 7,
value: 20000,
description: '20k',
},
{
id: 7,
value: 30000,
description: '30k',
id: 8,
value: 25000,
description: '25k',
},
{
id: 8,
id: 9,
value: 32000,
description: '32k',
},
{
id: 10,
value: 40000,
description: '40k',
},
{
id: 9,
id: 11,
value: 50000,
description: '50k',
},
{
id: 10,
value: 60000,
description: '60k',
},
{
id: 11,
value: 70000,
description: '70k',
},
{
id: 12,
value: 80000,
description: '80k',
value: 64000,
description: '64k',
},
{
id: 13,
value: 90000,
description: '90k',
value: 80000,
description: '80k',
},
{
id: 14,
@@ -955,10 +955,10 @@ export default {
return false
}
if (usVal < 60) {
this.periodNumber = 60
this.periodNumberInput = String(60 / this.unitMultiply)
this.pageToast('Reset to 60 us')
if (usVal < 10) {
this.periodNumber = 10
this.periodNumberInput = String(10 / this.unitMultiply)
this.pageToast('Reset to 10 us')
} else if (usVal > 1e6) { // freq: 1 to 20000Hz, so period < 1e6us (1e6/1e6=1)
this.periodNumber = 1e6
this.periodNumberInput = String(1e6 / this.unitMultiply)
@@ -1137,7 +1137,7 @@ export default {
// val unit: us
const val = (this.t1Range + this.t2Range + this.t3Range + this.t4Range) * 10 // UI us
if (val < 80) { // us
if (val < 10) { // us
for (const tRange of [1, 2, 3, 4]) {
this.updateTRangeErrorMsg({ stiIimeSelect: tRange, status: true, msg: 'Period < 0.08ms' })
}
@@ -0,0 +1,92 @@
<template>
<div class="row flex px-0 mx-0 my-0 py-0">
<div class="flex sm5 xl5 md5 xs5 px-0 mx-0 my-0 py-2">
<p class="display-6">Action<p/>
</div>
<div class="flex sm7 xl7 md7 xs7 px-0 mx-0 my-0 py-0" :key="inputKey">
<!-- <p class="display-6">Method<p/> -->
<va-input
@mouseleave.native="itemChange()"
@keyup.enter="itemChange()"
@blur="itemChange()"
v-model="methodInput"
class="my-2"
label="Method"
/>
<va-input
@mouseleave.native="itemChange()"
@keyup.enter="itemChange()"
@blur="itemChange()"
v-model="dataInput"
class="mb-0"
label="Data"
/>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import { mapActions } from 'vuex'
import deviceTypes from '@/store/modules/device/types'
export default {
name: 'Action',
components: {
},
computed: {
...mapFields('device', [
'triggerList',
]),
},
props: {
mac: {
type: String,
required: true,
},
channelIndex: {
type: Number,
required: true,
},
},
created () {
},
data () {
return {
inputKey: 0,
methodInput: '',
dataInput: '',
}
},
mqtt: {
},
methods: {
itemChange () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
obj.parameter[key].action = {
method: this.methodInput,
data: this.dataInput,
}
},
async refresh () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
const inputChannels = await this.deviceTriggerGetInputChannels({ mac: this.mac })
if (inputChannels.includes(String(this.channelIndex))) {
this.methodInput = obj.parameter[key].action.method
this.dataInput = String(obj.parameter[key].action.data)
}
},
...mapActions('device', [deviceTypes.trigger.getInputChannels]),
},
mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,52 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 my-3 py-0">
<div class="flex sm4 xl4 md4 xs4 px-0 py-0 mx-0 my-0">
<p class="display-6"> CHANNEL {{ channelIndex + 1 }} <p/>
</div>
<div class="flex sm8 xl8 md8 xs8 px-0 py-0 mx-0 mt-1" :key="inputKey">
<mode :ref="'mode'" class="mb-1" :mac="mac" :channelIndex="channelIndex" />
</div>
</div>
</template>
<script>
import Mode from './Mode'
export default {
name: 'Channel',
components: {
Mode,
},
props: {
mac: {
type: String,
required: true,
},
channelIndex: {
type: Number,
required: true,
},
},
created () {
},
data () {
return {
inputKey: 0,
}
},
mqtt: {
},
methods: {
refresh () {
this.$refs.mode.refresh()
},
},
mounted () {
},
watch: {
},
destroyed () {
},
}
</script>
@@ -0,0 +1,79 @@
<template>
<div class="row flex px-0 mx-0 my-0 py-0">
<div class="flex sm5 xl5 md5 xs5 px-0 mx-0 my-0 py-2">
<p class="display-6">Description<p/>
</div>
<div class="flex sm7 xl7 md7 xs7 px-0 mx-0 my-0 py-0">
<va-input
@mouseleave.native="itemChange()"
@keyup.enter="itemChange()"
@blur="itemChange()"
:key="inputKey"
style="float: left;"
v-model="itemInput"
class="mb-0"
/>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import { mapActions } from 'vuex'
import deviceTypes from '@/store/modules/device/types'
export default {
name: 'Description',
components: {
},
computed: {
...mapFields('device', [
'triggerList',
]),
},
props: {
mac: {
type: String,
required: true,
},
channelIndex: {
type: Number,
required: true,
},
},
created () {
},
data () {
return {
inputKey: 0,
itemInput: '',
}
},
mqtt: {
},
methods: {
itemChange () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
obj.parameter[key].description = this.itemInput
},
async refresh () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
const inputChannels = await this.deviceTriggerGetInputChannels({ mac: this.mac })
if (inputChannels.includes(String(this.channelIndex))) {
this.itemInput = obj.parameter[key].description
}
},
...mapActions('device', [deviceTypes.trigger.getInputChannels]),
},
mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,64 @@
<template>
<div class="row flex px-0 mx-0 my-0 py-0">
<div class="flex sm4 xl4 md4 xs4 px-0 mx-0 my-0 py-2">
<p class="display-6">INTERVAL<p/>
</div>
<div class="flex sm4 xl4 md4 xs4 px-0 mx-0 my-0 py-0">
<va-input
@mouseleave.native="itemChange()"
@keyup.enter="itemChange()"
@blur="itemChange()"
:key="inputKey"
v-model="itemInput"
class="mb-0"
/>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'Interval',
components: {
},
computed: {
...mapFields('device', [
'triggerList',
]),
},
props: {
mac: {
type: String,
required: true,
},
},
created () {
},
data () {
return {
inputKey: 0,
itemInput: 0.01,
}
},
mqtt: {
},
methods: {
itemChange () {
const obj = this.triggerList.find(o => o.mac === this.mac)
obj.parameter.runtime_interval = parseFloat(this.itemInput)
},
refresh () {
this.itemInput = this.triggerList.find(o => o.mac === this.mac).parameter.runtime_interval
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,135 @@
<template>
<div class="row">
<va-radio-button
v-for="(option, index) in optionMode"
:key="index"
v-model="selectMode"
:option="optionMode[index]"
:label="optionMode[index]"
class="pl-2"
@click.native.stop="itemChange(index)"
/>
<div v-show="selectMode === 'INPUT'" class="my-3 pl-3">
<ttl :ref="'ttl'" :mac="mac" :channelIndex="channelIndex" />
<description :ref="'desc'" :mac="mac" :channelIndex="channelIndex" />
<action :ref="'action'" :mac="mac" :channelIndex="channelIndex" />
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { mapFields } from 'vuex-map-fields'
import deviceTypes from '@/store/modules/device/types'
import Ttl from './Ttl'
import Description from './Description'
import Action from './Action'
export default {
name: 'Mode',
components: {
Ttl,
Description,
Action,
},
computed: {
...mapFields('device', [
'triggerList',
'allChannels',
]),
},
props: {
mac: {
type: String,
required: true,
},
channelIndex: {
type: Number,
required: true,
},
},
created () {
},
data () {
return {
inputeKey: 0,
selectMode: 'OUTPUT',
optionMode: [
'INPUT',
'OUTPUT',
],
}
},
mqtt: {
},
methods: {
getBitMode (channelList) {
let channelSet = ''
for (let i = 7; i >= 0; i--) {
if (channelList.indexOf(i) >= 0) {
channelSet += '1'
} else {
channelSet += '0'
}
}
return parseInt(channelSet, 2)
},
async updateBitMode (obj) {
const outputChannels = await this.deviceTriggerGetOutputChannels({ mac: this.mac })
this.selectMode === 'OUTPUT' ? outputChannels.add(this.channelIndex) : outputChannels.delete(this.channelIndex)
obj.parameter.bitmode = this.getBitMode(Array.from(outputChannels))
},
itemChange () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
if (this.selectMode === 'INPUT') {
if (Object.keys(obj.parameter[key]).length === 0) {
obj.parameter[key] = {
ttl: 'high',
description: 'camera on',
action: {},
broadcast: 1,
}
}
this.$refs.ttl.itemChange()
this.$refs.desc.itemChange()
this.$refs.action.itemChange()
} else {
obj.parameter[key] = {}
}
this.updateBitMode(obj)
},
async refreshSelectMode () {
const outputChannels = await this.deviceTriggerGetOutputChannels({ mac: this.mac })
if (outputChannels.has(this.channelIndex)) {
this.selectMode = 'OUTPUT'
}
const inputChannels = await this.deviceTriggerGetInputChannels({ mac: this.mac })
if (inputChannels.includes(this.channelIndex)) {
this.selectMode = 'INPUT'
}
},
refresh () {
this.refreshSelectMode()
if (this.$refs.ttl != null) {
this.$refs.ttl.refresh()
}
if (this.$refs.desc != null) {
this.$refs.desc.refresh()
}
if (this.$refs.action != null) {
this.$refs.action.refresh()
}
},
...mapActions('device', [deviceTypes.trigger.getOutputChannels, deviceTypes.trigger.getInputChannels]),
},
mounted () {
},
watch: {
},
destroyed () {
},
}
</script>
@@ -0,0 +1,80 @@
<template>
<div>
<div style="text-align: right;">
<!-- <va-button small v-if="display !== true" color="primary" @click="setDisplayParam(true)"> DISPLAY </va-button>
<va-button small v-if="display === true" color="danger" @click="setDisplayParam(false)"> HIDE </va-button> -->
<va-button small @click="updateParam"> SAVE </va-button>
</div>
<interval :ref="'interval'" :mac="mac" />
<div v-for="channelIndex in allChannels" :key="channelIndex">
<channel :ref="'channel'" :mac="mac" :channelIndex="channelIndex" />
</div>
</div>
</template>
<script>
import Interval from './Interval'
import Channel from './Channel'
import { taskInfo } from '@/data/task/TaskInfo'
import { mapFields } from 'vuex-map-fields'
export default {
name: 'TriggerControlWindow',
components: {
Interval,
Channel,
},
computed: {
...mapFields('device', [
'triggerList',
'allChannels',
'chartKey',
]),
},
props: {
mac: {
type: String,
required: true,
},
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
// setDisplayParam (status) {
// this.triggerList.find(o => o.mac === this.mac).display = status
// this.display = status
// this.chartKey++
// },
updateParam () {
const trigger = this.triggerList.find(o => o.mac === this.mac)
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/set_parameter', JSON.stringify({
mac: trigger.mac,
parameter_set: trigger.parameter,
}),
)
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
},
mounted () {
if (this.$refs.interval != null) {
this.$refs.interval.refresh()
}
if (this.$refs.channel != null) {
this.$refs.channel.forEach(ch => ch.refresh())
}
},
watch: {
},
destroyed () {
},
}
</script>
@@ -0,0 +1,77 @@
<template>
<div class="row pl-2">
<va-radio-button
v-for="(option, index) in optionMode"
:key="index"
v-model="selectMode"
:option="optionMode[index]"
:label="optionMode[index]"
@click.native.stop="itemChange()"
/>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import { mapActions } from 'vuex'
import deviceTypes from '@/store/modules/device/types'
export default {
name: 'Ttl',
components: {
},
computed: {
...mapFields('device', [
'triggerList',
]),
},
props: {
mac: {
type: String,
required: true,
},
channelIndex: {
type: Number,
required: true,
},
},
created () {
},
data () {
return {
inputeKey: 0,
selectMode: 'HIGH',
optionMode: [
'HIGH',
'LOW',
],
}
},
mqtt: {
},
methods: {
itemChange () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
obj.parameter[key].ttl = this.selectMode.toLowerCase()
},
async refresh () {
const key = 'channel_event_' + this.channelIndex
const obj = this.triggerList.find(o => o.mac === this.mac)
const inputChannels = await this.deviceTriggerGetInputChannels({ mac: this.mac })
if (inputChannels.includes(String(this.channelIndex))) {
this.selectMode = obj.parameter[key].ttl.toUpperCase()
}
},
...mapActions('device', [deviceTypes.trigger.getInputChannels]),
},
mounted () {
},
watch: {
},
destroyed () {
},
}
</script>
+2
View File
@@ -3,6 +3,7 @@ import Vuex from 'vuex'
import VuexI18n from 'vuex-i18n' // load vuex i18n module
// import app from './modules/app'
import setting from './modules/setting'
import device from './modules/device'
import taskContent from './modules/task/content'
import state from './state'
@@ -25,6 +26,7 @@ const store = new Vuex.Store({
replay,
spike,
setting,
device,
download,
file,
},
@@ -0,0 +1,4 @@
const controllerActs = {
}
export default controllerActs
@@ -0,0 +1,9 @@
import controllerAct from './controllerAct'
import triggerAct from './triggerAct'
import triggerChartAct from './triggerChartAct'
export default {
...controllerAct,
...triggerAct,
...triggerChartAct,
}
@@ -0,0 +1,56 @@
import types from '@/store/modules/device/types'
const typePath = types.trigger
const triggerActs = {
/**
* get trigger output channels set
* @param {string} mac
* @return {set}
*/
[typePath.getOutputChannels]: async ({ state, getters, commit, dispatch }, payload) => {
const outputChannels = new Set()
const obj = state.triggerList.find(o => o.mac === payload.mac)
const channelSet = obj.parameter.bitmode.toString(2).split('').reverse()
channelSet.forEach((element, index) => {
if (parseInt(element) > 0) {
outputChannels.add(index)
}
})
return outputChannels
},
/**
* get trigger input channels list
* @param {string} mac
* @return {array}
*/
[typePath.getInputChannels]: async ({ state, getters, commit, dispatch }, payload) => {
const outputChannels = Array.from(await dispatch(typePath.getOutputChannels, { mac: payload.mac }))
const remain = state.allChannels.filter(x => !outputChannels.includes(x))
const obj = state.triggerList.find(o => o.mac === payload.mac)
const inputChannels = []
for (const index of remain) {
const key = 'channel_event_' + index
if (Object.keys(obj.parameter[key]).length !== 0) {
inputChannels.push(index)
}
}
return inputChannels
},
/**
* get trigger active (input + output) channels list
* @param {string} mac
* @return {array}
*/
[typePath.getActiveChannels]: async ({ state, getters, commit, dispatch }, payload) => {
const outputChannels = Array.from(await dispatch(typePath.getOutputChannels, { mac: payload.mac }))
const inputChannels = await dispatch(typePath.getInputChannels, { mac: payload.mac })
const union = [...new Set([...outputChannels, ...inputChannels])].sort()
var result = union.map(function (x) {
return parseInt(x, 10)
})
return result
},
}
export default triggerActs
@@ -0,0 +1,20 @@
import types from '@/store/modules/device/types'
const typePath = types.triggerChart
const triggerChartActs = {
/**
* set trigger chart display or not
*/
[typePath.setDisplay]: async ({ state, getters, commit, dispatch }, payload) => {
state.triggerList.forEach((element, index) => {
if (element.status === 'RUNNING') {
element.display = true
} else {
element.display = false
}
})
},
}
export default triggerChartActs
@@ -0,0 +1,6 @@
import { getField } from 'vuex-map-fields'
export default {
getField,
}
+12
View File
@@ -0,0 +1,12 @@
import actions from './actions'
import getters from './getters'
import mutations from './mutations'
import state from './state'
export default {
namespaced: true,
state: state,
getters: getters,
actions: actions,
mutations: mutations,
}
@@ -0,0 +1,4 @@
const controllerMutations = {
}
export default controllerMutations
@@ -0,0 +1,7 @@
import controllerMut from './controllerMut'
import { updateField } from 'vuex-map-fields'
export default {
...controllerMut,
updateField,
}
+9
View File
@@ -0,0 +1,9 @@
function newState () {
return {
triggerList: [],
chartKey: 0, // chart refresh key
allChannels: [0, 1, 2, 3], // trigger channel
}
}
export default newState
+13
View File
@@ -0,0 +1,13 @@
const types = {
trigger: {
getOutputChannels: 'deviceTriggerGetOutputChannels',
getInputChannels: 'deviceTriggerGetInputChannels',
getActiveChannels: 'deviceTriggerGetActiveChannels',
},
triggerChart: {
setDisplay: 'deviceTriggerChartSetDisplay',
},
}
export default types
+1 -1
View File
@@ -92,7 +92,7 @@ const rawActs = {
if (raw.sd_data !== null && raw.sd_data.length > 0) {
const encodedData = raw.sd_data.split('"***"').slice(0, -1)
const unzipDatas = encodedData.map(ctx => {
return ctx.split(' ')
return ctx.split(' ').filter(el => el !== '')
})
for (let i = 0; i <= unzipDatas.length - 1; i++) {