Compare commits

...

39 Commits

Author SHA1 Message Date
Tommy Huang e204d0c041 test project 2022-03-29 10:06:43 +08:00
TommyXin 94696e4fa8 max point to 16348 2022-02-24 00:56:54 +08:00
TommyXin 37b402e1f7 testing control panel 2022-02-21 00:02:21 +08:00
TommyXin e66d3ad0cd testing chart 2022-02-20 01:12:31 +08:00
Jordan Hsu bae437c26b [modify] ble download button color to 'success' 2022-01-21 16:57:29 +08:00
Jordan Hsu 4295fc02e2 [modify] filetable sd card icon 2022-01-17 16:47:08 +08:00
Jordan Hsu 2ef4b4b393 [fix] sd card icon bug 2022-01-17 15:03:37 +08:00
Jordan Hsu 93c4692dc0 [add] 3.5ex missing code 2022-01-17 12:25:38 +08:00
Jordan Hsu f92f4ebb6c [add] 3.5ex sample rate & sti constraint 2022-01-17 11:42:23 +08:00
Jordan Hsu 65ca86b38d Merge branch 'feature/3.5EX_download_bug_fix' into release/AS_20220112 2022-01-14 12:21:55 +08:00
Jordan Hsu 5d3da784cb [fix] 3.5ex not to download empty file, will have bug 2022-01-14 12:21:30 +08:00
Jordan Hsu 8d05136dcc Merge branch 'feature/3.5EX_download_bug_fix' into release/AS_20220112 2022-01-14 11:47:08 +08:00
Jordan Hsu 8c2c33fdd0 fix 3.5ex bug 2022-01-14 11:46:32 +08:00
Jordan Hsu 3c5596144f Merge branch 'feature/3.5EX_download_bug_fix' into release/AS_20220112 2022-01-13 17:17:58 +08:00
Jordan Hsu 02eae10c82 [add] 3.5 ex download filename 2022-01-13 17:16:57 +08:00
Jordan Hsu 9976903789 Merge branch 'testing/trigger' into release/AS_20220112 2022-01-13 16:58:41 +08:00
Jordan Hsu 7ce9b2408b Merge branch 'feature/3.5EX_download_bug_fix' into release/AS_20220112 2022-01-13 16:58:12 +08:00
Jordan Hsu 764d62dc9c [add] 3.5 extension board download format 2022-01-13 16:46:31 +08:00
Jordan Hsu dba3e118f4 Merge branch 'testing/security' into feature/AS_wish 2022-01-12 09:53:43 +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
44 changed files with 2140 additions and 148 deletions
+11 -4
View File
@@ -3739,6 +3739,14 @@
"dev": true,
"requires": {
"pako": "~1.0.5"
},
"dependencies": {
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
}
}
},
"browserslist": {
@@ -12486,10 +12494,9 @@
"dev": true
},
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz",
"integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg=="
},
"parallel-transform": {
"version": "1.2.0",
+1
View File
@@ -39,6 +39,7 @@
"medium-editor": "^5.23.3",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"pako": "^2.0.4",
"popper.js": "^1.16.0",
"regenerator-runtime": "^0.13.3",
"register-service-worker": "^1.6.2",
+2 -2
View File
@@ -58,9 +58,9 @@ Vue.use(VueAxios, axios)
Vue.use(Toast)
// Vue.use(VueMqtt, 'ws://52.194.17.114:8083')
Vue.use(VueMqtt, 'ws://' + location.href.split('/')[2] + ':8083')
// Vue.use(VueMqtt, 'ws://' + location.href.split('/')[2] + ':8083')
// Vue.use(VueMqtt, 'ws://' + '192.168.151.125' + ':8083')
// Vue.use(VueMqtt, 'ws://' + '192.168.3.211' + ':8083')
Vue.use(VueMqtt, 'ws://' + '192.168.3.211' + ':8083')
Vue.prototype.GLOBAL = global_
@@ -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', '{}')
}
}
})
+26 -4
View File
@@ -74,6 +74,7 @@ export default {
types.export.setHeader,
types.export.setTitle,
types.export.setData,
types.export.getChipIndex,
types.download.closeWriter,
types.download.setStream,
types.download.setSampleRate,
@@ -111,11 +112,23 @@ export default {
// split to different subfile by channel
if (meta.device.library_name.indexOf('Neulive') >= 0) {
if (channelArray.some(el => el >= 256)) {
channelList.push(channelArray.filter(el => (el >= 256 && el <= 259)))
channelList.push(channelArray.filter(el => el < 256))
if (meta.device.library_name === 'Neulive3.5ex') {
if (channelArray.some(el => el >= 256)) { // 3-axis
channelList.push(channelArray.filter(el => (el >= 256 && el <= 259)))
}
if (channelArray.some(el => (el >= 32 && el <= 63))) { // chip 2
channelList.push(channelArray.filter(el => (el >= 32 && el <= 63)))
}
if (channelArray.some(el => (el >= 0 && el <= 31))) { // chip 1
channelList.push(channelArray.filter(el => (el >= 0 && el <= 31)))
}
} else {
channelList.push(channelArray)
if (channelArray.some(el => el >= 256)) {
channelList.push(channelArray.filter(el => (el >= 256 && el <= 259)))
channelList.push(channelArray.filter(el => el < 256))
} else {
channelList.push(channelArray)
}
}
} else if (meta.device.library_name.indexOf('Elite') >= 0) {
channelList.push(channelArray)
@@ -156,6 +169,15 @@ export default {
this.downloadInfo.filename = meta.name
this.downloadInfo.filename += (sd !== undefined) ? '-sd' : ''
this.downloadInfo.size = parseInt(meta.size / (maxSplitFiles))
if (this.downloadInfo.device[1] === '3.5ex') {
const chipIndex = await this.getChipIndexExport({ downloadInfo: this.downloadInfo })
if (chipIndex === 1) {
this.downloadInfo.filename += '_ch01-32'
} else if (chipIndex === 2) {
this.downloadInfo.filename += '_ch33-64'
}
}
}
} else if (meta.device.library_name.indexOf('Elite') >= 0) {
this.downloadInfo.device = [meta.device.library_name.slice(0, 5), meta.device.library_name.slice(5)]
+2 -2
View File
@@ -184,7 +184,7 @@
/>
</va-popover>
<va-popover :message="`download ${props.rowData.name} in csv format`" placement="top">
<va-button flat small color="gray" icon="fa fa-download" v-if="props.rowData.type !== 'folder' && props.rowData.device.library_name.indexOf('Neulive') >= 0" @click.stop="downloadCsv(props.rowData)"/>
<va-button flat small color="success" icon="fa fa-download" v-if="props.rowData.type !== 'folder' && props.rowData.device.library_name.indexOf('Neulive') >= 0" @click.stop="downloadCsv(props.rowData)"/>
</va-popover>
<va-popover :message="`download ${props.rowData.name} in excel format`" placement="top">
<va-button flat small color="gray" icon="fa fa-download" v-if="props.rowData.type !== 'folder' && props.rowData.device.library_name.indexOf('Elite') >= 0" @click.stop="downloadExcel(props.rowData)"/>
@@ -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
}
}
}
+4 -9
View File
@@ -10,16 +10,11 @@
flat
small
>
<va-icon
<i
v-if="meta.sd_data_uploaded === false"
class="va-button__content__icon va-button__content__icon-left"
name="material-icons"
color="grey"
@click.native.stop="performButtonAction()"
small
fixed-width
> sd_card
</va-icon>
class="i-sd-card"
@click="performButtonAction()"
/>
</va-button>
<sd-card-api-action :ref="'sd_card_api'"/>
+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>
@@ -38,7 +38,7 @@
</va-item-section>
<va-item-section class="mr-3" v-if="device.type == 'Neulive'" side>
<i v-if="!device.sdCardStatus" class="i-sd-card mr-3" style="width: 1rem; height: 1rem;"></i>
<i v-if="device.sdCardStatus" class="i-sd-card mr-3" style="width: 1rem; height: 1rem;"></i>
<!-- <va-icon
name="fas fa-sd-card"
:color="(device.sdCardStatus === true) ? 'primary' : 'grey'"
@@ -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,
@@ -203,43 +203,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' })
}
@@ -956,10 +956,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)
@@ -1138,7 +1138,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>
+268
View File
@@ -0,0 +1,268 @@
<template>
<div class="row testing">
<va-card class="mb-2" style="width: 100%; height: 5vh;">
<div class="row">
<va-button small style="width: 80px;" @click="scan()" :aria-disabled="true">
SCAN
<div v-if="scanning" class="flex-center spinner-box ml-1">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#fff'"
:size="15"
>
</fulfilling-bouncing-circle-spinner>
</div>
</va-button>
<va-select
class="mr-1"
style="width: 200px; height: 15px;"
:label="'DEVICE CONNECTED'"
v-model="deviceConnectedSelect"
textBy="mac"
:options="deviceConnected"
/>
<va-button small style="width: 120px;" @click="disconnect(deviceConnectedSelect.mac)" :color="'danger'">
DISCONNECT
<div v-if="disconnecting" class="flex-center spinner-box ml-1">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#fff'"
:size="15"
>
</fulfilling-bouncing-circle-spinner>
</div>
</va-button>
<va-select
class="mr-1"
style="width: 200px; height: 15px;"
:label="'DEVICE FOUND'"
v-model="deviceNewFoundSelect"
textBy="mac"
:options="deviceNewFound"
/>
<va-button small style="width: 100px;" @click="connect(deviceNewFoundSelect.mac)">
CONNECT
<div v-if="connecting" class="flex-center spinner-box ml-1">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#fff'"
:size="15"
>
</fulfilling-bouncing-circle-spinner>
</div>
</va-button>
<va-select
class="mr-1"
style="width: 200px; height: 15px;"
:label="'DEVICE LIBRARY'"
v-model="deviceLibrarySelect"
textBy="mac"
:options="deviceLibrary"
/>
<va-button small style="width: 100px;" @click="connect(deviceLibrarySelect.mac)">
CONNECT
<div v-if="connecting" class="flex-center spinner-box ml-1">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#fff'"
:size="15"
>
</fulfilling-bouncing-circle-spinner>
</div>
</va-button>
<!-- <va-input
style="width: 100px;"
v-model="dataRefreshInterval"
class="mb-0 mr-1"
:label="'Refresh Interval'"
>
<va-button slot="append" small @click="setRefreshInterval(parseInt(dataRefreshInterval))">
SET
</va-button>
</va-input> -->
</div>
</va-card>
<div style="width: 100%;">
<div
style="width: 100%;"
v-for="devConnected in deviceConnected"
:key="devConnected.mac"
>
<testing-chart :key="chartList" class="mb-2" style="width: 100%;" :mac="devConnected.mac" />
</div>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import TestingChart from './TestingChart.vue'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'testing',
props: {
},
components: {
TestingChart,
FulfillingBouncingCircleSpinner,
},
computed: {
...mapFields('', [
'chartkey',
]),
},
data () {
return {
dataRefreshTimer: null,
dataRefreshInterval: '200',
scanning: false,
connecting: false,
disconnecting: false,
deviceConnected: [],
deviceNewFound: [],
deviceLibrary: [],
deviceNewFoundSelect: '',
deviceLibrarySelect: '',
deviceConnectedSelect: '',
chartList: 0,
}
},
mqtt: {
async '+/data_server/device_manager/neulive_manager/scan' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
if (mes === 'start') {
this.scanning = true
} else {
this.scanning = false
this.getDeviceAll()
this.pageToast('scan done')
}
},
async '+/data_server/device_manager/neulive_manager/connect' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const status = JSON.parse(mes)
this.connecting = false
if (status.status === 'success') {
this.pageToast(`connect success: ${status.mac}`)
this.getDeviceAll()
} else {
this.pageToast(`connect fail: ${status.mac}`)
}
this.chartList++
},
async '+/data_server/device_manager/neulive_manager/remove' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const status = JSON.parse(mes)
this.disconnecting = false
this.getDeviceAll()
this.pageToast(`disconnect ${status.mac}`)
this.chartList++
},
async '+/data_server/device_manager/neulive_manager/info_all' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const infoAll = JSON.parse(mes).content
this.deviceConnected = infoAll.connected
this.deviceNewFound = infoAll.found
this.deviceLibrary = infoAll.library
this.deviceConnected.forEach(devConnected => {
for (let i = 0; i < this.deviceLibrary.length; i++) {
if (this.deviceLibrary[i].mac === devConnected.mac) {
this.deviceLibrary.splice(i, 1)
}
}
})
for (let i = 0; i < this.deviceConnected.length; i++) {
this.deviceConnected[i].id = i
}
for (let i = 0; i < this.deviceLibrary.length; i++) {
this.deviceLibrary[i].id = i
}
for (let i = 0; i < this.deviceNewFound.length; i++) {
this.deviceNewFound[i].id = i
}
this.deviceNewFoundSelect = ''
this.deviceLibrarySelect = ''
this.deviceConnectedSelect = ''
},
async '+/data_server/device_manager/neulive_manager/info' (data, topic) {
},
},
methods: {
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
pageMqttSub: function (id) {
this.mqttSub('dc:a6:32:6c:bc:b4/data_server/device_manager/neulive_manager/#')
},
pageMqttUnSub: function (id) {
},
scan () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/scan', JSON.stringify({}))
},
connect (mac) {
this.connecting = true
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/connect', JSON.stringify({
mac: mac,
}))
},
disconnect (mac) {
this.disconnecting = true
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/disconnect', JSON.stringify({
mac: mac,
}))
},
getDeviceConnected () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/info_connected', JSON.stringify({}))
},
getDeviceFound () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/info_found', JSON.stringify({}))
},
getDeviceAll () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/info_all', JSON.stringify({}))
},
pageToast: function (mes) {
this.showToast(
mes,
{
icon: 'fa-bell',
position: 'bottom-right',
duration: 4000,
},
)
},
refreshData () {
this.chartkey++
// if (this.chartList > 10000) {
// this.chartkey = 0
// }
},
setRefreshInterval (val) {
if (this.dataRefreshTimer != null) {
clearInterval(this.dataRefreshTimer)
}
this.dataRefreshTimer = setInterval(this.refreshData, val)
},
},
watch: {
},
async mounted () {
this.pageMqttSub()
this.getDeviceAll()
// this.setRefreshInterval(parseInt(this.dataRefreshInterval))
},
destroyed () {
},
}
</script>
+456
View File
@@ -0,0 +1,456 @@
<template>
<div class="testing-chart">
<va-card>
<div class="row">
<va-input
v-model="deviceMac"
class="mb-0 mr-1"
:label="'MAC'"
>
<va-button slot="append" small @click="setMac()">
SET
</va-button>
</va-input>
<va-input
v-model="deviceChannel"
class="mb-0 mr-1"
:label="'CHANNEL'"
>
<va-button slot="append" small @click="setChannel()">
SET
</va-button>
</va-input>
<va-input
v-model="deviceSampleRate"
class="mb-0 mr-1"
:label="'SAMPLE RATE'"
>
<va-button slot="append" small @click="setSampleRate()">
SET
</va-button>
</va-input>
<va-input
v-model="deviceThreshold"
class="mb-0 mr-1"
:label="'Threshold'"
>
<va-button slot="append" small @click="setThreshold()">
SET
</va-button>
</va-input>
<h6 class="mx-1 mt-2">
TOTAL DATA POINT: {{ parseInt(chartMaxPoint / deviceThreshold) }}
</h6>
<va-button small @click="record()" :color="recordColor">
{{ recordStatus }}
</va-button>
</div>
<chart
:key="chartKey"
:ref="'chart_ref'"
:style="'width: '+ 'auto' + '; height: ' + '500px' + ';'"
:options="testChartData[deviceMac]"
@dataZoom="updateZoom"
/>
</va-card>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
import newDataZoom from '@/factories/chart/dataZoomFactory'
import pako from 'pako'
export default {
name: 'testing-chart',
props: {
mac: {
type: String,
default: 'f7:34:3b:f9:4f:30',
},
},
computed: {
...mapFields('', [
'testChartData',
]),
},
data () {
return {
isSetMac: false,
deviceMac: this.mac,
deviceChannel: '0',
deviceSampleRate: '10000',
deviceThreshold: '1',
recordStatus: 'START',
recordColor: 'primary',
channelList: [],
chartKey: 0,
chartMaxPoint: 16384,
chartMaxPointCurrent: 16384,
// ChartData: {
// xAxis: [],
// yAxis: [],
// series: [],
// grid: [],
// },
dataIndex: 0,
lastTime: -1,
} // sync_data/data/{self._device_mac}/{str(channel)}
},
mqtt: {
async '+/data_server/device_manager/neulive_manager/info' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const info = JSON.parse(mes).content
if (info.mac !== this.deviceMac) {
return
}
if (info.status === 'RECORDING') {
this.recordStatus = 'STOP'
this.recordColor = 'danger'
} else {
this.recordStatus = 'START'
}
this.deviceThreshold = info.threshold.toString()
this.deviceSampleRate = parseInt(info.parameter.SAMPLE_RATE)
this.channelList = info.parameter.RECORDING_CH
this.deviceChannel = this.channelList.join(',')
this.chartMaxPointCurrent = parseInt((this.chartMaxPoint / this.channelList.length) / this.deviceThreshold)
console.log(this.chartMaxPointCurrent)
this.initChart()
this.chartKey++
},
async '+/data_server/device_manager/neulive_manager/record' (data, topic) {
const mes = new TextDecoder('utf-8').decode(data)
const info = JSON.parse(mes)
if (info.mac !== this.deviceMac) {
return
}
this.recordStatus = info.status.toUpperCase()
this.recordStatus = this.recordStatus === 'STOP' ? 'START' : 'STOP'
this.recordColor = this.recordStatus === 'STOP' ? 'danger' : 'primary'
},
async '+/data_server/device_manager/neulive_manager/set_parameter' (data, topic) {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/info', JSON.stringify({
mac: this.deviceMac,
}))
},
async '+/data_server/device_manager/neulive_manager/neulive_device/neulive_runtime/sync_data/+/+' (data, topic) {
const topicList = topic.split('/')
if (!this.isSetMac || this.deviceMac !== topicList[topicList.length - 2]) {
return
}
let mes = new TextDecoder('utf-8').decode(data)
mes = this.unzip(mes)
// const t2 = Date.now()
const channel = topicList[topicList.length - 1]
const mesList = mes.split(',')
const sampleRate = parseInt(mesList[0])
const timeSpare = parseInt(1e6 / sampleRate * this.channelList.length)
const timePairList = mesList[1].split(' ')
let dataList = mesList[2].split(' ')
let timeList = []
for (let i = 0; i < timePairList.length - 1; i = i + 2) {
// console.log('******', i, timeList.length)
const startTime = parseInt(timePairList[i])
while (this.lastTime + timeSpare < startTime && this.lastTime > 0) {
const diff = parseInt((startTime - (this.lastTime + timeSpare)) / timeSpare)
const _tmpTimeList = Array.from({ length: diff }, (_, i) => startTime + (timeSpare * i))
const _tmpDataList = Array.from({ length: diff }, (_, i) => null)
dataList.splice(timeList.length, 0, ..._tmpDataList)
// TODO Maximum call stack size exceeded
timeList = timeList.concat(_tmpTimeList)
this.lastTime = _tmpTimeList[_tmpTimeList.length - 1]
// console.log('######', _tmpTimeList.length, timeList.length)
}
// timeList.push(startTime)
const count = parseInt(timePairList[i + 1])
const tmpList = Array.from({ length: count }, (_, i) => startTime + (timeSpare * i))
this.lastTime = tmpList[tmpList.length - 1]
// console.log(startTime, tmpList)
timeList = timeList.concat(tmpList)
}
// console.log(mes)
dataList = this.testChartData[this.deviceMac].series[channel].data.concat(dataList)
timeList = this.testChartData[this.deviceMac].xAxis[channel].data.concat(timeList)
// console.log(dataList.length, timeList.length)
const maxPoint = this.chartMaxPointCurrent
if (dataList.length > maxPoint) {
dataList.splice(0, (dataList.length - maxPoint))
timeList.splice(0, (timeList.length - maxPoint))
}
// console.log(timeList)
this.testChartData[this.deviceMac].series[channel].data = dataList
this.testChartData[this.deviceMac].xAxis[channel].data = timeList
// this.testChartData[this.deviceMac].xAxis[0].data = Array.from({ length: dataList.length }, (_, i) => i + 1)
if (parseInt(channel) === this.channelList[this.channelList.length - 1]) {
this.chartKey++
if (this.chartKey > 10000) {
this.chartKey = 0
}
}
// console.log('total', Date.now() - t2)
},
},
methods: {
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
pageMqttSub: function (id) {
},
pageMqttUnSub: function (id) {
},
unzip (b64Data) {
let strData = atob(b64Data)
// Convert binary string to character-number array
const charData = strData.split('').map(function (x) { return x.charCodeAt(0) })
// Turn number array into byte-array
const binData = new Uint8Array(charData)
// // unzip
const data = pako.inflate(binData)
// Convert gunzipped byteArray back to ascii string:
strData = new TextDecoder('utf-8').decode(data)
return strData
},
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',
data: [],
sampling: 'average',
// sampling: function (frame) {
// return frame[0]
// },
connectNulls: true,
animation: false,
hoverAnimation: false,
xAxisIndex: xAxisIndex,
yAxisIndex: yAxisIndex,
itemStyle: {
normal: {
},
},
lineStyle: {
width: 1.5,
},
showSymbol: false,
showAllSymbol: false,
}
},
makeXAxis (gridIndex) {
return {
show: false,
type: 'category',
gridIndex: gridIndex,
min: 'dataMin',
max: 'dataMax',
axisLabel: {
formatter: function (value) {
let microSec = value
let miniSec = parseInt(microSec / 1e3)
let sec = parseInt(miniSec / 1e3)
let hours = Math.floor(sec / 3600)
let minutes = Math.floor((sec - (hours * 3600)) / 60)
sec = sec % 60
miniSec = miniSec % 1e3
microSec = microSec % 1e3
if (hours < 10) { hours = '0' + hours }
if (minutes < 10) { minutes = '0' + minutes }
if (sec < 10) { sec = '0' + sec }
return hours + 'h ' + minutes + 'm ' + sec + 's ' + miniSec + 'ms ' + microSec + 'us'
},
},
data: [],
}
},
makeYAxis (gridIndex) {
return {
type: 'value',
gridIndex: gridIndex,
splitNumber: 1,
splitLine: {
show: false,
},
axisLabel: {
formatter: function (value) {
return value + 'uV'
},
},
min: -2047,
max: 2047,
}
},
initChart () {
this.testChartData[this.deviceMac] = {
xAxis: [],
yAxis: [],
series: [],
dataZoom: newDataZoom(),
tooltip: {
hideDelay: 5000,
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 1)',
borderColor: 'rgba(0, 0, 200, 0.2)',
borderWidth: 1,
// borderRadius: 0,
padding: 10,
formatter: function (params) {
const timeFormat = function (value) {
let microSec = value
let miniSec = parseInt(microSec / 1e3)
let sec = parseInt(miniSec / 1e3)
let hours = Math.floor(sec / 3600)
let minutes = Math.floor((sec - (hours * 3600)) / 60)
sec = sec % 60
miniSec = miniSec % 1e3
microSec = microSec % 1e3
if (hours < 10) { hours = '0' + hours }
if (minutes < 10) { minutes = '0' + minutes }
if (sec < 10) { sec = '0' + sec }
return hours + 'h ' + minutes + 'm ' + sec + 's ' + miniSec + 'ms ' + microSec + 'us'
}
const time = timeFormat(parseInt(params[0].name))
let tip = '<span style="color: black;">' + time + '</span><br>'
for (let i = 0; i < params.length; i++) {
const name = params[i].seriesIndex
const style = 'color: ' + params[i].color
tip += '<span style="' + style + '">channel ' + name + ': </span><span style="' + style + '">' +
params[i].value + ' uV</span><br>'
}
return tip
},
},
grid: [],
}
this.testChartData[this.deviceMac].dataZoom[0].bottom = '0%'
this.testChartData[this.deviceMac].dataZoom[0].xAxisIndex = this.channelList
this.testChartData[this.deviceMac].dataZoom[1].xAxisIndex = this.channelList
this.testChartData[this.deviceMac].dataZoom[2].yAxisIndex = this.channelList
const gridLength = this.channelList.length
for (let gridIndex = 0; gridIndex < gridLength; gridIndex++) {
this.testChartData[this.deviceMac].grid.push(this.makeGrid(gridIndex, gridLength))
this.testChartData[this.deviceMac].xAxis.push(this.makeXAxis(gridIndex))
this.testChartData[this.deviceMac].yAxis.push(this.makeYAxis(gridIndex))
this.testChartData[this.deviceMac].series.push(this.makeSeries(gridIndex, gridIndex))
if (gridIndex === gridLength - 1) {
this.testChartData[this.deviceMac].xAxis[gridLength - 1].show = true
}
}
},
record () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/record', JSON.stringify({
mac: this.deviceMac,
status: this.recordStatus.toLowerCase(),
}))
this.recordStatus = this.recordStatus === 'STOP' ? 'START' : 'STOP'
this.recordColor = this.recordStatus === 'STOP' ? 'danger' : 'primary'
if (this.recordStatus === 'START') {
this.chartKey++
} else {
this.initChart()
this.chartKey++
}
},
setMac () {
this.pageMqttSub()
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/info', JSON.stringify({
mac: this.deviceMac,
}))
this.isSetMac = true
},
setSampleRate () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/set_parameter', JSON.stringify({
mac: this.deviceMac,
parameter_set: {
SAMPLE_RATE: parseInt(this.deviceSampleRate),
},
}))
},
setThreshold () {
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/set_threshold', JSON.stringify({
mac: this.deviceMac,
threshold: parseInt(this.deviceThreshold),
}))
this.chartMaxPointCurrent = parseInt((this.chartMaxPoint / this.channelList.length) / this.deviceThreshold)
},
setChannel () {
this.channelList = this.deviceChannel.split(',').map(Number)
this.chartMaxPointCurrent = parseInt((this.chartMaxPoint / this.channelList.length) / this.deviceThreshold)
this.mqttPub('dc:a6:32:6c:bc:b4/client/device_manager/neulive_manager/set_parameter', JSON.stringify({
mac: this.deviceMac,
parameter_set: {
RECORDING_CH: this.channelList,
},
}))
},
updateZoom: function (e) {
if (e.batch != null) {
// console.log(e.batch)
e.batch.forEach(dataZoom => {
const id = parseInt(dataZoom.dataZoomId.substr(dataZoom.dataZoomId.length - 3, dataZoom.dataZoomId.length - 1))
if (id === 1) {
this.testChartData[this.deviceMac].dataZoom[0].start = dataZoom.start
this.testChartData[this.deviceMac].dataZoom[0].end = dataZoom.end
// this.startTime = parseInt(this.maxTime * dataZoom.start / 100)
// this.endTime = parseInt(this.maxTime * dataZoom.end / 100)
// this.windowLength = this.endTime - this.startTime
// this.chartOverviewData.dataZoom[0].start = dataZoom.start
// this.chartOverviewData.dataZoom[0].end = dataZoom.end
}
})
} else {
const id = parseInt(e.dataZoomId.substr(e.dataZoomId.length - 3, e.dataZoomId.length - 1))
if (id === 0) {
this.testChartData[this.deviceMac].dataZoom[0].start = e.start
this.testChartData[this.deviceMac].dataZoom[0].end = e.end
// this.startTime = parseInt(this.maxTime * e.start / 100)
// this.endTime = parseInt(this.maxTime * e.end / 100)
// this.windowLength = this.endTime - this.startTime
// this.chartOverviewData.dataZoom[0].start = e.start
// this.chartOverviewData.dataZoom[0].end = e.end
}
}
},
},
watch: {
},
async mounted () {
this.setMac()
},
destroyed () {
},
}
</script>
+2 -2
View File
@@ -11,8 +11,8 @@ import analysis from './analysis'
import sdcard from './sdcard'
import connection from './connection'
// axios.defaults.baseURL = 'http://192.168.3.211:3000'
axios.defaults.baseURL = 'http://' + location.href.split('/')[2].split(':')[0] + ':3000'
axios.defaults.baseURL = 'http://192.168.3.211:3000'
// axios.defaults.baseURL = 'http://' + location.href.split('/')[2].split(':')[0] + ':3000'
const api = {
axios,
+2
View File
@@ -18,6 +18,8 @@ function newState () {
lighterGray: '#ddd',
},
},
testChartData: {},
chartkey: {},
isLoading: true,
licenseCheck: true,
}
+5
View File
@@ -112,6 +112,11 @@ export default new Router({
path: 'data-replay/elite/:metaList?',
component: () => import('../components/analysis/replay/elite/EliteDataReplay.vue'),
},
{
name: 'testing',
path: 'testing',
component: () => import('../components/testing/Testing.vue'),
},
{
name: 'data-spike',
path: 'data-spike/:metaList?',
+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
+77 -14
View File
@@ -4,34 +4,81 @@ import golbalMethods from '@/data/global/global'
const typePath = types.export
const typePathDownload = types.download
const NeuliveParameterOrder = ['RECORDING_CH', 'AXIS_CH', 'AMP_GAIN', 'SAMPLE_RATE']
const NeuliveParameterOrder = ['AXIS_CH', 'AMP_GAIN', 'SAMPLE_RATE'] // all version neulive common param
const Neulive30NameList = ['Neulive3.0', 'Neulive3.1']
const Neulive35NameList = ['Neulive3.5', 'Neulive3.5-REC', 'Neulive3.5-STI']
const Neulive35NameList = ['Neulive3.5', 'Neulive3.5-REC', 'Neulive3.5-STI', 'Neulive3.5ex']
const Neulive30AmpGain = [100, 400, 800]
const Neulive35AmpGain = [100, 500, 300]
const exportActs = {
[typePath.getHeader]: function ({ state, getters, commit, dispatch }, payload) {
[typePath.getHeader]: async function ({ state, getters, commit, dispatch }, payload) {
const meta = state.metaInfo
const device = state.downloadInfo.device
const deviceHeader = ['Device', meta.device.device_name, golbalMethods.transMac(meta.device.device_address)]
const libraryHeader = ['Libaray', meta.device.library_name, meta.device.library_version]
const parameter = []
// [Region] for neulive 3.5 ex
const chipIndex = await dispatch(typePath.getChipIndex, { downloadInfo: state.downloadInfo })
// [EndRegion]
if (device[0] === 'Neulive') {
NeuliveParameterOrder.forEach(p => {
const value = meta.parameter_set[p]
if (p === 'RECORDING_CH') {
let result = ''
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
// device-specific param
if (device[1] === '3.5') {
let result = ''
const lowChannelList = meta.parameter_set.RECORDING_CH_L.toString(2).split('').reverse()
lowChannelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
const highChannelList = meta.parameter_set.RECORDING_CH_H.toString(2).split('').reverse()
highChannelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 33},`
}
})
result = result.slice(0, -1)
parameter.push(['Record channels', result])
} else if (device[1] === '3.5ex') {
let result = ''
if (chipIndex === 1) {
const lowChannelList = meta.parameter_set.REC_CH_DEV00.toString(2).split('').reverse()
lowChannelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
result = result.slice(0, -1)
parameter.push(['Record channels', result])
} else if (p === 'AXIS_CH') {
} else if (chipIndex === 2) {
const highChannelList = meta.parameter_set.REC_CH_DEV01.toString(2).split('').reverse()
highChannelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 33},`
}
})
}
result = result.slice(0, -1)
parameter.push(['Record channels', result])
} else {
let result = ''
const channelList = meta.parameter_set.RECORDING_CH.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
result = result.slice(0, -1)
parameter.push(['Record channels', result])
}
// common param
NeuliveParameterOrder.forEach(p => {
if (!(p in meta.parameter_set)) {
return
}
const value = meta.parameter_set[p]
if (p === 'AXIS_CH') {
let result = ''
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
@@ -58,8 +105,18 @@ const exportActs = {
parameter.push(['Amp gain', Neulive35AmpGain[value]])
}
} else if (p === 'SAMPLE_RATE') {
const recordChannel = JSON.parse(meta.channels).filter(ch => parseInt(ch) < 256)
parameter.push(['Sample Rate', parseInt(value / recordChannel.length)])
if (device[1] === '3.5ex') {
if (chipIndex === 1) {
const chip1RecordChannel = JSON.parse(meta.channels).filter(ch => (ch >= 0 && ch <= 31))
parameter.push(['Sample Rate', parseInt(value / chip1RecordChannel.length)])
} else if (chipIndex === 2) {
const chip2RecordChannel = JSON.parse(meta.channels).filter(ch => (ch >= 32 && ch <= 63))
parameter.push(['Sample Rate', parseInt(value / chip2RecordChannel.length)])
}
} else {
const recordChannel = JSON.parse(meta.channels).filter(ch => parseInt(ch) < 256)
parameter.push(['Sample Rate', parseInt(value / recordChannel.length)])
}
}
})
} else if (device[0] === 'Elite') {
@@ -285,6 +342,12 @@ const exportActs = {
exportString = null
},
// for neulive 3.5ex
[typePath.getChipIndex]: async function ({ state, getters, commit, dispatch }, payload) {
const { downloadInfo } = payload
return downloadInfo.channel.every((val, i, arr) => val < 32) ? 1 : 2
},
}
export default exportActs
+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++) {
+1
View File
@@ -20,6 +20,7 @@ const types = {
getHeader: 'getHeaderExport',
getTitle: 'getTitleExport',
getData: 'getDataExport',
getChipIndex: 'getChipIndexExport',
},
raw: {
rawDataByID: 'rawDataByIDRaw',