Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73c11c9c49 | |||
| 6b16f3e8b4 | |||
| 6758d98e03 | |||
| 6514626c02 | |||
| 82c154d360 | |||
| 76de87368b | |||
| ae6646273a | |||
| f30720cf36 | |||
| 5f8a47215c | |||
| a8018c84b8 | |||
| c028814a1c | |||
| 0ae013521c | |||
| 2008b086f7 | |||
| 81fe11c7b6 | |||
| 595c9bcb91 | |||
| 32b3000d59 | |||
| de76e098db | |||
| ab75887da2 | |||
| 26f3d2abfc | |||
| 3ffe0463c6 | |||
| 6536267b1c | |||
| 60eb85d977 | |||
| cbd5ad79a7 | |||
| b4483c9bc1 | |||
| 29af33de31 | |||
| 0ea3731e71 | |||
| c28ecabf65 | |||
| d3b6530347 | |||
| 9aff6225d1 | |||
| 45bf01d178 | |||
| edc6592f31 | |||
| 60f3fe3089 | |||
| c4a6de8235 | |||
| a140c9983b | |||
| 5e0499d739 | |||
| ef6d518377 | |||
| 27aff31a45 | |||
| 172ab999fe | |||
| 3723df9bf8 | |||
| bdbfeab245 | |||
| 99fcdc9b4b | |||
| 5bdc7cd67f | |||
| 623196521b | |||
| 9be35f5541 | |||
| bc6971926d | |||
| b47836bc62 | |||
| f3d55099a6 | |||
| 4ac8dfb12f | |||
| 876add4789 | |||
| 64bf6fe313 | |||
| 1d6dbbaa66 | |||
| 9743290e69 | |||
| bb7e8287fd | |||
| 2ccca9b389 | |||
| 62a2bad4a6 | |||
| f8ed7c7ffd | |||
| 600abf40e6 | |||
| ae70d6d6d2 | |||
| dfad50aad7 | |||
| ea7a284b65 | |||
| 5b994f982a | |||
| 763016b105 | |||
| d7eca79154 | |||
| 26e0a82aa8 | |||
| 7e9775bd34 | |||
| d067b0da6b | |||
| d5dd7c2321 | |||
| 2158f19310 | |||
| 1b68eaef71 | |||
| 201f8f80cf | |||
| ba487da5f4 | |||
| 6ce33876a3 | |||
| f2c18924ae | |||
| d80f1dc996 | |||
| 1c90566e64 | |||
| 4b2caf0964 | |||
| f87ef1fb54 | |||
| bf83da0649 | |||
| 5cb772a18a | |||
| 77901c3667 | |||
| 98aacadec9 | |||
| fa572f3117 | |||
| 6f538bea59 | |||
| 87908e086c | |||
| c45faf8ce1 | |||
| 8b0cd8c7af | |||
| e17cdd1dea | |||
| 14a5474094 | |||
| a7297e2e85 | |||
| 787aaa2bf8 | |||
| 53bcef0f60 | |||
| ea2b3a0472 | |||
| 7987f26021 | |||
| ea2e8b23d8 | |||
| 9ceee6b3d1 | |||
| bc3b466312 | |||
| 0519f04acc | |||
| eff22d1494 | |||
| 54c8d2ac48 | |||
| 4836ffcde9 | |||
| 1ac306c419 | |||
| 7c93cf6620 | |||
| 64936c0cf1 | |||
| 5868caaec2 | |||
| c938a9b001 | |||
| 801babed35 | |||
| 1459128428 | |||
| d01a772a67 | |||
| ef6e7197a8 | |||
| 34e8294288 | |||
| 0a7b679aa9 | |||
| f53d88f2cf | |||
| 1dc09f4afa | |||
| e4e995e694 | |||
| 9d6f99e96b | |||
| ff0dc2622b | |||
| bae437c26b | |||
| 78b2d947fe | |||
| f61f6f178d | |||
| 4295fc02e2 | |||
| 2ef4b4b393 | |||
| 93c4692dc0 | |||
| f92f4ebb6c | |||
| 65ca86b38d | |||
| 5d3da784cb | |||
| 8d05136dcc | |||
| 8c2c33fdd0 | |||
| 3c5596144f | |||
| 02eae10c82 | |||
| 9976903789 | |||
| 7ce9b2408b | |||
| 764d62dc9c | |||
| dba3e118f4 | |||
| 8c53573857 | |||
| c8644f320a | |||
| 8b6348e77e | |||
| 48555dd793 | |||
| 02617bbb86 | |||
| e4a8f1e8d3 | |||
| 48ce6e6342 | |||
| 7f0564a3ca | |||
| aa9c8c21b7 | |||
| 0924c20e9e | |||
| a239223361 | |||
| e0341356c8 | |||
| b56c753063 | |||
| 04741d8a77 | |||
| 3721e4a515 |
Generated
+8
@@ -17237,6 +17237,14 @@
|
||||
"mqtt": "^2.18.3"
|
||||
}
|
||||
},
|
||||
"vue-multiple-progress": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-multiple-progress/-/vue-multiple-progress-1.6.0.tgz",
|
||||
"integrity": "sha512-TPk8B/vh9YrzuNQN9PcnMsMiNTaItsVia75+j23V7/VJuPCsRz+Omkwb/83p5CzQ97QT9ApEvdG7hupG+xb3aA==",
|
||||
"requires": {
|
||||
"vue": "^2.3.3"
|
||||
}
|
||||
},
|
||||
"vue-resize": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.4.5.tgz",
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"vue-epic-bus": "^0.1.2",
|
||||
"vue-gtm": "^2.2.0",
|
||||
"vue-mqtt": "^2.0.3",
|
||||
"vue-multiple-progress": "^1.6.0",
|
||||
"vue-router": "^3.1.3",
|
||||
"vue-slicksort": "^1.2.0",
|
||||
"vue-toastification": "^1.7.14",
|
||||
|
||||
@@ -8,6 +8,7 @@ burn_to_raspberry_util.sh : 給burn_to_raspberry.sh用的,裡面的指令為
|
||||
# Usage
|
||||
burn_to_raspberry.sh
|
||||
```shell
|
||||
要先安裝sshpass: https://www.codeleading.com/article/75275615628/
|
||||
sh ./burn_to_raspberry.sh <HOST> <PASSWORD>
|
||||
|
||||
e.g. sh ./burn_to_raspberry.sh pi@192.168.2.1 raspberry
|
||||
|
||||
@@ -34,6 +34,7 @@ import VueAxios from 'vue-axios'
|
||||
import 'nprogress/nprogress.css'
|
||||
|
||||
import Toast from 'vue-toastification'
|
||||
import Progress from 'vue-multiple-progress'
|
||||
import 'vue-toastification/dist/index.css'
|
||||
|
||||
import localforage from 'localforage'
|
||||
@@ -60,6 +61,7 @@ Vue.component('chart', ECharts)
|
||||
Vue.use(VueWorker)
|
||||
Vue.use(VueAxios, axios)
|
||||
Vue.use(Toast)
|
||||
Vue.use(Progress)
|
||||
|
||||
// Vue.use(VueMqtt, 'ws://52.194.17.114:8083')
|
||||
Vue.use(VueMqtt, 'ws://' + location.href.split('/')[2] + ':8083')
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 569 B |
Binary file not shown.
|
After Width: | Height: | Size: 473 B |
@@ -54,6 +54,20 @@ export const navigationRoutes = {
|
||||
iconClass: 'vuestic-iconset vuestic-iconset-files',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'trigger-file',
|
||||
displayName: 'TriggerFile',
|
||||
meta: {
|
||||
iconClass: 'vuestic-iconset vuestic-iconset-tables',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'sdcard-reader',
|
||||
displayName: 'SDcardReader',
|
||||
meta: {
|
||||
iconClass: 'vuestic-iconset vuestic-iconset-ui-elements',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
displayName: 'Settings',
|
||||
|
||||
@@ -23,6 +23,30 @@
|
||||
src="@/assets/img/gimer_gray.png"
|
||||
>
|
||||
</template>
|
||||
<template v-else-if="title == 'TriggerFile'">
|
||||
<img
|
||||
v-if="this.isHovered || this.isActive"
|
||||
class="app-sidebar-link__item-icon"
|
||||
src="@/assets/img/trigger_white.png"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="app-sidebar-link__item-icon"
|
||||
src="@/assets/img/trigger_gray.png"
|
||||
>
|
||||
</template>
|
||||
<template v-else-if="title == 'SDcardReader'">
|
||||
<img
|
||||
v-if="this.isHovered || this.isActive"
|
||||
class="app-sidebar-link__item-icon"
|
||||
src="@/assets/img/sdcard_reader_white.png"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="app-sidebar-link__item-icon"
|
||||
src="@/assets/img/sdcard_reader_gray.png"
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<va-icon
|
||||
v-if="icon"
|
||||
|
||||
@@ -510,7 +510,7 @@ export default {
|
||||
}
|
||||
this.drawDetailData() // reDraw
|
||||
},
|
||||
...mapActions('replay', [types.chart.init, types.chart.register, types.chart.clear, types.chart.updateGridList, types.chart.updateDataZoom, types.chart.setAxisDisplay]),
|
||||
...mapActions('replay', [types.chart.init, types.chart.register, types.chart.clear, types.chart.updateGridList, types.chart.updateDataZoom]),
|
||||
...mapActions('replay', [types.meta.init]),
|
||||
...mapActions('replay', [types.rec.init, types.rec.intoData, types.rec.initDisplaySetting, types.rec.updateDisplaySetting]),
|
||||
|
||||
@@ -530,7 +530,7 @@ export default {
|
||||
console.log(this.metaList)
|
||||
console.log(this.dataInfo)
|
||||
await this.register()
|
||||
this.setOverviewDataZoomStartAndEndChart()
|
||||
this.setOverviewDataZoomStartAndEndChart({ start: 0, end: 100 })
|
||||
this.$refs.top_control_panel.init() // will call changeAxis() here
|
||||
this.$refs.set_min_max.init()
|
||||
this.turnOnAxisEventHandler('click', 'yAxis.axisLabel', this.showMinMaxPanel, { axis: 'y' })
|
||||
|
||||
@@ -6,10 +6,15 @@
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-accordion>
|
||||
<va-collapse class="mb-2" :isOpenDefault="true">
|
||||
<span slot="header">Control Panel</span>
|
||||
<span slot="header">CONTROL PANEL</span>
|
||||
<div slot="body">
|
||||
<div>
|
||||
<top-control-panel :ref="'top_control_panel'" @refreshChart="setOption" @changeOverviewDataZoom="changeOverviewDataZoom"/>
|
||||
<top-control-panel
|
||||
:ref="'top_control_panel'"
|
||||
@changeOverviewDataZoom="changeOverviewDataZoom"
|
||||
@redrawAllChart="drawInitData()"
|
||||
@refreshChart="drawDetailData()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</va-collapse>
|
||||
@@ -21,9 +26,8 @@
|
||||
<div :class="chartAreaClass">
|
||||
<div class="row" >
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<neulive-replay-overview-chart :ref="'chart_overview'" class="mb-1" :cardheight="'7vh'" :data="chartOverviewData" @dataZoomEvent="dataZoomEvent($event)"/>
|
||||
<neulive-replay-chart :ref="'chart_detail'" @dblclick.native="distribute()" :cardheight="'80vh'" :data="chartDetailData" @dataZoomEvent="updateMarkArea($event)" />
|
||||
<change-channel-value-min-max :ref="'set_min_max'"/>
|
||||
<neulive-replay-overview-chart :ref="'chart_overview'" class="my-1 py-0" :cardheight="'7vh'" :data="chartOverviewData" @dataZoomEvent="dataZoomEvent($event)"/>
|
||||
<neulive-replay-chart :ref="'chart_detail'" class="my-1 py-0" @dblclick.native="distribute()" :cardheight="'84vh'" :data="chartDetailData" @dataZoomEvent="updateMarkArea($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,11 +40,12 @@ import { mapActions, mapGetters, mapMutations } from 'vuex'
|
||||
import NeuliveReplayChart from './NeuliveReplayChart.vue'
|
||||
import NeuliveReplayOverviewChart from './NeuliveReplayOverviewChart.vue'
|
||||
import TopControlPanel from './TopControlPanel.vue'
|
||||
import ChangeChannelValueMinMax from '@/components/analysis/replay/toolbox/ChangeChannelValueMinMax.vue'
|
||||
// import ChangeChannelValueMinMax from '@/components/analysis/replay/toolbox/ChangeChannelValueMinMax.vue'
|
||||
import api from '@/data/api/index'
|
||||
import { download } from '@/data/download/Download'
|
||||
import types from '@/store/modules/analysis/replay/types'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { download } from '@/data/download/Download'
|
||||
import { IIRFilter } from '@/data/task/filter/IIRFilter.js'
|
||||
|
||||
export default {
|
||||
name: 'neulive-data-replay',
|
||||
@@ -48,7 +53,7 @@ export default {
|
||||
NeuliveReplayChart,
|
||||
NeuliveReplayOverviewChart,
|
||||
TopControlPanel,
|
||||
ChangeChannelValueMinMax,
|
||||
// ChangeChannelValueMinMax,
|
||||
},
|
||||
computed: {
|
||||
displayType () {
|
||||
@@ -60,6 +65,7 @@ export default {
|
||||
|
||||
...mapFields('replay', [
|
||||
'chartData',
|
||||
'chartKey',
|
||||
'chartOverviewData',
|
||||
'chartDetailData',
|
||||
'currentScale',
|
||||
@@ -72,6 +78,12 @@ export default {
|
||||
'endTimeCurrent',
|
||||
'sampleMethod',
|
||||
'useSdCardData',
|
||||
'currentChunkDataSplitIndex',
|
||||
'maxChunkDataSplitNum',
|
||||
'sdDataChunkDict',
|
||||
'useFilter',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
@@ -104,6 +116,7 @@ export default {
|
||||
dataZoomStart: 0,
|
||||
dataZoomEnd: 100,
|
||||
inDistributeMode: false,
|
||||
userClickDirection: '',
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
@@ -112,12 +125,6 @@ export default {
|
||||
turnOnAxisEventHandler (eventName, query, handler, attr) {
|
||||
this.$refs.chart_detail.turnOnAxisEventHandler(eventName, query, handler, attr)
|
||||
},
|
||||
showMinMaxPanel (params) {
|
||||
this.$refs.set_min_max.showPanel(params)
|
||||
},
|
||||
setOption (options) {
|
||||
this.$refs.chart_detail.mergeOptions(options)
|
||||
},
|
||||
showChartLoading: function (chart) {
|
||||
const loadStyle = {
|
||||
text: '',
|
||||
@@ -142,22 +149,6 @@ export default {
|
||||
this.$refs.chart_overview.hideLoading()
|
||||
this.$refs.chart_detail.hideLoading()
|
||||
},
|
||||
getData: async function (scale, sampleMethod, id, channel, meta) {
|
||||
if (scale === 1) {
|
||||
// [TODO] 0: assume first meta temporarily
|
||||
if (this.useSdCardData && (this.metaList[0].sd_data_uploaded === true)) {
|
||||
return await download.rawSdDataByIDChannels(id, channel, meta)
|
||||
} else {
|
||||
return await download.rawByIDChannels(id, channel, meta)
|
||||
}
|
||||
} else {
|
||||
if (sampleMethod === 'mean') {
|
||||
return await download.miniMeanByIDChannel(id, channel, meta)
|
||||
} else if (sampleMethod === 'random') {
|
||||
return await download.miniRandomByIDChannel(id, channel, meta)
|
||||
}
|
||||
}
|
||||
},
|
||||
register: async function () {
|
||||
let chNumber = 0
|
||||
this.metaList.forEach(meta => {
|
||||
@@ -173,12 +164,12 @@ export default {
|
||||
|
||||
const register = this.metaList.map(meta => {
|
||||
this.metaList.map(meta => {
|
||||
JSON.parse(meta.channels).map(ch => {
|
||||
JSON.parse(meta.channels).sort((a, b) => a > b ? 1 : -1).map(ch => {
|
||||
this.registerChart({
|
||||
chartAllData: this.chartData,
|
||||
chartID: this.chartDetailData.chartID,
|
||||
gridIndex: _chNumber,
|
||||
deviceNameX: 'Time',
|
||||
deviceNameX: 'Category',
|
||||
deviceNameY: 'Neulive',
|
||||
deviceIDY: meta.name,
|
||||
channelY: ch,
|
||||
@@ -201,10 +192,7 @@ export default {
|
||||
}
|
||||
})
|
||||
await Promise.all(register)
|
||||
this.chartDetailData.series.forEach((series, index) => {
|
||||
this.chartDetailData.xAxis[series.xAxisIndex].min = 0
|
||||
this.chartDetailData.xAxis[series.xAxisIndex].max = this.maxTime
|
||||
})
|
||||
|
||||
this.chartOverviewData.series.forEach((series, index) => {
|
||||
this.chartOverviewData.xAxis[series.xAxisIndex].min = 0
|
||||
this.chartOverviewData.xAxis[series.xAxisIndex].max = this.maxTime
|
||||
@@ -221,30 +209,30 @@ export default {
|
||||
},
|
||||
updateMarkArea: function (e) {
|
||||
// console.log(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.dataZoomStart = dataZoom.start
|
||||
this.dataZoomEnd = dataZoom.end
|
||||
const startTime = (this.endTime - this.startTime) * dataZoom.start / 100
|
||||
const endTime = (this.endTime - this.startTime) * dataZoom.end / 100
|
||||
if (!this.useSdCardData) {
|
||||
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.dataZoomStart = dataZoom.start
|
||||
this.dataZoomEnd = dataZoom.end
|
||||
const startTime = (this.endTime - this.startTime) * dataZoom.start / 100
|
||||
const endTime = (this.endTime - this.startTime) * dataZoom.end / 100
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + startTime
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + endTime
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const id = parseInt(e.dataZoomId.substr(e.dataZoomId.length - 3, e.dataZoomId.length - 1))
|
||||
if (id === 0) {
|
||||
this.dataZoomStart = e.start
|
||||
this.dataZoomEnd = e.end
|
||||
const startTime = (this.endTime - this.startTime) * e.start / 100
|
||||
const endTime = (this.endTime - this.startTime) * e.end / 100
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + startTime
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + endTime
|
||||
// console.log(this.startTime, this.endTime)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const id = parseInt(e.dataZoomId.substr(e.dataZoomId.length - 3, e.dataZoomId.length - 1))
|
||||
if (id === 0) {
|
||||
this.dataZoomStart = e.start
|
||||
this.dataZoomEnd = e.end
|
||||
const startTime = (this.endTime - this.startTime) * e.start / 100
|
||||
const endTime = (this.endTime - this.startTime) * e.end / 100
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + startTime
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + endTime
|
||||
// console.log(this.startTime, this.endTime)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -260,7 +248,6 @@ export default {
|
||||
this.windowLength = this.endTime - this.startTime
|
||||
this.chartOverviewData.dataZoom[0].start = dataZoom.start
|
||||
this.chartOverviewData.dataZoom[0].end = dataZoom.end
|
||||
// console.log(this.startTime, this.endTime)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -271,7 +258,6 @@ export default {
|
||||
this.windowLength = this.endTime - this.startTime
|
||||
this.chartOverviewData.dataZoom[0].start = e.start
|
||||
this.chartOverviewData.dataZoom[0].end = e.end
|
||||
// console.log(this.startTime, this.endTime)
|
||||
}
|
||||
}
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + ((this.endTime - this.startTime) * (this.dataZoomStart / 100))
|
||||
@@ -279,138 +265,378 @@ export default {
|
||||
this.zoomTime = 0
|
||||
},
|
||||
changeOverviewDataZoom () {
|
||||
this.chartOverviewData.dataZoom[0].start = this.startTime * 100 / this.maxTime
|
||||
this.chartOverviewData.dataZoom[0].end = this.endTime * 100 / this.maxTime
|
||||
this.setOverviewDataZoomStartAndEndChart({
|
||||
start: this.startTime * 100 / this.maxTime,
|
||||
end: this.endTime * 100 / this.maxTime,
|
||||
})
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + ((this.endTime - this.startTime) * (this.dataZoomStart / 100))
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + ((this.endTime - this.startTime) * (this.dataZoomEnd / 100))
|
||||
this.$refs.chart_overview.mergeOptions(this.chartOverviewData)
|
||||
this.chartKey++
|
||||
},
|
||||
computeResampleScale (meta, duration) {
|
||||
const sampleRate = (meta.parameter_set.SAMPLE_RATE === 0) ? parseInt(8e5 / meta.parameter_set.ADC_CLOCK) : meta.parameter_set.SAMPLE_RATE // Hz
|
||||
const dataPointNum = (parseInt(duration) / 1e6) * sampleRate // us -> s: /1e6
|
||||
let scale
|
||||
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
|
||||
scale = '1000'
|
||||
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
|
||||
scale = '100'
|
||||
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
|
||||
scale = '10'
|
||||
} else {
|
||||
scale = '1'
|
||||
}
|
||||
return scale
|
||||
},
|
||||
drawInitData: async function () {
|
||||
this.showChartLoading('overview')
|
||||
this.setOverviewDataZoomStartAndEndChart({ start: 0, end: 100 })
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = 0
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.metaList[0].time_duration
|
||||
let metaIndex = 0
|
||||
for (const meta in this.dataInfo) {
|
||||
let metaInfo
|
||||
this.metaList.forEach(_meta => {
|
||||
if (parseInt(_meta.id) === parseInt(meta)) {
|
||||
metaInfo = _meta
|
||||
}
|
||||
})
|
||||
let scale = '1000'
|
||||
const sampleRate = (metaInfo.parameter_set.SAMPLE_RATE === 0) ? parseInt(8e5 / metaInfo.parameter_set.ADC_CLOCK) : metaInfo.parameter_set.SAMPLE_RATE // Hz
|
||||
const dataPointNum = (parseInt(metaInfo.time_duration) / 1e6) * sampleRate // us -> s: /1e6
|
||||
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
|
||||
scale = '1000'
|
||||
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
|
||||
scale = '100'
|
||||
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
|
||||
scale = '10'
|
||||
} else {
|
||||
scale = '1'
|
||||
}
|
||||
const metaInfo = this.metaList[metaIndex]
|
||||
const scale = this.computeResampleScale(metaInfo, metaInfo.time_duration)
|
||||
const allChannels = JSON.parse(metaInfo.channels)
|
||||
allChannels.sort((a, b) => a > b ? 1 : -1)
|
||||
this.currentScale = scale
|
||||
console.log('overview scale:', scale)
|
||||
const getAllData = async function (_this) {
|
||||
let seriesIndex = 0
|
||||
for (const ch in _this.dataInfo[meta]) {
|
||||
for (const ch of allChannels) {
|
||||
let _channels
|
||||
if (metaInfo.device.library_name === 'Neulive3.5ex') {
|
||||
if (ch >= 0 && ch <= 31) {
|
||||
_channels = allChannels.filter(c => c >= 0 && c <= 31)
|
||||
} else if (ch >= 32 && ch <= 63) {
|
||||
_channels = allChannels.filter(c => c >= 32 && c <= 63)
|
||||
}
|
||||
} else {
|
||||
_channels = allChannels
|
||||
}
|
||||
const idealTimeStep = parseFloat((1e6 / metaInfo.parameter_set.SAMPLE_RATE) * _channels.length)
|
||||
|
||||
_this.chartOverviewData.series[seriesIndex].data.length = 0
|
||||
_this.chartDetailData.xAxis[seriesIndex].data.length = 0
|
||||
_this.chartDetailData.series[seriesIndex].data.length = 0
|
||||
const dataList = _this.dataInfo[meta][ch][scale]
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
_this.intoDataRec({
|
||||
index: i,
|
||||
metaID: meta,
|
||||
const data = await _this.replayChartGetData({
|
||||
scale: parseInt(scale),
|
||||
sampleMethod: _this.sampleMethod,
|
||||
id: dataList[i].id,
|
||||
channel: ch,
|
||||
scale: scale,
|
||||
data: await _this.getData(parseInt(scale), _this.sampleMethod, dataList[i].id, ch, metaInfo),
|
||||
meta: metaInfo,
|
||||
})
|
||||
_this.chartOverviewData.series[seriesIndex].data.push(...dataList[i].data)
|
||||
_this.chartDetailData.series[seriesIndex].data.push(...dataList[i].data)
|
||||
_this.chartOverviewData.series[seriesIndex].data.push(...data)
|
||||
|
||||
for (let j = 0, len = data.length; j < len; j++) {
|
||||
const previousTime = _this.chartDetailData.xAxis[seriesIndex].data[_this.chartDetailData.xAxis[seriesIndex].data.length - 1]
|
||||
const dataTime = parseInt(data[j][0])
|
||||
if (_this.currentScale === '1' && previousTime + idealTimeStep < dataTime) {
|
||||
for (let lossTime = previousTime; lossTime < dataTime; lossTime += idealTimeStep) {
|
||||
_this.chartDetailData.xAxis[seriesIndex].data.push(lossTime)
|
||||
_this.chartDetailData.series[seriesIndex].data.push(null)
|
||||
}
|
||||
}
|
||||
_this.chartDetailData.xAxis[seriesIndex].data.push(dataTime)
|
||||
_this.chartDetailData.series[seriesIndex].data.push(data[j][1])
|
||||
}
|
||||
}
|
||||
_this.updateChartSamplingChart({ chartID: _this.chartOverviewData.chartID, seriesIndex: seriesIndex, method: function (frame) { return frame[0] } })
|
||||
_this.updateChartSamplingChart({ chartID: _this.chartDetailData.chartID, seriesIndex: seriesIndex, method: function (frame) { return frame[0] } })
|
||||
seriesIndex++
|
||||
}
|
||||
}
|
||||
await getAllData(this)
|
||||
metaIndex++
|
||||
}
|
||||
this.hideChartLoading()
|
||||
},
|
||||
getDataListIndex (dataList, pos) {
|
||||
let dataListIndex
|
||||
// [REGION]
|
||||
// because current chunk start time >= previous chunk end time
|
||||
// so the if-condition is for = condition
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
const chunkStartTime = parseInt(dataList[i].start_time_sec) * 1e6 + parseInt(dataList[i].start_time_micro)
|
||||
if (this.startTime === chunkStartTime) {
|
||||
dataListIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (dataListIndex === undefined) {
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
const chunkStartTime = parseInt(dataList[i].start_time_sec) * 1e6 + parseInt(dataList[i].start_time_micro)
|
||||
const chunkEndTime = parseInt(dataList[i].end_time_sec) * 1e6 + parseInt(dataList[i].end_time_micro)
|
||||
if (this.startTime >= chunkStartTime && this.startTime <= chunkEndTime) {
|
||||
dataListIndex = i
|
||||
break // only download 1 chunk data
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataListIndex === undefined) { // for the case start time not from 0s
|
||||
dataListIndex = 0
|
||||
}
|
||||
// [ENDREGION]
|
||||
if (pos === 'prev' && this.currentChunkDataSplitIndex === this.maxChunkDataSplitNum) {
|
||||
dataListIndex = Math.max(0, dataListIndex - 1)
|
||||
} else if (pos === 'next' && this.currentChunkDataSplitIndex === 1) {
|
||||
dataListIndex = Math.min(dataList.length - 1, dataListIndex + 1)
|
||||
} else if (pos === 'up') {
|
||||
dataListIndex = Math.max(0, dataListIndex - 1)
|
||||
} else if (pos === 'down') {
|
||||
dataListIndex = Math.min(dataList.length - 1, dataListIndex + 1)
|
||||
}
|
||||
return dataListIndex
|
||||
},
|
||||
async downloadSdDataChunk (scale, uuid, channel, startTime, endTime) {
|
||||
// only download 1 chunk data for efficiency
|
||||
return await this.replayChartGetData({
|
||||
scale: parseInt(scale),
|
||||
sampleMethod: null,
|
||||
id: null,
|
||||
channel: channel,
|
||||
meta: null,
|
||||
uuid: uuid,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
})
|
||||
},
|
||||
updateCurrentChunkDataSplitIndex (pos) {
|
||||
this.currentChunkDataSplitIndex = parseInt(this.currentChunkDataSplitIndex)
|
||||
if (pos === 'next') {
|
||||
this.currentChunkDataSplitIndex++
|
||||
if (this.currentChunkDataSplitIndex === this.maxChunkDataSplitNum + 1) {
|
||||
this.currentChunkDataSplitIndex = 1
|
||||
}
|
||||
} else if (pos === 'prev') {
|
||||
this.currentChunkDataSplitIndex--
|
||||
if (this.currentChunkDataSplitIndex < 1) {
|
||||
this.currentChunkDataSplitIndex = this.maxChunkDataSplitNum
|
||||
}
|
||||
} else if (pos === 'up' || pos === 'down') {
|
||||
this.currentChunkDataSplitIndex = 1
|
||||
}
|
||||
},
|
||||
async drawSdChart (metaIndex, pos) {
|
||||
this.showChartLoading('detail')
|
||||
const scale = '1'
|
||||
this.currentScale = scale
|
||||
const metaID = this.metaList[metaIndex].id
|
||||
const uuid = this.metaList[metaIndex].uuid
|
||||
const channelList = Object.keys(this.dataInfo[metaID])
|
||||
const ret = await download.rawSdDataAllTimeslotByUUID(uuid, channelList[0])
|
||||
// channelList[0]: first channel
|
||||
// cuz all channels data chunk will have same start time / end time
|
||||
// so use first channel info is enough, and can avoid keyboard input move forward multiple time
|
||||
const dataList = ret.data
|
||||
const dataListIndex = this.getDataListIndex(dataList, pos)
|
||||
let seriesIndex = 0
|
||||
for (const channel of channelList) {
|
||||
this.chartDetailData.xAxis[seriesIndex].data.length = 0
|
||||
this.chartDetailData.series[seriesIndex].data.length = 0
|
||||
if (pos === 'next' || pos === 'prev' || pos === 'up' || pos === 'down') {
|
||||
this.startTimeCurrent = this.startTime = parseInt(dataList[dataListIndex].start_time_sec) * 1e6 + parseInt(dataList[dataListIndex].start_time_micro)
|
||||
}
|
||||
this.endTimeCurrent = this.endTime = parseInt(dataList[dataListIndex].end_time_sec) * 1e6 + parseInt(dataList[dataListIndex].end_time_micro)
|
||||
this.windowLength = this.endTime - this.startTime
|
||||
|
||||
this.setOverviewDataZoomStartAndEndChart({
|
||||
start: this.startTime * 100 / parseInt(this.metaList[metaIndex].time_duration),
|
||||
end: this.endTime * 100 / parseInt(this.metaList[metaIndex].time_duration),
|
||||
})
|
||||
this.chartOverviewData.series[0].markArea.data[0][0].xAxis = this.startTime + (this.endTime - this.startTime) * ((this.currentChunkDataSplitIndex - 1) / this.maxChunkDataSplitNum)
|
||||
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + (this.endTime - this.startTime) * (this.currentChunkDataSplitIndex / this.maxChunkDataSplitNum)
|
||||
|
||||
if (this.currentChunkDataSplitIndex === 1 || this.currentChunkDataSplitIndex === this.maxChunkDataSplitNum) {
|
||||
this.isDownloading = true
|
||||
if (this.sdDataChunkDict[metaID] === undefined) {
|
||||
this.sdDataChunkDict[metaID] = {}
|
||||
}
|
||||
this.sdDataChunkDict[metaID][channel] = await this.downloadSdDataChunk(scale, uuid, channel, this.startTime, this.endTime)
|
||||
}
|
||||
const sdDataChunk = this.sdDataChunkDict[metaID][channel]
|
||||
const sdDataChunkSplit = sdDataChunk.slice(sdDataChunk.length * ((this.currentChunkDataSplitIndex - 1) / this.maxChunkDataSplitNum), sdDataChunk.length * (this.currentChunkDataSplitIndex / this.maxChunkDataSplitNum))
|
||||
const timeArr = sdDataChunkSplit.map(x => x[0])
|
||||
const valArr = sdDataChunkSplit.map(x => x[1])
|
||||
for (let i = 0, len = sdDataChunkSplit.length; i < len; i++) {
|
||||
this.chartDetailData.xAxis[seriesIndex].data.push(timeArr[i])
|
||||
let filterData = valArr[i]
|
||||
if (this.useFilter) {
|
||||
this.chartDetailData.series[seriesIndex].filter.forEach(filter => {
|
||||
if (filter.filter != null && filterData) {
|
||||
filterData = IIRFilter.filter(filter.filter, filterData)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.chartDetailData.series[seriesIndex].data.push(filterData)
|
||||
}
|
||||
seriesIndex++
|
||||
}
|
||||
this.chartKey++
|
||||
this.hideChartLoading()
|
||||
this.isDownloading = false
|
||||
},
|
||||
async moveToNextDataSubRegion () {
|
||||
if (this.useSdCardData) {
|
||||
this.updateCurrentChunkDataSplitIndex('next')
|
||||
await this.drawSdChart(0, 'next')
|
||||
}
|
||||
},
|
||||
async moveToPreviousDataSubRegion () {
|
||||
if (this.useSdCardData) {
|
||||
this.updateCurrentChunkDataSplitIndex('prev')
|
||||
await this.drawSdChart(0, 'prev')
|
||||
}
|
||||
},
|
||||
async moveToNextDataChunk () {
|
||||
if (this.useSdCardData) {
|
||||
this.updateCurrentChunkDataSplitIndex('down')
|
||||
await this.drawSdChart(0, 'down')
|
||||
} else {
|
||||
if (this.endTime + this.windowLength > this.metaList[0].time_duration) {
|
||||
this.endTime = this.metaList[0].time_duration
|
||||
this.startTime = this.endTime - this.windowLength
|
||||
} else {
|
||||
this.startTime = this.endTime
|
||||
this.endTime += this.windowLength
|
||||
}
|
||||
this.changeOverviewDataZoom()
|
||||
}
|
||||
},
|
||||
async moveToPreviousDataChunk () {
|
||||
if (this.useSdCardData) {
|
||||
this.updateCurrentChunkDataSplitIndex('up')
|
||||
await this.drawSdChart(0, 'up')
|
||||
} else {
|
||||
if (this.startTime - this.windowLength < 0) {
|
||||
this.startTime = 0
|
||||
this.endTime = this.windowLength
|
||||
} else {
|
||||
this.endTime = this.startTime
|
||||
this.startTime -= this.windowLength
|
||||
}
|
||||
this.changeOverviewDataZoom()
|
||||
}
|
||||
},
|
||||
drawDetailData: async function () {
|
||||
this.showChartLoading('detail')
|
||||
const timeDur = this.endTimeCurrent - this.startTimeCurrent
|
||||
let _metaIndex = 0
|
||||
for (const meta in this.dataInfo) {
|
||||
let metaInfo
|
||||
this.metaList.forEach(_meta => {
|
||||
if (parseInt(_meta.id) === parseInt(meta)) {
|
||||
metaInfo = _meta
|
||||
}
|
||||
})
|
||||
let scale = '1000'
|
||||
const sampleRate = (metaInfo.parameter_set.SAMPLE_RATE === 0) ? parseInt(8e5 / metaInfo.parameter_set.ADC_CLOCK) : metaInfo.parameter_set.SAMPLE_RATE // Hz
|
||||
const dataPointNum = (timeDur / 1e6) * sampleRate // us -> s: /1e6
|
||||
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
|
||||
scale = '1000'
|
||||
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
|
||||
scale = '100'
|
||||
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
|
||||
scale = '10'
|
||||
} else {
|
||||
scale = '1'
|
||||
}
|
||||
const metaInfo = this.metaList[_metaIndex]
|
||||
const allChannels = JSON.parse(metaInfo.channels)
|
||||
allChannels.sort((a, b) => a > b ? 1 : -1)
|
||||
|
||||
const scale = this.computeResampleScale(metaInfo, timeDur)
|
||||
this.currentScale = scale
|
||||
let seriesIndex = 0
|
||||
this.updateChartSamplingChart({ chartID: this.chartDetailData.chartID, seriesIndex: seriesIndex, method: function (frame) { return frame[0] } })
|
||||
for (const ch in this.dataInfo[meta]) {
|
||||
this.chartDetailData.series[seriesIndex].data.length = 0
|
||||
const dataList = this.dataInfo[meta][ch][scale]
|
||||
let startIndex = 0
|
||||
let endIndex = 0
|
||||
// get the subfile index
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
if (this.startTimeCurrent >= parseInt(dataList[i].start_time) && this.startTimeCurrent <= parseInt(dataList[i].end_time)) {
|
||||
startIndex = i
|
||||
if (this.useSdCardData) {
|
||||
this.updateCurrentChunkDataSplitIndex('curr')
|
||||
await this.drawSdChart(_metaIndex, 'curr')
|
||||
} else { // ble data
|
||||
for (const ch of allChannels) {
|
||||
let _channels
|
||||
if (metaInfo.device.library_name === 'Neulive3.5ex') {
|
||||
if (ch >= 0 && ch <= 31) {
|
||||
_channels = allChannels.filter(c => c >= 0 && c <= 31)
|
||||
} else if (ch >= 32 && ch <= 63) {
|
||||
_channels = allChannels.filter(c => c >= 32 && c <= 63)
|
||||
}
|
||||
} else {
|
||||
_channels = allChannels
|
||||
}
|
||||
if (this.endTimeCurrent >= parseInt(dataList[i].start_time) && this.endTimeCurrent <= parseInt(dataList[i].end_time)) {
|
||||
endIndex = i
|
||||
}
|
||||
}
|
||||
// download and push into chart
|
||||
let endFlag = false // because data is sorted by time, so if current processed data piece exceeds endTimeCurrent, can early stop
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const data = await this.getData(parseInt(scale), this.sampleMethod, dataList[i].id, ch, metaInfo)
|
||||
for (let j = 0, len = data.length; j < len; j++) {
|
||||
if (parseInt(data[j][0]) >= this.startTimeCurrent && parseInt(data[j][0]) <= this.endTimeCurrent) {
|
||||
const processedData = data[j]
|
||||
if (this.displayType.x === 'log') {
|
||||
processedData[0] = Math.abs(processedData[0])
|
||||
}
|
||||
if (this.displayType.y === 'log') {
|
||||
processedData[1] = Math.abs(processedData[1])
|
||||
}
|
||||
const idealTimeStep = parseFloat((1e6 / metaInfo.parameter_set.SAMPLE_RATE) * _channels.length)
|
||||
|
||||
this.chartDetailData.series[seriesIndex].data.push(processedData)
|
||||
this.chartDetailData.xAxis[seriesIndex].data.length = 0
|
||||
this.chartDetailData.series[seriesIndex].data.length = 0
|
||||
const dataList = this.dataInfo[meta][ch][scale]
|
||||
let startIndex = 0
|
||||
let endIndex = 0
|
||||
// get the subfile index
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
if (this.startTimeCurrent >= parseInt(dataList[i].start_time) && this.startTimeCurrent <= parseInt(dataList[i].end_time)) {
|
||||
startIndex = i
|
||||
}
|
||||
if (this.endTimeCurrent >= parseInt(dataList[i].start_time) && this.endTimeCurrent <= parseInt(dataList[i].end_time)) {
|
||||
endIndex = i
|
||||
}
|
||||
}
|
||||
// download and push into chart
|
||||
let endFlag = false // because data is sorted by time, so if current processed data piece exceeds endTimeCurrent, can early stop
|
||||
this.isDownloading = true
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const data = await this.replayChartGetData({
|
||||
scale: parseInt(scale),
|
||||
sampleMethod: this.sampleMethod,
|
||||
id: dataList[i].id,
|
||||
channel: ch,
|
||||
meta: metaInfo,
|
||||
})
|
||||
for (let j = 0, len = data.length; j < len; j++) {
|
||||
const previousTime = this.chartDetailData.xAxis[seriesIndex].data[this.chartDetailData.xAxis[seriesIndex].data.length - 1]
|
||||
const dataTime = parseInt(data[j][0])
|
||||
if (dataTime >= this.startTimeCurrent && dataTime <= this.endTimeCurrent) {
|
||||
if (this.currentScale === '1' && previousTime + idealTimeStep < dataTime) {
|
||||
for (let lossTime = previousTime; lossTime < dataTime; lossTime += idealTimeStep) {
|
||||
this.chartDetailData.xAxis[seriesIndex].data.push(lossTime)
|
||||
this.chartDetailData.series[seriesIndex].data.push(null)
|
||||
}
|
||||
}
|
||||
this.chartDetailData.xAxis[seriesIndex].data.push(dataTime)
|
||||
let filterData = data[j][1]
|
||||
if (this.useFilter) {
|
||||
this.chartDetailData.series[seriesIndex].filter.forEach(filter => {
|
||||
if (filter.filter != null && filterData) {
|
||||
filterData = IIRFilter.filter(filter.filter, filterData)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.chartDetailData.series[seriesIndex].data.push(filterData)
|
||||
}
|
||||
// early stop
|
||||
if (dataTime > this.endTimeCurrent) {
|
||||
endFlag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// early stop
|
||||
if (parseInt(data[j][0]) > this.endTimeCurrent) {
|
||||
endFlag = true
|
||||
if (endFlag === true) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// early stop
|
||||
if (endFlag === true) {
|
||||
break
|
||||
}
|
||||
seriesIndex++
|
||||
}
|
||||
for (let i = 0, len = this.chartDetailData.xAxis.length; i < len; i++) {
|
||||
this.chartDetailData.xAxis[i].min = this.startTimeCurrent
|
||||
this.chartDetailData.xAxis[i].max = this.endTimeCurrent
|
||||
}
|
||||
seriesIndex++
|
||||
}
|
||||
_metaIndex++
|
||||
}
|
||||
this.chartKey++
|
||||
this.hideChartLoading()
|
||||
this.isDownloading = false
|
||||
},
|
||||
checkDataZoom: function () {
|
||||
checkDataZoom: async function () {
|
||||
this.zoomTime++
|
||||
if (this.zoomTime >= 2) {
|
||||
if (this.startTimeCurrent !== this.startTime || this.endTimeCurrent !== this.endTime) {
|
||||
this.startTimeCurrent = this.startTime
|
||||
this.endTimeCurrent = this.endTime
|
||||
if (this.useSdCardData) {
|
||||
const metaID = this.metaList[0].id
|
||||
const uuid = this.metaList[0].uuid
|
||||
const channelList = Object.keys(this.dataInfo[metaID])
|
||||
const ret = await download.rawSdDataAllTimeslotByUUID(uuid, channelList[0])
|
||||
// channelList[0]: first channel
|
||||
// cuz all channels data chunk will have same start time / end time
|
||||
// so use first channel info is enough, and can avoid keyboard input move forward multiple time
|
||||
const dataList = ret.data
|
||||
const dataListIndex = this.getDataListIndex(dataList, 'curr')
|
||||
|
||||
this.startTimeCurrent = this.startTime = parseInt(dataList[dataListIndex].start_time_sec) * 1e6 + parseInt(dataList[dataListIndex].start_time_micro)
|
||||
this.endTimeCurrent = this.endTime = parseInt(dataList[dataListIndex].end_time_sec) * 1e6 + parseInt(dataList[dataListIndex].end_time_micro)
|
||||
} else {
|
||||
this.startTimeCurrent = this.startTime
|
||||
this.endTimeCurrent = this.endTime
|
||||
}
|
||||
this.windowLength = this.endTime - this.startTime
|
||||
this.currentChunkDataSplitIndex = 1
|
||||
this.drawDetailData()
|
||||
}
|
||||
}
|
||||
@@ -480,7 +706,30 @@ export default {
|
||||
this.inDistributeMode = true
|
||||
}
|
||||
},
|
||||
...mapActions('replay', [types.chart.init, types.chart.register, types.chart.clear, types.chart.updateGridList, types.chart.updateDataZoom]),
|
||||
|
||||
async handleKeyboardInputEvent (event) {
|
||||
if (event.key === 'ArrowRight') {
|
||||
this.userClickDirection = 'next'
|
||||
await this.moveToNextDataSubRegion()
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
this.userClickDirection = 'prev'
|
||||
await this.moveToPreviousDataSubRegion()
|
||||
} else if (event.key === 'ArrowDown') {
|
||||
this.userClickDirection = 'next'
|
||||
await this.moveToNextDataChunk()
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
this.userClickDirection = 'prev'
|
||||
await this.moveToPreviousDataChunk()
|
||||
}
|
||||
},
|
||||
|
||||
async eventHandler (event) {
|
||||
if (this.isInitDone && !this.isDownloading) {
|
||||
await this.handleKeyboardInputEvent(event)
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions('replay', [types.chart.init, types.chart.register, types.chart.clear, types.chart.updateGridList, types.chart.updateDataZoom, types.chart.getData]),
|
||||
...mapActions('replay', [types.meta.init]),
|
||||
...mapActions('replay', [types.rec.init, types.rec.intoData, types.rec.initDisplaySetting, types.rec.updateDisplaySetting]),
|
||||
|
||||
@@ -490,9 +739,6 @@ export default {
|
||||
...mapGetters('replay', [types.meta.getAll]),
|
||||
...mapGetters('replay', [types.rec.getAll]),
|
||||
},
|
||||
// use watch because va-select with @change will have bug
|
||||
watch: {
|
||||
},
|
||||
async mounted () {
|
||||
this.showChartLoading()
|
||||
const metaIDList = this.$route.params.metaList.split('-')
|
||||
@@ -503,16 +749,19 @@ export default {
|
||||
console.log(this.metaList)
|
||||
console.log(this.dataInfo)
|
||||
await this.register()
|
||||
this.setOverviewDataZoomStartAndEndChart()
|
||||
await this.drawInitData()
|
||||
this.dataZoomTimer = setInterval(this.checkDataZoom, 1000)
|
||||
this.$refs.top_control_panel.init()
|
||||
this.$refs.set_min_max.init()
|
||||
this.dataZoomTimer = setInterval(this.checkDataZoom, 1000)
|
||||
this.turnOnAxisEventHandler('click', 'yAxis.axisLabel', this.showMinMaxPanel, { axis: 'y' })
|
||||
// [TODO] change the global event listener to chart-specific event listener
|
||||
window.addEventListener('keyup', this.eventHandler)
|
||||
|
||||
await this.drawInitData()
|
||||
this.isInitDone = true
|
||||
console.log(this.chartOverviewData)
|
||||
console.log(this.chartDetailData)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.currentChunkDataSplitIndex = 1
|
||||
this.$refs.chart_overview.clear()
|
||||
this.$refs.chart_detail.clear()
|
||||
for (const id in this.chartData) {
|
||||
@@ -522,8 +771,13 @@ export default {
|
||||
delete this.chartData[id]
|
||||
}
|
||||
this.clearChart({ chartAllData: this.chartData })
|
||||
this.startTime = 0
|
||||
this.endTime = 0
|
||||
this.startTimeCurrent = 0
|
||||
this.endTimeCurrent = 0
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('keyup', this.eventHandler)
|
||||
clearInterval(this.dataZoomTimer)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div class="flex" :class="cardclass">
|
||||
<va-card :style="'height: ' + cardheight + ';'">
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
<div>
|
||||
<va-button small class="mb-3" @click="selectAllSeries">
|
||||
SELECT ALL
|
||||
</va-button>
|
||||
<va-button small class="mb-3" @click="inverseSelectAllSeries">
|
||||
INVERSE SELECT
|
||||
</va-button>
|
||||
</div>
|
||||
<chart :ref="'chart_ref'" :key="chartKey" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -9,6 +17,7 @@
|
||||
<script>
|
||||
import { mapMutations } from 'vuex'
|
||||
import types from '@/store/modules/analysis/replay/types'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'neulive-replay-chart',
|
||||
@@ -40,28 +49,34 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chartID: 1,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartKey',
|
||||
'chartData',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
resize: function () {
|
||||
chartEvent: function () {
|
||||
if (this.$refs.chart_ref != null) {
|
||||
this.$refs.chart_ref.resize()
|
||||
}
|
||||
if (this.$refs.chart_ref.chart != null) {
|
||||
this.$refs.chart_ref.chart.on('legendselectchanged', this.legendChange)
|
||||
this.$refs.chart_ref.chart.on('legendselectall', this.legendChange)
|
||||
this.$refs.chart_ref.chart.on('legendinverseselect', this.legendChange)
|
||||
}
|
||||
},
|
||||
updateZoom: function (e) {
|
||||
this.$emit('dataZoomEvent', e)
|
||||
},
|
||||
mergeOptions (options) {
|
||||
this.$refs.chart_ref.mergeOptions(options)
|
||||
},
|
||||
turnOnAxisEventHandler (eventName, query, handler, attr) {
|
||||
const chartID = 1
|
||||
this.updateAxisEventHandlerChart({
|
||||
chartID: chartID,
|
||||
chartID: this.chartID,
|
||||
axis: attr.axis,
|
||||
status: 'on',
|
||||
})
|
||||
@@ -69,9 +84,8 @@ export default {
|
||||
this.$refs.chart_ref.chart._zr.on(eventName, query, handler)
|
||||
},
|
||||
turnOffAxisEventHandler (eventName, handler, attr) {
|
||||
const chartID = 1
|
||||
this.updateAxisEventHandlerChart({
|
||||
chartID: chartID,
|
||||
chartID: this.chartID,
|
||||
axis: attr.axis,
|
||||
status: 'off',
|
||||
})
|
||||
@@ -87,11 +101,36 @@ export default {
|
||||
clear () {
|
||||
this.$refs.chart_ref.clear()
|
||||
},
|
||||
legendChange (params) {
|
||||
console.log('legendChange', params)
|
||||
const activeLegendLen = Object.values(params.selected).filter(val => val === true).length
|
||||
let index = 0
|
||||
for (const [gridIndex, value] of Object.values(params.selected).entries()) { // [TODO] 技術債,目前real time chart一個grid只對應一條series
|
||||
if (value === true) {
|
||||
this.chartData[this.chartID].grid[gridIndex].height = (82 / activeLegendLen) - 2 + '%'
|
||||
this.chartData[this.chartID].grid[gridIndex].top = index * (82 / activeLegendLen) + 7 + '%'
|
||||
index++
|
||||
} else {
|
||||
this.chartData[this.chartID].grid[gridIndex].height = '0%'
|
||||
this.chartData[this.chartID].grid[gridIndex].top = '0%'
|
||||
}
|
||||
}
|
||||
},
|
||||
selectAllSeries () {
|
||||
this.$refs.chart_ref.dispatchAction({
|
||||
type: 'legendAllSelect',
|
||||
})
|
||||
},
|
||||
inverseSelectAllSeries () {
|
||||
this.$refs.chart_ref.dispatchAction({
|
||||
type: 'legendInverseSelect',
|
||||
})
|
||||
},
|
||||
|
||||
...mapMutations('replay', [types.chart.updateAxisEventHandler]),
|
||||
},
|
||||
async mounted () {
|
||||
this.resizeTimer = setInterval(this.resize, 1000)
|
||||
mounted () {
|
||||
this.resizeTimer = setInterval(this.chartEvent, 1000)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.resizeTimer)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<div class="flex" :class="cardclass">
|
||||
<va-card :style="'height: 8vh;'">
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
<chart :ref="'chart_ref'" :key="chartKey" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'neulive-replay-overview-chart',
|
||||
@@ -41,6 +42,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartKey',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
@@ -53,9 +57,6 @@ export default {
|
||||
updateZoom: function (e) {
|
||||
this.$emit('dataZoomEvent', e)
|
||||
},
|
||||
mergeOptions (options) {
|
||||
this.$refs.chart_ref.mergeOptions(options)
|
||||
},
|
||||
showLoading (options) {
|
||||
this.$refs.chart_ref.showLoading(options)
|
||||
},
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-card>
|
||||
<div class="flex-container">
|
||||
<edit-file-name class="flex-child" :ref="'edit_file_name'"/>
|
||||
<download-file class="flex-child2"/>
|
||||
<delete-file class="flex-child2"/>
|
||||
<edit-file-name :ref="'edit_file_name'"/>
|
||||
<div class="row py-2">
|
||||
<div class="col">
|
||||
<download-file />
|
||||
</div>
|
||||
<div class="col">
|
||||
<delete-file />
|
||||
</div>
|
||||
<div class="col">
|
||||
<va-button flat small color="gray" icon="fa fa-filter" @click="openFilterPanel"/>
|
||||
</div>
|
||||
</div>
|
||||
<show-resample-rate/>
|
||||
<div class="flex-container">
|
||||
<change-window-start-and-size class="flex-child" :ref="'change_window_start_and_size'" @changeOverviewDataZoom="changeOverviewDataZoom"/>
|
||||
<lock-data-zoom class="flex-child2" :chartID="0"/>
|
||||
<div>
|
||||
<show-parameter v-if="metaList[0] && metaList[0].device.library_name !== 'Neulive3.5-REC'" class="mb-2" :ref="'show_param'"/>
|
||||
<show-resample-rate/>
|
||||
<div class="flex-container">
|
||||
<change-window-start-and-size class="flex-child" :ref="'change_window_start_and_size'" @changeOverviewDataZoom="changeOverviewDataZoom"/>
|
||||
<lock-data-zoom class="flex-child2" :chartID="0"/>
|
||||
</div>
|
||||
<change-axis v-show="showXAxis" :ref="'change_axis_x'" axis="x"/>
|
||||
<change-axis v-show="showYAxis" :ref="'change_axis_y'" axis="y"/>
|
||||
<change-unit v-show="showXUnit" :ref="'change_unit_x'" axis="x"/>
|
||||
<change-unit :ref="'change_unit_y'" class="box_inline-block" axis="y"/>
|
||||
<sd-card-toggle :ref="'sd_card_toggle'" :metaIndex="0" @redrawAllChart="redrawAllChart" @refreshChart="refreshChart"/>
|
||||
<change-current-chunk-index @refreshChart="refreshChart"/>
|
||||
</div>
|
||||
<change-axis v-show="showXAxis" :ref="'change_axis_x'" axis="x"/>
|
||||
<change-axis v-show="showYAxis" :ref="'change_axis_y'" axis="y"/>
|
||||
<change-unit v-show="showXUnit" :ref="'change_unit_x'" axis="x" @refreshChart="refreshChart"/>
|
||||
<change-unit :ref="'change_unit_y'" class="box_inline-block" axis="y" @refreshChart="refreshChart"/>
|
||||
<change-scale :ref="'change_scale_y'" class="box_inline-block" axis="y"/>
|
||||
<sd-card-toggle :ref="'sd_card_toggle'" :metaIndex="0"/>
|
||||
<show-parameter :ref="'show_param'"/>
|
||||
<filter-toggle :ref="'filter_toggle'" @refreshChart="refreshChart"/>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -26,7 +36,7 @@
|
||||
import ShowResampleRate from '@/components/analysis/replay/toolbox/ShowResampleRate.vue'
|
||||
import ChangeAxis from '@/components/analysis/replay/toolbox/ChangeAxis.vue'
|
||||
import ChangeUnit from '@/components/analysis/replay/toolbox/ChangeUnit.vue'
|
||||
import ChangeScale from '@/components/analysis/replay/toolbox/ChangeScale.vue'
|
||||
import ChangeCurrentChunkIndex from '@/components/analysis/replay/toolbox/ChangeCurrentChunkIndex.vue'
|
||||
import DownloadFile from '@/components/analysis/replay/toolbox/DownloadFile.vue'
|
||||
import DeleteFile from '@/components/analysis/replay/toolbox/DeleteFile.vue'
|
||||
import EditFileName from '@/components/analysis/replay/toolbox/EditFileName.vue'
|
||||
@@ -34,6 +44,7 @@ import ShowParameter from '@/components/analysis/replay/toolbox/ShowParameter.vu
|
||||
import SdCardToggle from '@/components/analysis/replay/toolbox/SdCardToggle.vue'
|
||||
import LockDataZoom from '@/components/analysis/replay/toolbox/LockDataZoom.vue'
|
||||
import ChangeWindowStartAndSize from '@/components/analysis/replay/toolbox/ChangeWindowStartAndSize.vue'
|
||||
import FilterToggle from '@/components/analysis/replay/toolbox/FilterToggle.vue'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
@@ -42,7 +53,7 @@ export default {
|
||||
ShowResampleRate,
|
||||
ChangeAxis,
|
||||
ChangeUnit,
|
||||
ChangeScale,
|
||||
ChangeCurrentChunkIndex,
|
||||
DownloadFile,
|
||||
DeleteFile,
|
||||
EditFileName,
|
||||
@@ -50,11 +61,15 @@ export default {
|
||||
SdCardToggle,
|
||||
LockDataZoom,
|
||||
ChangeWindowStartAndSize,
|
||||
FilterToggle,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'metaList',
|
||||
'showCyclePanel',
|
||||
'useSdCardData',
|
||||
'startTime',
|
||||
'endTime',
|
||||
]),
|
||||
},
|
||||
async created () {
|
||||
@@ -76,18 +91,29 @@ export default {
|
||||
this.$refs.change_axis_y.init(this.deviceName)
|
||||
this.$refs.change_unit_x.init(this.deviceName)
|
||||
this.$refs.change_unit_y.init(this.deviceName)
|
||||
this.$refs.change_scale_y.init(this.deviceName)
|
||||
this.$refs.edit_file_name.init()
|
||||
this.$refs.show_param.init(this.deviceName)
|
||||
if (this.deviceName !== 'Neulive3.5-REC') {
|
||||
this.$refs.show_param.init(this.deviceName)
|
||||
}
|
||||
this.$refs.change_window_start_and_size.init()
|
||||
this.$refs.sd_card_toggle.init()
|
||||
this.$refs.filter_toggle.init()
|
||||
},
|
||||
changeOverviewDataZoom () {
|
||||
this.$emit('changeOverviewDataZoom')
|
||||
},
|
||||
refreshChart (options) {
|
||||
this.$emit('refreshChart', options)
|
||||
redrawAllChart () {
|
||||
this.$emit('redrawAllChart')
|
||||
},
|
||||
refreshChart () {
|
||||
this.$emit('refreshChart')
|
||||
},
|
||||
openFilterPanel () {
|
||||
const sd = this.useSdCardData ? 'sd' : 'ble'
|
||||
const href = location.href.split('/')[2]
|
||||
window.open(`http://${href}/#/admin/data-spike/${this.metaList[0].id}/${this.startTime}/${this.endTime}/${sd}`)
|
||||
},
|
||||
// startTime
|
||||
},
|
||||
// use watch because va-select with @change will have bug
|
||||
watch: {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<script>
|
||||
|
||||
import { mapActions, mapMutations } from 'vuex'
|
||||
import { mapMutations } from 'vuex'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import types from '@/store/modules/analysis/replay/types'
|
||||
|
||||
@@ -99,7 +99,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions('replay', [types.chart.setAxisDisplay]),
|
||||
...mapMutations('replay', [types.chart.updateSeries]),
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -52,6 +52,7 @@ export default {
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartData',
|
||||
'chartKey',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
@@ -85,7 +86,7 @@ export default {
|
||||
minVal: this.minMaxInfo[this.gridIndex].min,
|
||||
maxVal: this.minMaxInfo[this.gridIndex].max,
|
||||
})
|
||||
this.$emit('refreshChart', this.chartData[this.chartID])
|
||||
this.chartKey++
|
||||
},
|
||||
...mapMutations('replay', [types.chart.updateAxisValMinMax]),
|
||||
},
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="row flex sm12 xl12 md12 xs12 px-0 py-0 mx-0 my-0">
|
||||
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0" v-if="useSdCardData">
|
||||
<p class="display-6"> Chunk Index </p>
|
||||
<va-input
|
||||
@keyup.enter="refreshChart()"
|
||||
v-model="currentChunkDataSplitIndex"
|
||||
class="mb-0"
|
||||
:readonly="!isInitDone || isDownloading"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;">
|
||||
/{{ maxChunkDataSplitNum }}
|
||||
</p>
|
||||
</va-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'ChangeWindowSize',
|
||||
components: {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'useSdCardData',
|
||||
'currentChunkDataSplitIndex',
|
||||
'maxChunkDataSplitNum',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
refreshChart () {
|
||||
this.$emit('refreshChart')
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -177,7 +177,6 @@ export default {
|
||||
this.chartData[this.chartID].xAxis[this.metaIndex].max = endTime
|
||||
counter++
|
||||
}
|
||||
// this.$emit('refreshChart', this.chartData[this.chartID])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-select
|
||||
@input="changeUnit()"
|
||||
:label="''"
|
||||
v-if="deviceName!=='EliteEIS'"
|
||||
v-model="valueFormat"
|
||||
textBy="unit"
|
||||
class="va-select-without-margin"
|
||||
:options="valueFormatOptions[dataType]"
|
||||
:noClear='true'
|
||||
/>
|
||||
<va-select
|
||||
@input="changeUnit()"
|
||||
:label="''"
|
||||
v-if="deviceName==='EliteEIS'"
|
||||
v-model="valueFormat"
|
||||
textBy="unit"
|
||||
class="va-select-without-margin"
|
||||
:options="eisValueFormatOptions[dataType]"
|
||||
:noClear='true'
|
||||
/>
|
||||
<div class="row flex sm12 xl12 md12 xs12 px-0 py-0 mx-0 my-0">
|
||||
<div class="flex sm12 xl12 md12 xs12 px-0 mx-0">
|
||||
<p class="display-6"> UNIT </p>
|
||||
<va-select
|
||||
@input="changeUnit()"
|
||||
:label="''"
|
||||
v-if="deviceName!=='EliteEIS'"
|
||||
v-model="valueFormat"
|
||||
textBy="unit"
|
||||
class="va-select-without-margin"
|
||||
:options="valueFormatOptions[dataType]"
|
||||
:noClear='true'
|
||||
:disabled="!isInitDone || isDownloading"
|
||||
/>
|
||||
<va-select
|
||||
@input="changeUnit()"
|
||||
:label="''"
|
||||
v-if="deviceName==='EliteEIS'"
|
||||
v-model="valueFormat"
|
||||
textBy="unit"
|
||||
class="va-select-without-margin"
|
||||
:options="eisValueFormatOptions[dataType]"
|
||||
:noClear='true'
|
||||
:disabled="!isInitDone || isDownloading"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,11 +51,14 @@ export default {
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartData',
|
||||
'chartKey',
|
||||
'metaList',
|
||||
'valueFormatX',
|
||||
'valueFormatY',
|
||||
'axisXDataType',
|
||||
'axisYDataType',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
@@ -187,7 +195,6 @@ export default {
|
||||
channel: -1, // any num < 256, cuz channel only use for determining whether add unit text
|
||||
})
|
||||
}
|
||||
this.$emit('refreshChart', this.chartData[chartID])
|
||||
},
|
||||
changeUnitByAxis () {
|
||||
if (this.axis === 'x') {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="row flex sm12 xl12 md12 xs12 px-0 py-0 mx-0 my-0">
|
||||
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
|
||||
<p class="display-6"> Window Start <p/>
|
||||
<p class="display-6"> WINDOW START <p/>
|
||||
<va-input
|
||||
@keyup.enter="startItemChange(startTimeInput)"
|
||||
@blur="startItemChange(startTimeInput)"
|
||||
v-model="startTimeInput"
|
||||
:readonly="useSdCardData || !isInitDone || isDownloading"
|
||||
class="mb-0 flex-child"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
@@ -14,11 +14,11 @@
|
||||
</va-input>
|
||||
</div>
|
||||
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
|
||||
<p class="display-6"> Window Size <p/>
|
||||
<p class="display-6"> WINDOW SIZE <p/>
|
||||
<va-input
|
||||
@keyup.enter="lenItemChange(windowLengthInput)"
|
||||
@blur="lenItemChange(windowLengthInput)"
|
||||
v-model="windowLengthInput"
|
||||
:readonly="useSdCardData || !isInitDone || isDownloading"
|
||||
class="mb-0 flex-child"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
@@ -63,6 +63,9 @@ export default {
|
||||
'startTime',
|
||||
'endTime',
|
||||
'windowLength',
|
||||
'useSdCardData',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
created () {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-button flat small color="gray" icon="fa fa-download" @click.stop="downloadCSV()"/>
|
||||
<va-popover :message="`SD card data`" placement="top">
|
||||
<div v-if="metaList[0]">
|
||||
<va-button v-if="metaList[0].sd_data_uploaded" flat small color="primary" icon="fa fa-download" @click.stop="downloadSdCard()"/>
|
||||
</div>
|
||||
</va-popover>
|
||||
<va-popover :message="`BLE data`" placement="top">
|
||||
<va-button flat small color="success" icon="fa fa-download" @click.stop="downloadCsv()"/>
|
||||
</va-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { download } from '@/data/download/Download'
|
||||
// import { exportData } from '@/data/export/export'
|
||||
import api from '@/data/api/index'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
@@ -29,14 +35,24 @@ export default {
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
downloadCSV: async function () {
|
||||
for (let i = 0; i < this.metaList.length; i++) {
|
||||
const meta = this.metaList[i]
|
||||
if (meta.device.library_name.indexOf('Elite') >= 0) {
|
||||
window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/download/excel/' + meta.id)
|
||||
} else if (meta.device.library_name.indexOf('Neulive') >= 0) {
|
||||
window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/download/csv/' + meta.id)
|
||||
}
|
||||
async downloadCsv () {
|
||||
const href = location.href.split('/')[2]
|
||||
const meta = this.metaList[0]
|
||||
const connectCheck = await api.connection.check()
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/csv/' + meta.id)
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv/' + meta.id)
|
||||
}
|
||||
},
|
||||
async downloadSdCard () {
|
||||
const href = location.href.split('/')[2]
|
||||
const meta = this.metaList[0]
|
||||
const connectCheck = await api.connection.check()
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/csv/' + meta.id + '/sd')
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv/' + meta.id + '/sd')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-button flat small color="gray" icon="fa fa-file-text-o" @click.stop="editFileConfirm()"> {{ filename }} </va-button>
|
||||
<span class="display-6"> {{ filename }} </span>
|
||||
<va-button flat small color="gray" icon="fa fa-pencil" @click.stop="editFileConfirm()" />
|
||||
<va-modal
|
||||
class="flex sm12 xl12 md12 xs12"
|
||||
v-model="showModal.editFile"
|
||||
@@ -91,6 +92,7 @@ export default {
|
||||
editFile: async function () {
|
||||
if (this.editInfo.success === true) {
|
||||
await api.meta.update(this.editInfo.info.id, { name: this.editInfo.changedName })
|
||||
await api.meta.update30(this.editInfo.info.uuid, { name: this.editInfo.changedName })
|
||||
this.editInfo.name = this.editInfo.changedName
|
||||
this.filename = this.editInfo.changedName
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="box_inline-block display-6"> RUN FILTER </span>
|
||||
<va-toggle
|
||||
class="box_inline-block"
|
||||
flat
|
||||
small
|
||||
v-model="useFilter"
|
||||
@input="refreshChart()"
|
||||
:disable="!isInitDone || isDownloading"
|
||||
/>
|
||||
<div class="row mt-2" v-if="initDone">
|
||||
<div v-for="(filter, filterIndex) in filterList" :key="filter.id" :index="filterIndex">
|
||||
<global-filter :ref="'filter_ref'" :chartID="chartID" :filterIndex="filterIndex" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<va-button color="success" small :disabled="useFilter" @click="openAllFilter()">
|
||||
Activate All Filters
|
||||
</va-button>
|
||||
<va-button color="danger" small :disabled="useFilter" @click="closeAllFilter()">
|
||||
Deactivate All Filters
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import GlobalFilter from '@/components/analysis/replay/toolbox/GlobalFilter.vue'
|
||||
|
||||
export default {
|
||||
name: 'FilterToggle',
|
||||
components: {
|
||||
GlobalFilter,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartData',
|
||||
'useFilter',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
initDone: false,
|
||||
filterList: [],
|
||||
chartID: 1,
|
||||
maxFilterNum: 3,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
this.useFilter = false
|
||||
this.initDone = true
|
||||
},
|
||||
getFilterStatus () {
|
||||
let isAllClose = true
|
||||
let isReady = true
|
||||
this.chartData[this.chartID].series.forEach(_series => {
|
||||
if (_series.filter.some(_filter => _filter.filter !== null)) {
|
||||
isAllClose = false
|
||||
}
|
||||
})
|
||||
if (this.$refs.filter_ref.some(_ref => _ref.isLoading === true)) {
|
||||
isReady = false
|
||||
}
|
||||
return { isAllClose: isAllClose, isReady: isReady }
|
||||
},
|
||||
refreshChart () {
|
||||
const { isAllClose, isReady } = this.getFilterStatus()
|
||||
if (!isAllClose && isReady) {
|
||||
this.$emit('refreshChart')
|
||||
} else {
|
||||
this.useFilter = false
|
||||
this.pageToast('Please open either filter or wait for filter loading done')
|
||||
}
|
||||
},
|
||||
addFilter () {
|
||||
let availableID
|
||||
for (let i = 0; i < this.maxFilterNum; i++) {
|
||||
if (this.filterList.some(block => block.id === i) === false) { // ref: https://www.tutorialrepublic.com/faq/how-to-check-if-an-array-includes-an-object-in-javascript.php
|
||||
availableID = i
|
||||
break
|
||||
}
|
||||
}
|
||||
this.filterList.push({ id: availableID })
|
||||
},
|
||||
async openAllFilter () {
|
||||
this.closeAllFilter()
|
||||
for (let i = 0; i < this.maxFilterNum; i++) {
|
||||
await this.$refs.filter_ref[i].updateFilterInfo(i)
|
||||
}
|
||||
},
|
||||
closeAllFilter () {
|
||||
this.chartData[this.chartID].series.forEach(_series => {
|
||||
_series.filter.forEach(_filter => {
|
||||
_filter.filter = null
|
||||
})
|
||||
})
|
||||
},
|
||||
pageToast: function (mes) {
|
||||
this.showToast(
|
||||
mes,
|
||||
{
|
||||
icon: 'fa-bell',
|
||||
position: 'bottom-right',
|
||||
duration: 4000,
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
for (let i = 0; i < this.maxFilterNum; i++) {
|
||||
this.addFilter()
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.box_inline-block {
|
||||
padding: 1px;
|
||||
margin: 1px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<div class="row pl-2 ma-0">
|
||||
<va-checkbox
|
||||
v-if="!isLoading"
|
||||
:disabled="useFilter"
|
||||
:key="filterKey"
|
||||
:value="isActive"
|
||||
@input="updateFilterInfo(filterIndex)"
|
||||
/>
|
||||
<fulfilling-bouncing-circle-spinner
|
||||
v-if="isLoading"
|
||||
:animation-duration="3000"
|
||||
:color="'#6c7fee'"
|
||||
:size="20"
|
||||
class="mx-2 mt-3"
|
||||
/>
|
||||
<div class="filter-input my-0 pa-0 mx-1">
|
||||
<va-select
|
||||
small
|
||||
noClear
|
||||
:label="'FILTER ' + (filterIndex + 1)"
|
||||
:disabled="useFilter"
|
||||
v-model="selectFilter"
|
||||
textBy="name"
|
||||
:options="optionFilter"
|
||||
@input="clearThenReload()"
|
||||
/>
|
||||
</div>
|
||||
<va-input
|
||||
:label="'order'"
|
||||
:disabled="useFilter"
|
||||
class="mx-1 order-input"
|
||||
:class="inputDisabledStyle"
|
||||
v-model.lazy="order"
|
||||
@change="clearThenReload()"
|
||||
:error="showError"
|
||||
/>
|
||||
<va-input
|
||||
v-if="[1, 2, 3].indexOf(selectFilter.id) >= 0"
|
||||
:label="'FREQ'"
|
||||
:disabled="useFilter"
|
||||
class="mx-1 freq-input"
|
||||
:class="inputDisabledStyle"
|
||||
v-model.lazy="freq2"
|
||||
@change="clearThenReload()"
|
||||
:error="showError"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
Hz
|
||||
</p>
|
||||
</va-input>
|
||||
<va-input
|
||||
v-if="selectFilter.id > 3"
|
||||
:label="'FREQ MIN'"
|
||||
:disabled="useFilter"
|
||||
class="mx-1 freq-input"
|
||||
:class="inputDisabledStyle"
|
||||
v-model.lazy="freq0"
|
||||
@change="clearThenReload()"
|
||||
:error="showError"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
Hz
|
||||
</p>
|
||||
</va-input>
|
||||
<va-input
|
||||
v-if="selectFilter.id > 3"
|
||||
:label="'FREQ MAX'"
|
||||
:disabled="useFilter"
|
||||
class="mx-1 freq-input"
|
||||
:class="inputDisabledStyle"
|
||||
v-model.lazy="freq1"
|
||||
@change="clearThenReload()"
|
||||
:error="showError"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
Hz
|
||||
</p>
|
||||
</va-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/data/api'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { IIRFilter } from '@/data/task/filter/IIRFilter.js'
|
||||
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
|
||||
|
||||
export default {
|
||||
name: 'global-filter',
|
||||
props: {
|
||||
chartID: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
filterIndex: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
FulfillingBouncingCircleSpinner,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('replay', [
|
||||
'chartData',
|
||||
'metaList',
|
||||
'useFilter',
|
||||
'currentScale',
|
||||
]),
|
||||
isActive () {
|
||||
if (this.data && this.data.series.length > 0 && this.data.series.every(item => item != null)) {
|
||||
return !!this.data.series.every(item => item.filter[this.filterIndex].filter != null)
|
||||
}
|
||||
return false
|
||||
},
|
||||
inputDisabledStyle () {
|
||||
return this.useFilter ? 'va-select--disabled' : ''
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filterKey: 0,
|
||||
data: null,
|
||||
selectFilter: {
|
||||
id: 1,
|
||||
name: 'notch',
|
||||
},
|
||||
order: 20,
|
||||
freq0: 60,
|
||||
freq1: 60,
|
||||
freq2: 60,
|
||||
optionFilter: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'notch',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'lowpass',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'highpass',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'bandstop',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'bandpass',
|
||||
},
|
||||
],
|
||||
showError: false,
|
||||
isLoading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilter () {
|
||||
for (let i = 0; i < this.data.series.length; i++) {
|
||||
if (this.data.series[i].filter[this.filterIndex].filter != null) {
|
||||
this.data.series[i].filter[this.filterIndex].filter = null
|
||||
}
|
||||
}
|
||||
},
|
||||
clearThenReload () {
|
||||
this.clearFilter()
|
||||
this.updateFilterInfo(this.filterIndex)
|
||||
},
|
||||
async updateFilterInfo (index) {
|
||||
if (this.isActive) {
|
||||
this.clearFilter()
|
||||
return
|
||||
}
|
||||
this.isLoading = true
|
||||
let fs = 0
|
||||
let sos = []
|
||||
for (let i = 0; i < this.data.series.length; i++) {
|
||||
const _fs = this.metaList[0].parameter_set.SAMPLE_RATE / parseInt(this.currentScale) // currentScale: for the case 10, 100, 1000:1
|
||||
const _chNum = Object.keys(this.metaList[0].raw_data).length
|
||||
if (fs !== _fs) {
|
||||
fs = _fs
|
||||
const ret = await api.analysis.IIRFilter({
|
||||
type: this.selectFilter.name,
|
||||
order: this.order,
|
||||
fs: parseFloat(_fs / _chNum),
|
||||
f0: parseFloat(this.freq0),
|
||||
f1: parseFloat(this.freq1),
|
||||
f2: parseFloat(this.freq2),
|
||||
})
|
||||
|
||||
if (ret.status !== 200) {
|
||||
this.showError = true
|
||||
this.isLoading = false
|
||||
return false
|
||||
} else {
|
||||
this.showError = false
|
||||
}
|
||||
|
||||
const _sosRet = ret.data.split('/')
|
||||
const _sos = []
|
||||
_sosRet.forEach((v, vi) => {
|
||||
const _tmp = v.split(',')
|
||||
const _arr = []
|
||||
_tmp.forEach(s => {
|
||||
_arr.push(parseFloat(s))
|
||||
})
|
||||
_sos.push(_arr)
|
||||
})
|
||||
sos = _sos
|
||||
}
|
||||
this.data.series[i].filter[index].type = this.selectFilter.name
|
||||
this.data.series[i].filter[index].order = this.order
|
||||
// sampling rate changed by channel number
|
||||
this.data.series[i].filter[index].fs = parseFloat(_fs / _chNum)
|
||||
this.data.series[i].filter[index].f0 = parseFloat(this.freq0)
|
||||
this.data.series[i].filter[index].f1 = parseFloat(this.freq1)
|
||||
this.data.series[i].filter[index].f2 = parseFloat(this.freq2)
|
||||
this.data.series[i].filter[index].sos = sos
|
||||
this.data.series[i].filter[index].filter = IIRFilter.init(sos)
|
||||
}
|
||||
this.filterKey++
|
||||
this.isLoading = false
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.data = this.chartData[this.chartID]
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
.filter-input {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.freq-input {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.order-input {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.filter-apply-button {
|
||||
max-width: 25px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="box_inline-block"> use SD card data </span>
|
||||
<va-toggle :disable="disableToggle" flat small class="box_inline-block" v-model="useSdCardData"/>
|
||||
<div v-if="showToggle">
|
||||
<span class="box_inline-block display-6"> USE SD CARD DATA </span>
|
||||
<va-toggle
|
||||
class="box_inline-block"
|
||||
flat
|
||||
small
|
||||
v-model="useSdCardData"
|
||||
@input="refreshChart()"
|
||||
:disable="!isInitDone || isDownloading"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -17,6 +24,13 @@ export default {
|
||||
...mapFields('replay', [
|
||||
'metaList',
|
||||
'useSdCardData',
|
||||
'currentChunkDataSplitIndex',
|
||||
'startTime',
|
||||
'endTime',
|
||||
'startTimeCurrent',
|
||||
'endTimeCurrent',
|
||||
'isInitDone',
|
||||
'isDownloading',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
@@ -29,18 +43,26 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
disableToggle: true,
|
||||
showToggle: false,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
if (this.metaList[this.metaIndex].sd_data_uploaded === true) {
|
||||
this.disableToggle = false
|
||||
this.useSdCardData = false
|
||||
if (this.metaList[this.metaIndex].sd_data_uploaded) {
|
||||
this.showToggle = true
|
||||
}
|
||||
},
|
||||
refreshChart () {
|
||||
this.startTime = this.startTimeCurrent = 0
|
||||
this.currentChunkDataSplitIndex = 1
|
||||
if (!this.useSdCardData) {
|
||||
this.endTime = this.endTimeCurrent = this.metaList[this.metaIndex].time_duration
|
||||
this.$emit('redrawAllChart')
|
||||
} else {
|
||||
this.disableToggle = true
|
||||
this.useSdCardData = false
|
||||
this.$emit('refreshChart')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<va-tree-root>
|
||||
<va-tree-category label="Experiment Setting" :isOpen="false">
|
||||
<pre><div style="text-transform: none;" class="title mb-3" :style="{color: this.$themes.primary}" v-html="content"></div></pre>
|
||||
</va-tree-category>
|
||||
</va-tree-root>
|
||||
<div>
|
||||
<span class="display-6"> EXPERIMENT SETTING </span>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="isOpen = !isOpen"/>
|
||||
<pre><div v-if="isOpen" class="mb-3" :style="{color: this.$themes.dark}" v-html="content"></div></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -24,211 +24,16 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isOpen: false,
|
||||
deviceName: '',
|
||||
content: '',
|
||||
params: {},
|
||||
showDict: {},
|
||||
// [region] for neulive
|
||||
ampGainOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: '25',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
description: '100',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
description: '400',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: '800',
|
||||
},
|
||||
],
|
||||
stimulateElectrodeOptions: [
|
||||
{
|
||||
label: 'Monopolar',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: 'Bipolar',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
cycleNumberOptions: [
|
||||
{
|
||||
label: 'limite',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: 'Unlimite',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
patternModeOptions: [
|
||||
{
|
||||
label: 'Monophasic',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: 'Biphasic',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
unitOptions: [
|
||||
{
|
||||
label: 's',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: 'ms',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: 'us',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
// [Endregion]
|
||||
// [region] for elite
|
||||
workingModeOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: 'I-V Curve',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
description: 'Cycle I-V',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
description: 'Function Generator',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: 'R-T Graph',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
description: 'V-T Graph',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
description: 'I-T Graph',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
description: 'ADC_Test',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
description: 'Constant Current',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
description: 'Cyclic Voltammetry',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
description: 'Cyclic Voltammetry-hi-cycles',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
description: 'Linear Sweep Voltammetry',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
description: 'Chronoamperometric Graph',
|
||||
},
|
||||
],
|
||||
currentRangeOptions: [
|
||||
{
|
||||
value: 0,
|
||||
label: '<40 μA',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '30-1350 μA',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '>1000 μA',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'Auto',
|
||||
},
|
||||
],
|
||||
stepTimeOptions: [
|
||||
{
|
||||
value: 0,
|
||||
label: '0.5s',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '1.0s',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '2.0s',
|
||||
},
|
||||
],
|
||||
resisterRangeOptions: [
|
||||
{
|
||||
value: 0,
|
||||
label: '>50 kΩ',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '2-100 kΩ',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '<10 kΩ',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'Auto',
|
||||
},
|
||||
],
|
||||
eliteSampleRateOptions: [
|
||||
{
|
||||
value: 0,
|
||||
label: '100',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '10',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '5',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '1',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '0.1',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '0.01',
|
||||
},
|
||||
],
|
||||
chargeOptions: [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Charge',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'Discharge',
|
||||
},
|
||||
],
|
||||
// [Endregion]
|
||||
chipChannelDict: {}, // for 3.5ex
|
||||
channelList: [], // total selected channels
|
||||
sampleRateList: [], // each chip per channel sample rate
|
||||
preprocessKey: [],
|
||||
ignoreKey: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -239,185 +44,105 @@ export default {
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
init (deviceName) {
|
||||
this.deviceName = deviceName
|
||||
init (_deviceName) {
|
||||
this.deviceName = _deviceName.replace(/[.]/g, '') // remove all '.'
|
||||
const meta = this.metaList[this.metaIndex]
|
||||
const params = meta.parameter_set
|
||||
this.content += 'Duration: ' + parseInt(parseInt(meta.time_duration / 1e6) / 60) + ' m ' + parseInt((meta.time_duration % 6e7) / 1e6) + ' s<br/>'
|
||||
this.content += '<br/>Parameter: <br/>'
|
||||
this.params = meta.parameter_set
|
||||
this.content += 'DURATION: ' + parseInt(parseInt(meta.time_duration / 1e6) / 60) + ' m ' + parseInt((meta.time_duration % 6e7) / 1e6) + ' s<br/>'
|
||||
if (this.deviceName.startsWith('Neulive')) {
|
||||
const recordingParam = paramTable[this.deviceName].showParameterRec
|
||||
const stimulationParam = paramTable[this.deviceName].showParameterSti
|
||||
// recording param
|
||||
this.content += '<br/> Recording: <br/>'
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (!(key.includes('sti') || key.includes('STI'))) {
|
||||
let showKey = ''
|
||||
let showValue = value
|
||||
if (key === 'RECORDING_CH' || key === 'AXIS_CH' || key === 'AMP_GAIN' || key === 'SAMPLE_RATE') {
|
||||
if (key === 'RECORDING_CH') {
|
||||
showKey = 'Channels'
|
||||
// convert decimal to binary
|
||||
const binaryChannelValue = showValue.toString(2)
|
||||
const channelValue = binaryChannelValue.split('')
|
||||
showValue = ''
|
||||
channelValue.forEach(function (value, i) {
|
||||
if (value === '1') {
|
||||
showValue += `${i + 1} `
|
||||
}
|
||||
})
|
||||
if (showValue === '') {
|
||||
showValue = 'None'
|
||||
}
|
||||
} else if (key === 'AXIS_CH') {
|
||||
showKey = '3-AXIS'
|
||||
// convert decimal to binary
|
||||
const binaryChannelValue = showValue.toString(2)
|
||||
const channelValue = binaryChannelValue.split('').reverse()
|
||||
showValue = ''
|
||||
channelValue.forEach(function (value, i) {
|
||||
if (value === '1') {
|
||||
if (i === 0) {
|
||||
showValue += 'x '
|
||||
} else if (i === 1) {
|
||||
showValue += 'y '
|
||||
} else if (i === 2) {
|
||||
showValue += 'z '
|
||||
} else if (i === 3) {
|
||||
showValue += 'mag '
|
||||
}
|
||||
}
|
||||
})
|
||||
if (showValue === '') {
|
||||
showValue = 'None'
|
||||
}
|
||||
} else if (key === 'AMP_GAIN') {
|
||||
showKey = 'Amp gain'
|
||||
const targetID = (value + 1) % 4
|
||||
for (let i = 0; i < this.ampGainOptions.length; i++) {
|
||||
if (this.ampGainOptions[i].id === targetID) {
|
||||
showValue = this.ampGainOptions[i].description
|
||||
}
|
||||
}
|
||||
} else if (key === 'SAMPLE_RATE') {
|
||||
showKey = 'Sample Rate'
|
||||
showValue = value
|
||||
}
|
||||
this.content += ' ' + showKey + ': ' + showValue + '<br/>'
|
||||
}
|
||||
}
|
||||
}
|
||||
// stimulation param
|
||||
this.content += '<br/> Stimulation: <br/>'
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (key.includes('sti') || key.includes('STI')) {
|
||||
let showKey = ''
|
||||
let showValue = value
|
||||
if (key === 'STI_H_BRIDGE' || key === 'STI_CHANNEL' || key === 'STI_POLARITY' || key === 'CONTINUOUS_STI' ||
|
||||
key === 'STI_AMPLITUDE' || key === 'STI_PATTERN' || key === 'STI_T1' || key === 'STI_T2' || key === 'STI_T3' || key === 'STI_T4') {
|
||||
if (key === 'STI_H_BRIDGE') {
|
||||
showKey = 'ELECTRODE'
|
||||
if (value === true) {
|
||||
showValue = this.stimulateElectrodeOptions[1].label
|
||||
} else {
|
||||
showValue = this.stimulateElectrodeOptions[0].label
|
||||
}
|
||||
} else if (key === 'STI_CHANNEL' || key === 'STI_POLARITY') {
|
||||
if (key === 'STI_CHANNEL') {
|
||||
showKey = 'Channels'
|
||||
} else {
|
||||
showKey = 'Polarity'
|
||||
}
|
||||
// convert decimal to binary
|
||||
const binaryChannelValue = showValue.toString(2)
|
||||
const channelValue = binaryChannelValue.split('')
|
||||
showValue = ''
|
||||
channelValue.forEach(function (value, i) {
|
||||
if (value === '1') {
|
||||
showValue += `${i + 1} `
|
||||
}
|
||||
})
|
||||
if (showValue === '') {
|
||||
showValue = 'None'
|
||||
}
|
||||
} else if (key === 'CONTINUOUS_STI') {
|
||||
showKey = 'CYCLE NUMBER'
|
||||
if (value === true) {
|
||||
showValue = this.cycleNumberOptions[1].label
|
||||
} else {
|
||||
showValue = this.cycleNumberOptions[0].label
|
||||
}
|
||||
} else if (key === 'STI_AMPLITUDE') {
|
||||
showKey = 'AMPLITUDE (uA)'
|
||||
showValue = value * 5
|
||||
} else if (key === 'STI_PATTERN') {
|
||||
showKey = 'PATTERN'
|
||||
if (value === true) {
|
||||
showValue = this.patternModeOptions[1].label
|
||||
} else {
|
||||
showValue = this.patternModeOptions[0].label
|
||||
}
|
||||
} else if (key === 'STI_T1') {
|
||||
showKey = 'T1 Range'
|
||||
showValue = `${value[0] * 10 / 1000} ms` // 10 is cuz 1 value = 10us, [0] is cuz now all values in array are same
|
||||
} else if (key === 'STI_T2') {
|
||||
showKey = 'T2 Range'
|
||||
showValue = `${value[0] * 10 / 1000} ms` // 10 is cuz 1 value = 10us
|
||||
} else if (key === 'STI_T3') {
|
||||
showKey = 'T3 Range'
|
||||
showValue = `${value[0] * 10 / 1000} ms` // 10 is cuz 1 value = 10us
|
||||
} else if (key === 'STI_T4') {
|
||||
showKey = 'T4 Range'
|
||||
showValue = `${value[0] * 10 / 1000} ms` // 10 is cuz 1 value = 10us
|
||||
}
|
||||
this.content += ' ' + showKey + ': ' + showValue + '<br/>'
|
||||
}
|
||||
}
|
||||
}
|
||||
if ('STI_T1' in params && 'STI_T2' in params && 'STI_T3' in params && 'STI_T4' in params) {
|
||||
const period = (params.STI_T1[0] + params.STI_T2[0] + params.STI_T3[0] + params.STI_T4[0]) * 10 / 1000 // 10 is cuz 1 value = 10us
|
||||
this.content += ' Frequency: ' + String(1000 / period) + ' Hz' + '<br/>' // 1000 is cuz 1s = 1000ms
|
||||
this.content += ' Period: ' + String(period) + ' ms' + '<br/>'
|
||||
}
|
||||
} else if (this.deviceName === 'EliteZM15' || this.deviceName === 'EliteEIS') {
|
||||
const mode = params.MODE
|
||||
const modeName = paramTable[this.deviceName].MODE[mode].name
|
||||
const showParams = paramTable[this.deviceName].MODE[mode].showParameter
|
||||
this.showDict.MODE = modeName
|
||||
if (modeName === 'Pulse Sensing') {
|
||||
const duplicateParam = ['V_initial', 't_pulse', 'CURR_REC_START', 'CURR_REC_END']
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const key = 'segment_order_' + String(i)
|
||||
const order = paramTable[this.deviceName][key].rev_func(params[key])
|
||||
if (parseInt(order) !== 0) { // if === 0, 代表沒有使用這個block
|
||||
for (let key of duplicateParam) {
|
||||
key = key + '_' + String(i)
|
||||
this.fillShowDict(key, params[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
const remainParams = showParams.filter((e) => { // 差集
|
||||
return duplicateParam.indexOf(e) === -1
|
||||
})
|
||||
for (const key of remainParams) {
|
||||
this.fillShowDict(key, params[key])
|
||||
this.content += '<br/>RECORDING: <br/><br/>'
|
||||
this.preprocessKey.push('SAMPLE_RATE')
|
||||
const totalSampleRate = paramTable[this.deviceName].SAMPLE_RATE.rev_func(this.params.SAMPLE_RATE)
|
||||
let chipAmount = 1
|
||||
if (_deviceName === 'Neulive3.5ex') {
|
||||
this.preprocessKey.push(...['REC_CH_DEV00', 'REC_CH_DEV01'])
|
||||
chipAmount = 2
|
||||
for (let i = 1, len = chipAmount; i <= len; i++) {
|
||||
this.chipChannelDict['chip' + String(i)] = []
|
||||
}
|
||||
this.chipChannelDict.chip1 = paramTable[this.deviceName].REC_CH_DEV00.rev_func(this.params.REC_CH_DEV00)
|
||||
this.chipChannelDict.chip2 = paramTable[this.deviceName].REC_CH_DEV01.rev_func(this.params.REC_CH_DEV01)
|
||||
this.channelList.push(...this.chipChannelDict.chip1)
|
||||
this.channelList.push(...this.chipChannelDict.chip2)
|
||||
this.sampleRateList.push(parseInt(totalSampleRate / this.chipChannelDict.chip1.length))
|
||||
this.sampleRateList.push(parseInt(totalSampleRate / this.chipChannelDict.chip2.length))
|
||||
} else if (_deviceName === 'Neulive3.5') {
|
||||
this.preprocessKey.push(...['RECORDING_CH_L', 'RECORDING_CH_H'])
|
||||
this.channelList.push(...paramTable[this.deviceName].RECORDING_CH_L.rev_func(this.params.RECORDING_CH_L))
|
||||
this.channelList.push(...paramTable[this.deviceName].RECORDING_CH_H.rev_func(this.params.RECORDING_CH_H))
|
||||
this.sampleRateList.push(parseInt(totalSampleRate / this.channelList.length))
|
||||
} else {
|
||||
for (const key of showParams) {
|
||||
this.fillShowDict(key, params[key])
|
||||
this.preprocessKey.push(...['RECORDING_CH'])
|
||||
this.channelList.push(...paramTable[this.deviceName].RECORDING_CH.rev_func(this.params.RECORDING_CH))
|
||||
this.sampleRateList.push(parseInt(totalSampleRate / this.channelList.length))
|
||||
}
|
||||
|
||||
const chipChannelDictKeys = Object.keys(this.chipChannelDict)
|
||||
if (chipAmount === 1) {
|
||||
this.content += ' CHANNELS: ' + this.channelList.toString() + '<br/>'
|
||||
this.content += ' SAMPLING RATE PER CHANNEL: ' + (this.sampleRateList[0] / 1e3).toFixed(1) + 'k<br/>'
|
||||
} else if (chipAmount > 1) { // e.g. 3.5ex
|
||||
for (const [chipNumber, channels] of Object.entries(this.chipChannelDict)) {
|
||||
this.content += ' CHANNELS (' + chipNumber + '): ' + channels.toString() + '<br/>'
|
||||
}
|
||||
for (let i = 0, len = chipAmount; i < len; i++) {
|
||||
this.content += ' SAMPLING RATE PER CHANNEL (' + chipChannelDictKeys[i] + '): ' + (this.sampleRateList[i] / 1e3).toFixed(1) + 'k<br/>'
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of recordingParam) {
|
||||
if (!this.preprocessKey.includes(key)) {
|
||||
this.fillShowDict(key)
|
||||
}
|
||||
}
|
||||
this.dumpDict()
|
||||
|
||||
// stimulation param
|
||||
this.content += '<br/>STIMULATION: <br/><br/>'
|
||||
if (+this.params.CONTINUOUS_STI === 1) { // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
this.ignoreKey.push(...['STI_CYCLE_LIST', 'STI_CYCLE_CH0', 'REPEAT_TIME_littleEndian', 'REPEAT_INTERVAL_littleEndian'])
|
||||
}
|
||||
for (const key of stimulationParam) {
|
||||
if (!this.ignoreKey.includes(key)) {
|
||||
this.fillShowDict(key)
|
||||
}
|
||||
}
|
||||
this.dumpDict()
|
||||
}
|
||||
this.dump()
|
||||
this.reset()
|
||||
},
|
||||
fillShowDict (key, value) {
|
||||
const expVal = paramTable[this.deviceName][key].rev_func(value)
|
||||
fillShowDict (key) {
|
||||
const value = this.params[key]
|
||||
let expVal
|
||||
if (key === 'STI_CHANNEL') {
|
||||
expVal = paramTable[this.deviceName][key].rev_func(value, this.params.STI_POLARITY)
|
||||
} else if (key === 'STI_POLARITY') {
|
||||
expVal = paramTable[this.deviceName][key].rev_func(value, this.params.STI_CHANNEL)
|
||||
} else {
|
||||
expVal = paramTable[this.deviceName][key].rev_func(value)
|
||||
}
|
||||
const name = paramTable[this.deviceName][key].showName
|
||||
const unit = paramTable[this.deviceName][key].defaultUnit
|
||||
this.showDict[name] = unit !== undefined ? expVal.toString() + unit : expVal.toString()
|
||||
const defaultUnit = paramTable[this.deviceName][key].defaultUnit
|
||||
const unitScale = defaultUnit !== undefined ? paramTable[this.deviceName][key].unit[defaultUnit] : 1
|
||||
this.showDict[name] = defaultUnit !== undefined ? (expVal / unitScale).toString() + defaultUnit : expVal.toString()
|
||||
},
|
||||
dump () {
|
||||
dumpDict () {
|
||||
for (const [showKey, showValue] of Object.entries(this.showDict)) {
|
||||
this.content += ' ' + showKey + ': ' + showValue + '<br/>'
|
||||
}
|
||||
this.showDict = {}
|
||||
},
|
||||
reset () {
|
||||
this.params = {}
|
||||
this.showDict = {}
|
||||
this.chipChannelDict = {}
|
||||
this.channelList.length = 0
|
||||
this.sampleRateList.length = 0
|
||||
this.preprocessKey.length = 0
|
||||
this.ignoreKey.length = 0
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<p class="display-6"> Resample: {{ currentScale }} to 1 <p/>
|
||||
<p class="display-6"> RESAMPLE: {{ currentScale }} to 1 <p/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -3,18 +3,19 @@
|
||||
<va-card>
|
||||
<p style="font-size: 125%; text-decoration: underline;"> Channel </p>
|
||||
<channels/>
|
||||
<p style="font-size: 125%; text-decoration: underline;"> Detect </p>
|
||||
<p :style="{color: this.$themes.primary}" class="display-6"> High pass filter </p>
|
||||
<cutoff-freq/>
|
||||
<filter-order/>
|
||||
<sd-card-toggle ref="sd_card_toggle" :metaIndex="0"/>
|
||||
<set-time-interval ref="set_time_interval" @setTimeInterval="setTimeInterval"/>
|
||||
<p class="display-6"> High pass filter </p>
|
||||
<cutoff-freq class="pl-3 ml-1"/>
|
||||
<filter-order class="pl-3 ml-1"/>
|
||||
<filter-button ref="filterButtonRef" @loadFilterData="loadFilterData"/>
|
||||
<p :style="{color: this.$themes.danger}" class="display-6"> Threshold </p>
|
||||
<p class="display-6"> Threshold </p>
|
||||
<threshold/>
|
||||
<br/>
|
||||
<p class="display-6"> Waveform </p>
|
||||
<wave-length/>
|
||||
<prethreshold/>
|
||||
<refractory-period/>
|
||||
<wave-length class="pl-3 ml-1" />
|
||||
<prethreshold class="pl-3 ml-1" />
|
||||
<refractory-period class="pl-3 ml-1" />
|
||||
<detect-button ref="detectButtonRef" @loadThresholdData="loadThresholdData"/>
|
||||
</va-card>
|
||||
</div>
|
||||
@@ -22,6 +23,8 @@
|
||||
|
||||
<script>
|
||||
import Channels from '@/components/analysis/spike/item/Channels.vue'
|
||||
import SdCardToggle from '@/components/analysis/spike/item/SdCardToggle.vue'
|
||||
import SetTimeInterval from '@/components/analysis/spike/item/SetTimeInterval.vue'
|
||||
import CutoffFreq from '@/components/analysis/spike/item/CutoffFreq.vue'
|
||||
import FilterOrder from '@/components/analysis/spike/item/FilterOrder.vue'
|
||||
import Prethreshold from '@/components/analysis/spike/item/Prethreshold.vue'
|
||||
@@ -35,6 +38,8 @@ export default {
|
||||
name: 'control-panel',
|
||||
components: {
|
||||
Channels,
|
||||
SdCardToggle,
|
||||
SetTimeInterval,
|
||||
CutoffFreq,
|
||||
FilterOrder,
|
||||
FilterButton,
|
||||
@@ -55,11 +60,13 @@ export default {
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
init ({ startTime, endTime }) {
|
||||
this.$refs.sd_card_toggle.init()
|
||||
this.$refs.set_time_interval.init({ startTime, endTime })
|
||||
this.enableFilterButton()
|
||||
},
|
||||
refreshChart (options) {
|
||||
this.$emit('refreshChart', options)
|
||||
setTimeInterval ({ startTime, endTime }) {
|
||||
this.$emit('setTimeInterval', { startTime: startTime, endTime: endTime })
|
||||
},
|
||||
loadThresholdData () {
|
||||
this.$emit('loadThresholdData')
|
||||
@@ -87,29 +94,3 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-child {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.flex-child1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-child2 {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.flex-child:first-child {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
:ref="'control_panel'"
|
||||
@loadFilterData="loadFilterData"
|
||||
@loadThresholdData="loadThresholdData"
|
||||
@setTimeInterval="setTimeInterval"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'chartData',
|
||||
'chartKey',
|
||||
'chartOverviewData',
|
||||
'chartDetailData',
|
||||
'metaList',
|
||||
@@ -67,10 +69,10 @@ export default {
|
||||
'waveLength',
|
||||
'preThreshold',
|
||||
'refractoryPeriod',
|
||||
'useSdCardData',
|
||||
'showError',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
},
|
||||
async created () {
|
||||
this.chartOverviewData = await this.initChart({
|
||||
chartAllData: this.chartData,
|
||||
@@ -96,6 +98,8 @@ export default {
|
||||
chartAreaClass: ['flex', 'xs12', 'sm12', 'md12', 'xl9', 'pa-0'],
|
||||
randomColorArray: [],
|
||||
controllerList: [],
|
||||
startTime: 0,
|
||||
endTime: -1,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
@@ -107,7 +111,7 @@ export default {
|
||||
const dataStr = utf8decoder.decode(data)
|
||||
const dataArray = JSON.parse(dataStr)
|
||||
|
||||
this.chartOverviewData.series[0].data.push(...dataArray)
|
||||
dataArray.forEach(d => this.chartOverviewData.series[0].data.push(d))
|
||||
} else if (topicSplitArray[3] === 'time_mark') {
|
||||
const utf8decoder = new TextDecoder()
|
||||
const timeMarkStr = JSON.parse(utf8decoder.decode(data))
|
||||
@@ -237,9 +241,12 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
// this.updateChartSamplingChart({ chartID: this.chartOverviewData.chartID, seriesIndex: 0, method: function (frame) { return frame[0] } })
|
||||
this.chartOverviewData.xAxis[0].min = 0
|
||||
this.chartOverviewData.xAxis[0].max = this.maxTime
|
||||
this.updateChartSamplingChart({ chartID: this.chartOverviewData.chartID, seriesIndex: 0, method: function (frame) { return frame[0] } })
|
||||
this.chartOverviewData.xAxis[0].min = this.startTime
|
||||
this.chartOverviewData.xAxis[0].max = this.endTime
|
||||
},
|
||||
getSuffix () {
|
||||
return this.useSdCardData ? 'sd' : 'ble'
|
||||
},
|
||||
loadFilterData: async function () {
|
||||
this.showChartLoading('overview')
|
||||
@@ -247,41 +254,49 @@ export default {
|
||||
this.$refs.control_panel.disableDetectButton()
|
||||
|
||||
for (const meta in this.dataInfo) {
|
||||
// reset overview chart
|
||||
this.resetOverviewChart()
|
||||
|
||||
this.chartOverviewData.series[0].data.length = 0
|
||||
this.chartOverviewData.series[0].markArea.data.length = 0
|
||||
this.chartOverviewData.color = ['#000000'] // black
|
||||
|
||||
await api.analysis.spikeDetect({
|
||||
const ret = await api.analysis.spikeDetect({
|
||||
controller_id: this.controllerList[0].id,
|
||||
meta_id: meta,
|
||||
channel: this.selectedChannel,
|
||||
filter_order: this.filterOrder,
|
||||
cut_off_freq: this.cutoffFreq,
|
||||
data_type: 'filter_data',
|
||||
data_type: 'filter_data_' + this.getSuffix(),
|
||||
start_time: this.startTime,
|
||||
end_time: this.endTime,
|
||||
})
|
||||
|
||||
if (ret.status === 200) {
|
||||
this.showError.freq = false
|
||||
this.$refs.control_panel.enableDetectButton()
|
||||
console.log(this.chartOverviewData)
|
||||
} else {
|
||||
console.error(ret.data)
|
||||
if (ret.status === 400) {
|
||||
this.showError.freq = true
|
||||
} else if (ret.status === 500) {
|
||||
this.showError.freq = false
|
||||
}
|
||||
const msgList = ret.data.split('\n')
|
||||
this.pageToast(msgList[msgList.length - 2])
|
||||
}
|
||||
this.hideChartLoading('overview')
|
||||
this.$refs.control_panel.enableDetectButton()
|
||||
console.log(this.chartOverviewData)
|
||||
}
|
||||
},
|
||||
loadThresholdData: async function () {
|
||||
this.showChartLoading('detail')
|
||||
this.chartOverviewData.series[0].markArea.data.length = 0
|
||||
this.chartOverviewData.color = ['#000000'] // black
|
||||
|
||||
for (const meta in this.dataInfo) {
|
||||
let thresholdValue
|
||||
if (this.selectedThreshold === 'over') {
|
||||
thresholdValue = this.upperThreshold
|
||||
} else if (this.selectedThreshold === 'bellow') {
|
||||
} else if (this.selectedThreshold === 'below') {
|
||||
thresholdValue = this.lowerThreshold
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.chartDetailData.series.length; i++) {
|
||||
this.chartDetailData.series[i].name = ''
|
||||
this.chartDetailData.series[i].data.length = 0
|
||||
}
|
||||
this.resetDetailChart()
|
||||
|
||||
await api.analysis.spikeDetect({
|
||||
controller_id: this.controllerList[0].id,
|
||||
@@ -294,9 +309,12 @@ export default {
|
||||
thresholdValue,
|
||||
],
|
||||
waveForm: [this.waveLength, this.preThreshold, this.refractoryPeriod],
|
||||
ret_data_type: 'threshold_data',
|
||||
data_type: 'threshold_data_' + this.getSuffix(),
|
||||
start_time: this.startTime,
|
||||
end_time: this.endTime,
|
||||
})
|
||||
}
|
||||
this.chartKey++
|
||||
this.hideChartLoading('detail')
|
||||
},
|
||||
drawThresholdData (detailSignalArray) {
|
||||
@@ -329,6 +347,7 @@ export default {
|
||||
// push legendName
|
||||
this.chartDetailData.legend.data.push(legendName)
|
||||
this.chartDetailData.series[i].name = legendName
|
||||
this.updateChartSamplingChart({ chartID: this.chartDetailData.chartID, seriesIndex: i, method: function (frame) { return frame[0] } })
|
||||
// set legend and series color
|
||||
this.chartDetailData.color.push(this.randomColorArray[i])
|
||||
}
|
||||
@@ -336,7 +355,7 @@ export default {
|
||||
this.chartDetailData.xAxis[0].max = parseInt(this.waveLength)
|
||||
this.chartDetailData.xAxis[0].axisLabel.show = true
|
||||
},
|
||||
drawTimeMark (timeMarkArray, period) {
|
||||
drawTimeMark (timeMarkArray) {
|
||||
// reset overview
|
||||
this.chartOverviewData.series[0].markArea.data.length = 0
|
||||
|
||||
@@ -348,7 +367,6 @@ export default {
|
||||
this.randomColorArray.push(randomColor)
|
||||
if (Number.isInteger(parseInt(timeMarkArray[i]))) { // cuz maybe is '' if no any match after filtering
|
||||
const timeMark = parseInt(timeMarkArray[i])
|
||||
|
||||
let startTime = timeMark - parseInt(this.preThreshold)
|
||||
let endTime = timeMark + parseInt(this.refractoryPeriod)
|
||||
if (startTime < 0) {
|
||||
@@ -393,6 +411,38 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
setTimeInterval ({ startTime, endTime }) {
|
||||
this.startTime = startTime
|
||||
this.endTime = endTime
|
||||
this.chartOverviewData.xAxis[0].min = startTime
|
||||
this.chartOverviewData.xAxis[0].max = endTime
|
||||
this.resetOverviewChart()
|
||||
this.resetDetailChart()
|
||||
this.$refs.control_panel.disableDetectButton()
|
||||
this.chartKey++
|
||||
},
|
||||
resetOverviewChart () {
|
||||
this.chartOverviewData.series[0].data.length = 0
|
||||
this.chartOverviewData.series[0].markArea.data.length = 0
|
||||
this.chartOverviewData.color = ['#000000'] // black
|
||||
},
|
||||
resetDetailChart () {
|
||||
for (let i = 0; i < this.chartDetailData.series.length; i++) {
|
||||
this.chartDetailData.series[i].data.length = 0
|
||||
}
|
||||
this.chartDetailData.series.length = 0
|
||||
this.chartDetailData.legend.data.length = 0
|
||||
},
|
||||
pageToast: function (mes) {
|
||||
this.showToast(
|
||||
mes,
|
||||
{
|
||||
icon: 'fa-bell',
|
||||
position: 'bottom-right',
|
||||
duration: 7000,
|
||||
},
|
||||
)
|
||||
},
|
||||
...mapActions('spike', [types.chart.init, types.chart.initSelectedChannel, types.chart.register, types.chart.clear, types.chart.updateGridList, types.chart.updateDataZoom]),
|
||||
...mapActions('spike', [types.meta.init]),
|
||||
...mapActions('spike', [types.rec.init, types.rec.intoData, types.rec.initDisplaySetting, types.rec.updateDisplaySetting]),
|
||||
@@ -413,17 +463,36 @@ export default {
|
||||
const metaIDList = this.$route.params.metaList.split('-')
|
||||
await this.initMeta({ metaIDList: metaIDList, api: api })
|
||||
this.metaList = this.getAllMeta()
|
||||
if (this.$route.params.startTime) {
|
||||
this.startTime = parseInt(this.$route.params.startTime)
|
||||
}
|
||||
if (this.$route.params.endTime) {
|
||||
this.endTime = parseInt(this.$route.params.endTime)
|
||||
} else {
|
||||
this.endTime = parseInt(this.metaList[0].time_duration)
|
||||
}
|
||||
if (this.$route.params.sd === undefined || this.$route.params.sd === 'sd') {
|
||||
this.useSdCardData = true
|
||||
} else if (this.$route.params.sd === 'ble') {
|
||||
this.useSdCardData = false
|
||||
}
|
||||
this.initSelectedChannelChart({ metaIndex: 0 })
|
||||
await this.initRec({ api: api })
|
||||
this.dataInfo = this.getAllRec()
|
||||
console.log(this.metaList)
|
||||
console.log(this.dataInfo)
|
||||
await this.register()
|
||||
this.$refs.control_panel.init()
|
||||
this.$refs.control_panel.init({
|
||||
startTime: this.startTime,
|
||||
endTime: this.endTime,
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.pageMqttUnSub(this.controllerList[0].id)
|
||||
this.$refs.chart_overview.clear()
|
||||
this.$refs.chart_detail.clear()
|
||||
this.resetOverviewChart()
|
||||
this.resetDetailChart()
|
||||
for (const id in this.chartData) {
|
||||
for (const _series of this.chartData[id].series) {
|
||||
_series.length = 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex" :class="cardclass">
|
||||
<va-card :style="'height: ' + cardheight + ';'">
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
<chart :ref="'chart_ref'" :key="chartKey" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -9,6 +9,7 @@
|
||||
<script>
|
||||
import { mapMutations } from 'vuex'
|
||||
import types from '@/store/modules/analysis/spike/types'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'spike-replay-chart',
|
||||
@@ -43,6 +44,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'chartKey',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
@@ -55,9 +59,6 @@ export default {
|
||||
updateZoom: function (e) {
|
||||
this.$emit('dataZoomEvent', e)
|
||||
},
|
||||
mergeOptions (options) {
|
||||
this.$refs.chart_ref.mergeOptions(options)
|
||||
},
|
||||
turnOnAxisEventHandler (eventName, query, handler, attr) {
|
||||
const chartID = 1
|
||||
this.updateAxisEventHandlerChart({
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<template>
|
||||
<div class="flex" :class="cardclass">
|
||||
<va-card :style="'height: ' + cardheight + ';'">
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
<chart :ref="'chart_ref'" :key="chartKey" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" @dataZoom="updateZoom" :auto-resize="true"></chart>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'spike-overview-chart',
|
||||
components: {
|
||||
@@ -41,6 +43,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'chartKey',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
@@ -50,9 +55,6 @@ export default {
|
||||
this.$refs.chart_ref.resize()
|
||||
}
|
||||
},
|
||||
mergeOptions (options) {
|
||||
this.$refs.chart_ref.mergeOptions(options)
|
||||
},
|
||||
updateZoom: function (e) {
|
||||
this.$emit('dataZoomEvent', e)
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@blur="itemChange(cutoffFreq)"
|
||||
v-model="cutoffFreq"
|
||||
class="mb-0"
|
||||
:error="showError.freq"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
Hz
|
||||
@@ -30,6 +31,7 @@ export default {
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'cutoffFreq',
|
||||
'showError',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-button class="right" small @click="startDetect" :disabled="isDisable">
|
||||
<va-button small @click="startDetect" :disabled="isDisable">
|
||||
Start Detect
|
||||
</va-button>
|
||||
</div>
|
||||
@@ -21,6 +21,7 @@ export default {
|
||||
'chartDetailData',
|
||||
'selectedChannel',
|
||||
'waveLength',
|
||||
'refractoryPeriod',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
@@ -36,7 +37,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
startDetect () {
|
||||
this.$emit('loadThresholdData')
|
||||
if (this.refractoryPeriod <= 0) {
|
||||
this.pageToast('Invalid value')
|
||||
} else {
|
||||
this.$emit('loadThresholdData')
|
||||
}
|
||||
},
|
||||
enableButton () {
|
||||
this.isDisable = false
|
||||
@@ -44,6 +49,16 @@ export default {
|
||||
disableButton () {
|
||||
this.isDisable = true
|
||||
},
|
||||
pageToast: function (mes) {
|
||||
this.showToast(
|
||||
mes,
|
||||
{
|
||||
icon: 'fa-bell',
|
||||
position: 'bottom-right',
|
||||
duration: 4000,
|
||||
},
|
||||
)
|
||||
},
|
||||
...mapMutations('spike', [types.chart.updateAxisLabel]),
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex xl12 md12 sm12 xs12 mb-4">
|
||||
<va-button class="right" small @click="startFilter" :disabled="isDisable">
|
||||
<va-button small @click="startFilter" :disabled="isDisable">
|
||||
Start Filter
|
||||
</va-button>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'waveLength',
|
||||
'preThreshold',
|
||||
'refractoryPeriod',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
@@ -44,7 +46,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
itemChange (val) {
|
||||
this.preThreshold = val
|
||||
let inputVal
|
||||
if (val > 0) {
|
||||
inputVal = val
|
||||
} else {
|
||||
inputVal = 800
|
||||
}
|
||||
this.preThreshold = inputVal
|
||||
this.refractoryPeriod = this.waveLength - inputVal
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
</div>
|
||||
<div class="flex sm5 xl5 md5 xs5 px-0 mx-0">
|
||||
<va-input
|
||||
@mouseleave.native="itemChange(refractoryPeriod)"
|
||||
@keyup.enter="itemChange(refractoryPeriod)"
|
||||
@blur="itemChange(refractoryPeriod)"
|
||||
readonly
|
||||
v-model="refractoryPeriod"
|
||||
class="mb-0"
|
||||
:error="refractoryPeriod <= 0"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
µs
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div v-if="showToggle">
|
||||
<span class="box_inline-block display-6"> Use SD Card Data </span>
|
||||
<va-toggle
|
||||
class="box_inline-block"
|
||||
flat
|
||||
small
|
||||
v-model="useSdCardData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'SdCardToggle',
|
||||
components: {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'metaList',
|
||||
'useSdCardData',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
metaIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showToggle: false,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
if (this.metaList[this.metaIndex].sd_data_uploaded) {
|
||||
this.showToggle = true
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.box_inline-block {
|
||||
padding: 1px;
|
||||
margin: 1px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="row flex sm12 xl12 md12 xs12 px-0 py-0 mx-0 my-0">
|
||||
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
|
||||
<p class="display-6"> START TIME <p/>
|
||||
<va-input
|
||||
@change="startTimeChange(startTimeInput)"
|
||||
v-model.lazy="startTimeInput"
|
||||
class="mb-0 flex-child"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
ms
|
||||
</p>
|
||||
</va-input>
|
||||
</div>
|
||||
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
|
||||
<p class="display-6"> END TIME <p/>
|
||||
<va-input
|
||||
@change="endTimeChange(endTimeInput)"
|
||||
v-model.lazy="endTimeInput"
|
||||
class="mb-0 flex-child"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
ms
|
||||
</p>
|
||||
</va-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
name: 'set-start-end-time',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'metaList',
|
||||
'useSdCardData',
|
||||
]),
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
startTimeInput: 'start',
|
||||
endTimeInput: 'end',
|
||||
startTime: 0,
|
||||
endTime: -1,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init ({ startTime, endTime }) {
|
||||
this.startTime = startTime
|
||||
this.endTime = endTime
|
||||
this.startTimeInput = String(startTime / 1e3)
|
||||
this.endTimeInput = String(endTime / 1e3)
|
||||
},
|
||||
translator (val) {
|
||||
if (val === 'start') {
|
||||
return 0
|
||||
} else if (val === 'end') {
|
||||
return parseInt(this.metaList[0].time_duration)
|
||||
}
|
||||
return parseInt(val * 1e3) // us
|
||||
},
|
||||
startTimeChange (val) {
|
||||
this.startTime = this.translator(val) // us
|
||||
this.$emit('setTimeInterval', { startTime: this.startTime, endTime: this.endTime })
|
||||
},
|
||||
endTimeChange (val) {
|
||||
this.endTime = this.translator(val) // us
|
||||
this.$emit('setTimeInterval', { startTime: this.startTime, endTime: this.endTime })
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-child {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.flex-child2 {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.flex-child:first-child {
|
||||
margin-right: 1px;
|
||||
}
|
||||
</style>
|
||||
@@ -6,26 +6,27 @@
|
||||
v-model="selectedThreshold"
|
||||
:option="option"
|
||||
:label="`${option}`"
|
||||
@click.native.stop="validateThreshold()"
|
||||
/>
|
||||
<va-input
|
||||
v-if="selectedThreshold === 'over'"
|
||||
@mouseleave.native="itemChange(upperThreshold, 'upper')"
|
||||
@keyup.enter="itemChange(upperThreshold, 'upper')"
|
||||
@blur="itemChange(upperThreshold, 'upper')"
|
||||
v-model="upperThreshold"
|
||||
@change="validateThreshold()"
|
||||
v-model.lazy="upperThreshold"
|
||||
class="mb-0"
|
||||
:error="showError"
|
||||
:error-messages="errorMsg"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
µV
|
||||
</p>
|
||||
</va-input>
|
||||
<va-input
|
||||
v-if="selectedThreshold === 'bellow'"
|
||||
@mouseleave.native="itemChange(lowerThreshold, 'lower')"
|
||||
@keyup.enter="itemChange(lowerThreshold, 'lower')"
|
||||
@blur="itemChange(lowerThreshold, 'lower')"
|
||||
v-model="lowerThreshold"
|
||||
v-if="selectedThreshold === 'below'"
|
||||
@change="validateThreshold()"
|
||||
v-model.lazy="lowerThreshold"
|
||||
class="mb-0"
|
||||
:error="showError"
|
||||
:error-messages="errorMsg"
|
||||
>
|
||||
<p slot="append" style="margin-right: 0;" small>
|
||||
µV
|
||||
@@ -55,22 +56,36 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showError: false,
|
||||
errorMsg: [''],
|
||||
options: [
|
||||
// 'Auto',
|
||||
'over',
|
||||
'bellow',
|
||||
'below',
|
||||
],
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
itemChange (val, type) {
|
||||
if (type === 'upper') {
|
||||
this.upperThreshold = val
|
||||
} else if (type === 'lower') {
|
||||
this.lowerThreshold = val
|
||||
validateThreshold () {
|
||||
if (this.selectedThreshold === 'over') {
|
||||
if (this.upperThreshold < 0) {
|
||||
this.upperThreshold = 0
|
||||
this.showError = true
|
||||
this.errorMsg[0] = 'need >= 0'
|
||||
return
|
||||
}
|
||||
} else if (this.selectedThreshold === 'below') {
|
||||
if (this.lowerThreshold > 0) {
|
||||
this.lowerThreshold = 0
|
||||
this.showError = true
|
||||
this.errorMsg[0] = 'need <= 0'
|
||||
return
|
||||
}
|
||||
}
|
||||
this.showError = false
|
||||
this.errorMsg[0] = ''
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -30,6 +30,8 @@ export default {
|
||||
computed: {
|
||||
...mapFields('spike', [
|
||||
'waveLength',
|
||||
'preThreshold',
|
||||
'refractoryPeriod',
|
||||
]),
|
||||
},
|
||||
props: {
|
||||
@@ -44,7 +46,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
itemChange (val) {
|
||||
this.waveLength = val
|
||||
let inputVal
|
||||
if (val > 0) {
|
||||
inputVal = val
|
||||
} else {
|
||||
inputVal = 1600
|
||||
}
|
||||
this.waveLength = inputVal
|
||||
this.refractoryPeriod = inputVal - this.preThreshold
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'progress-bar',
|
||||
data () {
|
||||
@@ -24,28 +25,57 @@ export default {
|
||||
}
|
||||
},
|
||||
props: {
|
||||
// unit: sec
|
||||
time: {
|
||||
type: String,
|
||||
meta: { // control system 2.0 meta
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
animateValues () {
|
||||
async estimateTime () {
|
||||
let sampleRate
|
||||
if (this.meta.device.library_name !== 'Neulive3.5ex') {
|
||||
sampleRate = this.meta.parameter_set.SAMPLE_RATE
|
||||
} else {
|
||||
const channelList = JSON.parse(this.meta.channels)
|
||||
const chip1Channels = channelList.filter(c => c >= 0 && c <= 31)
|
||||
const chip2Channels = channelList.filter(c => c >= 32 && c <= 63)
|
||||
if (chip1Channels.length !== 0 && chip2Channels.length !== 0) {
|
||||
sampleRate = this.meta.parameter_set.SAMPLE_RATE * 2
|
||||
} else {
|
||||
sampleRate = this.meta.parameter_set.SAMPLE_RATE
|
||||
}
|
||||
}
|
||||
return parseFloat(this.meta.time_duration / 1e6) * parseFloat(sampleRate / 1e3) * 0.006 * 4
|
||||
// 經驗公式
|
||||
// time_duration/1e6: us -> s
|
||||
// sampleRate/1e3: sample rate unit to (k)
|
||||
// *4: control system 2.0 upload rate is about 250kb/s
|
||||
},
|
||||
animateValues (estimatedTime) { // estimatedTime unit: seconds
|
||||
let currentTime = this.progressValue = 0
|
||||
this.timeInterval = setInterval(() => {
|
||||
this.progressValue += 1
|
||||
|
||||
if (this.progressValue === 99) {
|
||||
if (estimatedTime < 1) {
|
||||
this.progressValue = 99
|
||||
clearInterval(this.timeInterval)
|
||||
} else if (this.progressValue < 90) {
|
||||
currentTime += (1 / estimatedTime)
|
||||
this.progressValue = parseInt(currentTime * 100)
|
||||
} else if (this.progressValue >= 90 && this.progressValue < 99) {
|
||||
currentTime += (1 / (estimatedTime * 10)) // *10: buffer
|
||||
this.progressValue = parseInt(currentTime * 100)
|
||||
} else {
|
||||
this.progressValue = 99
|
||||
clearInterval(this.timeInterval)
|
||||
}
|
||||
}, parseInt(this.time * 10))
|
||||
}, 1000)
|
||||
},
|
||||
progressDone () {
|
||||
this.progressValue = 100
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.animateValues()
|
||||
async mounted () {
|
||||
const estimatedTime = await this.estimateTime()
|
||||
this.animateValues(estimatedTime)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.timeInterval)
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
hide-default-actions
|
||||
>
|
||||
<progressBar
|
||||
:time="time"
|
||||
:meta="meta"
|
||||
:ref="'progressBarRef'"
|
||||
/>
|
||||
<div class="flex-center sm12 xl12 md12 xs12">
|
||||
<va-button @click="hide">Cancel</va-button>
|
||||
<va-button @click="hide">Close</va-button>
|
||||
</div>
|
||||
</va-modal>
|
||||
</template>
|
||||
@@ -29,9 +29,8 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// unit: sec
|
||||
time: {
|
||||
type: String,
|
||||
meta: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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).connected
|
||||
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(':')
|
||||
@@ -289,7 +362,7 @@ export default {
|
||||
device: parseInt(mes[1]),
|
||||
}))
|
||||
this.mqttPub(topicSplit[0] + '_user', JSON.stringify({
|
||||
header: 'device_sd_card_status/0',
|
||||
header: 'get_sd_status/0',
|
||||
device: parseInt(mes[1]),
|
||||
}))
|
||||
// this.mqttPub(topicSplit[0] + '_user', JSON.stringify({
|
||||
@@ -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,9 +478,8 @@ export default {
|
||||
this.mqttPub(controller.id + '_user', JSON.stringify({
|
||||
header: 'hardware_info/0',
|
||||
}))
|
||||
// this.mqttPub(controller.id + '_user', JSON.stringify({
|
||||
// header: 'device_update_usb/0',
|
||||
// }))
|
||||
this.mqttPub(controller.id + '/client/trigger_manager/scan', '{}')
|
||||
this.mqttPub(controller.id + '/client/trigger_manager/info_all', '{}')
|
||||
this.createInitialCollection('admin')
|
||||
})
|
||||
},
|
||||
@@ -427,6 +501,7 @@ export default {
|
||||
this.mqttPub(controller.id + '_user', JSON.stringify({
|
||||
header: 'hardware_scan/0',
|
||||
}))
|
||||
this.mqttPub(controller.id + '/client/trigger_manager/scan', '{}')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -69,11 +69,11 @@ export default {
|
||||
...mapActions('download',
|
||||
[
|
||||
types.raw.rawDataByID,
|
||||
types.raw.rawDataByIDs,
|
||||
types.raw.rawSdDataByID,
|
||||
types.export.setHeader,
|
||||
types.export.setTitle,
|
||||
types.export.setData,
|
||||
types.export.getChipIndex,
|
||||
types.download.closeWriter,
|
||||
types.download.setStream,
|
||||
types.download.setSampleRate,
|
||||
@@ -111,11 +111,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 +168,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)]
|
||||
@@ -188,24 +209,56 @@ export default {
|
||||
await this.setTitleExport()
|
||||
|
||||
// start download data
|
||||
let rowIndex = 0
|
||||
|
||||
while (rowIndex < this.downloadInfo.maxRows) {
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
for (const idx in this.downloadInfo.channelInfo[channel].downloadIDList) {
|
||||
const _idx = parseInt(idx)
|
||||
const rawID = this.downloadInfo.channelInfo[channel].downloadIDList[_idx][rowIndex]
|
||||
// const rawID = this.downloadInfo.channelInfo[channel].downloadIDList[_idx].splice(0, 10).join(',')
|
||||
// console.log(rowIndex, _idx, rawID)
|
||||
if (rawID !== undefined) {
|
||||
if (sd === 'sd') {
|
||||
await this.rawSdDataByIDRaw({
|
||||
channel: channel,
|
||||
rawID: rawID,
|
||||
idx: _idx,
|
||||
api: api,
|
||||
})
|
||||
if (sd === 'sd') {
|
||||
const dataList = await api.raw.getSdDataAllTimeslotByUUID(this.metaInfo.uuid, this.downloadInfo.channel[0])
|
||||
const timeslots = dataList.data
|
||||
for (let idx = 0; idx < timeslots.length; idx++) { // 哪一個row的data (index, start from 0)
|
||||
let dataString = ''
|
||||
let maxLength = -1
|
||||
// download all channel data
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
await this.rawSdDataByIDRaw({
|
||||
uuid: this.metaInfo.uuid,
|
||||
channel: channel,
|
||||
idx: idx,
|
||||
api: api,
|
||||
})
|
||||
if (this.downloadInfo.channelInfo[channel].downloadDataBuffer.length > maxLength) {
|
||||
maxLength = this.downloadInfo.channelInfo[channel].downloadDataBuffer.length
|
||||
}
|
||||
}
|
||||
|
||||
// write data stream
|
||||
const rowData = []
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
const dataBuffer = this.downloadInfo.channelInfo[channel].downloadDataBuffer
|
||||
if (dataBuffer[i] === undefined) {
|
||||
rowData.push(['', ''])
|
||||
} else {
|
||||
rowData.push(dataBuffer[i])
|
||||
}
|
||||
}
|
||||
dataString = dataString + rowData.join(',') + '\r\n'
|
||||
rowData.length = 0
|
||||
}
|
||||
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
|
||||
this.downloadInfo.stream.writer.write(encode(dataString))
|
||||
|
||||
// reset buffer
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
this.downloadInfo.channelInfo[channel].downloadDataBuffer.length = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let rowIndex = 0
|
||||
while (rowIndex < this.downloadInfo.maxRows) {
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
for (const idx in this.downloadInfo.channelInfo[channel].downloadIDList) {
|
||||
const _idx = parseInt(idx)
|
||||
const rawID = this.downloadInfo.channelInfo[channel].downloadIDList[_idx][rowIndex]
|
||||
if (rawID !== undefined) {
|
||||
await this.rawDataByIDRaw({
|
||||
channel: channel,
|
||||
rawID: rawID,
|
||||
@@ -215,9 +268,9 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.setDataExport({ last: (rowIndex >= this.downloadInfo.maxRows - 1) })
|
||||
rowIndex += 1
|
||||
}
|
||||
await this.setDataExport({ last: (rowIndex >= this.downloadInfo.maxRows - 1) })
|
||||
rowIndex += 1
|
||||
}
|
||||
this.closeWriterDownload()
|
||||
fileIndex += 1
|
||||
|
||||
@@ -74,16 +74,18 @@ export default {
|
||||
...mapActions('download',
|
||||
[
|
||||
types.raw.rawDataByID,
|
||||
types.raw.rawDataByIDs,
|
||||
types.raw.rawSdDataByID,
|
||||
types.export.setHeader,
|
||||
types.export.setTitle,
|
||||
types.export.setData,
|
||||
types.export.setAll,
|
||||
types.export.getHeader,
|
||||
types.export.getTitle,
|
||||
types.download.closeWriter,
|
||||
types.download.setStream,
|
||||
types.download.setSampleRate,
|
||||
types.download.setIDList,
|
||||
types.download.saveFile,
|
||||
],
|
||||
),
|
||||
...mapMutations('download',
|
||||
@@ -189,24 +191,66 @@ export default {
|
||||
|
||||
// start download data
|
||||
fileIndex += 1
|
||||
let rowIndex = 0
|
||||
|
||||
while (rowIndex < this.downloadInfo.maxRows) {
|
||||
this.$refs.progressModalRef.setValue(parseInt((rowIndex / this.downloadInfo.maxRows) * 100))
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
for (const idx in this.downloadInfo.channelInfo[channel].downloadIDList) {
|
||||
const _idx = parseInt(idx)
|
||||
const rawID = this.downloadInfo.channelInfo[channel].downloadIDList[_idx][rowIndex]
|
||||
// console.log(rowIndex, _idx, rawID)
|
||||
if (rawID !== undefined) {
|
||||
if (sd === 'sd') {
|
||||
await this.rawSdDataByIDRaw({
|
||||
channel: channel,
|
||||
rawID: rawID,
|
||||
idx: _idx,
|
||||
api: api,
|
||||
})
|
||||
if (sd === 'sd') {
|
||||
let dataString = ''
|
||||
const headerString = await this.getHeaderExport()
|
||||
const { titleString, channelString } = await this.getTitleExport()
|
||||
const stringArray = [] // use for generate Blob
|
||||
stringArray.push(headerString)
|
||||
stringArray.push(channelString)
|
||||
stringArray.push(titleString)
|
||||
const dataList = await api.raw.getSdDataAllTimeslotByUUID(this.metaInfo.uuid, this.downloadInfo.channel[0])
|
||||
const timeslots = dataList.data
|
||||
for (let idx = 0; idx < timeslots.length; idx++) { // 哪一個row的data (index, start from 0)
|
||||
let maxLength = -1
|
||||
// download all channel data
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
await this.rawSdDataByIDRaw({
|
||||
uuid: this.metaInfo.uuid,
|
||||
channel: channel,
|
||||
idx: idx,
|
||||
api: api,
|
||||
})
|
||||
if (this.downloadInfo.channelInfo[channel].downloadDataBuffer.length > maxLength) {
|
||||
maxLength = this.downloadInfo.channelInfo[channel].downloadDataBuffer.length
|
||||
}
|
||||
}
|
||||
|
||||
// write data
|
||||
const rowData = []
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
const dataBuffer = this.downloadInfo.channelInfo[channel].downloadDataBuffer
|
||||
if (dataBuffer[i] === undefined) {
|
||||
rowData.push(['', ''])
|
||||
} else {
|
||||
rowData.push(dataBuffer[i])
|
||||
}
|
||||
}
|
||||
dataString = dataString + rowData.join(',') + '\r\n'
|
||||
rowData.length = 0
|
||||
}
|
||||
|
||||
// reset buffer
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
this.downloadInfo.channelInfo[channel].downloadDataBuffer.length = 0
|
||||
}
|
||||
this.$refs.progressModalRef.setValue(parseInt((100 / timeslots.length) * (idx + 1)))
|
||||
}
|
||||
stringArray.push(dataString)
|
||||
const exportString = new Blob(stringArray)
|
||||
this.saveFileDownload({ data: exportString })
|
||||
} else {
|
||||
let rowIndex = 0
|
||||
while (rowIndex < this.downloadInfo.maxRows) {
|
||||
this.$refs.progressModalRef.setValue(parseInt((rowIndex / this.downloadInfo.maxRows) * 100))
|
||||
for (const channel of this.downloadInfo.channel) {
|
||||
for (const idx in this.downloadInfo.channelInfo[channel].downloadIDList) {
|
||||
const _idx = parseInt(idx)
|
||||
const rawID = this.downloadInfo.channelInfo[channel].downloadIDList[_idx][rowIndex]
|
||||
// console.log(rowIndex, _idx, rawID)
|
||||
if (rawID !== undefined) {
|
||||
await this.rawDataByIDRaw({
|
||||
channel: channel,
|
||||
rawID: rawID,
|
||||
@@ -216,11 +260,11 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
rowIndex += 1
|
||||
}
|
||||
rowIndex += 1
|
||||
await this.setAllExport({ last: (rowIndex >= this.downloadInfo.maxRows - 1) })
|
||||
this.$refs.progressModalRef.setValue(100)
|
||||
}
|
||||
await this.setAllExport({ last: (rowIndex >= this.downloadInfo.maxRows - 1) })
|
||||
this.$refs.progressModalRef.setValue(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<va-card>
|
||||
<sdcard-file-option />
|
||||
<sdcard-file-table />
|
||||
<sdcard-upload-modal />
|
||||
<sdcard-delete-modal />
|
||||
</va-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SdcardFileOption from './option/SdcardFileOption.vue'
|
||||
import SdcardFileTable from './sdcard/SdcardFileTable.vue'
|
||||
import SdcardUploadModal from './sdcard/SdcardUploadModal.vue'
|
||||
import SdcardDeleteModal from './sdcard/SdcardDeleteModal.vue'
|
||||
import api from '@/data/api/index'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SdcardFileOption,
|
||||
SdcardFileTable,
|
||||
SdcardUploadModal,
|
||||
SdcardDeleteModal,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('sdcard', [
|
||||
'controllerList',
|
||||
]),
|
||||
},
|
||||
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 () {
|
||||
this.controllerList.forEach(controller => {
|
||||
this.mqttSub(controller.mac_address + '/data-server/s_d_card_manager/#')
|
||||
})
|
||||
},
|
||||
pageMqttUnSub: function () {
|
||||
this.controllerList.forEach(controller => {
|
||||
this.mqttUnSub(controller.mac_address + '/data-server/s_d_card_manager/#')
|
||||
})
|
||||
},
|
||||
pageInitPub: function () {
|
||||
this.controllerList.forEach(controller => {
|
||||
this.mqttPub(controller.mac_address + '/client/s_d_card_manager/info_all', '{}')
|
||||
})
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
const ret = await api.controller.getAll()
|
||||
this.controllerList = ret.data
|
||||
this.pageMqttSub()
|
||||
this.pageInitPub()
|
||||
},
|
||||
destroyed () {
|
||||
this.pageMqttUnSub()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<va-card>
|
||||
<file-option />
|
||||
<trigger-file-table />
|
||||
</va-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FileOption from './option/FileOption.vue'
|
||||
import TriggerFileTable from './trigger/TriggerFileTable.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FileOption,
|
||||
TriggerFileTable,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -64,9 +64,6 @@
|
||||
<va-card class="flex sm12 xl12 md12 xs12">
|
||||
<template slot="header">
|
||||
<div class="flex sm12 xl12 md12 xs12" style="text-align: end;">
|
||||
<!-- <va-button class="" color="success" v-if="selected.length" @click="downloadSelected" target="_blank" small>
|
||||
DOWNLOAD
|
||||
</va-button> -->
|
||||
<va-button class="" color="danger" v-if="selected.length" @click="deleteMultiFilesConfirm" target="_blank" small>
|
||||
DELETE
|
||||
</va-button>
|
||||
@@ -166,6 +163,9 @@
|
||||
<template slot="size" slot-scope="props">
|
||||
{{transFileSize(props.rowData.size)}}
|
||||
</template>
|
||||
<template slot="sd_size" slot-scope="props">
|
||||
{{estimateSDFileSize(props.rowData)}}
|
||||
</template>
|
||||
<template slot="time_duration" slot-scope="props">
|
||||
{{transTime(props.rowData.time_duration)}}
|
||||
</template>
|
||||
@@ -177,17 +177,13 @@
|
||||
<sd-card-button
|
||||
v-if="(props.rowData.type !== 'folder') && (props.rowData.device.library_name.indexOf('Neulive') >= 0) && (props.rowData.sd_data_record === true)"
|
||||
:meta="props.rowData"
|
||||
:uuidList="SDuuidList"
|
||||
:controller="controllerList[0]"
|
||||
@refreshPage="refreshPage"
|
||||
@downloadSdCard="downloadSdCard"
|
||||
/>
|
||||
</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-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)"/>
|
||||
<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 csv-edf 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="downloadExcel(props.rowData)"/>
|
||||
@@ -273,7 +269,6 @@ export default {
|
||||
height: '2rem',
|
||||
},
|
||||
},
|
||||
SDuuidList: [],
|
||||
controllerList: [],
|
||||
}
|
||||
},
|
||||
@@ -308,7 +303,14 @@ export default {
|
||||
dataClass: 'text-center',
|
||||
}, {
|
||||
name: '__slot:size',
|
||||
title: 'size',
|
||||
title: 'size (BLE)',
|
||||
sortField: 'size',
|
||||
width: '10%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
}, {
|
||||
name: '__slot:sd_size',
|
||||
title: 'size (SD)',
|
||||
sortField: 'size',
|
||||
width: '10%',
|
||||
titleClass: 'text-center',
|
||||
@@ -371,6 +373,28 @@ export default {
|
||||
return this.GLOBAL.transFileSize(fileSize)
|
||||
}
|
||||
},
|
||||
estimateSDFileSize: function (_meta) {
|
||||
if (_meta.size === undefined || !_meta.sd_data_record) {
|
||||
return '--'
|
||||
} else {
|
||||
if (_meta.parameter_set && _meta.parameter_set.SAMPLE_RATE > 1e4) {
|
||||
let scale = _meta.parameter_set.SAMPLE_RATE / 1e4 // 1e4: 以10k為基準
|
||||
const channels = JSON.parse(_meta.channels)
|
||||
if (_meta.device.library_name === 'Neulive3.5ex') {
|
||||
const chip1ChannelLen = channels.filter(c => c >= 0 && c <= 31).length
|
||||
const chip2ChannelLen = channels.filter(c => c >= 32 && c <= 63).length
|
||||
if (chip1ChannelLen > 0 && chip2ChannelLen > 0) { // select two chip
|
||||
scale = _meta.parameter_set.SAMPLE_RATE * 2 * 2 / 1e4 // 多一個*2是擴充板downsample那邊的issue
|
||||
} else { // only select one chip
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
return this.GLOBAL.transFileSize(_meta.size * scale)
|
||||
} else {
|
||||
return this.GLOBAL.transFileSize(_meta.size)
|
||||
}
|
||||
}
|
||||
},
|
||||
transDate: function (dateTime) {
|
||||
return this.GLOBAL.dateToStr(dateTime)
|
||||
},
|
||||
@@ -501,6 +525,7 @@ export default {
|
||||
// TODO editFile
|
||||
if (this.editInfo.type !== 'folder') {
|
||||
await api.meta.update(this.editInfo.info.id, { name: this.editInfo.changedName })
|
||||
await api.meta.update30(this.editInfo.info.uuid, { name: this.editInfo.changedName })
|
||||
} else {
|
||||
}
|
||||
await this.editFileDone()
|
||||
@@ -671,53 +696,25 @@ export default {
|
||||
downloadExcel: async function (meta) {
|
||||
const href = location.href.split('/')[2]
|
||||
const connectCheck = await api.connection.check()
|
||||
if (meta.device.library_name.indexOf('Elite') >= 0) {
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/excel/' + meta.id)
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/excel/' + meta.id)
|
||||
}
|
||||
} else if (meta.device.library_name.indexOf('Neulive') >= 0) {
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/csv-edf/' + meta.id)
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv-edf/' + meta.id)
|
||||
}
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/csv-edf/' + meta.id)
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv-edf/' + meta.id)
|
||||
}
|
||||
},
|
||||
downloadSdCard: async function (meta) {
|
||||
const href = location.href.split('/')[2]
|
||||
if (meta.device.library_name.indexOf('Neulive') >= 0) {
|
||||
if (window.navigator.onLine) {
|
||||
window.open('http://' + href + '/#/download/online/csv/' + meta.id + '/sd')
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv/' + meta.id + '/sd')
|
||||
}
|
||||
}
|
||||
},
|
||||
async getSDuuidList () {
|
||||
// const uuidList = ['32abdb14-fc0c-11eb-9aa8-dca6326cbc6b', '940ca938-f662-11eb-8a41-dca6326cbc6b']
|
||||
if (this.controllerList.length > 0) {
|
||||
if (this.controllerList[0].sd_uploading === false) {
|
||||
const ret = await api.sdcard.getuuidList()
|
||||
if (ret.status === 200) {
|
||||
this.SDuuidList = ret.data
|
||||
}
|
||||
}
|
||||
const connectCheck = await api.connection.check()
|
||||
if (connectCheck.status === 200) {
|
||||
window.open('http://' + href + '/#/download/online/csv/' + meta.id + '/sd')
|
||||
} else {
|
||||
window.open('http://' + href + '/#/download/offline/csv/' + meta.id + '/sd')
|
||||
}
|
||||
},
|
||||
replay: function (meta) {
|
||||
if (meta.device.library_name.includes('Neulive') === true) {
|
||||
// window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/admin/data-replay/' + meta.id)
|
||||
this.$router.push({
|
||||
path: '/admin/data-replay/' + meta.id,
|
||||
})
|
||||
} else if (meta.device.library_name.includes('Elite') === true) {
|
||||
// window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/admin/data-replay/elite/' + meta.id)
|
||||
this.$router.push({
|
||||
path: '/admin/data-replay/elite/' + meta.id,
|
||||
})
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/admin/data-replay/' + meta.id,
|
||||
})
|
||||
},
|
||||
spike: function (meta) {
|
||||
if (meta.device.library_name.includes('Neulive') === true) {
|
||||
@@ -730,7 +727,6 @@ export default {
|
||||
async mounted () {
|
||||
const controllerList = await api.controller.getAll()
|
||||
this.controllerList = controllerList.data
|
||||
this.getSDuuidList()
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="row flex xs12 md6 xl6 sm6 select-box px-0">
|
||||
<va-select
|
||||
:ref="'selection'"
|
||||
class="flex xs6 md6 xl6 sm6 my-0 py-0 pr-0 file-option-select-box"
|
||||
:label="'select'"
|
||||
v-model="selectionModel"
|
||||
textBy="description"
|
||||
:options="selectionOptions"
|
||||
:input="selectionEvent()"
|
||||
/>
|
||||
<va-select
|
||||
:ref="'action'"
|
||||
class="flex xs6 md6 xl6 sm6 my-0 py-0 pr-0 file-option-select-box"
|
||||
:label="'action'"
|
||||
v-model="actionModel"
|
||||
textBy="description"
|
||||
:options="actionOptions"
|
||||
:input="actionEvent()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { mapActions } from 'vuex'
|
||||
import fileTypes from '@/store/modules/file/types'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('file', [
|
||||
'dataList',
|
||||
]),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selectionModel: {},
|
||||
selectionOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: 'Select All File',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
description: 'Clear',
|
||||
},
|
||||
],
|
||||
actionModel: {},
|
||||
actionOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: 'Export (.csv)',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectionEvent () {
|
||||
if (this.selectionModel == null) {
|
||||
return null
|
||||
}
|
||||
switch (this.selectionModel.description) {
|
||||
case 'Select All File':
|
||||
this.fileTableSelectAll()
|
||||
break
|
||||
case 'Clear':
|
||||
this.fileTableSelectClear()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if (this.$refs.selection !== undefined && this.$refs.selection !== null) {
|
||||
this.$refs.selection.clear()
|
||||
}
|
||||
},
|
||||
actionEvent () {
|
||||
if (this.actionModel == null) {
|
||||
return null
|
||||
}
|
||||
switch (this.actionModel.description) {
|
||||
case 'Export (.csv)':
|
||||
this.fileDownloadCsv()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if (this.$refs.action !== undefined && this.$refs.action !== null) {
|
||||
this.$refs.action.clear()
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions('file', [fileTypes.file.downloadCsv]),
|
||||
...mapActions('file', [fileTypes.table.selectAll, fileTypes.table.selectClear]),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.file-option-select-box {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="row flex xs12 md6 xl6 sm6 select-box px-0">
|
||||
<va-select
|
||||
:ref="'selection'"
|
||||
class="flex xs6 md6 xl6 sm6 my-0 py-0 pr-0 file-option-select-box"
|
||||
:label="'select'"
|
||||
v-model="selectionModel"
|
||||
textBy="description"
|
||||
:options="selectionOptions"
|
||||
:input="selectionEvent()"
|
||||
/>
|
||||
<va-select
|
||||
:ref="'action'"
|
||||
class="flex xs6 md6 xl6 sm6 my-0 py-0 pr-0 file-option-select-box"
|
||||
:label="'action'"
|
||||
v-model="actionModel"
|
||||
textBy="description"
|
||||
:options="actionOptions"
|
||||
:input="actionEvent()"
|
||||
/>
|
||||
</div>
|
||||
<va-modal
|
||||
class="flex sm12 xl12 md12 xs12"
|
||||
v-model="deleteConfirmModal"
|
||||
:title="'Those files will be deleted:'"
|
||||
:okText=" $t('modal.confirm') "
|
||||
@ok="deleteFile()"
|
||||
>
|
||||
<div v-for="(data, index) in dataList" :key="index">
|
||||
<div v-if="data.selected" class="my-2 py-2">
|
||||
<p class="display-6"> {{ data.name }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { mapActions } from 'vuex'
|
||||
import fileTypes from '@/store/modules/sdcard/types'
|
||||
import api from '@/data/api/index'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
computed: {
|
||||
...mapFields('sdcard', [
|
||||
'sdInfoList',
|
||||
'dataList',
|
||||
'controllerList',
|
||||
'sdcardUploadModal',
|
||||
'sdcardDeleteModal',
|
||||
]),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selectionModel: {},
|
||||
selectionOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: 'Select All File',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
description: 'Clear',
|
||||
},
|
||||
],
|
||||
actionModel: {},
|
||||
actionOptions: [
|
||||
{
|
||||
id: 0,
|
||||
description: 'Upload to hub',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
description: 'Delete',
|
||||
},
|
||||
],
|
||||
deleteConfirmModal: false,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
async '+/data-server/s_d_card_manager/#' (data, topic) {
|
||||
const mes = new TextDecoder('utf-8').decode(data)
|
||||
const topicSplit = topic.split('/')
|
||||
const baseIndex = topicSplit.indexOf('s_d_card_manager')
|
||||
const actionIndex = baseIndex + 1
|
||||
const action = topicSplit[actionIndex]
|
||||
let ret
|
||||
let dataObj
|
||||
switch (action) {
|
||||
case 'upload_raw':
|
||||
ret = JSON.parse(mes)
|
||||
dataObj = this.dataList.find(d => (d.uuid === ret.meta_uuid && d.sdName === ret.sd_name))
|
||||
dataObj.uploading = false
|
||||
if (ret.status === 'success') {
|
||||
dataObj.uploaded = true
|
||||
const ret = await api.meta.getByUUID(dataObj.uuid) // control system 2.0 meta
|
||||
const metaID = ret.data[0].id
|
||||
await api.meta.update(metaID, { sd_data_uploaded: true })
|
||||
}
|
||||
break
|
||||
case 'delete_meta':
|
||||
ret = JSON.parse(mes)
|
||||
dataObj = this.dataList.find(d => (d.uuid === ret.meta_uuid && d.sdName === ret.sd_name))
|
||||
dataObj.deleting = false
|
||||
if (ret.status === 'success') {
|
||||
dataObj.deleted = true
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
mqttPub: function (topic, mes) {
|
||||
this.$mqtt.publish(topic, mes)
|
||||
},
|
||||
selectionEvent () {
|
||||
if (this.selectionModel == null) {
|
||||
return null
|
||||
}
|
||||
switch (this.selectionModel.description) {
|
||||
case 'Select All File':
|
||||
this.sdcardTableSelectAll()
|
||||
break
|
||||
case 'Clear':
|
||||
this.sdcardTableSelectClear()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if (this.$refs.selection !== undefined && this.$refs.selection !== null) {
|
||||
this.$refs.selection.clear()
|
||||
}
|
||||
},
|
||||
actionEvent () {
|
||||
if (this.actionModel == null) {
|
||||
return null
|
||||
}
|
||||
switch (this.actionModel.description) {
|
||||
case 'Upload to hub':
|
||||
this.uploadFile()
|
||||
this.sdcardUploadModal = true
|
||||
break
|
||||
case 'Delete':
|
||||
this.deleteConfirmModal = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if (this.$refs.action !== undefined && this.$refs.action !== null) {
|
||||
this.$refs.action.clear()
|
||||
}
|
||||
},
|
||||
uploadFile () {
|
||||
for (let i = 0; i < this.dataList.length; i++) {
|
||||
const file = this.dataList[i]
|
||||
if (file.selected) {
|
||||
file.uploading = true
|
||||
const sdName = this.sdInfoList.find(info => info.meta_list.includes(file.uuid)).name
|
||||
this.mqttPub(this.controllerList[0].mac_address + '/client/s_d_card_manager/upload_raw', JSON.stringify({ sd_name: sdName, meta_uuid: file.uuid }))
|
||||
}
|
||||
}
|
||||
},
|
||||
async deleteFile () {
|
||||
for (let i = 0; i < this.dataList.length; i++) {
|
||||
const file = this.dataList[i]
|
||||
if (file.selected) {
|
||||
file.deleting = true
|
||||
const sdName = this.sdInfoList.find(info => info.meta_list.includes(file.uuid)).name
|
||||
this.mqttPub(this.controllerList[0].mac_address + '/client/s_d_card_manager/delete_meta', JSON.stringify({ sd_name: sdName, meta_uuid: file.uuid }))
|
||||
}
|
||||
}
|
||||
this.sdcardDeleteModal = true
|
||||
},
|
||||
|
||||
...mapActions('sdcard', [fileTypes.file.uploadToHub]),
|
||||
...mapActions('sdcard', [fileTypes.table.selectAll, fileTypes.table.selectClear]),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.file-option-select-box {
|
||||
text-align: right;
|
||||
font-weight: normal;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
</style>
|
||||
@@ -45,7 +45,11 @@ export default {
|
||||
this.initMetaInfo(meta)
|
||||
this.pageToast('Start Moving to database...')
|
||||
const result = await apiManager.call('Move_to_DB', controller.id, this.metaID, this.mac_address, this.uuid)
|
||||
return result
|
||||
if (result !== undefined) {
|
||||
return result
|
||||
} else {
|
||||
throw new Error('error')
|
||||
}
|
||||
},
|
||||
async deleteSDCard (meta, controller) {
|
||||
this.initMetaInfo(meta)
|
||||
|
||||
@@ -10,16 +10,12 @@
|
||||
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"
|
||||
style="width: 1rem; height: 1rem;"
|
||||
@click="performButtonAction()"
|
||||
/>
|
||||
</va-button>
|
||||
|
||||
<sd-card-api-action :ref="'sd_card_api'"/>
|
||||
@@ -33,7 +29,7 @@
|
||||
/>
|
||||
<progress-modal
|
||||
title="SD card data uploading"
|
||||
:time="(parseInt(meta.time_duration) / 2e6).toString()"
|
||||
:meta="meta"
|
||||
:ref="'progressModalRef'"
|
||||
/>
|
||||
</div>
|
||||
@@ -54,10 +50,6 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
uuidList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
meta: {
|
||||
type: Object,
|
||||
required: true,
|
||||
@@ -77,7 +69,7 @@ export default {
|
||||
mqtt: {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
async init () {
|
||||
this.metaID = this.meta.id
|
||||
this.mac_address = this.meta.device.device_address.map(data =>
|
||||
data.toString(16)
|
||||
@@ -90,11 +82,7 @@ export default {
|
||||
const uploaded = this.meta.sd_data_uploaded // true or false
|
||||
if (recorded === true) {
|
||||
if (uploaded !== true) {
|
||||
if (this.uuidList.includes(this.uuid)) {
|
||||
this.moveToDBModal = true
|
||||
} else {
|
||||
this.pageToast('Can not access data. Please connect your device to hub by usb.')
|
||||
}
|
||||
this.moveToDBModal = true
|
||||
} else {
|
||||
this.executeDownload()
|
||||
}
|
||||
@@ -104,19 +92,16 @@ export default {
|
||||
await this.executeUpload()
|
||||
this.$emit('refreshPage')
|
||||
},
|
||||
// async executeCheckInSD () {
|
||||
// return await this.$refs.sd_card_api.checkInSD(this.meta)
|
||||
// },
|
||||
async executeUpload () {
|
||||
this.$refs.progressModalRef.show()
|
||||
const result = await this.$refs.sd_card_api.moveToDB(this.meta, this.controller).catch(_ => {
|
||||
this.$refs.progressModalRef.hide()
|
||||
this.pageToast('Upload SD card data error, please connect Neullive to Hub by usb or data corrupted.')
|
||||
this.pageToast('Upload SD card data error, please connect Neulive to Hub by usb or data corrupted or data deleted.')
|
||||
})
|
||||
if (result.data === true) {
|
||||
this.pageToast('Upload SD card data done.')
|
||||
} else {
|
||||
this.pageToast('Upload SD card data error, please connect Neullive to Hub by usb or data corrupted.')
|
||||
this.pageToast('Upload SD card data error, please connect Neulive to Hub by usb or data corrupted or data deleted.')
|
||||
}
|
||||
this.$refs.progressModalRef.progressDone()
|
||||
// remove delete sd card first, still have problem.
|
||||
@@ -137,11 +122,11 @@ export default {
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
this.init()
|
||||
await this.init()
|
||||
},
|
||||
watch: { // cuz data table will not call mounted
|
||||
meta: function (val) {
|
||||
this.init()
|
||||
meta: async function (val) {
|
||||
await this.init()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-modal
|
||||
class="flex sm12 xl12 md12 xs12"
|
||||
v-model="sdcardDeleteModal"
|
||||
:cancelText="''"
|
||||
:okText=" $t('modal.close') "
|
||||
@cancel="removeDeletedFiles()"
|
||||
@ok="removeDeletedFiles()"
|
||||
>
|
||||
<div v-for="(data, index) in dataList" :key="index">
|
||||
<div v-if="data.selected" class="my-2 py-2">
|
||||
<p class="display-6"> {{ data.name }} </p>
|
||||
<vm-progress v-if="data.deleting" :percentage="50" :text-inside="true" :stroke-width="18" :striped="true"></vm-progress>
|
||||
<vm-progress v-if="!data.deleting && !data.deleted" :percentage="0" status="exception"></vm-progress>
|
||||
<vm-progress v-if="!data.deleting && data.deleted" :percentage="100" status="success"></vm-progress>
|
||||
</div>
|
||||
</div>
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('sdcard', [
|
||||
'dataList',
|
||||
'sdcardDeleteModal',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
removeDeletedFiles () {
|
||||
this.dataList.forEach(item => {
|
||||
item.deleting = false
|
||||
})
|
||||
this.dataList = this.dataList.filter(d => d.deleted !== true)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div style="height: 94vh; overflow: auto;">
|
||||
<va-card class="flex sm12 xl12 md12 xs12">
|
||||
<!-- FileListTable -->
|
||||
<div v-if="tableLoading" class="row flex sm12 xl12 md12 xs12">
|
||||
<div class="flex-center spinner-box mt-4 mb-5">
|
||||
<fulfilling-bouncing-circle-spinner
|
||||
:animation-duration="3000"
|
||||
:color="'#6c7fee'"
|
||||
:size="60"
|
||||
>
|
||||
</fulfilling-bouncing-circle-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-data-table
|
||||
:fields="fields"
|
||||
:data="dataList"
|
||||
:per-page="parseInt(perPage)"
|
||||
hoverable
|
||||
clickable
|
||||
v-else
|
||||
:ref="'datatable_ref'"
|
||||
>
|
||||
<template slot="select" slot-scope="props">
|
||||
<va-checkbox v-model="props.rowData.selected"/>
|
||||
</template>
|
||||
<template slot="name" slot-scope="props">
|
||||
<va-icon
|
||||
:name="'fa fa-file'"
|
||||
size="0.9rem"
|
||||
class="mr-1"
|
||||
style="cursor: pointer;"
|
||||
:color="'dark'"
|
||||
/>
|
||||
{{ props.rowData.name }}
|
||||
</template>
|
||||
<template slot="create_date" slot-scope="props">
|
||||
{{ props.rowData.create_date }}
|
||||
</template>
|
||||
<template slot="sd_name" slot-scope="props">
|
||||
{{ props.rowData.sdName }}
|
||||
</template>
|
||||
<template slot="device_info" slot-scope="props">
|
||||
{{ props.rowData.device.library_name + ', ' + props.rowData.device.device_version }}
|
||||
</template>
|
||||
</va-data-table>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { mapActions } from 'vuex'
|
||||
import fileTypes from '@/store/modules/sdcard/types'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FulfillingBouncingCircleSpinner,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('sdcard', [
|
||||
'tableLoading',
|
||||
'tableKey',
|
||||
'sdInfoList',
|
||||
'dataList',
|
||||
]),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
perPage: 80,
|
||||
fields: [
|
||||
{
|
||||
name: '__slot:select',
|
||||
width: '5%',
|
||||
},
|
||||
{
|
||||
name: '__slot:name',
|
||||
title: 'name',
|
||||
sortField: 'name',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
{
|
||||
name: '__slot:create_date',
|
||||
title: 'create date',
|
||||
sortField: 'create_date',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
{
|
||||
name: '__slot:sd_name',
|
||||
title: 'sd_name',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
}, {
|
||||
name: '__slot:device_info',
|
||||
title: 'device info',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
async '+/data-server/s_d_card_manager/#' (data, topic) {
|
||||
const mes = new TextDecoder('utf-8').decode(data)
|
||||
const topicSplit = topic.split('/')
|
||||
const baseIndex = topicSplit.indexOf('s_d_card_manager')
|
||||
const actionIndex = baseIndex + 1
|
||||
const action = topicSplit[actionIndex]
|
||||
let foundIndex
|
||||
let mesJson
|
||||
switch (action) {
|
||||
case 'info_all':
|
||||
this.sdInfoList = JSON.parse(mes)
|
||||
await this.sdcardUpdateDataList()
|
||||
break
|
||||
case 'register':
|
||||
mesJson = JSON.parse(mes)
|
||||
foundIndex = this.sdInfoList.findIndex(o => o.name === mesJson.name)
|
||||
if (foundIndex < 0) { // -1: not found
|
||||
this.sdInfoList.push(mesJson)
|
||||
} else {
|
||||
this.sdInfoList[foundIndex] = mesJson
|
||||
}
|
||||
await this.sdcardUpdateDataList()
|
||||
break
|
||||
case 'remove':
|
||||
foundIndex = this.sdInfoList.findIndex(o => o.name === mes)
|
||||
this.sdInfoList.splice(foundIndex, 1)
|
||||
await this.sdcardUpdateDataList()
|
||||
break
|
||||
// case 'info':
|
||||
// foundIndex = this.triggerList.findIndex(o => o.mac === _mac)
|
||||
// this.triggerList[foundIndex] = mesJson
|
||||
// break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('sdcard', [fileTypes.file.updateList]),
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div>
|
||||
<vm-progress v-if="meta.uploading" :percentage="progressValue" :text-inside="true" :stroke-width="18" :striped="true"></vm-progress>
|
||||
<vm-progress v-if="!meta.uploading && !meta.uploaded" :percentage="0" status="exception"></vm-progress>
|
||||
<vm-progress v-if="!meta.uploading && meta.uploaded" :percentage="100" status="success"></vm-progress>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/data/api/index'
|
||||
|
||||
export default {
|
||||
name: 'sdcard-reader-progress-bar',
|
||||
data () {
|
||||
return {
|
||||
progressValue: 0,
|
||||
timeInterval: null,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
meta: { // control system 3.0 meta
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async estimateTime () {
|
||||
const oldMetaList = await api.meta.getByUUID(this.meta.uuid) // control system 2.0 meta table
|
||||
const oldMeta = oldMetaList.data[0]
|
||||
const timeDuration = oldMeta.time_duration
|
||||
let sampleRate
|
||||
if (oldMeta.device.library_name !== 'Neulive3.5ex') {
|
||||
sampleRate = oldMeta.parameter_set.SAMPLE_RATE
|
||||
} else {
|
||||
const channelList = JSON.parse(oldMeta.channels)
|
||||
const chip1Channels = channelList.filter(c => c >= 0 && c <= 31)
|
||||
const chip2Channels = channelList.filter(c => c >= 32 && c <= 63)
|
||||
if (chip1Channels.length !== 0 && chip2Channels.length !== 0) {
|
||||
sampleRate = oldMeta.parameter_set.SAMPLE_RATE * 2
|
||||
} else {
|
||||
sampleRate = oldMeta.parameter_set.SAMPLE_RATE
|
||||
}
|
||||
}
|
||||
return parseFloat(timeDuration / 1e6) * parseFloat(sampleRate / 1e3) * 0.006 * 1
|
||||
// 經驗公式
|
||||
// time_duration/1e6: us -> s
|
||||
// sampleRate/1e3: sample rate unit to (k)
|
||||
// *3.5: control system 2.0 upload rate is about 250~350kb/s
|
||||
},
|
||||
animateValues (estimatedTime) { // estimatedTime unit: seconds
|
||||
let currentTime = this.progressValue = 0
|
||||
this.timeInterval = setInterval(() => {
|
||||
if (estimatedTime < 1) {
|
||||
this.progressValue = 99
|
||||
clearInterval(this.timeInterval)
|
||||
} else if (this.progressValue < 90) {
|
||||
currentTime += (1 / estimatedTime)
|
||||
this.progressValue = parseInt(currentTime * 100)
|
||||
} else if (this.progressValue >= 90 && this.progressValue < 99) {
|
||||
currentTime += (1 / (estimatedTime * 10)) // *10: buffer
|
||||
this.progressValue = parseInt(currentTime * 100)
|
||||
} else {
|
||||
this.progressValue = 99
|
||||
clearInterval(this.timeInterval)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
progressDone () {
|
||||
this.progressValue = 100
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
const estimatedTime = await this.estimateTime()
|
||||
this.animateValues(estimatedTime)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.timeInterval)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-modal
|
||||
class="flex sm12 xl12 md12 xs12"
|
||||
v-model="sdcardUploadModal"
|
||||
:cancelText="''"
|
||||
:okText=" $t('modal.close') "
|
||||
>
|
||||
<div v-for="(data, index) in dataList" :key="index">
|
||||
<div v-if="data.selected" class="my-2 py-2">
|
||||
<p class="display-6"> {{ data.name }} </p>
|
||||
<progress-bar :meta="data" />
|
||||
</div>
|
||||
</div>
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import ProgressBar from '@/components/file/sdcard/SdcardReaderProgressBar.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProgressBar,
|
||||
},
|
||||
props: {
|
||||
},
|
||||
created () {
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapFields('sdcard', [
|
||||
'dataList',
|
||||
'sdcardUploadModal',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div style="height: 94vh; overflow: auto;">
|
||||
<va-card class="flex sm12 xl12 md12 xs12">
|
||||
<!-- FileListTable -->
|
||||
<div v-if="tableLoading" class="row flex sm12 xl12 md12 xs12">
|
||||
<div class="flex-center spinner-box mt-4 mb-5">
|
||||
<fulfilling-bouncing-circle-spinner
|
||||
:animation-duration="3000"
|
||||
:color="'#6c7fee'"
|
||||
:size="60"
|
||||
>
|
||||
</fulfilling-bouncing-circle-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-data-table
|
||||
:fields="fields"
|
||||
:data="dataList"
|
||||
:per-page="parseInt(perPage)"
|
||||
hoverable
|
||||
clickable
|
||||
v-else
|
||||
:ref="'datatable_ref'"
|
||||
>
|
||||
<template slot="select" slot-scope="props">
|
||||
<va-checkbox v-model="props.rowData.selected"/>
|
||||
</template>
|
||||
<template slot="name" slot-scope="props">
|
||||
<va-icon
|
||||
:name="'fa fa-file'"
|
||||
size="0.9rem"
|
||||
class="mr-1"
|
||||
style="cursor: pointer;"
|
||||
:color="'dark'"
|
||||
/>
|
||||
{{ props.rowData.name }}
|
||||
</template>
|
||||
<template slot="create_date" slot-scope="props">
|
||||
{{ props.rowData.create_date }}
|
||||
</template>
|
||||
<template slot="time_duration" slot-scope="props">
|
||||
{{ transTime(props.rowData.time_duration) }}
|
||||
</template>
|
||||
<template slot="device_info" slot-scope="props">
|
||||
{{ props.rowData.device.library }}
|
||||
</template>
|
||||
</va-data-table>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
|
||||
import { mapFields } from 'vuex-map-fields'
|
||||
import { mapActions } from 'vuex'
|
||||
import fileTypes from '@/store/modules/file/types'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FulfillingBouncingCircleSpinner,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('file', [
|
||||
'tableLoading',
|
||||
'tableKey',
|
||||
'dataList',
|
||||
]),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
perPage: 80,
|
||||
fields: [
|
||||
{
|
||||
name: '__slot:select',
|
||||
width: '5%',
|
||||
},
|
||||
{
|
||||
name: '__slot:name',
|
||||
title: 'name',
|
||||
sortField: 'name',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
{
|
||||
name: '__slot:create_date',
|
||||
title: 'create date',
|
||||
sortField: 'create_date',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
{
|
||||
name: '__slot:time_duration',
|
||||
title: 'duration',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
}, {
|
||||
name: '__slot:device_info',
|
||||
title: 'device info',
|
||||
width: '15%',
|
||||
titleClass: 'text-center',
|
||||
dataClass: 'text-center',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transTime: function (time) {
|
||||
if (time === undefined) {
|
||||
return '--'
|
||||
} else {
|
||||
return this.GLOBAL.microsecondsFormat(time * 1e3) // ms -> us
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions('file', [fileTypes.file.updateList]),
|
||||
},
|
||||
async mounted () {
|
||||
await this.fileUpdateDataList()
|
||||
},
|
||||
destroyed () {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -54,6 +54,12 @@
|
||||
<va-button class="" color="success" target="_blank" @click="startAllClick()" small>
|
||||
START ALL
|
||||
</va-button>
|
||||
<va-button class="" color="primary" target="_blank" @click="recordAllClick()" small>
|
||||
RECORD ALL
|
||||
</va-button>
|
||||
<va-button class="" color="warning" target="_blank" @click="stimulateAllClick()" small>
|
||||
STIMULATE ALL
|
||||
</va-button>
|
||||
<va-button class="no-dark-style-va-button" color="danger" target="_blank" @click="stopAllClick()" small>
|
||||
STOP ALL
|
||||
</va-button>
|
||||
@@ -78,6 +84,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;">
|
||||
@@ -99,9 +114,11 @@ import { dataStreamBuffer } from '../../../data/task/DataStreamBuffer'
|
||||
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 taskTypes from '@/store/modules/task/content/types'
|
||||
// import TaskFormula from './formula/TaskFormula'
|
||||
|
||||
@@ -112,6 +129,7 @@ export default {
|
||||
components: {
|
||||
CanvasChart,
|
||||
TaskDevices,
|
||||
TriggerChart,
|
||||
},
|
||||
computed: {
|
||||
...mapFields('taskContent', [
|
||||
@@ -120,6 +138,9 @@ export default {
|
||||
'taskContentChartPanelKey',
|
||||
'taskContentChartNumber',
|
||||
]),
|
||||
...mapFields('device', [
|
||||
'triggerList',
|
||||
]),
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -187,16 +208,92 @@ export default {
|
||||
taskInfo.deviceSavePath(String.fromCharCode.apply(null, data))
|
||||
// console.log(taskInfo.getTaskInfo())
|
||||
},
|
||||
async '+/device_sd_card_status/+' (data, topic) {
|
||||
const dataDecode = JSON.parse(String.fromCharCode.apply(null, data))
|
||||
for (const device of this.$refs.devices_ref.deviceList) {
|
||||
if (dataDecode.device === taskInfo.getRawDeviceID(device.id)) {
|
||||
device.sdCardStatus = dataDecode.data
|
||||
async '+/device_get_sd_storage/+' (data, topic) {
|
||||
const mes = new TextDecoder('utf-8').decode(data)
|
||||
const mesJson = JSON.parse(mes)
|
||||
const storage = parseInt(mesJson.data) // unit: kb
|
||||
const deviceID = parseInt(mesJson.device)
|
||||
const libraryName = taskInfo.getDeviceInfoByRawID(deviceID).info.library_name
|
||||
let sampleRate
|
||||
if (libraryName !== 'Neulive3.5ex') {
|
||||
sampleRate = taskInfo.getDeviceInfoByRawID(deviceID).parameterSet.SAMPLE_RATE
|
||||
} else {
|
||||
const channelList = this.findAllIndexes(taskInfo.getDeviceInfoByRawID(deviceID).parameterSet.CH_LIST, 1)
|
||||
const chip1Channels = channelList.filter(c => c >= 0 && c <= 31)
|
||||
const chip2Channels = channelList.filter(c => c >= 32 && c <= 63)
|
||||
if (chip1Channels.length !== 0 && chip2Channels.length !== 0) {
|
||||
sampleRate = taskInfo.getDeviceInfoByRawID(deviceID).parameterSet.SAMPLE_RATE * 2
|
||||
} else {
|
||||
sampleRate = taskInfo.getDeviceInfoByRawID(deviceID).parameterSet.SAMPLE_RATE
|
||||
}
|
||||
}
|
||||
await taskInfo.updateSdCardStatus(String.fromCharCode.apply(null, data))
|
||||
// 經驗公式
|
||||
// 3: 1 sample = 3 bytes
|
||||
// /1000: sample rate unit to k
|
||||
// 1.1: 10% buffer
|
||||
const estimatedRemainTime = parseInt(storage / (3 * (sampleRate / 1000) * 1.1))
|
||||
const showTime = this.GLOBAL.transTime(estimatedRemainTime * 1e6) // *1e6: s -> us
|
||||
this.pageToast(`The SD card can still record ${showTime} with sampling rate ${(sampleRate / 1e3).toFixed(1) + 'k'}`)
|
||||
},
|
||||
async '+/get_sd_status/+' (data, topic) {
|
||||
const mes = new TextDecoder('utf-8').decode(data)
|
||||
const mesJson = JSON.parse(mes)
|
||||
for (const device of this.$refs.devices_ref.deviceList) {
|
||||
if (mesJson.device === taskInfo.getRawDeviceID(device.id)) {
|
||||
device.sdCardStatus = mesJson.data.sd_mount
|
||||
}
|
||||
}
|
||||
await taskInfo.updateSdCardStatus(mes)
|
||||
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).connected
|
||||
this.deviceTriggerChartSetDisplay()
|
||||
this.taskContentChartRefreshPanel()
|
||||
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()
|
||||
this.taskContentChartRefreshPanel()
|
||||
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
|
||||
this.deviceTriggerChartSetDisplay()
|
||||
this.taskContentChartRefreshPanel()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
async '+/broadcast' (data, topic) {
|
||||
const mes = String.fromCharCode.apply(null, data).split(':')
|
||||
// let content
|
||||
@@ -282,7 +379,9 @@ export default {
|
||||
this.mqttSub(id + '/get_device_info/+')
|
||||
this.mqttSub(id + '/device_parameter/+')
|
||||
this.mqttSub(id + '/device_parameter_value/+')
|
||||
this.mqttSub(id + '/device_sd_card_status/+')
|
||||
this.mqttSub(id + '/device_get_sd_storage/+')
|
||||
this.mqttSub(id + '/get_sd_status/+')
|
||||
this.mqttSub(id + '/data-server/trigger_manager/#')
|
||||
},
|
||||
pageMqttUnSub: function (id) {
|
||||
for (const device in taskInfo.getTaskInfo().deviceList) {
|
||||
@@ -294,7 +393,9 @@ export default {
|
||||
this.mqttUnSub(id + '/get_device_info/+')
|
||||
this.mqttUnSub(id + '/device_parameter/+')
|
||||
this.mqttUnSub(id + '/device_parameter_value/+')
|
||||
this.mqttUnSub(id + '/device_sd_card_status/+')
|
||||
this.mqttUnSub(id + '/device_get_sd_storage/+')
|
||||
this.mqttUnSub(id + '/get_sd_status/+')
|
||||
this.mqttUnSub(id + '/data-server/trigger_manager/#')
|
||||
},
|
||||
pageInit: async function () {
|
||||
if (await this.$refs.devices_ref != null) {
|
||||
@@ -314,6 +415,8 @@ export default {
|
||||
}))
|
||||
|
||||
this.taskContentChartNumber = Object.keys(this.chartData).length
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/scan', '{}')
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '/client/trigger_manager/info_all', '{}')
|
||||
},
|
||||
pageToast: function (mes) {
|
||||
this.showToast(
|
||||
@@ -325,8 +428,15 @@ export default {
|
||||
},
|
||||
)
|
||||
},
|
||||
findAllIndexes (arr, val) {
|
||||
var index = []; var i = -1
|
||||
while ((i = arr.indexOf(val, i + 1)) !== -1) {
|
||||
index.push(i)
|
||||
}
|
||||
return index
|
||||
},
|
||||
refreshData: function () {
|
||||
// TODO don't need to refresh chart with stop status
|
||||
// [TODO] don't need to refresh chart with stop status
|
||||
for (const chart in this.chartData) {
|
||||
this.taskContentChartRefreshData({
|
||||
chartID: parseInt(chart),
|
||||
@@ -338,9 +448,6 @@ export default {
|
||||
if (this.$refs.devices_ref != null) {
|
||||
this.$refs.devices_ref.refresh()
|
||||
}
|
||||
// api.task.update({
|
||||
// device_config: taskInfo.getTaskInfo().deviceList,
|
||||
// })
|
||||
},
|
||||
addChart: async function (isDefault) {
|
||||
this.taskContentChartAdd({
|
||||
@@ -489,6 +596,28 @@ export default {
|
||||
this.refreshDevicesWindow()
|
||||
this.pageToast('The all devices have been started')
|
||||
},
|
||||
recordAllClick: async function () {
|
||||
for (const device of this.deviceList) {
|
||||
if (this.$refs.devices_ref === undefined) {
|
||||
this.startDevice(device)
|
||||
} else {
|
||||
await this.$refs.devices_ref.$refs.pdlist_ref.record(device)
|
||||
}
|
||||
}
|
||||
this.refreshDevicesWindow()
|
||||
this.pageToast('The all devices are recording')
|
||||
},
|
||||
stimulateAllClick: async function () {
|
||||
for (const device of this.deviceList) {
|
||||
if (this.$refs.devices_ref === undefined) {
|
||||
this.startDevice(device)
|
||||
} else {
|
||||
await this.$refs.devices_ref.$refs.pdlist_ref.stimulate(device)
|
||||
}
|
||||
}
|
||||
this.refreshDevicesWindow()
|
||||
this.pageToast('The all devices are stimulating')
|
||||
},
|
||||
startDevice: async function (device) {
|
||||
await this.taskContentChartDefaultChartGenerator({
|
||||
_deviceList: this.deviceList,
|
||||
@@ -566,6 +695,8 @@ export default {
|
||||
taskTypes.chart.refreshChart,
|
||||
taskTypes.chart.refreshPanel,
|
||||
]),
|
||||
|
||||
...mapActions('device', [deviceTypes.triggerChart.setDisplay]),
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
@@ -578,7 +709,6 @@ export default {
|
||||
await this.pageInit()
|
||||
|
||||
this.dataRefreshTimer = setInterval(this.refreshData, 100)
|
||||
this.chartRefreshTimer = setInterval(this.taskContentChartRefreshChart, 1000)
|
||||
|
||||
// const _this = this
|
||||
// window.onresize = function () { // define the event of window size changing
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</va-tabs>
|
||||
<div v-if="tabValue == 0" >
|
||||
<div v-if="chartData[chartID].legend.data.length != 0">
|
||||
<chart :key="taskContentChartKey" :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="chartData[chartID]" :auto-resize="true"></chart>
|
||||
<chart :key="taskContentChartKey" :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="data" :auto-resize="true"></chart>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="tabValue == 1" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" >
|
||||
@@ -147,6 +147,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: null,
|
||||
tabValue: 0,
|
||||
resizeTimer: null,
|
||||
mappingSelect: '',
|
||||
@@ -324,6 +325,7 @@ export default {
|
||||
]),
|
||||
},
|
||||
async mounted () {
|
||||
this.data = this.chartData[this.chartID]
|
||||
this.resizeTimer = setInterval(this.resize, 1000)
|
||||
this.nameCard = this.chartData[this.chartID].cardName
|
||||
await this.refreshMappingSelect()
|
||||
|
||||
@@ -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,14 +38,13 @@
|
||||
</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>
|
||||
<!-- <va-icon
|
||||
name="fas fa-sd-card"
|
||||
:color="(device.sdCardStatus === true) ? 'primary' : 'grey'"
|
||||
class="mr-3"
|
||||
@click.native.stop="sdCardStatus(device)"
|
||||
>
|
||||
</va-icon> -->
|
||||
<img
|
||||
v-if="device.sdCardStatus"
|
||||
src="@/assets/icons/sd-card-solid.svg"
|
||||
:class="getSDEnableStatus(device.id) ? 'mr-3 filter-none' : 'mr-3 filter-grey'"
|
||||
style="width: 1rem; height: 1rem;"
|
||||
@click.stop="enableSD(device)"
|
||||
/>
|
||||
<va-icon
|
||||
size="1rem"
|
||||
:name="(device.transmission_type === 0) ? 'fa fa-bluetooth-b' : 'fa fa-usb'"
|
||||
@@ -66,6 +65,7 @@
|
||||
<va-button @click.stop="stimulate(device)" v-if="device.type == 'Neulive' && !device.isStimulate" color="warning" class="px-2 py-0 mr-0 no-dark-style-va-button" small>STIMULATE</va-button>
|
||||
<va-button @click.stop="stopStimulate(device)" v-if="device.type == 'Neulive' && device.isStimulate" color="danger" class="px-2 py-0 mr-0" small>STOP STI</va-button>
|
||||
<va-button @click.stop="fastSettle(device)" v-if="device.type == 'Neulive'" color="info" class="px-2 py-0 mr-0 no-dark-style-va-button" small>R</va-button>
|
||||
<va-button @click.stop="checkSDStatus(device)" v-if="device.type == 'Neulive'" :disabled="device.isSync || device.isStimulate" color="success" class="px-2 py-0 mr-0 no-dark-style-va-button" small>SD</va-button>
|
||||
</va-item-label>
|
||||
</va-item-section>
|
||||
</va-item>
|
||||
@@ -98,6 +98,7 @@ import NeuliveParameterWindow from '../parameter/NeuliveParameterWindow'
|
||||
import FileControlWindow from '../meta/FileControlWindow'
|
||||
import { mapActions } from 'vuex'
|
||||
import types from '@/store/modules/task/content/types'
|
||||
import { taskInfo } from '@/data/task/TaskInfo'
|
||||
|
||||
export default {
|
||||
name: 'TaskDeviceList',
|
||||
@@ -122,12 +123,6 @@ export default {
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
sdCardStatus: function (device) {
|
||||
this.$emit('sd_card_status', {
|
||||
id: device.id,
|
||||
sdCardStatus: device.sdCardStatus,
|
||||
})
|
||||
},
|
||||
removeCheck: function (device) {
|
||||
this.selectDevice = device
|
||||
this.showDeleteModal = true
|
||||
@@ -233,14 +228,28 @@ export default {
|
||||
this.$emit('fast_settle', device)
|
||||
this.pageToast('The device ' + device.name + ' fast settle.')
|
||||
},
|
||||
checkSDStatus (device) {
|
||||
this.$emit('check_sd_card_status', device)
|
||||
this.pageToast('Check ' + device.name + ' SD card status.')
|
||||
},
|
||||
getSDEnableStatus (deviceID) {
|
||||
return taskInfo.getDeviceInfo(deviceID).parameterSet.SD_ENABLE
|
||||
},
|
||||
enableSD (device) {
|
||||
if (!device.isSync && !device.isStimulate) {
|
||||
if (taskInfo.getDeviceInfo(device.id).parameterSet.SD_ENABLE) {
|
||||
this.pageToast('Try to disable' + device.name + ' SD card.')
|
||||
} else {
|
||||
this.pageToast('Try to enable' + device.name + ' SD card.')
|
||||
}
|
||||
this.$emit('enable_sd', device)
|
||||
}
|
||||
},
|
||||
recoverDetectButton () {
|
||||
document.getElementById('detectButton').className += ' no-dark-style-va-button'
|
||||
},
|
||||
...mapActions('taskContent', [types.param.checkIfRecordingSetDone, types.param.checkIfStimulationSetDone, types.param.checkIfDeviceSetDone]),
|
||||
},
|
||||
destroyed () {
|
||||
// this.deviceList.length = 0
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -250,4 +259,12 @@ export default {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-none {
|
||||
filter: grayscale(0%);
|
||||
}
|
||||
|
||||
.filter-grey {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,9 +25,32 @@
|
||||
@sti="sti($event)"
|
||||
@sti_stop="stiStop($event)"
|
||||
@fast_settle="fastSettle($event)"
|
||||
@enable_sd="enableSD($event)"
|
||||
@generate="generateChart($event)"
|
||||
@detect="detectDevice($event)"
|
||||
@sd_card_status="sdCardStatus($event)"
|
||||
@check_sd_card_status="checkSDCardStatus($event)"
|
||||
/>
|
||||
</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>
|
||||
|
||||
@@ -38,6 +61,7 @@
|
||||
<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 { dataStreamBuffer } from '../../../../data/task/DataStreamBuffer'
|
||||
@@ -52,6 +76,7 @@ export default {
|
||||
// TaskDeviceListGM,
|
||||
FulfillingBouncingCircleSpinner,
|
||||
TaskDeviceList,
|
||||
TriggerList,
|
||||
},
|
||||
props: {
|
||||
cardclass: {
|
||||
@@ -72,7 +97,6 @@ export default {
|
||||
return {
|
||||
deviceSelect: '',
|
||||
deviceOptions: taskInfo.getDeviceListNotRegister(),
|
||||
// deviceList: this.deviceList,
|
||||
state: {
|
||||
LOADING: false,
|
||||
},
|
||||
@@ -99,6 +123,10 @@ export default {
|
||||
const index = this.deviceList.findIndex(d => taskInfo.getRawDeviceID(d.id) === i)
|
||||
if (index < 0) {
|
||||
await this.addDevice(i)
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'get_sd_status/0',
|
||||
device: i,
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
const index = this.deviceList.findIndex(d => taskInfo.getRawDeviceID(d.id) === i)
|
||||
@@ -129,6 +157,10 @@ export default {
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
instruction: 'record',
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_get_sd_storage/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
@@ -182,25 +214,25 @@ export default {
|
||||
},
|
||||
stopDevice: async function (device) {
|
||||
await dataStreamBuffer.removeByDevice(taskInfo.getRawDeviceID(device.id))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
instruction: 'stop_stimulate',
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_parameter/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
parameter: 'STIMULATING',
|
||||
content: 0,
|
||||
}))
|
||||
device.isStimulate = false
|
||||
if (taskInfo.getDeviceInfo(device.id).info.isSync) {
|
||||
if (taskInfo.getDeviceInfo(device.id).info.library_name.startsWith('Neulive')) {
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
instruction: 'stop_stimulate',
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
instruction: 'stop_record',
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_parameter/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
parameter: 'STIMULATING',
|
||||
content: 0,
|
||||
}))
|
||||
device.isStimulate = false
|
||||
} else if (taskInfo.getDeviceInfo(device.id).info.library_name.startsWith('Elite')) {
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
@@ -237,6 +269,10 @@ export default {
|
||||
_chartID: null,
|
||||
_type: 'device',
|
||||
})
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_get_sd_storage/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
}))
|
||||
if (!taskInfo.getDeviceInfo(device.id).info.isSync) {
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_instruction/0',
|
||||
@@ -314,11 +350,47 @@ export default {
|
||||
instruction: 'FAST_SET',
|
||||
}))
|
||||
},
|
||||
sdCardStatus: function (device) {
|
||||
checkSDCardStatus: function (device) {
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_sd_card_status/0',
|
||||
header: 'get_sd_status/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_get_sd_storage/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
}))
|
||||
},
|
||||
enableSD (device) {
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_parameter/0',
|
||||
device: taskInfo.getRawDeviceID(device.id),
|
||||
parameter: 'SD_ENABLE',
|
||||
content: !taskInfo.getDeviceInfo(device.id).parameterSet.SD_ENABLE,
|
||||
}))
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'get_sd_status/0',
|
||||
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) {
|
||||
|
||||
@@ -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>
|
||||
@@ -1,130 +1,132 @@
|
||||
<template>
|
||||
<div class="flex" >
|
||||
<va-tree-root >
|
||||
<va-tree-category
|
||||
isOpen
|
||||
style="font-size: 20px;"
|
||||
:label="'Channels'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]"
|
||||
:key="channel"
|
||||
<div>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">CHANNELS</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in channelArray"
|
||||
:key="channel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
<va-button
|
||||
class="mx-1 px-1"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="mx-1 px-1"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'3-AXIS'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
<va-button
|
||||
class="mx-1 px-1"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="mx-1 px-1"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Amp gain'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">3-AXIS SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Sample Rate'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE GAIN</p>
|
||||
</div>
|
||||
<div class="row" v-if="ampGainSelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Accelerator Sensitivity'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">SAMPLING RATE</p>
|
||||
</div>
|
||||
<div class="row" v-if="sampleRateSelect">
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</va-tree-root>
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<span> sps in total </span>
|
||||
</div>
|
||||
<!-- v-if="sampleRateSelect" -> ref: https://stackoverflow.com/questions/6003884/how-do-i-check-for-null-values-in-javascript -->
|
||||
<div class="flex sm12 xl12 md12 xs12 pt-0 pb-2 my-0" v-if="sampleRateSelect">
|
||||
<p v-if="channelList.length !== 0"> {{ (sampleRateSelect.value / (channelList.length * 1e3)).toFixed(1) + 'k' }} sps per selected channel </p>
|
||||
<p v-else> NaN sps per selected channel </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">ACCELERATOR SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row" v-if="acceleratorSensitivitySelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -153,6 +155,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
channelArray: Array.from({ length: 16 }, (_, i) => i + 1),
|
||||
channelList: [],
|
||||
ampGainSelect: null,
|
||||
ampGainOptions: [
|
||||
|
||||
@@ -1,130 +1,130 @@
|
||||
<template>
|
||||
<div class="flex" >
|
||||
<va-tree-root >
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Channels'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]"
|
||||
:key="channel"
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">CHANNELS</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]"
|
||||
:key="channel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">3-AXIS SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'3-AXIS'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Amp gain'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE GAIN</p>
|
||||
</div>
|
||||
<div class="row" v-if="ampGainSelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Sample Rate'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">SAMPLING RATE</p>
|
||||
</div>
|
||||
<div class="row" v-if="sampleRateSelect">
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Accelerator Sensitivity'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<span> sps in total </span>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</va-tree-root>
|
||||
<!-- v-if="sampleRateSelect" -> ref: https://stackoverflow.com/questions/6003884/how-do-i-check-for-null-values-in-javascript -->
|
||||
<div class="flex sm12 xl12 md12 xs12 pt-0 pb-2 my-0" v-if="sampleRateSelect">
|
||||
<p v-if="channelList.length !== 0"> {{ (sampleRateSelect.value / (channelList.length * 1e3)).toFixed(1) + 'k' }} sps per selected channel </p>
|
||||
<p v-else> NaN sps per selected channel </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">ACCELERATOR SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row" v-if="acceleratorSensitivitySelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,130 +1,144 @@
|
||||
<template>
|
||||
<div class="flex" >
|
||||
<va-tree-root >
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Channels'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in channelArray"
|
||||
:key="channel"
|
||||
<div>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">CHANNELS</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="channel in channelArray"
|
||||
:key="channel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList != null && channelList.indexOf(channel) >= 0"
|
||||
@click="channelClickRemove(channel)"
|
||||
small
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'3-AXIS'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
{{channel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="channelList == null || channelList.indexOf(channel) < 0"
|
||||
@click="channelClickAdd(channel)"
|
||||
small outline
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
{{channel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Amp gain'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">3-AXIS SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<div
|
||||
style='display: inline-block;'
|
||||
v-for="axisChannel in ['x','y','z','m']"
|
||||
:key="axisChannel"
|
||||
>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList != null && axisChannelList.indexOf(axisChannel) >= 0"
|
||||
@click="axisChannelClickRemove(axisChannel)"
|
||||
small
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
<va-button
|
||||
class="ma-1 pa-0 circle-button"
|
||||
v-if="axisChannelList == null || axisChannelList.indexOf(axisChannel) < 0"
|
||||
@click="axisChannelClickAdd(axisChannel)"
|
||||
small outline
|
||||
>
|
||||
{{axisChannel}}
|
||||
</va-button>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Sample Rate'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE GAIN</p>
|
||||
</div>
|
||||
<div class="row" v-if="ampGainSelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ampGainSelectChange()"
|
||||
:label="''"
|
||||
v-model="ampGainSelect"
|
||||
textBy="description"
|
||||
:options="ampGainOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Accelerator Sensitivity'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">SAMPLING RATE</p>
|
||||
</div>
|
||||
<div class="row" v-if="sampleRateSelect">
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<va-select
|
||||
@input="sampleRateSelectChange()"
|
||||
:label="''"
|
||||
v-model="sampleRateSelect"
|
||||
textBy="description"
|
||||
:options="sampleRateOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</va-tree-root>
|
||||
<div class="flex sm6 xl6 md6 xs6 pt-3 pb-0">
|
||||
<span> sps in total </span>
|
||||
</div>
|
||||
<!-- v-if="sampleRateSelect" -> ref: https://stackoverflow.com/questions/6003884/how-do-i-check-for-null-values-in-javascript -->
|
||||
<div class="flex sm12 xl12 md12 xs12 pt-0 pb-2 my-0" v-if="sampleRateSelect">
|
||||
<va-popover
|
||||
:delay="{ show: 250 }"
|
||||
placement="bottom"
|
||||
message="channel 1 ~ 32"
|
||||
>
|
||||
<p> {{ computePerChipSampleRate(1) }} sps per selected channel in <span style="text-decoration: underline;"> chip 1 </span></p>
|
||||
</va-popover>
|
||||
<va-popover
|
||||
:delay="{ show: 250 }"
|
||||
placement="bottom"
|
||||
message="channel 33 ~ 64"
|
||||
>
|
||||
<p> {{ computePerChipSampleRate(2) }} sps per selected channel in <span style="text-decoration: underline;"> chip 2 </span></p>
|
||||
</va-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">ACCELERATOR SENSITIVITY</p>
|
||||
</div>
|
||||
<div class="row" v-if="acceleratorSensitivitySelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="acceleratorSensitivitySelectChange()"
|
||||
:label="''"
|
||||
v-model="acceleratorSensitivitySelect"
|
||||
textBy="description"
|
||||
:options="acceleratorSensitivityOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -208,43 +222,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,
|
||||
@@ -279,6 +293,7 @@ export default {
|
||||
// sampleRate: null,
|
||||
recordOn: false,
|
||||
axisChannelList: [],
|
||||
chipSampleRate: 0,
|
||||
}
|
||||
},
|
||||
mqtt: {
|
||||
@@ -312,7 +327,21 @@ export default {
|
||||
this.ampGainSelect = option
|
||||
}
|
||||
})
|
||||
this.sampleRateSelect = this.sampleRateOptions[this.sampleRateOptions.findIndex(ele => ele.value === taskInfo.getDeviceInfo(this.deviceID).parameterSet.SAMPLE_RATE)]
|
||||
const chip1Channels = this.channelList.filter(c => c >= 1 && c <= 32)
|
||||
const chip2Channels = this.channelList.filter(c => c >= 33 && c <= 64)
|
||||
if (chip1Channels.length !== 0 && chip2Channels.length !== 0) {
|
||||
const obj = this.sampleRateOptions.find(ele => ele.value === taskInfo.getDeviceInfo(this.deviceID).parameterSet.SAMPLE_RATE * 2)
|
||||
if (obj) {
|
||||
this.sampleRateSelect = obj
|
||||
this.updatePerChipSampleRate()
|
||||
}
|
||||
} else {
|
||||
const obj = this.sampleRateOptions.find(ele => ele.value === taskInfo.getDeviceInfo(this.deviceID).parameterSet.SAMPLE_RATE)
|
||||
if (obj) {
|
||||
this.sampleRateSelect = obj
|
||||
this.updatePerChipSampleRate()
|
||||
}
|
||||
}
|
||||
this.recordOn = taskInfo.getDeviceInfo(this.deviceID).parameterSet.RECORDING
|
||||
this.axisChannelList.length = 0
|
||||
const axisChannelSet = taskInfo.getDeviceInfo(this.deviceID).parameterSet.AXIS_CH.toString(2).split('').reverse()
|
||||
@@ -332,6 +361,19 @@ export default {
|
||||
|
||||
this.acceleratorSensitivitySelect = this.acceleratorSensitivityOptions[taskInfo.getDeviceInfo(this.deviceID).parameterSet.ACCELERATOR_S]
|
||||
},
|
||||
computePerChipSampleRate (whichChip) {
|
||||
let channelLen
|
||||
if (whichChip === 1) {
|
||||
channelLen = this.channelList.filter(c => c >= 1 && c <= 32).length
|
||||
} else if (whichChip === 2) {
|
||||
channelLen = this.channelList.filter(c => c >= 33 && c <= 64).length
|
||||
}
|
||||
if (channelLen !== 0) {
|
||||
return (this.chipSampleRate / (channelLen * 1e3)).toFixed(1) + 'k'
|
||||
} else {
|
||||
return 'NaN'
|
||||
}
|
||||
},
|
||||
channelClickAdd (val) {
|
||||
if (this.channelList.indexOf(val) < 0 && val <= 32 && val > 0) {
|
||||
this.channelList.push(val)
|
||||
@@ -378,6 +420,7 @@ export default {
|
||||
} else {
|
||||
this.pageToast('Recording channels cannot choose more than 64.')
|
||||
}
|
||||
this.sampleRateSelectChange()
|
||||
},
|
||||
channelClickRemove (val) {
|
||||
if (this.channelList.indexOf(val) >= 0 && val <= 32 && val > 0) {
|
||||
@@ -419,6 +462,7 @@ export default {
|
||||
}))
|
||||
}
|
||||
}
|
||||
this.sampleRateSelectChange()
|
||||
},
|
||||
ampGainSelectChange () {
|
||||
if (this.ampGainSelect != null && this.ampGainSelect.id !== ((taskInfo.getDeviceInfo(this.deviceID).parameterSet.AMP_GAIN) % 4)) {
|
||||
@@ -430,14 +474,26 @@ export default {
|
||||
}))
|
||||
}
|
||||
},
|
||||
updatePerChipSampleRate () {
|
||||
if (this.sampleRateSelect != null) {
|
||||
const chip1Channels = this.channelList.filter(c => c >= 1 && c <= 32)
|
||||
const chip2Channels = this.channelList.filter(c => c >= 33 && c <= 64)
|
||||
if (chip1Channels.length !== 0 && chip2Channels.length !== 0) {
|
||||
this.chipSampleRate = parseInt(this.sampleRateSelect.value / 2)
|
||||
} else {
|
||||
this.chipSampleRate = this.sampleRateSelect.value
|
||||
}
|
||||
}
|
||||
},
|
||||
sampleRateSelectChange () {
|
||||
if (this.sampleRateSelect != null && this.sampleRateSelect.value !== ((taskInfo.getDeviceInfo(this.deviceID).parameterSet.SAMPLE_RATE))) {
|
||||
const adcClock = 8e6 / this.sampleRateSelect.value
|
||||
if (this.sampleRateSelect != null) {
|
||||
this.updatePerChipSampleRate()
|
||||
const adcClock = 8e6 / this.chipSampleRate
|
||||
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
|
||||
header: 'device_parameter/0',
|
||||
device: taskInfo.getRawDeviceID(this.deviceID),
|
||||
parameter: 'SAMPLE_RATE|ADC_CLOCK',
|
||||
content: `${this.sampleRateSelect.value}|${String(adcClock)}`,
|
||||
content: `${this.chipSampleRate}|${String(adcClock)}`,
|
||||
}))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,63 +1,65 @@
|
||||
<template>
|
||||
<div class="flex" >
|
||||
<va-tree-root >
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Radio Frequency'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="radioFrequencySelectChange()"
|
||||
:label="''"
|
||||
v-model="radioFrequencySelect"
|
||||
textBy="description"
|
||||
:options="radioFrequencyOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">NEULIVE RF</p>
|
||||
</div>
|
||||
<div class="row" v-if="radioFrequencySelect">
|
||||
<div class="flex sm10 xl10 md10 xs10">
|
||||
<va-select
|
||||
@input="radioFrequencySelectChange()"
|
||||
:label="''"
|
||||
v-model="radioFrequencySelect"
|
||||
textBy="description"
|
||||
:options="radioFrequencyOptions"
|
||||
:noClear='true'
|
||||
>
|
||||
</va-select>
|
||||
</div>
|
||||
<div class="flex sm2 xl2 md2 xs2">
|
||||
dB
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Mem Radio Frequency'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="memRadioFrequencySelectChange()"
|
||||
:label="''"
|
||||
v-model="memRadioFrequencySelect"
|
||||
textBy="description"
|
||||
:options="memRadioFrequencyOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
|
||||
<va-tree-category
|
||||
style="font-size: 20px;"
|
||||
:label="'Led Brightness'">
|
||||
<va-tree-node>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ledBrightnessSelectChange()"
|
||||
:label="''"
|
||||
v-model="ledBrightnessSelect"
|
||||
textBy="description"
|
||||
:options="ledBrightnessOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</va-tree-node>
|
||||
</va-tree-category>
|
||||
</va-tree-root>
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">HUB RF</p>
|
||||
</div>
|
||||
<div class="row" v-if="memRadioFrequencySelect">
|
||||
<div class="flex sm10 xl10 md10 xs10">
|
||||
<va-select
|
||||
@input="memRadioFrequencySelectChange()"
|
||||
:label="''"
|
||||
v-model="memRadioFrequencySelect"
|
||||
textBy="description"
|
||||
:options="memRadioFrequencyOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
<div class="flex sm2 xl2 md2 xs2">
|
||||
dB
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">LED BRIGHTNESS</p>
|
||||
</div>
|
||||
<div class="row" v-if="ledBrightnessSelect">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
<va-select
|
||||
@input="ledBrightnessSelectChange()"
|
||||
:label="''"
|
||||
v-model="ledBrightnessSelect"
|
||||
textBy="description"
|
||||
:options="ledBrightnessOptions"
|
||||
:noClear='true'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ export default {
|
||||
cycleNumberSelect: null,
|
||||
cycleNumberOptions: [
|
||||
{
|
||||
label: 'limited',
|
||||
label: 'Limited',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,30 +1,6 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<div>
|
||||
<!-- <div>
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<va-button
|
||||
class="mx-1 px-2"
|
||||
color="success"
|
||||
v-if="!stimulationOn"
|
||||
:disabled="stimulateDisable"
|
||||
@click="stimulateClick"
|
||||
small
|
||||
>
|
||||
Stimulate
|
||||
</va-button>
|
||||
<va-button
|
||||
class="mx-1 px-2"
|
||||
color="danger"
|
||||
v-else
|
||||
@click="stimulateClick"
|
||||
small
|
||||
>
|
||||
Stop
|
||||
</va-button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">ELECTRODE</p>
|
||||
@@ -88,7 +64,7 @@
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">CYCLE NUMBER</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
@@ -128,7 +104,7 @@
|
||||
<div class="mt-4" v-if="cycleNumberSelect==0">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">REPEAT NUMBER</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -157,7 +133,7 @@
|
||||
<div class="mt-4" v-if="cycleNumberSelect==0">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">REPEAT INTERVAL = Ti = (Ts + Tr)</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -182,7 +158,7 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE PHASE1 (uA)</p>
|
||||
<p class="display-6">AMPLITUDE DURING T1</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -210,7 +186,7 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE PHASE2 (uA)</p>
|
||||
<p class="display-6">AMPLITUDE DURING T3</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -432,26 +408,26 @@
|
||||
<img src='@/assets/img/sti_user_guide.png' class="sti_guideline">
|
||||
</va-modal>
|
||||
<va-modal
|
||||
v-model="showGuideLineModal.SKETCHMAP"
|
||||
v-model="showGuideLineModal.REPEAT"
|
||||
position="right"
|
||||
cancelText=""
|
||||
>
|
||||
<img src='@/assets/img/repeat.jpg' class="sti_guideline">
|
||||
<p class="display-6 my-2"> Pattern: </p>
|
||||
<!-- <p class="display-6 my-2"> Pattern: </p>
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="sketchmapData" :auto-resize="true"></chart>
|
||||
<neulive-parameter-stimulation-sketchmap-param class="my-2" :deviceID="deviceID" :unitMultiply="unitMultiply" />
|
||||
<neulive-parameter-stimulation-sketchmap-param class="my-2" :deviceID="deviceID" :unitMultiply="unitMultiply" /> -->
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskInfo } from '../../../../data/task/TaskInfo'
|
||||
import NeuliveParameterStimulationSketchmapParam from './NeuliveParameterStimulationSketchmapParam'
|
||||
// import NeuliveParameterStimulationSketchmapParam from './NeuliveParameterStimulationSketchmapParam'
|
||||
|
||||
export default {
|
||||
name: 'NeuliveParameterStimulation35',
|
||||
components: {
|
||||
NeuliveParameterStimulationSketchmapParam,
|
||||
// NeuliveParameterStimulationSketchmapParam,
|
||||
},
|
||||
props: {
|
||||
deviceID: {
|
||||
@@ -465,7 +441,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showGuideLineModal: { ELECTRODE: false, PATTERN: false, SKETCHMAP: false },
|
||||
showGuideLineModal: { ELECTRODE: false, PATTERN: false, REPEAT: false },
|
||||
stimulationOn: false,
|
||||
posChannelList: [2],
|
||||
negChannelList: [1],
|
||||
@@ -484,7 +460,7 @@ export default {
|
||||
cycleNumberSelect: null,
|
||||
cycleNumberOptions: [
|
||||
{
|
||||
label: 'limited',
|
||||
label: 'Limited',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,30 +1,6 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<div>
|
||||
<!-- <div>
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<va-button
|
||||
class="mx-1 px-2"
|
||||
color="success"
|
||||
v-if="!stimulationOn"
|
||||
:disabled="stimulateDisable"
|
||||
@click="stimulateClick"
|
||||
small
|
||||
>
|
||||
Stimulate
|
||||
</va-button>
|
||||
<va-button
|
||||
class="mx-1 px-2"
|
||||
color="danger"
|
||||
v-else
|
||||
@click="stimulateClick"
|
||||
small
|
||||
>
|
||||
Stop
|
||||
</va-button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">ELECTRODE</p>
|
||||
@@ -88,7 +64,7 @@
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">CYCLE NUMBER</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex sm12 xl12 md12 xs12">
|
||||
@@ -128,7 +104,7 @@
|
||||
<div class="mt-4" v-if="cycleNumberSelect==0">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">REPEAT NUMBER</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -157,7 +133,7 @@
|
||||
<div class="mt-4" v-if="cycleNumberSelect==0">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">REPEAT INTERVAL = Ti = (Ts + Tr)</p>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('SKETCHMAP')"/>
|
||||
<va-button flat color="gray" icon="fa fa-info-circle" style="margin: 0.001rem; padding: 0.001rem;" @click.stop="toggleModal('REPEAT')"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -182,7 +158,7 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE PHASE1 (uA)</p>
|
||||
<p class="display-6">AMPLITUDE DURING T1</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -210,7 +186,7 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="row sm12 xl12 md12 xs12">
|
||||
<p class="display-6">AMPLITUDE PHASE2 (uA)</p>
|
||||
<p class="display-6">AMPLITUDE DURING T3</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -432,26 +408,26 @@
|
||||
<img src='@/assets/img/sti_user_guide.png' class="sti_guideline">
|
||||
</va-modal>
|
||||
<va-modal
|
||||
v-model="showGuideLineModal.SKETCHMAP"
|
||||
v-model="showGuideLineModal.REPEAT"
|
||||
position="right"
|
||||
cancelText=""
|
||||
>
|
||||
<img src='@/assets/img/repeat.jpg' class="sti_guideline">
|
||||
<p class="display-6 my-2"> Pattern: </p>
|
||||
<!-- <p class="display-6 my-2"> Pattern: </p>
|
||||
<chart :ref="'chart_ref'" :style="'width: '+ cardwidth + '; height: ' + cardheight + ';'" :options="sketchmapData" :auto-resize="true"></chart>
|
||||
<neulive-parameter-stimulation-sketchmap-param class="my-2" :deviceID="deviceID" :unitMultiply="unitMultiply" />
|
||||
<neulive-parameter-stimulation-sketchmap-param class="my-2" :deviceID="deviceID" :unitMultiply="unitMultiply" /> -->
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskInfo } from '../../../../data/task/TaskInfo'
|
||||
import NeuliveParameterStimulationSketchmapParam from './NeuliveParameterStimulationSketchmapParam'
|
||||
// import NeuliveParameterStimulationSketchmapParam from './NeuliveParameterStimulationSketchmapParam'
|
||||
|
||||
export default {
|
||||
name: 'NeuliveParameterStimulation35',
|
||||
components: {
|
||||
NeuliveParameterStimulationSketchmapParam,
|
||||
// NeuliveParameterStimulationSketchmapParam,
|
||||
},
|
||||
props: {
|
||||
deviceID: {
|
||||
@@ -466,7 +442,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
channelArray: Array.from({ length: 32 }, (_, i) => i + 1),
|
||||
showGuideLineModal: { ELECTRODE: false, PATTERN: false, SKETCHMAP: false },
|
||||
showGuideLineModal: { ELECTRODE: false, PATTERN: false, REPEAT: false },
|
||||
stimulationOn: false,
|
||||
posChannelList: [2],
|
||||
negChannelList: [1],
|
||||
@@ -485,7 +461,7 @@ export default {
|
||||
cycleNumberSelect: null,
|
||||
cycleNumberOptions: [
|
||||
{
|
||||
label: 'limited',
|
||||
label: 'Limited',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
@@ -956,10 +932,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 +1114,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>
|
||||
@@ -7,7 +7,7 @@ async function spikeDetect (attrs) {
|
||||
return await api.axios.post('/api/analysis/spike_detect', attrs, { headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import mini from './mini'
|
||||
import analysis from './analysis'
|
||||
import sdcard from './sdcard'
|
||||
import connection from './connection'
|
||||
import trigger from './trigger'
|
||||
|
||||
// axios.defaults.baseURL = 'http://192.168.3.211:3000'
|
||||
axios.defaults.baseURL = 'http://' + location.href.split('/')[2].split(':')[0] + ':3000'
|
||||
@@ -27,6 +28,7 @@ const api = {
|
||||
analysis,
|
||||
sdcard,
|
||||
connection,
|
||||
trigger,
|
||||
}
|
||||
|
||||
export default api
|
||||
|
||||
@@ -62,6 +62,16 @@ async function getByID (id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getByUUID (uuid) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get(`/api/file/meta/get_by_uuid/${uuid}`, { responseType: 'json', headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getByName (name) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
@@ -72,6 +82,37 @@ async function getByName (name) {
|
||||
}
|
||||
}
|
||||
|
||||
// control system 3.0
|
||||
async function update30 (uuid, attrs) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.post(`/api/file/meta/update3_0/${uuid}`, attrs, { headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getMetaByUUID (uuid) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get(`/api/file/meta/get_meta_by_uuid/${uuid}`, { responseType: 'json', headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getMetaAll () {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get('/api/file/meta/get_meta_all', { responseType: 'json', headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const meta = {
|
||||
create,
|
||||
update,
|
||||
@@ -79,7 +120,12 @@ const meta = {
|
||||
getByParent,
|
||||
getByParentWithLimit,
|
||||
getByID,
|
||||
getByUUID,
|
||||
getByName,
|
||||
// control system 3.0
|
||||
update30,
|
||||
getMetaByUUID,
|
||||
getMetaAll,
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
@@ -31,6 +31,26 @@ async function getByID (channel, id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getSdDataByUUID (uuid, channel, startTime, endTime) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get(`/api/file/raw/get_sd_data_by_uuid/${uuid}/${channel}/${startTime}/${endTime}`, { responseType: 'json', headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getSdDataAllTimeslotByUUID (uuid, channel) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get(`/api/file/raw/get_sd_data_all_timeslot_by_uuid/${uuid}/${channel}`, { responseType: 'json', headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getAttrByID (channel, id, attr) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
@@ -85,6 +105,8 @@ const raw = {
|
||||
update,
|
||||
clearDel,
|
||||
getByID,
|
||||
getSdDataByUUID,
|
||||
getSdDataAllTimeslotByUUID,
|
||||
getAttrByID,
|
||||
getByIDs,
|
||||
getAttrByIDs,
|
||||
|
||||
@@ -28,20 +28,10 @@ async function upload (attrs) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getuuidList () {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get('/api/file/sdcard/get', { headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
const sdcard = {
|
||||
exist,
|
||||
deleted,
|
||||
upload,
|
||||
getuuidList,
|
||||
}
|
||||
|
||||
export default sdcard
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import api from './index'
|
||||
import auth from './auth'
|
||||
|
||||
async function getRawByUUID (uuid) {
|
||||
const _header = auth.getHeader()
|
||||
try {
|
||||
return await api.axios.get(`/api/file/trigger/get_raw_by_uuid/${uuid}`, { headers: _header })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const trigger = {
|
||||
getRawByUUID,
|
||||
}
|
||||
|
||||
export default trigger
|
||||
@@ -76,11 +76,31 @@ export const download = {
|
||||
await this.parseRawData(rawData.data, null, meta, data)
|
||||
return data
|
||||
},
|
||||
rawSdDataByIDChannels: async function (id, channel, meta) {
|
||||
const data = []
|
||||
const rawSdData = await api.raw.getByID(channel, id)
|
||||
await this.parseRawSdData(rawSdData.data, null, meta, data)
|
||||
return data
|
||||
rawSdDataByUUIDAndChannel: async function (uuid, channel, startTime, endTime) {
|
||||
// console.log(channel)
|
||||
const rawSdData = await api.raw.getSdDataByUUID(uuid, channel, startTime, endTime)
|
||||
const metaList = await api.meta.getByUUID(uuid) // control system 2.0 meta table
|
||||
// console.log(channel)
|
||||
const sampleRate = metaList.data[0].parameter_set.SAMPLE_RATE
|
||||
|
||||
const allChannels = JSON.parse(metaList.data[0].channels)
|
||||
allChannels.sort((a, b) => a > b ? 1 : -1)
|
||||
let channels
|
||||
if (metaList.data[0].device.library_name === 'Neulive3.5ex') {
|
||||
if (channel >= 0 && channel <= 31) {
|
||||
channels = allChannels.filter(c => c >= 0 && c <= 31)
|
||||
} else if (channel >= 32 && channel <= 63) {
|
||||
channels = allChannels.filter(c => c >= 32 && c <= 63)
|
||||
}
|
||||
} else {
|
||||
channels = allChannels
|
||||
}
|
||||
const dataTimeStep = parseFloat((1e6 / sampleRate) * channels.length)
|
||||
const offset = dataTimeStep * (channels.findIndex((c) => c === parseInt(channel)) / channels.length)
|
||||
return await this.parseRawSdData(rawSdData.data, channel, dataTimeStep, offset, startTime, endTime)
|
||||
},
|
||||
rawSdDataAllTimeslotByUUID: async function (uuid, channel) {
|
||||
return await api.raw.getSdDataAllTimeslotByUUID(uuid, channel)
|
||||
},
|
||||
miniMeanByIDChannel: async function (id, channel, meta) {
|
||||
const miniData = await api.mini.getByID(channel, id)
|
||||
@@ -107,22 +127,41 @@ export const download = {
|
||||
}
|
||||
return prevLastTime
|
||||
},
|
||||
parseRawSdData: async function (rawData, prevLastTime, meta, dataArray) {
|
||||
parseRawSdData: async function (rawData, channel, dataTimeStep, offset, startTime, endTime) {
|
||||
const dataArray = []
|
||||
for (const raw of rawData) {
|
||||
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(' ')
|
||||
})
|
||||
|
||||
for (let i = 0; i <= unzipDatas.length - 1; i++) {
|
||||
if (unzipDatas[i].length > 0) {
|
||||
prevLastTime = this.parseDataWithTimeStamp(unzipDatas[i], prevLastTime, meta, dataArray)
|
||||
if (raw.data !== null) {
|
||||
const rawDataArr = raw.data.split(' ')
|
||||
const rawDataArrLen = rawDataArr.length
|
||||
if (rawDataArrLen > 0) {
|
||||
// data_time format: 'raw_data_index1 data_start_time raw_data_index2 data_start_time ...'
|
||||
const rawDataIndexArr = raw.data_time.split(' ').filter((item, index) => index % 2 === 0) // 留奇數項
|
||||
const rawDataTimeArr = raw.data_time.split(' ').filter((item, index) => index % 2 !== 0) // 留偶數項
|
||||
let rawDataIndexArrPointer = 0
|
||||
let currTime
|
||||
for (let i = 0; i < rawDataArrLen; i++) {
|
||||
if (i !== parseInt(rawDataIndexArr[rawDataIndexArrPointer])) {
|
||||
currTime += dataTimeStep
|
||||
} else {
|
||||
currTime += dataTimeStep
|
||||
const nextTime = parseInt(rawDataTimeArr[rawDataIndexArrPointer])
|
||||
for (let lossTime = currTime; lossTime < nextTime; lossTime += dataTimeStep) {
|
||||
dataArray.push([parseInt(lossTime), null])
|
||||
}
|
||||
currTime = nextTime + offset
|
||||
if (rawDataIndexArrPointer < rawDataIndexArr.length) {
|
||||
rawDataIndexArrPointer++
|
||||
}
|
||||
}
|
||||
const data = (channel >= 256 && channel <= 259) ? (rawDataArr[i] / 100).toFixed(3) : rawDataArr[i]
|
||||
if (startTime <= currTime && currTime <= endTime) {
|
||||
dataArray.push([parseInt(currTime), data])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return prevLastTime
|
||||
return dataArray
|
||||
},
|
||||
parseMiniData: async function (miniData, miniType, meta) {
|
||||
const channelData = []
|
||||
@@ -155,37 +194,9 @@ export const download = {
|
||||
return channelData
|
||||
},
|
||||
parseDataWithTimeStamp: function (currentData, prevLastTime, meta, dataArray) {
|
||||
// const transData = []
|
||||
// let timeSpare = 0
|
||||
// let channel = 0
|
||||
// if (meta !== undefined) {
|
||||
// channel = JSON.parse(meta.channels).length
|
||||
// if (meta.device.library_name.startsWith('Neulive')) {
|
||||
// timeSpare = parseInt((1 / (parseInt(meta.parameter_set.ADC_CLOCK) / 200)) * 1000 / 1000)
|
||||
// } else if (meta.device.library_name.startsWith('Elite')) {
|
||||
// // TODO Elite
|
||||
// }
|
||||
// }
|
||||
// const realTimeSpare = channel * timeSpare
|
||||
for (let i = 0; i <= currentData.length - 1; i += 2) {
|
||||
if (currentData[i].length !== 0) {
|
||||
const time = parseInt(currentData[i])
|
||||
// if (prevLastTime && (time - prevLastTime >= 2 * realTimeSpare) && realTimeSpare !== 0) {
|
||||
// console.log('data loss time', time, prevLastTime)
|
||||
// let timeDiff = time - prevLastTime
|
||||
// let timeCorrection = 0
|
||||
// while (timeDiff > 0) {
|
||||
// timeCorrection += realTimeSpare
|
||||
// timeDiff -= realTimeSpare
|
||||
// if (timeDiff > 0) {
|
||||
// const _time = parseInt(prevLastTime) + parseInt(timeCorrection)
|
||||
// const data = 'NaN'
|
||||
// const tmpData = [_time, data]
|
||||
// transData.push(tmpData)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (meta !== undefined) {
|
||||
if (time <= parseInt(meta.time_duration)) {
|
||||
const data = currentData[i + 1]
|
||||
|
||||
@@ -120,6 +120,26 @@ const transTime = (microsecond) => {
|
||||
return result
|
||||
}
|
||||
|
||||
function microsecondsFormat (microsec) {
|
||||
if (isNaN(microsec) || microsec == null || microsec === 0) {
|
||||
return '--'
|
||||
}
|
||||
const sec = Math.ceil(microsec / 1000000)
|
||||
let hour = Math.floor(sec / 3600)
|
||||
let minute = Math.floor((sec - hour * 3600) / 60)
|
||||
let second = sec - hour * 3600 - minute * 60
|
||||
if (hour < 10) {
|
||||
hour = '0' + hour
|
||||
}
|
||||
if (minute < 10) {
|
||||
minute = '0' + minute
|
||||
}
|
||||
if (second < 10) {
|
||||
second = '0' + second
|
||||
}
|
||||
return hour + ':' + minute + ':' + second
|
||||
}
|
||||
|
||||
const dateToStr = (time) => {
|
||||
const _time = new Date(time).toLocaleString('tw', { hour12: false })
|
||||
return _time
|
||||
@@ -136,6 +156,7 @@ export default
|
||||
transMac,
|
||||
transFileSize,
|
||||
transTime,
|
||||
microsecondsFormat,
|
||||
dateToStr,
|
||||
TaskTotalSettingView,
|
||||
controllerMaxNumber,
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
const Neulive31 = {
|
||||
showParameterRec: ['RECORDING_CH', 'AXIS_CH', 'AMP_GAIN', 'SAMPLE_RATE', 'ACCELERATOR_S'],
|
||||
showParameterSti: ['STI_H_BRIDGE', 'STI_CHANNEL', 'CONTINUOUS_STI', 'STI_CYCLE_LIST',
|
||||
'STI_AMPLITUDE', 'STI_PATTERN', 'STI_T1', 'STI_T2', 'STI_T3', 'STI_T4'],
|
||||
// rec
|
||||
RECORDING_CH: {
|
||||
type: 'array',
|
||||
showName: 'Channels',
|
||||
range: Array(16).fill([...Array(16).keys()].map(x => ++x)), // UI上能輸入的最大最小值
|
||||
func: (chList) => {
|
||||
let channelSet = ''
|
||||
for (let i = 16; i >= 1; i--) {
|
||||
if (chList.indexOf(i) >= 0) {
|
||||
channelSet += '1'
|
||||
} else {
|
||||
channelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(channelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const channelList = []
|
||||
const channelSet = val.toString(2).split('').reverse()
|
||||
channelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
channelList.push(index + 1)
|
||||
}
|
||||
})
|
||||
return channelList
|
||||
},
|
||||
},
|
||||
AXIS_CH: {
|
||||
type: 'array',
|
||||
showName: '3-AXIS',
|
||||
start: 256,
|
||||
range: Array(4).fill(['x', 'y', 'z', 'm']),
|
||||
func: (chList) => {
|
||||
const axisChannel = ['m', 'z', 'y', 'x']
|
||||
let axisChannelSet = ''
|
||||
for (const axis of axisChannel) {
|
||||
if (chList.indexOf(axis) >= 0) {
|
||||
axisChannelSet += '1'
|
||||
} else {
|
||||
axisChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(axisChannelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const axisChannelList = []
|
||||
const axisChannelSet = val.toString(2).split('').reverse()
|
||||
axisChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) === 1) {
|
||||
if (index === 0) {
|
||||
axisChannelList.push('x')
|
||||
} else if (index === 1) {
|
||||
axisChannelList.push('y')
|
||||
} else if (index === 2) {
|
||||
axisChannelList.push('z')
|
||||
} else if (index === 3) {
|
||||
axisChannelList.push('m')
|
||||
}
|
||||
}
|
||||
})
|
||||
return axisChannelList
|
||||
},
|
||||
},
|
||||
AMP_GAIN: {
|
||||
type: 'array',
|
||||
showName: 'Amp gain',
|
||||
range: ['100', '400', '800'],
|
||||
func: (val) => {
|
||||
const ampGainArr = ['100', '400', '800']
|
||||
return ampGainArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const ampGainArr = ['100', '400', '800']
|
||||
return ampGainArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
SAMPLE_RATE: {
|
||||
type: 'array',
|
||||
showName: 'Sample Rate',
|
||||
range: [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 2e4, 2.5e4, 3.125e4],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
// const sampleRateArr = [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 2e4, 2.5e4, 3.125e4]
|
||||
// const idx = sampleRateArr.indexOf(val)
|
||||
// const sampleRateStrArr = ['1k', '2k', '4k', '5k', '8k', '10k', '20k', '25k', '31.25k']
|
||||
// return sampleRateStrArr[idx]
|
||||
return val
|
||||
},
|
||||
},
|
||||
ADC_CLOCK: {
|
||||
type: 'number',
|
||||
showName: 'ADC_CLOCK',
|
||||
range: Object.freeze({ min: 1e3, max: 2e4 }),
|
||||
func: (val) => {
|
||||
return 8e5 / parseInt(val)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return 8e5 / parseInt(val)
|
||||
},
|
||||
},
|
||||
RECORDING: {
|
||||
type: 'bool',
|
||||
showName: 'RECORDING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
ACCELERATOR_S: {
|
||||
type: 'array',
|
||||
showName: 'Accelerator Sensitivity',
|
||||
range: ['2G', '4G', '8G', '16G'],
|
||||
func: (val) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
const index = accArr.indexOf(val.toString())
|
||||
return index
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
return accArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
// sti
|
||||
STIMULATING: {
|
||||
type: 'bool',
|
||||
showName: 'STIMULATING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
STI_CHANNEL: {
|
||||
type: 'array',
|
||||
showName: 'Channels',
|
||||
range: Object.freeze({ pos: Array(8).fill([...Array(8).keys()].map(x => ++x)), neg: Array(8).fill([...Array(8).keys()].map(x => ++x)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiChannelSet = ''
|
||||
for (let i = 8; i >= 1; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else {
|
||||
stiChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiChannelSet, 2)
|
||||
},
|
||||
rev_func: (val, stiPolarityVal) => {
|
||||
const stiChannelSet = val.toString(2).split('').reverse()
|
||||
const stiPolaritySet = stiPolarityVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
posChannelList.push(index + 1)
|
||||
} else {
|
||||
negChannelList.push(index + 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_POLARITY: {
|
||||
type: 'array',
|
||||
showName: 'Polarity',
|
||||
range: Object.freeze({ pos: Array(8).fill([...Array(8).keys()].map(x => ++x)), neg: Array(8).fill([...Array(8).keys()].map(x => ++x)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiPolaritySet = ''
|
||||
for (let i = 8; i >= 1; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '0'
|
||||
} else {
|
||||
stiPolaritySet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiPolaritySet, 2)
|
||||
},
|
||||
rev_func: (val, stiChannelVal) => {
|
||||
const stiPolaritySet = val.toString(2).split('').reverse()
|
||||
const stiChannelSet = stiChannelVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
posChannelList.push(index + 1)
|
||||
} else {
|
||||
negChannelList.push(index + 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_H_BRIDGE: {
|
||||
type: 'array',
|
||||
showName: 'ELECTRODE',
|
||||
range: ['Monopolar', 'Bipolar'],
|
||||
func: (val) => {
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr[idx]
|
||||
},
|
||||
},
|
||||
CONTINUOUS_STI: {
|
||||
type: 'array',
|
||||
showName: 'CYCLE',
|
||||
range: ['Limited', 'Unlimited'],
|
||||
func: (val) => {
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr[idx]
|
||||
},
|
||||
},
|
||||
STI_CYCLE_LIST: {
|
||||
type: 'number',
|
||||
showName: 'CYCLE NUMBER',
|
||||
range: Object.freeze({ min: 1, max: 255 }),
|
||||
func: (cycleNum) => {
|
||||
const cycleNumberArray = Array(9).fill(1)
|
||||
cycleNumberArray.fill(cycleNum, 0)
|
||||
return '0:9=' + cycleNumberArray.toString()
|
||||
},
|
||||
rev_func: (_list) => {
|
||||
return _list[0]
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_H: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_HIGH',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_L: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_LOW',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_PATTERN: {
|
||||
type: 'array',
|
||||
showName: 'PATTERN',
|
||||
range: ['Monophasic', 'Biphasic'],
|
||||
func: (val) => {
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr[idx]
|
||||
},
|
||||
},
|
||||
STI_T1: {
|
||||
type: 'number',
|
||||
showName: 'T1',
|
||||
amount: 8, // total 8 channels
|
||||
range: Object.freeze({ min: 100, max: 10230 }), // 0.1ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
const tRangeArray = Array(9).fill(0)
|
||||
tRangeArray.fill(tRange, 0)
|
||||
return '0:9=' + String(tRangeArray)
|
||||
},
|
||||
rev_func: (_list) => { // output unit: us
|
||||
return _list[0] * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T2: {
|
||||
type: 'number',
|
||||
showName: 'T2',
|
||||
amount: 8, // total 8 channels
|
||||
range: Object.freeze({ min: 0, max: 10230 }), // 0ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
const tRangeArray = Array(9).fill(0)
|
||||
tRangeArray.fill(tRange, 0)
|
||||
return '0:9=' + String(tRangeArray)
|
||||
},
|
||||
rev_func: (_list) => { // output unit: us
|
||||
return _list[0] * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T3: {
|
||||
type: 'number',
|
||||
showName: 'T3',
|
||||
amount: 8, // total 8 channels
|
||||
range: Object.freeze({ min: 100, max: 10230 }), // 0.1ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
const tRangeArray = Array(9).fill(0)
|
||||
tRangeArray.fill(tRange, 0)
|
||||
return '0:9=' + String(tRangeArray)
|
||||
},
|
||||
rev_func: (_list) => { // output unit: us
|
||||
return _list[0] * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T4: {
|
||||
type: 'number',
|
||||
showName: 'T4',
|
||||
amount: 8, // total 8 channels
|
||||
range: Object.freeze({ min: 0, max: 1310710 }), // 0ms~1310.71ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
const tRangeArray = Array(9).fill(0)
|
||||
tRangeArray.fill(tRange, 0)
|
||||
return '0:9=' + String(tRangeArray)
|
||||
},
|
||||
rev_func: (_list) => { // output unit: us
|
||||
return _list[0] * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default Neulive31
|
||||
@@ -0,0 +1,469 @@
|
||||
const Neulive35 = {
|
||||
showParameterRec: ['RECORDING_CH_L', 'RECORDING_CH_H', 'AXIS_CH', 'AMP_GAIN', 'SAMPLE_RATE', 'ACCELERATOR_S'],
|
||||
showParameterSti: ['STI_H_BRIDGE', 'STI_CHANNEL', 'CONTINUOUS_STI', 'STI_CYCLE_CH0',
|
||||
'REPEAT_TIME_littleEndian', 'REPEAT_INTERVAL_littleEndian',
|
||||
'STI_AMPLITUDE_L1', 'STI_AMPLITUDE_H3',
|
||||
'STI_PATTERN', 'STI_T1_CH0', 'STI_T2_CH0', 'STI_T3_CH0', 'STI_T4_CH0'],
|
||||
// rec
|
||||
RECORDING_CH_L: {
|
||||
type: 'array',
|
||||
showName: 'CHANNEL LOW',
|
||||
range: Array(16).fill([...Array(16).keys()].map(x => ++x)), // UI上能輸入的最大最小值
|
||||
func: (chList) => {
|
||||
let channelSet = ''
|
||||
for (let i = 16; i >= 1; i--) {
|
||||
if (chList.indexOf(i) >= 0) {
|
||||
channelSet += '1'
|
||||
} else {
|
||||
channelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(channelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const channelList = []
|
||||
const channelSet = val.toString(2).split('').reverse()
|
||||
channelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
channelList.push(index + 1)
|
||||
}
|
||||
})
|
||||
return channelList
|
||||
},
|
||||
},
|
||||
RECORDING_CH_H: {
|
||||
type: 'array',
|
||||
showName: 'CHANNEL HIGH',
|
||||
range: Array(16).fill([...Array(16).keys()].map(x => x + 17)), // UI上能輸入的最大最小值
|
||||
func: (chList) => {
|
||||
let channelSet = ''
|
||||
for (let i = 32; i >= 17; i--) {
|
||||
if (chList.indexOf(i) >= 0) {
|
||||
channelSet += '1'
|
||||
} else {
|
||||
channelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(channelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const channelList = []
|
||||
const channelSet = val.toString(2).split('').reverse()
|
||||
channelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
channelList.push(index + 17)
|
||||
}
|
||||
})
|
||||
return channelList
|
||||
},
|
||||
},
|
||||
AMP_GAIN: {
|
||||
type: 'array',
|
||||
showName: 'AMPLITUDE GAIN',
|
||||
range: ['100', '500', '300'],
|
||||
func: (val) => {
|
||||
const ampGainArr = ['100', '500', '300']
|
||||
return ampGainArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const ampGainArr = ['100', '500', '300']
|
||||
return ampGainArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
SAMPLE_RATE: {
|
||||
type: 'array',
|
||||
showName: 'SAMPLING RATE',
|
||||
range: [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 1.6e4, 2e4, 2.5e4, 3.2e4, 4e4, 5e4, 6.4e4, 8e4, 1e5],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
// const sampleRateArr = [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 1.6e4, 2e4, 2.5e4, 3.2e4, 4e4, 5e4, 6.4e4, 8e4, 1e5]
|
||||
// const idx = sampleRateArr.indexOf(val)
|
||||
// const sampleRateStrArr = ['1k', '2k', '4k', '5k', '8k', '10k', '16k', '20k', '25k', '32k', '40k', '50k', '64k', '80k', '100k']
|
||||
// return sampleRateStrArr[idx]
|
||||
return val
|
||||
},
|
||||
defaultUnit: 'sps',
|
||||
unit: {
|
||||
sps: 1,
|
||||
},
|
||||
},
|
||||
ADC_CLOCK: {
|
||||
type: 'number',
|
||||
showName: 'ADC_CLOCK',
|
||||
range: Object.freeze({ min: 1e3, max: 1e5 }),
|
||||
func: (val) => {
|
||||
return 8e6 / parseInt(val)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return 8e6 / parseInt(val)
|
||||
},
|
||||
},
|
||||
RECORDING: {
|
||||
type: 'bool',
|
||||
showName: 'RECORDING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
AXIS_CH: {
|
||||
type: 'array',
|
||||
showName: '3-AXIS',
|
||||
range: Array(4).fill(['x', 'y', 'z', 'm']),
|
||||
func: (chList) => {
|
||||
const axisChannel = ['m', 'z', 'y', 'x']
|
||||
let axisChannelSet = ''
|
||||
for (const axis of axisChannel) {
|
||||
if (chList.indexOf(axis) >= 0) {
|
||||
axisChannelSet += '1'
|
||||
} else {
|
||||
axisChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(axisChannelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const axisChannelList = []
|
||||
const axisChannelSet = val.toString(2).split('').reverse()
|
||||
axisChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) === 1) {
|
||||
if (index === 0) {
|
||||
axisChannelList.push('x')
|
||||
} else if (index === 1) {
|
||||
axisChannelList.push('y')
|
||||
} else if (index === 2) {
|
||||
axisChannelList.push('z')
|
||||
} else if (index === 3) {
|
||||
axisChannelList.push('m')
|
||||
}
|
||||
}
|
||||
})
|
||||
return axisChannelList
|
||||
},
|
||||
},
|
||||
ACCELERATOR_S: {
|
||||
type: 'array',
|
||||
showName: 'ACCELERATOR SENSITIVITY',
|
||||
range: ['2G', '4G', '8G', '16G'],
|
||||
func: (val) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
const index = accArr.indexOf(val.toString())
|
||||
return index
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
return accArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
// sti
|
||||
STIMULATING: {
|
||||
type: 'bool',
|
||||
showName: 'STIMULATING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
STI_CHANNEL: {
|
||||
type: 'array',
|
||||
showName: 'CHANNELS',
|
||||
range: Object.freeze({ pos: Array(16).fill([...Array(16).keys()].map(x => x + 17)), neg: Array(16).fill([...Array(16).keys()].map(x => x + 17)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiChannelSet = ''
|
||||
for (let i = 32; i >= 17; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else {
|
||||
stiChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiChannelSet, 2)
|
||||
},
|
||||
rev_func: (val, stiPolarityVal) => {
|
||||
const stiChannelSet = val.toString(2).split('').reverse()
|
||||
const stiPolaritySet = stiPolarityVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
posChannelList.push(index + 17)
|
||||
} else {
|
||||
negChannelList.push(index + 17)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_POLARITY: {
|
||||
type: 'array',
|
||||
showName: 'POLARITY',
|
||||
range: Object.freeze({ pos: Array(16).fill([...Array(16).keys()].map(x => ++x)), neg: Array(16).fill([...Array(16).keys()].map(x => ++x)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiPolaritySet = ''
|
||||
for (let i = 16; i >= 1; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '0'
|
||||
} else {
|
||||
stiPolaritySet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiPolaritySet, 2)
|
||||
},
|
||||
rev_func: (val, stiChannelVal) => {
|
||||
const stiPolaritySet = val.toString(2).split('').reverse()
|
||||
const stiChannelSet = stiChannelVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
posChannelList.push(index + 1)
|
||||
} else {
|
||||
negChannelList.push(index + 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_H_BRIDGE: {
|
||||
type: 'array',
|
||||
showName: 'ELECTRODE',
|
||||
range: ['Monopolar', 'Bipolar'],
|
||||
func: (val) => {
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr[idx]
|
||||
},
|
||||
},
|
||||
CONTINUOUS_STI: {
|
||||
type: 'array',
|
||||
showName: 'CYCLE',
|
||||
range: ['Limited', 'Unlimited'],
|
||||
func: (val) => {
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr[idx]
|
||||
},
|
||||
},
|
||||
STI_CYCLE_CH0: {
|
||||
type: 'number',
|
||||
showName: 'CYCLE NUMBER',
|
||||
range: Object.freeze({ min: 1, max: 256 }),
|
||||
func: (cycleNum) => {
|
||||
return String(cycleNum - 1)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return parseInt(val) + 1
|
||||
},
|
||||
defaultUnit: 'rounds',
|
||||
unit: {
|
||||
rounds: 1,
|
||||
},
|
||||
},
|
||||
REPEAT_TIME_littleEndian: {
|
||||
type: 'number',
|
||||
showName: 'REPEAT NUMBER',
|
||||
range: Object.freeze({ min: 1, max: 256 }),
|
||||
func: (val) => {
|
||||
return String(val)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return parseInt(val)
|
||||
},
|
||||
defaultUnit: 'rounds',
|
||||
unit: {
|
||||
rounds: 1,
|
||||
},
|
||||
},
|
||||
REPEAT_INTERVAL_littleEndian: {
|
||||
type: 'number',
|
||||
showName: 'REPEAT INTERVAL',
|
||||
range: Object.freeze({ min: 0, max: Infinity }),
|
||||
func: (val) => {
|
||||
return parseFloat(parseFloat(val).toFixed(2)) * 1e3
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
defaultUnit: 's',
|
||||
unit: {
|
||||
s: 1e3,
|
||||
ms: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_L1: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE DURING T1',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_H3: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE DURING T3',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_H1: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_HIGH_1',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5 / 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5 * 2
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_L3: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_LOW_3',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5 / 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5 * 2
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_PATTERN: {
|
||||
type: 'array',
|
||||
showName: 'PATTERN',
|
||||
range: ['Monophasic', 'Biphasic'],
|
||||
func: (val) => {
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr[idx]
|
||||
},
|
||||
},
|
||||
STI_T1_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T1',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 100, max: 10230 }), // 0.1ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T2_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T2',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 10230 }), // 0ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T3_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T3',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 10230 }), // 0ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T4_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T4',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 1310710 }), // 0ms~1310.71ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default Neulive35
|
||||
@@ -0,0 +1,466 @@
|
||||
const Neulive35ex = {
|
||||
showParameterRec: ['REC_CH_DEV00', 'REC_CH_DEV01', 'AXIS_CH', 'AMP_GAIN', 'SAMPLE_RATE', 'ACCELERATOR_S'],
|
||||
showParameterSti: ['STI_H_BRIDGE', 'STI_CHANNEL', 'CONTINUOUS_STI', 'STI_CYCLE_CH0',
|
||||
'REPEAT_TIME_littleEndian', 'REPEAT_INTERVAL_littleEndian',
|
||||
'STI_AMPLITUDE_L1', 'STI_AMPLITUDE_H3',
|
||||
'STI_PATTERN', 'STI_T1_CH0', 'STI_T2_CH0', 'STI_T3_CH0', 'STI_T4_CH0'],
|
||||
// rec
|
||||
REC_CH_DEV00: {
|
||||
type: 'array',
|
||||
showName: 'Channel Low',
|
||||
range: Array(32).fill([...Array(32).keys()].map(x => ++x)), // UI上能輸入的最大最小值
|
||||
func: (chList) => {
|
||||
let channelSet = ''
|
||||
for (let i = 32; i >= 1; i--) {
|
||||
if (chList.indexOf(i) >= 0) {
|
||||
channelSet += '1'
|
||||
} else {
|
||||
channelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(channelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const channelList = []
|
||||
const channelSet = val.toString(2).split('').reverse()
|
||||
channelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
channelList.push(index + 1)
|
||||
}
|
||||
})
|
||||
return channelList
|
||||
},
|
||||
},
|
||||
REC_CH_DEV01: {
|
||||
type: 'array',
|
||||
showName: 'Channel High',
|
||||
range: Array(32).fill([...Array(32).keys()].map(x => x + 33)), // UI上能輸入的最大最小值
|
||||
func: (chList) => {
|
||||
let channelSet = ''
|
||||
for (let i = 64; i >= 33; i--) {
|
||||
if (chList.indexOf(i) >= 0) {
|
||||
channelSet += '1'
|
||||
} else {
|
||||
channelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(channelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const channelList = []
|
||||
const channelSet = val.toString(2).split('').reverse()
|
||||
channelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
channelList.push(index + 33)
|
||||
}
|
||||
})
|
||||
return channelList
|
||||
},
|
||||
},
|
||||
AMP_GAIN: {
|
||||
type: 'array',
|
||||
showName: 'Amp gain',
|
||||
range: ['100', '500', '300'],
|
||||
func: (val) => {
|
||||
const ampGainArr = ['100', '500', '300']
|
||||
return ampGainArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const ampGainArr = ['100', '500', '300']
|
||||
return ampGainArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
SAMPLE_RATE: { // [TODO] sample rate per chip
|
||||
type: 'array',
|
||||
showName: 'Sample Rate',
|
||||
range: [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 1.6e4, 2e4, 2.5e4, 3.2e4, 4e4, 5e4, 6.4e4, 8e4, 1e5],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
// const sampleRateArr = [1e3, 2e3, 4e3, 5e3, 8e3, 1e4, 1.6e4, 2e4, 2.5e4, 3.2e4, 4e4, 5e4, 6.4e4, 8e4, 1e5]
|
||||
// const idx = sampleRateArr.indexOf(val)
|
||||
// const sampleRateStrArr = ['1k', '2k', '4k', '5k', '8k', '10k', '16k', '20k', '25k', '32k', '40k', '50k', '64k', '80k', '100k']
|
||||
// return sampleRateStrArr[idx]
|
||||
return val
|
||||
},
|
||||
},
|
||||
ADC_CLOCK: {
|
||||
type: 'number',
|
||||
showName: 'ADC_CLOCK',
|
||||
range: Object.freeze({ min: 1e3, max: 1e5 }),
|
||||
func: (val) => {
|
||||
return 8e6 / parseInt(val)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return 8e6 / parseInt(val)
|
||||
},
|
||||
},
|
||||
RECORDING: {
|
||||
type: 'bool',
|
||||
showName: 'RECORDING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
AXIS_CH: {
|
||||
type: 'array',
|
||||
showName: '3-AXIS',
|
||||
range: Array(4).fill(['x', 'y', 'z', 'm']),
|
||||
func: (chList) => {
|
||||
const axisChannel = ['m', 'z', 'y', 'x']
|
||||
let axisChannelSet = ''
|
||||
for (const axis of axisChannel) {
|
||||
if (chList.indexOf(axis) >= 0) {
|
||||
axisChannelSet += '1'
|
||||
} else {
|
||||
axisChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(axisChannelSet, 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const axisChannelList = []
|
||||
const axisChannelSet = val.toString(2).split('').reverse()
|
||||
axisChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) === 1) {
|
||||
if (index === 0) {
|
||||
axisChannelList.push('x')
|
||||
} else if (index === 1) {
|
||||
axisChannelList.push('y')
|
||||
} else if (index === 2) {
|
||||
axisChannelList.push('z')
|
||||
} else if (index === 3) {
|
||||
axisChannelList.push('m')
|
||||
}
|
||||
}
|
||||
})
|
||||
return axisChannelList
|
||||
},
|
||||
},
|
||||
ACCELERATOR_S: {
|
||||
type: 'array',
|
||||
showName: 'Accelerator Sensitivity',
|
||||
range: ['2G', '4G', '8G', '16G'],
|
||||
func: (val) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
const index = accArr.indexOf(val.toString())
|
||||
return index
|
||||
},
|
||||
rev_func: (idx) => {
|
||||
const accArr = ['2G', '4G', '8G', '16G']
|
||||
return accArr[parseInt(idx)]
|
||||
},
|
||||
},
|
||||
// sti
|
||||
STIMULATING: {
|
||||
type: 'bool',
|
||||
showName: 'STIMULATING',
|
||||
range: [false, true],
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
STI_CHANNEL: {
|
||||
type: 'array',
|
||||
showName: 'Channels',
|
||||
range: Object.freeze({ pos: Array(32).fill([...Array(32).keys()].map(x => ++x)), neg: Array(32).fill([...Array(32).keys()].map(x => ++x)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiChannelSet = ''
|
||||
for (let i = 32; i >= 1; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiChannelSet += '1'
|
||||
} else {
|
||||
stiChannelSet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiChannelSet, 2)
|
||||
},
|
||||
rev_func: (val, stiPolarityVal) => {
|
||||
const stiChannelSet = val.toString(2).split('').reverse()
|
||||
const stiPolaritySet = stiPolarityVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
const channel = index + 1
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
channel > 16 ? posChannelList.push(channel + 32) : posChannelList.push(channel + 16)
|
||||
} else {
|
||||
channel > 16 ? negChannelList.push(channel + 32) : negChannelList.push(channel + 16)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_POLARITY: {
|
||||
type: 'array',
|
||||
showName: 'Polarity',
|
||||
range: Object.freeze({ pos: Array(32).fill([...Array(32).keys()].map(x => ++x)), neg: Array(32).fill([...Array(32).keys()].map(x => ++x)) }),
|
||||
func: (posChList, negChList) => {
|
||||
let stiPolaritySet = ''
|
||||
for (let i = 32; i >= 1; i--) {
|
||||
if (posChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '1'
|
||||
} else if (negChList.indexOf(i) >= 0) {
|
||||
stiPolaritySet += '0'
|
||||
} else {
|
||||
stiPolaritySet += '0'
|
||||
}
|
||||
}
|
||||
return parseInt(stiPolaritySet, 2)
|
||||
},
|
||||
rev_func: (val, stiChannelVal) => {
|
||||
const stiPolaritySet = val.toString(2).split('').reverse()
|
||||
const stiChannelSet = stiChannelVal.toString(2).split('').reverse()
|
||||
const posChannelList = []
|
||||
const negChannelList = []
|
||||
stiChannelSet.forEach((element, index) => {
|
||||
if (parseInt(element) > 0) {
|
||||
if (parseInt(stiPolaritySet[index]) > 0) {
|
||||
posChannelList.push(index + 1)
|
||||
} else {
|
||||
negChannelList.push(index + 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
return JSON.stringify({ pos: posChannelList, neg: negChannelList })
|
||||
},
|
||||
},
|
||||
STI_H_BRIDGE: {
|
||||
type: 'array',
|
||||
showName: 'ELECTRODE',
|
||||
range: ['Monopolar', 'Bipolar'],
|
||||
func: (val) => {
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const electrodeArr = ['Monopolar', 'Bipolar']
|
||||
return electrodeArr[idx]
|
||||
},
|
||||
},
|
||||
CONTINUOUS_STI: {
|
||||
type: 'array',
|
||||
showName: 'CYCLE',
|
||||
range: ['Limited', 'Unlimited'],
|
||||
func: (val) => {
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const cycleNumberArr = ['Limited', 'Unlimited']
|
||||
return cycleNumberArr[idx]
|
||||
},
|
||||
},
|
||||
STI_CYCLE_CH0: {
|
||||
type: 'number',
|
||||
showName: 'CYCLE NUMBER',
|
||||
range: Object.freeze({ min: 1, max: 256 }),
|
||||
func: (cycleNum) => {
|
||||
return String(cycleNum - 1)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return parseInt(val) + 1
|
||||
},
|
||||
defaultUnit: 'rounds',
|
||||
unit: {
|
||||
rounds: 1,
|
||||
},
|
||||
},
|
||||
REPEAT_TIME_littleEndian: {
|
||||
type: 'number',
|
||||
showName: 'REPEAT NUMBER',
|
||||
range: Object.freeze({ min: 1, max: 256 }),
|
||||
func: (val) => {
|
||||
return String(val)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return parseInt(val)
|
||||
},
|
||||
defaultUnit: 'rounds',
|
||||
unit: {
|
||||
rounds: 1,
|
||||
},
|
||||
},
|
||||
REPEAT_INTERVAL_littleEndian: {
|
||||
type: 'number',
|
||||
showName: 'REPEAT INTERVAL',
|
||||
range: Object.freeze({ min: 0, max: Infinity }),
|
||||
func: (val) => {
|
||||
return parseFloat(parseFloat(val).toFixed(2)) * 1e3
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val
|
||||
},
|
||||
defaultUnit: 's',
|
||||
unit: {
|
||||
s: 1e3,
|
||||
ms: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_L1: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE DURING T1',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_H3: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE DURING T3',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_H1: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_HIGH_1',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5 / 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5 * 2
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_AMPLITUDE_L3: {
|
||||
type: 'number',
|
||||
showName: 'AMPLITUDE_LOW_3',
|
||||
range: Object.freeze({ min: 5, max: 4000 }),
|
||||
func: (val) => {
|
||||
return parseInt(val / 5 / 2)
|
||||
},
|
||||
rev_func: (val) => {
|
||||
return val * 5 * 2
|
||||
},
|
||||
defaultUnit: 'uA',
|
||||
unit: {
|
||||
uA: 1,
|
||||
},
|
||||
},
|
||||
STI_PATTERN: {
|
||||
type: 'array',
|
||||
showName: 'PATTERN',
|
||||
range: ['Monophasic', 'Biphasic'],
|
||||
func: (val) => {
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr.indexOf(val.toString())
|
||||
},
|
||||
rev_func: (val) => {
|
||||
const idx = +val // +: bool to int, ref: https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer
|
||||
const patternArr = ['Monophasic', 'Biphasic']
|
||||
return patternArr[idx]
|
||||
},
|
||||
},
|
||||
STI_T1_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T1',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 100, max: 10230 }), // 0.1ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T2_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T2',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 10230 }), // 0ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T3_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T3',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 10230 }), // 0ms~10.23ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
STI_T4_CH0: {
|
||||
type: 'number',
|
||||
showName: 'T4',
|
||||
amount: 16, // total 16 channels
|
||||
range: Object.freeze({ min: 0, max: 1310710 }), // 0ms~1310.71ms
|
||||
func: (val) => { // input unit: us
|
||||
const tRange = parseFloat(val / 10)
|
||||
return String(tRange)
|
||||
},
|
||||
rev_func: (val) => { // output unit: us
|
||||
return val * 10
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default Neulive35ex
|
||||
@@ -1,7 +1,13 @@
|
||||
import Neulive31 from './Neulive31/index'
|
||||
import Neulive35 from './Neulive35/index'
|
||||
import Neulive35ex from './Neulive35ex/index'
|
||||
import EliteZM15 from './EliteZM15/index'
|
||||
import EliteEIS from './EliteEIS/index'
|
||||
|
||||
const paramTable = {
|
||||
Neulive31,
|
||||
Neulive35,
|
||||
Neulive35ex,
|
||||
EliteZM15,
|
||||
EliteEIS,
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ export const taskInfo = {
|
||||
const mesObj = JSON.parse(mes)
|
||||
const update = async () => {
|
||||
try {
|
||||
_taskInfo.deviceList[mesObj.device].info.sd_card_status = mesObj.data
|
||||
_taskInfo.deviceList[mesObj.device].info.sd_card_status = mesObj.data.sd_mount
|
||||
// _taskInfo.deviceList[mesObj.device].info.sd_card_enable_status = mesObj.data.sd_enable
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return false
|
||||
|
||||
@@ -37,7 +37,6 @@ function newSeries (type, legendName, xAxisIndex, yAxisIndex, xAxisSource, yAxis
|
||||
showAllSymbol: false,
|
||||
symbolSize: 4,
|
||||
// [modified 2021/08/10] sampling method managed by vuex
|
||||
// sampling: 'average',
|
||||
// sampling: function (frame) {
|
||||
// return frame[0]
|
||||
// },
|
||||
@@ -60,6 +59,7 @@ function newSeries (type, legendName, xAxisIndex, yAxisIndex, xAxisSource, yAxis
|
||||
dataXMin: null,
|
||||
dataYMax: null,
|
||||
dataYMin: null,
|
||||
connectNulls: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
},
|
||||
|
||||
@@ -1,29 +1,4 @@
|
||||
function getTime (val) {
|
||||
let label = ''
|
||||
const hour = Math.floor(val / 3.6e9)
|
||||
const minute = Math.floor((val % 3.6e9) / 1000000 / 60)
|
||||
const second = Math.floor((val % 6e7) / 1000000)
|
||||
const millisecond = Math.floor((val % 1e6) / 1000)
|
||||
const microsecond = Math.floor(val % 1e6) - 1000 * millisecond
|
||||
if (hour > 0) {
|
||||
label += hour + ' h '
|
||||
}
|
||||
if (minute > 0) {
|
||||
label += minute + ' m '
|
||||
}
|
||||
if (second > 0) {
|
||||
label += second + ' s '
|
||||
}
|
||||
if (millisecond > 0) {
|
||||
label += millisecond + ' ms '
|
||||
}
|
||||
if (microsecond > 0 && hour === 0 && minute === 0 && second === 0) {
|
||||
label += microsecond + ' µs '
|
||||
}
|
||||
return label
|
||||
}
|
||||
|
||||
function newTooltip (chartID) {
|
||||
function newTooltip (formatter) {
|
||||
return {
|
||||
hideDelay: 5000,
|
||||
trigger: 'axis',
|
||||
@@ -39,86 +14,7 @@ function newTooltip (chartID) {
|
||||
// backgroundColor: '#505765',
|
||||
// },
|
||||
// },
|
||||
formatter: function (params) {
|
||||
if (params.length) {
|
||||
if (params[0].seriesName.split('_')[0] === 'T' && params[0].seriesName.split('_')[1] !== 'T') {
|
||||
params.unshift({ seriesName: 'Time', value: [null, getTime(parseFloat(params[0].value[0]))], color: '#5193f2' })
|
||||
} else if (params[0].seriesName.split('_')[0] !== 'T' && params[0].seriesName.split('_')[1] === 'T') {
|
||||
params.unshift({ seriesName: 'Time', value: [null, getTime(parseFloat(params[0].value[1]))], color: '#5193f2' })
|
||||
} else {
|
||||
params.unshift({ seriesName: 'Time', value: [null, getTime(parseFloat(params[0].value[2]))], color: '#5193f2' })
|
||||
}
|
||||
let tip = ''
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
const param = params[i]
|
||||
const style = 'color: ' + param.color
|
||||
let value
|
||||
// console.log(param.value)
|
||||
if (param.seriesName === 'Time') {
|
||||
value = param.value[1]
|
||||
} else if (param.seriesName.split('_')[1] === 'T' && param.seriesName.split('_')[0] !== 'T') {
|
||||
const physicalQuantity = param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1]
|
||||
if (physicalQuantity === 'V') {
|
||||
value = parseFloat(param.value[0]).toFixed(3) + ' μV'
|
||||
} else if (physicalQuantity === 'I') {
|
||||
value = parseFloat(param.value[0]).toFixed(3) + ' nA'
|
||||
} else if (physicalQuantity === 'R') {
|
||||
value = parseFloat(param.value[0]).toFixed(3) + ' mΩ'
|
||||
} else if (physicalQuantity === 'X' || physicalQuantity === 'Y' || physicalQuantity === 'Z' || physicalQuantity === 'M') {
|
||||
value = (parseFloat(param.value[0]) / 100).toFixed(3) + ' (g)'
|
||||
} else if (!isNaN(physicalQuantity)) {
|
||||
value = (parseFloat(param.value[0]) / 1000).toFixed(3) + ' (mV)'
|
||||
} else {
|
||||
value = parseFloat(param.value[0]).toFixed(3)
|
||||
}
|
||||
} else if (param.seriesName.split('_')[1] !== 'T' && param.seriesName.split('_')[0] === 'T') {
|
||||
const physicalQuantity = param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1]
|
||||
if (physicalQuantity === 'V') {
|
||||
value = parseFloat(param.value[1]).toFixed(3) + ' μV'
|
||||
} else if (physicalQuantity === 'I') {
|
||||
value = parseFloat(param.value[1]).toFixed(3) + ' nA'
|
||||
} else if (physicalQuantity === 'R') {
|
||||
value = parseFloat(param.value[1]).toFixed(3) + ' mΩ'
|
||||
} else if (physicalQuantity === 'X' || physicalQuantity === 'Y' || physicalQuantity === 'Z' || physicalQuantity === 'M') {
|
||||
value = (parseFloat(param.value[1]) / 100).toFixed(3) + ' (g)'
|
||||
} else if (!isNaN(physicalQuantity)) {
|
||||
value = (parseFloat(param.value[1]) / 1000).toFixed(3) + ' (mV)'
|
||||
} else {
|
||||
value = parseFloat(param.value[1]).toFixed(3)
|
||||
}
|
||||
} else {
|
||||
let value1
|
||||
let value2
|
||||
if (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'V') {
|
||||
value1 = parseFloat(param.value[0]).toFixed(3) + ' μV'
|
||||
} else if (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'I') {
|
||||
value1 = parseFloat(param.value[0]).toFixed(3) + ' nA'
|
||||
} else if (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'R') {
|
||||
value1 = parseFloat(param.value[0]).toFixed(3) + ' mΩ'
|
||||
} else {
|
||||
value1 = parseFloat(param.value[0]).toFixed(3)
|
||||
}
|
||||
|
||||
if (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'V') {
|
||||
value2 = parseFloat(param.value[1]).toFixed(3) + ' μV'
|
||||
} else if (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'I') {
|
||||
value2 = parseFloat(param.value[1]).toFixed(3) + ' nA'
|
||||
} else if (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'R') {
|
||||
value2 = parseFloat(param.value[1]).toFixed(3) + ' mΩ'
|
||||
} else {
|
||||
value2 = parseFloat(param.value[1]).toFixed(3)
|
||||
}
|
||||
|
||||
value = value1 + ', ' + value2
|
||||
}
|
||||
tip += '<span style="' + style + '">' +
|
||||
param.seriesName +
|
||||
':</span><span style="' +
|
||||
style + '">' + value + '</span><br>'
|
||||
}
|
||||
return tip
|
||||
}
|
||||
},
|
||||
formatter: formatter,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
function newParameterSet () {
|
||||
return {
|
||||
// Neulive
|
||||
SD_ENABLE: false,
|
||||
CH_NUMBER: 0,
|
||||
ADC_CLOCK: 0,
|
||||
ADDRESS: 0,
|
||||
|
||||
+11
-1
@@ -114,7 +114,7 @@ export default new Router({
|
||||
},
|
||||
{
|
||||
name: 'data-spike',
|
||||
path: 'data-spike/:metaList?',
|
||||
path: 'data-spike/:metaList?/:startTime?/:endTime?/:sd?',
|
||||
component: () => import('../components/analysis/spike/Spike.vue'),
|
||||
},
|
||||
{
|
||||
@@ -135,6 +135,16 @@ export default new Router({
|
||||
// keepAlive: true,
|
||||
// },
|
||||
},
|
||||
{
|
||||
name: 'trigger-file',
|
||||
path: 'trigger-file',
|
||||
component: () => import('../components/file/TriggerFile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'sdcard-reader',
|
||||
path: 'sdcard-reader',
|
||||
component: () => import('../components/file/SdcardFile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
path: 'settings',
|
||||
|
||||
+5
-1
@@ -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'
|
||||
@@ -10,8 +11,9 @@ import mutations from './mutations'
|
||||
|
||||
import replay from './modules/analysis/replay'
|
||||
import spike from './modules/analysis/spike'
|
||||
import file from './modules/file'
|
||||
import download from './modules/download'
|
||||
import file from './modules/file' // [2022/02/10] for trigger file now
|
||||
import sdcard from './modules/sdcard'
|
||||
|
||||
import * as getters from './getters'
|
||||
|
||||
@@ -25,8 +27,10 @@ const store = new Vuex.Store({
|
||||
replay,
|
||||
spike,
|
||||
setting,
|
||||
device,
|
||||
download,
|
||||
file,
|
||||
sdcard,
|
||||
},
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
|
||||
@@ -3,6 +3,7 @@ import newChart from '@/factories/chart/chartFactory'
|
||||
import newTooltip from '@/factories/chart/tooltipFactory'
|
||||
import newDataZoom from '@/factories/chart/dataZoomFactory'
|
||||
import types from '@/store/modules/analysis/replay/types'
|
||||
import { download } from '@/data/download/Download'
|
||||
|
||||
const typePath = types.chart
|
||||
|
||||
@@ -27,6 +28,7 @@ function makeXAxis (gridIndex, type) {
|
||||
scale: true,
|
||||
min: null,
|
||||
max: null,
|
||||
data: [],
|
||||
}
|
||||
return xAxis
|
||||
}
|
||||
@@ -91,6 +93,9 @@ function getLegendName (sourceList) {
|
||||
case 'Time':
|
||||
legendName += 'T'
|
||||
break
|
||||
case 'Category':
|
||||
legendName += 'T'
|
||||
break
|
||||
case 'Neulive':
|
||||
switch (source.channel) {
|
||||
case 256:
|
||||
@@ -173,14 +178,14 @@ function getLegendName (sourceList) {
|
||||
// generate grid
|
||||
function makeGrid (index, length, chartID) {
|
||||
const _height = (82 / length) - 2 + '%'
|
||||
const _top = index * (82 / length) + 1 + '%'
|
||||
const _top = index * (82 / length) + 7 + '%'
|
||||
let _left
|
||||
let _right
|
||||
if (chartID === 0) {
|
||||
_left = '0%'
|
||||
_right = '0%'
|
||||
} else {
|
||||
_left = '8%'
|
||||
_left = '5%'
|
||||
_right = '4%'
|
||||
}
|
||||
return {
|
||||
@@ -193,7 +198,79 @@ function makeGrid (index, length, chartID) {
|
||||
}
|
||||
}
|
||||
|
||||
function getTime (val) {
|
||||
let label = ''
|
||||
const hour = Math.floor(val / 3.6e9)
|
||||
const minute = Math.floor((val % 3.6e9) / 1000000 / 60)
|
||||
const second = Math.floor((val % 6e7) / 1000000)
|
||||
const millisecond = Math.floor((val % 1e6) / 1000)
|
||||
const microsecond = Math.floor(val % 1e6) - 1000 * millisecond
|
||||
if (hour > 0) {
|
||||
label += hour + ' h '
|
||||
}
|
||||
if (minute > 0) {
|
||||
label += minute + ' m '
|
||||
}
|
||||
if (second > 0) {
|
||||
label += second + ' s '
|
||||
}
|
||||
if (millisecond > 0) {
|
||||
label += millisecond + ' ms '
|
||||
}
|
||||
if (microsecond > 0 && hour === 0 && minute === 0 && second === 0) {
|
||||
label += microsecond + ' µs '
|
||||
}
|
||||
return label
|
||||
}
|
||||
|
||||
function tooltipFormatter (params) {
|
||||
if (params.length) {
|
||||
params.unshift({ seriesName: 'Time', value: getTime(parseFloat(params[params.length - 1].axisValue)), color: '#5193f2' })
|
||||
let tip = ''
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
const param = params[i]
|
||||
const style = 'color: ' + param.color
|
||||
let value
|
||||
if (param.seriesName === 'Time') {
|
||||
value = param.value
|
||||
} else {
|
||||
value = (parseFloat(param.value) / 1000).toFixed(3) + ' (mV)'
|
||||
}
|
||||
tip += '<span style="' + style + '">' +
|
||||
param.seriesName +
|
||||
':</span><span style="' +
|
||||
style + '">' + value + '</span><br>'
|
||||
}
|
||||
return tip
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
/**
|
||||
* download data from API
|
||||
*
|
||||
* @param {number} scale
|
||||
* @param {string} sampleMethod
|
||||
* @param {number} id
|
||||
* @param {number} channel
|
||||
* @param {object} meta
|
||||
*/
|
||||
[typePath.getData]: async ({ state, getters, commit }, payload) => {
|
||||
const { scale, sampleMethod, id, channel, meta, uuid, startTime, endTime } = payload
|
||||
if (state.useSdCardData) {
|
||||
return await download.rawSdDataByUUIDAndChannel(uuid, channel, startTime, endTime)
|
||||
} else { // BLE data
|
||||
if (scale === 1) {
|
||||
return await download.rawByIDChannels(id, channel, meta)
|
||||
} else {
|
||||
if (sampleMethod === 'mean') {
|
||||
return await download.miniMeanByIDChannel(id, channel, meta)
|
||||
} else if (sampleMethod === 'random') {
|
||||
return await download.miniRandomByIDChannel(id, channel, meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init data to chartData via chartID
|
||||
@@ -210,7 +287,9 @@ const actions = {
|
||||
if (payload.chartAllData[payload.chartID] == null) {
|
||||
payload.chartAllData[payload.chartID] = newChart(payload.chartID)
|
||||
if (payload.type !== 'overview') {
|
||||
payload.chartAllData[payload.chartID].tooltip = newTooltip()
|
||||
payload.chartAllData[payload.chartID].tooltip = newTooltip(tooltipFormatter)
|
||||
payload.chartAllData[payload.chartID].legend.type = 'scroll'
|
||||
payload.chartAllData[payload.chartID].legend.top = '1%'
|
||||
}
|
||||
payload.chartAllData[payload.chartID].chartType = payload.type
|
||||
payload.chartAllData[payload.chartID].chartDevice = payload.device
|
||||
@@ -329,6 +408,7 @@ const actions = {
|
||||
payload.chartAllData[payload.chartID].dataZoom[0].xAxisIndex = dataZoomXAxisIndex
|
||||
payload.chartAllData[payload.chartID].dataZoom[1].xAxisIndex = dataZoomXAxisIndex
|
||||
payload.chartAllData[payload.chartID].dataZoom[2].yAxisIndex = dataZoomYAxisIndex
|
||||
payload.chartAllData[payload.chartID].dataZoom[0].top = '0%'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -377,6 +457,10 @@ const actions = {
|
||||
xAxisType = 'time'
|
||||
xAxisSource = [-1, -1]
|
||||
break
|
||||
case 'Category':
|
||||
xAxisType = 'category'
|
||||
xAxisSource = [-1, -1]
|
||||
break
|
||||
default:
|
||||
xAxisType = 'value'
|
||||
xAxisSource = [sourceList[0].id, sourceList[0].channel]
|
||||
@@ -387,6 +471,10 @@ const actions = {
|
||||
yAxisType = 'time'
|
||||
yAxisSource = [-1, -1]
|
||||
break
|
||||
case 'Category':
|
||||
yAxisType = 'category'
|
||||
yAxisSource = [-1, -1]
|
||||
break
|
||||
default:
|
||||
yAxisType = 'value'
|
||||
yAxisSource = [sourceList[1].id, sourceList[1].channel]
|
||||
@@ -521,90 +609,6 @@ const actions = {
|
||||
|
||||
return chartData
|
||||
},
|
||||
|
||||
// /**
|
||||
// * remove device-channel from chart TODO
|
||||
// *
|
||||
// * @param {object} chartAllData
|
||||
// * @param {int} chartID
|
||||
// * @param {int} legendName
|
||||
// * @return {object} : chart data
|
||||
// */
|
||||
// [typePath.remove]: ({ state, getters, commit }, payload) => {
|
||||
// const chartData = getters.getDataByIDChart(payload.chartID)
|
||||
// if (chartData == null || chartData.legend.data.length === 0) {
|
||||
// return false
|
||||
// }
|
||||
|
||||
// let _xAxisIndex = -1
|
||||
// let _yAxisIndex = -1
|
||||
|
||||
// // remove legend
|
||||
// const _legendIndex = chartData.legend.data.indexOf(payload.legendName)
|
||||
// // console.log(_legendIndex)
|
||||
// if (_legendIndex >= 0) {
|
||||
// chartData.legend.data.splice(chartData.legend.data.indexOf(payload.legendName), 1)
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
// // remove series
|
||||
// for (let i = 0; i < chartData.series.length; i++) {
|
||||
// if (chartData.series[i].name === payload.legendName) {
|
||||
// _xAxisIndex = chartData.series[i].xAxisIndex
|
||||
// _yAxisIndex = chartData.series[i].yAxisIndex
|
||||
// chartData.series.splice(i, 1)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// // remove grid series name
|
||||
// const _gridIndex = chartData.xAxis[_xAxisIndex].gridIndex
|
||||
// for (let i = 0; i < chartData.grid[_gridIndex].series.length; i++) {
|
||||
// if (chartData.grid[_gridIndex].series[i] === payload.legendName) {
|
||||
// chartData.grid[_gridIndex].series.splice(i, 1)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// for (let i = 0; i < chartData.grid.length; i++) {
|
||||
// if (chartData.grid[i].name === payload.legendName) {
|
||||
// _xAxisIndex = chartData.series[i].xAxisIndex
|
||||
// _yAxisIndex = chartData.series[i].yAxisIndex
|
||||
// chartData.series.splice(i, 1)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// chartData.series.forEach(series => {
|
||||
// if (series.xAxisIndex === _xAxisIndex) {
|
||||
// _xAxisIndex = -1
|
||||
// }
|
||||
// if (series.yAxisIndex === _yAxisIndex) {
|
||||
// _yAxisIndex = -1
|
||||
// }
|
||||
// })
|
||||
// // remove xAxis
|
||||
// if (_xAxisIndex !== -1) {
|
||||
// chartData.xAxis.splice(_xAxisIndex, 1)
|
||||
// chartData.series.forEach(series => {
|
||||
// if (series.xAxisIndex > _xAxisIndex) {
|
||||
// series.xAxisIndex--
|
||||
// }
|
||||
// })
|
||||
// chartData.dataZoom[0].xAxisIndex.pop()
|
||||
// }
|
||||
// // remove yAxis
|
||||
// if (_yAxisIndex !== -1) {
|
||||
// chartData.yAxis.splice(_yAxisIndex, 1)
|
||||
// chartData.series.forEach(series => {
|
||||
// if (series.yAxisIndex > _yAxisIndex) {
|
||||
// series.yAxisIndex--
|
||||
// }
|
||||
// })
|
||||
// chartData.dataZoom[2].yAxisIndex.pop()
|
||||
// }
|
||||
|
||||
// // console.log(chartData)
|
||||
// return true
|
||||
// },
|
||||
|
||||
}
|
||||
|
||||
export default actions
|
||||
|
||||
@@ -116,10 +116,11 @@ const chartMutations = {
|
||||
axisLabel.formatter = function (val) {
|
||||
let label = ''
|
||||
if (unit === 'auto') { // time
|
||||
const hour = parseInt(val / 3.6e9)
|
||||
const minute = parseInt((val % 3.6e9) / 1000000 / 60)
|
||||
const second = parseInt((val % 6e7) / 1000000)
|
||||
const millisecond = parseInt((val % 1e6) / 1000)
|
||||
const hour = Math.floor(val / 3.6e9)
|
||||
const minute = Math.floor((val % 3.6e9) / 1000000 / 60)
|
||||
const second = Math.floor((val % 6e7) / 1000000)
|
||||
const millisecond = Math.floor((val % 1e6) / 1000)
|
||||
const microsecond = Math.floor(val % 1e6) - 1000 * millisecond
|
||||
if (hour > 0) {
|
||||
label += hour + ' h '
|
||||
}
|
||||
@@ -132,8 +133,11 @@ const chartMutations = {
|
||||
if (millisecond > 0) {
|
||||
label += millisecond + ' ms '
|
||||
}
|
||||
if (microsecond > 0 && hour === 0 && minute === 0 && second === 0) {
|
||||
label += microsecond + ' µs '
|
||||
}
|
||||
} else { // not time, e.g. I, V, R
|
||||
const num = String(val / unitScale)
|
||||
const num = (val / unitScale).toFixed(1)
|
||||
label = num + unit
|
||||
}
|
||||
return label
|
||||
@@ -164,6 +168,7 @@ const chartMutations = {
|
||||
return label
|
||||
}
|
||||
}
|
||||
state.chartKey++
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -413,12 +418,15 @@ const chartMutations = {
|
||||
|
||||
/**
|
||||
* for manually set datazoom start and end
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
*
|
||||
*/
|
||||
[typePath.setOverviewDataZoomStartAndEnd]: (state, payload) => {
|
||||
const { start, end } = payload
|
||||
if (state.chartOverviewData.dataZoom != null) {
|
||||
state.chartOverviewData.dataZoom[0].start = 0
|
||||
state.chartOverviewData.dataZoom[0].end = 100
|
||||
state.chartOverviewData.dataZoom[0].start = start
|
||||
state.chartOverviewData.dataZoom[0].end = end
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -35,6 +35,14 @@ function newState () {
|
||||
showWhichCycles: '1',
|
||||
useSdCardData: false,
|
||||
isLockDataZoom: false,
|
||||
chartKey: 0,
|
||||
isInitDone: false,
|
||||
isDownloading: false,
|
||||
// for sd replay
|
||||
currentChunkDataSplitIndex: 1, // 1 ~ maxChunkDataSplitNum
|
||||
maxChunkDataSplitNum: 10,
|
||||
sdDataChunkDict: {},
|
||||
useFilter: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ const types = {
|
||||
updateChartSampling: 'updateChartSamplingChart',
|
||||
lockDataZoom: 'lockDataZoomChart',
|
||||
setOverviewDataZoomStartAndEnd: 'setOverviewDataZoomStartAndEndChart',
|
||||
getData: 'replayChartGetData',
|
||||
},
|
||||
meta: {
|
||||
init: 'initMeta',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user