Compare commits

...

49 Commits

Author SHA1 Message Date
Jordan Hsu e8d75b9877 [init] spike ui 2021-08-26 02:03:30 +08:00
Jordan Hsu 8b78cdcdbd Merge remote-tracking branch 'origin/release/0812_test' into feature/spike_UI 2021-08-20 16:08:37 +08:00
peterlu14 94feff568a Merge remote-tracking branch 'origin/release/0812_test' into release/0812_test 2021-08-20 15:49:07 +08:00
peterlu14 06336bbd61 [update] add api spikeDetect 2021-08-20 15:48:18 +08:00
TommyXin 33b041a31b Merge remote-tracking branch 'origin/release/0812_test' into release/0812_test 2021-08-19 10:40:27 +08:00
TommyXin 14dda58fac neulive uV 2021-08-19 10:40:12 +08:00
Jordan Hsu f9bf41980b [modify] neulive replay function name 2021-08-18 14:55:45 +08:00
Jordan Hsu 6b20902c74 [fix] neulive stimulation phasic bug 2021-08-18 14:16:45 +08:00
peterlu14 ced0362910 Merge remote-tracking branch 'origin/release/0812_test' into release/0812_test 2021-08-17 16:59:13 +08:00
peterlu14 467016140f [update] real-time axis setting & edf format 2021-08-17 16:58:40 +08:00
Jordan Hsu f6e642cd5f Merge remote-tracking branch 'origin/release/0812_test' into release/0812_test 2021-08-13 16:00:48 +08:00
Jordan Hsu 7c5aed4dbc [fix] sti t range check bug 2021-08-13 15:54:55 +08:00
peterlu14 c069354415 [fix] EIS download 2021-08-13 13:14:10 +08:00
Jordan Hsu 738b58fded [modify] speed up neulive replay 2021-08-12 18:59:19 +08:00
Jordan Hsu f7c89585ac [modify] elite chart sampling method to lttb algo. and fix overview scale bug 2021-08-12 18:06:34 +08:00
peterlu14 36ed59816d Merge branch '0809_release_version' into release/0812_test 2021-08-12 17:01:34 +08:00
Jordan Hsu 516d20333c Merge remote-tracking branch 'origin/0809_release_version' into 0809_release_version 2021-08-12 17:00:56 +08:00
Jordan Hsu 74b8472c1d [modify] replay resampling method 2021-08-12 17:00:34 +08:00
peterlu14 ecb9e02610 [update] about to setting 2021-08-12 16:54:44 +08:00
peterlu14 74dea74476 Merge remote-tracking branch 'origin/0809_release_version' into release/0812_test 2021-08-12 14:40:47 +08:00
TommyXin 588a791bc1 Merge remote-tracking branch 'origin/0809_release_version' into 0809_release_version 2021-08-12 14:20:48 +08:00
TommyXin cd3e6f08f5 time 500ms&neulive uV&device panel height 2021-08-12 14:20:06 +08:00
peterlu14 1370e5659e [update] axis data divide 100 2021-08-12 13:42:48 +08:00
peterlu14 e18fa916b5 Merge branch 'download_rebuild' into 0809_release_version 2021-08-12 13:11:04 +08:00
peterlu14 b5a920338a [update] remove old download routes 2021-08-12 13:10:19 +08:00
peterlu14 6c4660966c [update] adjust tooltip formatter 2021-08-12 12:57:27 +08:00
peterlu14 0db13d3404 [update] remove old download pages 2021-08-12 12:55:45 +08:00
TommyXin 8b96a7308c fix stop button 2021-08-12 11:06:05 +08:00
peterlu14 d7e69e250e Merge remote-tracking branch 'origin/0809_release_version' into download_rebuild 2021-08-12 10:30:25 +08:00
TommyXin 14217aa1ea fix EIS freq max 2021-08-12 09:50:50 +08:00
peterlu14 506080c518 [update] replay download url fix 2021-08-12 04:23:53 +08:00
peterlu14 794b71cc9f [update] rebuild download structure 2021-08-12 04:16:40 +08:00
Jordan Hsu 7e355448e0 [fix] replay change scale bug 2021-08-10 19:53:37 +08:00
TommyXin 1bcf0d99e9 Merge remote-tracking branch 'origin/0809_release_version' into 0809_release_version 2021-08-10 19:28:48 +08:00
TommyXin af094ad93a fix subplot deviceOpions lose time 2021-08-10 19:28:37 +08:00
Jordan Hsu 062023bd95 [add] replay overview sampling function 2021-08-10 18:43:20 +08:00
Jordan Hsu fcef23b199 [fix] delete bug 2021-08-10 17:59:31 +08:00
Jordan Hsu 56051c881a [fix] replay layout bug 2021-08-10 17:29:19 +08:00
TommyXin 9bc027b445 fix subplot multi device selection 2021-08-10 16:27:57 +08:00
TommyXin 06528addc7 remove some style 2021-08-10 16:17:24 +08:00
peterlu14 279a1056f4 Merge branch '0809_release_version' into feature/edf_format 2021-08-10 13:31:09 +08:00
peterlu14 0ef305c434 Merge remote-tracking branch 'origin/file_page_rebuild' into 0809_release_version 2021-08-10 00:21:45 +08:00
TommyXin 2161b37932 subplot add error mes 2021-08-09 23:20:24 +08:00
TommyXin 6b63b2b406 multi-device register 2021-08-09 21:09:36 +08:00
TommyXin ccdc193067 fix mlit-page start plot error 2021-08-09 17:47:02 +08:00
TommyXin 2ba4644a78 [api] collection add /file 2021-08-09 13:28:58 +08:00
peterlu14 35c436ee67 [update] data timestamp align 2021-08-05 18:21:39 +08:00
TommyXin 7edde3d7b2 vuex download 2021-08-04 18:25:08 +08:00
TommyXin 8ba6bcb8cb [api] get meta by limit&offset 2021-08-04 17:17:32 +08:00
159 changed files with 15563 additions and 15088 deletions
+5 -1
View File
@@ -18,6 +18,9 @@ import 'echarts/lib/chart/line'
import 'echarts/lib/component/dataZoom'
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/legend/ScrollableLegendModel.js'
import 'echarts/lib/component/legend/ScrollableLegendView.js'
import 'echarts/lib/component/legend/scrollableLegendAction.js'
import 'echarts/lib/component/axisPointer'
import 'echarts/lib/component/grid'
import 'echarts/lib/component/markArea'
@@ -51,7 +54,8 @@ Vue.use(VueAxios, axios)
// Vue.use(VueMqtt, 'ws://52.194.17.114:8083')
// Vue.use(VueMqtt, 'ws://' + location.href.split('/')[2] + ':8083')
Vue.use(VueMqtt, 'ws://' + '192.168.3.48' + ':8083')
// Vue.use(VueMqtt, 'ws://' + '192.168.151.125' + ':8083')
Vue.use(VueMqtt, 'ws://' + '192.168.151.125' + ':8083')
Vue.prototype.GLOBAL = global_
+3 -3
View File
@@ -136,7 +136,7 @@ export default {
</style>
<style lang='scss'>
.layout.gutter--xl {
padding: 0.5rem;
}
// .layout.gutter--xl {
// padding: 0.5rem;
// }
</style>
@@ -1,7 +1,7 @@
<template>
<div>
<div class="row row-equal">
<div class="flex md12 xl3 xs12 sm12 area-style">
<div class="flex md12 xl3 xs12 sm12">
<div class="row">
<div class="flex sm12 xl12 md12 xs12">
<va-accordion>
@@ -18,7 +18,7 @@
</div>
</div>
<div :class="chartAreaClass" class="area-style">
<div :class="chartAreaClass">
<div class="row" >
<div class="flex sm12 xl12 md12 xs12">
<elite-replay-overview-chart v-show="showOverviewChart" :ref="'chart_overview'" class="mb-1" :cardheight="'7vh'" :data="chartOverviewData" @dataZoomEvent="dataZoomEvent($event)"/>
@@ -247,14 +247,12 @@ export default {
let scale = '100'
if (this.axisXDataType.description === 'Time') {
const sampleRate = metaInfo.parameter_set.SAMPLE_RATE / 10
scale = (((parseInt(metaInfo.time_duration) / 100000) * sampleRate) / 4000) * 0.1
console.log(scale)
// console.log(metaInfo.time_duration)
if (scale > 500) {
const dataPointNum = (parseInt(metaInfo.time_duration) / 1e6) * sampleRate // us -> s: /1e6
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
scale = '1000'
} else if (scale > 200) {
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
scale = '100'
} else if (scale > 30) {
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
scale = '10'
} else {
scale = '1'
@@ -308,14 +306,12 @@ export default {
let scale = '100'
if (this.axisXDataType.description === 'Time') {
const sampleRate = metaInfo.parameter_set.SAMPLE_RATE / 10
scale = (((timeDur / 100000) * sampleRate) / 4000) * 0.1
console.log(scale)
// console.log(timeDur)
if (scale > 500) {
const dataPointNum = (parseInt(timeDur) / 1e6) * sampleRate // us -> s: /1e6
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
scale = '1000'
} else if (scale > 200) {
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
scale = '100'
} else if (scale > 30) {
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
scale = '10'
} else {
scale = '1'
@@ -323,12 +319,11 @@ export default {
} else {
scale = '1' // [TODO] select by user
}
// scale = '1000'
// elite的sample rate不像neulive這麼高,因此試試scale = 100 or 1000時不要再sampling一次了,不然有些形狀(e.g. peak)會不一樣
if (scale === '100' || scale === '1000') {
this.updateChartSamplingChart({ chartID: this.chartDetailData.chartID, seriesIndex: _gridNumber, method: null })
} else {
this.updateChartSamplingChart({ chartID: this.chartDetailData.chartID, seriesIndex: _gridNumber, method: function (frame) { return frame[0] } })
this.updateChartSamplingChart({ chartID: this.chartDetailData.chartID, seriesIndex: _gridNumber, method: 'lttb' })
}
this.currentScale = scale
@@ -502,14 +497,15 @@ export default {
console.log(this.metaList)
console.log(this.dataInfo)
await this.register()
this.drawOverviewData()
this.$refs.top_control_panel.init() // will call changeAxis() here
this.$refs.set_min_max.init()
this.drawOverviewData()
this.turnOnAxisEventHandler('click', 'yAxis.axisLabel', this.showMinMaxPanel, { axis: 'y' })
this.updateAxisSplitNumberChart({ chartID: this.chartDetailData.chartID, axis: 'x' })
this.updateAxisSplitNumberChart({ chartID: this.chartDetailData.chartID, axis: 'y' })
this.dataZoomTimer = setInterval(this.checkDataZoom, 1000)
this.initDone = true
console.log(this.chartOverviewData)
console.log(this.chartDetailData)
},
beforeDestroy () {
@@ -528,21 +524,3 @@ export default {
},
}
</script>
<style lang="scss">
.area-style {
height: auto;
overflow-y: hidden;
overflow-x: hidden;
@include media-breakpoint-only(xl) {
height: 88vh;
overflow-y: scroll;
overflow-x: hidden;
}
}
.layout.gutter--xl .row .flex {
padding: 0.4rem 0.5rem 0.1rem 0.5rem;
}
</style>
@@ -1,17 +1,21 @@
<template>
<div>
<va-card>
<edit-file-name style="display: inline;" :ref="'edit_file_name'"/>
<download-file style="display: inline;"/>
<delete-file style="display: inline;"/>
<br/>
<change-axis class="box_inline-block" :ref="'change_axis_x'" deviceName="elite" axis="x" @changeAxis="changeAxis" @changeUnit="changeUnit" @clearCycle="clearCycle" @resetScale="resetScale"/>
<change-unit class="box_inline-block" :ref="'change_unit_x'" deviceName="elite" axis="x" @refreshChart="refreshChart"/>
<change-scale class="box_inline-block" :ref="'change_scale_x'" deviceName="elite" axis="x" @refreshChart="refreshChart" @clearCycle="clearCycle"/>
<br/>
<change-axis class="box_inline-block" :ref="'change_axis_y'" deviceName="elite" axis="y" @changeAxis="changeAxis" @changeUnit="changeUnit" @clearCycle="clearCycle" @resetScale="resetScale"/>
<change-unit class="box_inline-block" :ref="'change_unit_y'" deviceName="elite" axis="y" @refreshChart="refreshChart"/>
<change-scale class="box_inline-block" :ref="'change_scale_y'" deviceName="elite" axis="y" @refreshChart="refreshChart" @clearCycle="clearCycle"/>
<div class="flex-container">
<edit-file-name class="flex-child" :ref="'edit_file_name'"/>
<download-file class="flex-child2"/>
<delete-file class="flex-child2"/>
</div>
<div class="flex-container">
<change-axis class="flex-child" :ref="'change_axis_x'" deviceName="elite" axis="x" @changeAxis="changeAxis" @changeUnit="changeUnit" @clearCycle="clearCycle" @resetScale="resetScale"/>
<change-unit class="flex-child2" :ref="'change_unit_x'" deviceName="elite" axis="x" @refreshChart="refreshChart"/>
<change-scale class="flex-child1" :ref="'change_scale_x'" deviceName="elite" axis="x" @refreshChart="refreshChart" @clearCycle="clearCycle"/>
</div>
<div class="flex-container">
<change-axis class="flex-child" :ref="'change_axis_y'" deviceName="elite" axis="y" @changeAxis="changeAxis" @changeUnit="changeUnit" @clearCycle="clearCycle" @resetScale="resetScale"/>
<change-unit class="flex-child2" :ref="'change_unit_y'" deviceName="elite" axis="y" @refreshChart="refreshChart"/>
<change-scale class="flex-child1" :ref="'change_scale_y'" deviceName="elite" axis="y" @refreshChart="refreshChart" @clearCycle="clearCycle"/>
</div>
<change-cycle v-show="showCyclePanel" style="margin: 3px;" :ref="'change_cycle'" @refreshChart="refreshChart"/>
<show-resample-rate style="margin: 3px;"/>
<show-parameter style="margin: 3px;" :ref="'show_param'" deviceName="elite"/>
@@ -107,4 +111,24 @@ export default {
margin: 2px;
display: inline-block;
}
.flex-container {
display: flex;
}
.flex-child {
flex: 2;
}
.flex-child1 {
flex: 1;
}
.flex-child2 {
flex: 0;
}
.flex-child:first-child {
margin-right: 1px;
}
</style>
@@ -1,7 +1,7 @@
<template>
<div>
<div class="row row-equal">
<div class="flex md12 xl3 xs12 sm12 area-style">
<div class="flex md12 xl3 xs12 sm12">
<div class="row">
<div class="flex sm12 xl12 md12 xs12">
<va-accordion>
@@ -18,7 +18,7 @@
</div>
</div>
<div :class="chartAreaClass" class="area-style">
<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)"/>
@@ -256,7 +256,7 @@ export default {
this.chartOverviewData.series[0].markArea.data[0][1].xAxis = this.startTime + ((this.endTime - this.startTime) * (this.dataZoomEnd / 100))
this.zoomTime = 0
},
drawOverviewData: async function () {
drawInitData: async function () {
this.showChartLoading('overview')
for (const meta in this.dataInfo) {
let metaInfo
@@ -265,43 +265,39 @@ export default {
metaInfo = _meta
}
})
const sampleRate = (800000 / metaInfo.parameter_set.ADC_CLOCK)
let scale = (((metaInfo.time_duration / 100000) * sampleRate) / 4000) * 0.1
if (scale > 500) {
let scale = '1000'
const sampleRate = (800000 / metaInfo.parameter_set.ADC_CLOCK) // Hz
const dataPointNum = (parseInt(metaInfo.time_duration) / 1e6) * sampleRate // us -> s: /1e6
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
scale = '1000'
} else if (scale > 200) {
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
scale = '100'
} else if (scale > 30) {
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
scale = '10'
} else {
scale = '1'
}
this.currentScale = scale
console.log('overview scale:', scale)
const getAllData = async function (_this) {
let seriesIndex = 0
for (const ch in _this.dataInfo[meta]) {
const dataList = _this.dataInfo[meta][ch][scale]
for (let i = 0; i < dataList.length; i++) {
if (dataList[i].data.length === 0) {
_this.intoDataRec({
index: i,
metaID: meta,
channel: ch,
scale: scale,
data: await _this.getData(parseInt(scale), _this.sampleMethod, dataList[i].id, ch, metaInfo),
})
}
_this.intoDataRec({
index: i,
metaID: meta,
channel: ch,
scale: scale,
data: await _this.getData(parseInt(scale), _this.sampleMethod, dataList[i].id, ch, metaInfo),
})
_this.chartOverviewData.series[seriesIndex].data.push(...dataList[i].data)
_this.chartDetailData.series[seriesIndex].data.push(...dataList[i].data)
}
seriesIndex++
}
}
await getAllData(this)
let seriesIndex = 0
for (const ch in this.dataInfo[meta]) {
const dataList = this.dataInfo[meta][ch][scale]
for (let i = 0; i < dataList.length; i++) {
this.chartOverviewData.series[seriesIndex].data.push(...dataList[i].data)
}
seriesIndex++
}
}
this.hideChartLoading()
},
@@ -315,19 +311,21 @@ export default {
metaInfo = _meta
}
})
const sampleRate = (800000 / metaInfo.parameter_set.ADC_CLOCK)
let scale = (((timeDur / 100000) * sampleRate) / 4000) * 0.1
if (scale > 500) {
let scale = '1000'
const sampleRate = (800000 / metaInfo.parameter_set.ADC_CLOCK) // Hz
const dataPointNum = (timeDur / 1e6) * sampleRate // us -> s: /1e6
if (dataPointNum > 2e6) { // 2000 (points) * 1000 (resample) = 2e6
scale = '1000'
} else if (scale > 200) {
} else if (dataPointNum > 2e5) { // 2000 (points) * 100 (resample) = 2e5
scale = '100'
} else if (scale > 30) {
} else if (dataPointNum > 2e4) { // 2000 (points) * 10 (resample) = 2e4
scale = '10'
} else {
scale = '1'
}
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]
@@ -343,12 +341,22 @@ export default {
}
}
// 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) {
this.chartDetailData.series[seriesIndex].data.push(data[j])
}
// early stop
if (parseInt(data[j][0]) > this.endTimeCurrent) {
endFlag = true
break
}
}
// early stop
if (endFlag === true) {
break
}
}
for (let i = 0, len = this.chartDetailData.xAxis.length; i < len; i++) {
@@ -440,7 +448,7 @@ export default {
...mapActions('replay', [types.meta.init]),
...mapActions('replay', [types.rec.init, types.rec.intoData, types.rec.initDisplaySetting, types.rec.updateDisplaySetting]),
...mapMutations('replay', [types.chart.updateAxisLabel]),
...mapMutations('replay', [types.chart.updateAxisLabel, types.chart.updateChartSampling]),
...mapMutations('replay', [types.meta.updateByIndex]),
...mapGetters('replay', [types.meta.getAll]),
@@ -459,12 +467,12 @@ export default {
console.log(this.metaList)
console.log(this.dataInfo)
await this.register()
this.drawOverviewData()
this.drawDetailData()
await this.drawInitData()
this.dataZoomTimer = setInterval(this.checkDataZoom, 1000)
this.$refs.top_control_panel.init()
this.$refs.set_min_max.init()
this.turnOnAxisEventHandler('click', 'yAxis.axisLabel', this.showMinMaxPanel, { axis: 'y' })
console.log(this.chartOverviewData)
console.log(this.chartDetailData)
},
beforeDestroy () {
@@ -483,21 +491,3 @@ export default {
},
}
</script>
<style lang="scss">
.area-style {
height: auto;
overflow-y: hidden;
overflow-x: hidden;
@include media-breakpoint-only(xl) {
height: 93vh;
overflow-y: scroll;
overflow-x: hidden;
}
}
.layout.gutter--xl .row .flex {
padding: 0.75rem 0.5rem 0.1rem 0.5rem;
}
</style>
@@ -107,7 +107,7 @@ export default {
changeAxis () {
if (this.deviceName === 'neulive') {
// for elite now, cuz neulive now only have v-t graph, for future neulive replay need to adjust'
console.log('(Last updated: 2021/06/19)')
// console.log('(Last updated: 2021/06/19)')
} else if (this.deviceName === 'elite') {
if (this.axis === 'x') {
this.axisXDataType = this.channelFormat
@@ -127,10 +127,10 @@ export default {
} else if (this.deviceName === 'elite') {
seriesData = []
const cycleNum = this.metaInfo.parameter_set.CYCLE_NUMBER
const isThisModeHasCycle = this.GLOBAL.hasCycleMode(this.metaInfo.parameter_set.MODE)
let startTime
let endTime
if (cycleNum > 0 && this.axisXDataType.description !== 'Time') { // if有cycle的mode且目前x軸不為時間,抓出第一圈所在的時間段,初始化時先畫第一圈就好,加快初始時間
if (isThisModeHasCycle === true && this.axisXDataType.description !== 'Time') { // if有cycle的mode且目前x軸不為時間,抓出第一圈所在的時間段,初始化時先畫第一圈就好,加快初始時間
const cycleDataID = 3 // recorded cycle 所在的channel index
const dataCycleList = this.recInfo[this.metaID][cycleDataID][this.currentScale]
const cycleDataList = []
@@ -71,20 +71,8 @@ export default {
deleteFile: async function () {
for (let i = 0; i < this.deleteInfoList.length; i++) {
const deleteInfo = this.deleteInfoList[i]
for (const item of deleteInfo.rawChannelAndIDArrays) {
const channel = item[0]
const id = item[1]
await api.raw.update(channel, id, { deleted: true })
await api.raw.clearDel(channel, id)
}
for (const item of deleteInfo.miniChannelAndIDArrays) {
const channel = item[0]
const id = item[1]
await api.mini.update(channel, id, { deleted: true })
await api.mini.clearDel(channel, id)
}
await api.meta.update(deleteInfo.metaID, { deleted: true })
await api.meta.clearDel(deleteInfo.metaID)
await api.meta.clearDel()
}
await this.deleteFileDone()
},
@@ -32,9 +32,10 @@ export default {
downloadCSV: async function () {
for (let i = 0; i < this.metaList.length; i++) {
const meta = this.metaList[i]
window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/download/neulive/csv/' + meta.id + '/raw/true', 'raw')
if (JSON.parse(meta.channels).some(el => el >= 256)) {
window.open('http://' + location.href.split('/')[2].split(':')[0] + '/#/download/neulive/csv/' + meta.id + '/acc/true', 'acc')
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)
}
}
},
@@ -80,7 +80,7 @@ export default {
// [TODO] 0: not sure future UX design
const meta = this.metaList[0]
file.clearFileList()
const files = await api.meta.getCollectionByParent('folder', meta.parent.folder[0])
const files = await api.meta.getByParent('folder', meta.parent.folder[0])
file.updateFileList(files)
const tmpFiles = file.getFileList()
for (let i = 0; i < tmpFiles.length; i++) {
@@ -0,0 +1,96 @@
<template>
<div>
<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/>
<p :style="{color: this.$themes.danger}" class="display-6"> Threshold </p>
<threshold/>
<br/>
<p class="display-6"> Waveform </p>
<wave-length/>
<prethreshold/>
<refractory-period/>
<detect-button @loadChartData="loadChartData"/>
</va-card>
</div>
</template>
<script>
import Channels from '@/components/analysis/spike/item/Channels.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'
import RefractoryPeriod from '@/components/analysis/spike/item/RefractoryPeriod.vue'
import Threshold from '@/components/analysis/spike/item/Threshold.vue'
import WaveLength from '@/components/analysis/spike/item/WaveLength.vue'
import DetectButton from '@/components/analysis/spike/item/DetectButton.vue'
export default {
name: 'control-panel',
components: {
Channels,
CutoffFreq,
FilterOrder,
Prethreshold,
RefractoryPeriod,
Threshold,
WaveLength,
DetectButton,
},
props: {
},
async created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
init () {
},
refreshChart (options) {
this.$emit('refreshChart', options)
},
loadChartData () {
this.$emit('loadChartData')
},
},
// use watch because va-select with @change will have bug
watch: {
},
mounted () {
},
}
</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>
+354
View File
@@ -0,0 +1,354 @@
<template>
<div>
<div class="row row-equal">
<div class="flex md12 xl3 xs12 sm12">
<div class="row">
<div class="flex sm12 xl12 md12 xs12">
<va-accordion>
<va-collapse class="mb-2" :isOpenDefault="true">
<span slot="header">Control Panel</span>
<div slot="body">
<div>
<control-panel :ref="'control_panel'" @loadChartData="drawSpikeData"/>
</div>
</div>
</va-collapse>
</va-accordion>
</div>
</div>
</div>
<div :class="chartAreaClass">
<div class="row" >
<div class="flex sm12 xl12 md12 xs12">
<spike-overview-chart :ref="'chart_overview'" :cardheight="'30vh'" :data="chartOverviewData"/>
<spike-chart :ref="'chart_detail'" :cardheight="'60vh'" :data="chartDetailData"/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations } from 'vuex'
import SpikeOverviewChart from './SpikeOverviewChart.vue'
import SpikeChart from './SpikeChart.vue'
import ControlPanel from './ControlPanel.vue'
import api from '@/data/api/index'
import types from '@/store/modules/analysis/spike/types'
import { mapFields } from 'vuex-map-fields'
import newSeries from '@/factories/chart/seriesFactory'
export default {
name: 'spike-data-replay',
components: {
SpikeOverviewChart,
SpikeChart,
ControlPanel,
},
computed: {
...mapFields('spike', [
'chartData',
'chartOverviewData',
'chartDetailData',
'metaList',
'selectedChannel',
'cutoffFreq',
'selectedThreshold',
'upperThreshold',
'lowerThreshold',
'waveLength',
'preThreshold',
'refractoryPeriod',
]),
},
props: {
},
async created () {
this.chartOverviewData = await this.initChart({
chartAllData: this.chartData,
chartID: 0,
gridLength: 1,
type: 'overview',
device: 'neulive',
mode: 0,
})
this.chartDetailData = await this.initChart({
chartAllData: this.chartData,
chartID: 1,
gridLength: 1,
type: 'detail',
device: 'neulive',
mode: 0,
})
},
data () {
return {
dataInfo: {},
maxTime: 0,
chartAreaClass: ['flex', 'xs12', 'sm12', 'md12', 'xl9', 'pa-0'],
}
},
mqtt: {
},
methods: {
showChartLoading: function (chart) {
const loadStyle = {
text: '',
color: '#cccccc',
maskColor: 'rgba(255, 255, 255, 0.8)',
zlevel: 0,
}
switch (chart) {
case 'overview':
this.$refs.chart_overview.showLoading(loadStyle)
break
case 'detail':
this.$refs.chart_detail.showLoading(loadStyle)
break
default:
this.$refs.chart_overview.showLoading(loadStyle)
this.$refs.chart_detail.showLoading(loadStyle)
break
}
},
hideChartLoading: function (chart) {
switch (chart) {
case 'overview':
this.$refs.chart_overview.hideLoading()
break
case 'detail':
this.$refs.chart_detail.hideLoading()
break
default:
this.$refs.chart_overview.hideLoading()
this.$refs.chart_detail.hideLoading()
break
}
},
register: async function () {
const gridLength = 1
this.updateGridListChart({
chartAllData: this.chartData,
chartID: this.chartDetailData.chartID,
gridLength: gridLength,
})
const register = this.metaList.map(meta => {
this.registerChart({
chartAllData: this.chartData,
chartID: this.chartOverviewData.chartID,
gridIndex: 0,
deviceNameX: 'Time',
deviceNameY: 'Neulive',
deviceIDY: meta.name,
channelY: this.selectedChannel,
})
this.registerChart({
chartAllData: this.chartData,
chartID: this.chartDetailData.chartID,
gridIndex: 0,
deviceNameX: 'Time',
deviceNameY: 'Neulive',
deviceIDY: meta.name,
channelY: this.selectedChannel,
})
if (this.maxTime < parseInt(meta.time_duration)) {
this.maxTime = parseInt(meta.time_duration)
}
})
await Promise.all(register)
for (let i = 0; i < 2; i++) {
// ID 0 = overview, ID 1 = detail
this.updateAxisLabelChart({
chartID: i,
axis: 'x',
axisIndex: 0,
channel: this.selectedChannel,
})
this.updateAxisLabelChart({
chartID: i,
axis: 'y',
axisIndex: 0,
channel: this.selectedChannel,
})
this.updateDataZoomChart({
chartAllData: this.chartData,
chartID: i,
})
}
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
},
drawSpikeData: async function () {
this.showChartLoading('overview')
this.showChartLoading('detail')
for (const meta in this.dataInfo) {
let metaInfo
this.metaList.forEach(_meta => {
if (parseInt(_meta.id) === parseInt(meta)) {
metaInfo = _meta
}
})
const sampleRate = (800000 / metaInfo.parameter_set.ADC_CLOCK) / JSON.parse(metaInfo.channels).length // Hz
const period = 1e6 / sampleRate
let thresholdValue
if (this.selectedThreshold === 'over') {
thresholdValue = this.upperThreshold
} else if (this.selectedThreshold === 'bellow') {
thresholdValue = this.lowerThreshold
}
const randomColorArray = []
// [Region] whole processed signal
const allSpikeData = await api.analysis.spikeDetect({
id: meta,
channel: this.selectedChannel,
cut_off_freq: this.cutoffFreq,
threshold: [
`'${this.selectedThreshold}'`,
thresholdValue,
],
waveForm: [this.waveLength, this.preThreshold, this.refractoryPeriod],
ret_data_type: 'all',
})
this.chartOverviewData.series[0].data.length = 0
this.chartOverviewData.color = ['#000000'] // black
const splitData = allSpikeData.data.split('\n')
const allTimeArray = splitData[0].split(' ')
const allTimeArrayLength = allTimeArray.length
const allSignalArray = splitData[1].split(' ')
const allTimeMarkIndexArray = splitData[2].split(' ')
for (let i = 0; i < allTimeArrayLength; i++) {
this.chartOverviewData.series[0].data.push([parseInt(allTimeArray[i]), allSignalArray[i]])
}
this.chartOverviewData.xAxis[0].min = parseInt(allTimeArray[0])
this.chartOverviewData.xAxis[0].max = parseInt(allTimeArray[allTimeArrayLength - 1])
this.chartOverviewData.xAxis[0].axisLabel.show = true
this.chartOverviewData.series[0].markArea.data.length = 0
for (let i = 0; i < allTimeMarkIndexArray.length; i++) {
// generate color array for overview markArea and detail chart series
const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16) // ref: https://css-tricks.com/snippets/javascript/random-hex-color/
randomColorArray.push(randomColor)
if (Number.isInteger(parseInt(allTimeMarkIndexArray[i]))) { // cuz maybe is '' if no any match after filtering
const timeMarkIndex = parseInt(allTimeMarkIndexArray[i])
const start = parseInt(this.chartOverviewData.series[0].data[timeMarkIndex - parseInt(this.preThreshold / period)][0])
const end = parseInt(this.chartOverviewData.series[0].data[timeMarkIndex + parseInt(this.refractoryPeriod / period)][0])
this.chartOverviewData.series[0].markArea.data.push([
{
itemStyle: {
color: randomColor,
},
name: String(i + 1),
xAxis: start,
},
{
xAxis: end,
},
])
}
}
// [EndRegion]
// [Region] each detailed signal piece
const spikeData = await api.analysis.spikeDetect({
id: meta,
channel: this.selectedChannel,
cut_off_freq: this.cutoffFreq,
threshold: [
`'${this.selectedThreshold}'`,
thresholdValue,
],
waveForm: [this.waveLength, this.preThreshold, this.refractoryPeriod],
ret_data_type: 'partial',
})
for (let i = 0; i < this.chartDetailData.series.length; i++) {
this.chartDetailData.series[i].name = ''
this.chartDetailData.series[i].data.length = 0
}
const detailSignalArray = Object.values(spikeData.data)
if (detailSignalArray.length <= 8) {
this.chartDetailData.tooltip.show = true
} else {
this.chartDetailData.tooltip.show = false
}
this.chartDetailData.legend.data.length = 0
this.chartDetailData.legend.type = 'scroll'
this.chartDetailData.legend.top = '0.25%'
// this.chartDetailData.legendpageButtonPosition = 'start'
this.chartDetailData.animation = false
this.chartDetailData.color = []
for (let i = 0; i < detailSignalArray.length; i++) {
const detailSignal = detailSignalArray[i].split(' ')
const arrayLength = detailSignal.length
const step = parseInt(this.waveLength) / (arrayLength - 1)
const xAxisIndex = 0
const yAxisIndex = 0
const legendName = 'T_' + String(i + 1)
if (this.chartDetailData.series.length <= i) {
const _series = newSeries('line', legendName, xAxisIndex, yAxisIndex, [-1, -1], [metaInfo.name, this.selectedChannel], null, null)
this.chartDetailData.series.push(_series)
}
for (let j = 0; j < arrayLength; j++) {
this.chartDetailData.series[i].data.push([j * step, detailSignal[j]])
}
// push legendName
this.chartDetailData.legend.data.push(legendName)
this.chartDetailData.series[i].name = legendName
// set legend and series color
this.chartDetailData.color.push(randomColorArray[i])
}
this.chartDetailData.xAxis[0].min = 0
this.chartDetailData.xAxis[0].max = parseInt(this.waveLength)
this.chartDetailData.xAxis[0].axisLabel.show = true
// [EndRegion]
}
this.hideChartLoading()
console.log(this.chartOverviewData)
console.log(this.chartDetailData)
},
...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]),
...mapMutations('spike', [types.chart.updateAxisLabel, types.chart.updateChartSampling]),
...mapMutations('spike', [types.meta.updateByIndex]),
...mapGetters('spike', [types.meta.getAll]),
...mapGetters('spike', [types.rec.getAll]),
},
// use watch because va-select with @change will have bug
watch: {
},
async mounted () {
const metaIDList = this.$route.params.metaList.split('-')
await this.initMeta({ metaIDList: metaIDList, api: api })
this.metaList = this.getAllMeta()
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()
},
beforeDestroy () {
this.$refs.chart_detail.clear()
for (const id in this.chartData) {
for (const _series of this.chartData[id].series) {
_series.length = 0
}
delete this.chartData[id]
}
this.clearChart({ chartAllData: this.chartData })
},
}
</script>
@@ -0,0 +1,110 @@
<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>
</va-card>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import types from '@/store/modules/analysis/spike/types'
// import { chartData } from '../../../../data/task/ChartsManagement'
export default {
name: 'spike-replay-chart',
components: {
},
props: {
data: {
type: Object,
default: function () {
return {}
},
},
cardclass: {
type: Array,
default: function () {
return ['xs12', 'md12', 'xl12', 'sm12']
},
},
cardheight: {
type: String,
default: '82vh',
},
cardwidth: {
type: String,
default: 'auto',
},
},
created () {
},
data () {
return {
}
},
computed: {
},
watch: {
},
methods: {
resize: function () {
if (this.$refs.chart_ref != null) {
this.$refs.chart_ref.resize()
}
},
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,
axis: attr.axis,
status: 'on',
})
// doc: https://echarts.apache.org/zh/api.html#echartsInstance.on
this.$refs.chart_ref.chart._zr.on(eventName, query, handler)
},
turnOffAxisEventHandler (eventName, handler, attr) {
const chartID = 1
this.updateAxisEventHandlerChart({
chartID: chartID,
axis: attr.axis,
status: 'off',
})
// doc: https://echarts.apache.org/zh/api.html#echartsInstance.off
this.$refs.chart_ref.chart._zr.off(eventName, handler)
},
showLoading (options) {
this.$refs.chart_ref.showLoading(options)
},
hideLoading () {
this.$refs.chart_ref.hideLoading()
},
clear () {
this.$refs.chart_ref.clear()
},
...mapMutations('spike', [types.chart.updateAxisEventHandler]),
},
async mounted () {
this.resizeTimer = setInterval(this.resize, 1000)
},
destroyed () {
clearInterval(this.resizeTimer)
},
}
</script>
<style lang='scss'>
.va-card {
&__body {
padding-top: 0.25rem;
}
}
</style>
@@ -0,0 +1,86 @@
<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>
</va-card>
</div>
</template>
<script>
// import { chartData } from '../../../../data/task/ChartsManagement'
export default {
name: 'spike-overview-chart',
components: {
},
props: {
data: {
type: Object,
default: function () {
return {}
},
},
cardclass: {
type: Array,
default: function () {
return ['xs12', 'md12', 'xl12', 'sm12']
},
},
cardheight: {
type: String,
default: '82vh',
},
cardwidth: {
type: String,
default: 'auto',
},
},
created () {
},
data () {
return {
}
},
computed: {
},
watch: {
},
methods: {
resize: function () {
if (this.$refs.chart_ref != null) {
this.$refs.chart_ref.resize()
}
},
mergeOptions (options) {
this.$refs.chart_ref.mergeOptions(options)
},
updateZoom: function (e) {
this.$emit('dataZoomEvent', e)
},
showLoading (options) {
this.$refs.chart_ref.showLoading(options)
},
hideLoading () {
this.$refs.chart_ref.hideLoading()
},
clear () {
this.$refs.chart_ref.clear()
},
},
async mounted () {
this.resizeTimer = setInterval(this.resize, 1000)
},
destroyed () {
clearInterval(this.resizeTimer)
},
}
</script>
<style lang='scss'>
.va-card {
&__body {
padding-top: 0.25rem;
}
}
</style>
@@ -0,0 +1,43 @@
<template>
<div>
<va-button-toggle small flat v-model="selectedChannel" :options="selectedChannelOptions" @click.native="reloadData(selectedChannel)"/>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'Channels',
components: {
},
computed: {
...mapFields('spike', [
'selectedChannel',
'selectedChannelOptions',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
reloadData (val) {
this.selectedChannel = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,57 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<p>Cut-off Freq<p/>
</div>
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<va-input
@mouseleave.native="itemChange(cutoffFreq)"
@keyup.enter="itemChange(cutoffFreq)"
@blur="itemChange(cutoffFreq)"
v-model="cutoffFreq"
class="mb-0"
>
<p slot="append" style="margin-right: 0;" small>
Hz
</p>
</va-input>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'CutoffFreq',
components: {
},
computed: {
...mapFields('spike', [
'cutoffFreq',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
itemChange (val) {
this.cutoffFreq = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,49 @@
<template>
<div>
<va-button class="right" small @click="startDetect">
Start Detect
</va-button>
</div>
</template>
<script>
import types from '@/store/modules/analysis/spike/types'
import { mapFields } from 'vuex-map-fields'
import { mapMutations } from 'vuex'
export default {
name: 'Channels',
components: {
},
computed: {
...mapFields('spike', [
'chartDetailData',
'selectedChannel',
'waveLength',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
startDetect () {
this.$emit('loadChartData')
},
...mapMutations('spike', [types.chart.updateAxisLabel]),
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,54 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<p>Filter order<p/>
</div>
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<va-input
@mouseleave.native="itemChange(filterOrder)"
@keyup.enter="itemChange(filterOrder)"
@blur="itemChange(filterOrder)"
v-model="filterOrder"
class="mb-0"
>
</va-input>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'FilterOrder',
components: {
},
computed: {
...mapFields('spike', [
'filterOrder',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
itemChange (val) {
this.filterOrder = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,57 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<p>Prethreshold<p/>
</div>
<div class="flex sm6 xl6 md6 xs6 px-0 mx-0">
<va-input
@mouseleave.native="itemChange(preThreshold)"
@keyup.enter="itemChange(preThreshold)"
@blur="itemChange(preThreshold)"
v-model="preThreshold"
class="mb-0"
>
<p slot="append" style="margin-right: 0;" small>
µs
</p>
</va-input>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'Prethreshold',
components: {
},
computed: {
...mapFields('spike', [
'preThreshold',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
itemChange (val) {
this.preThreshold = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,57 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<div class="flex sm7 xl7 md7 xs7 px-0 mx-0">
<p>Refractory period<p/>
</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)"
v-model="refractoryPeriod"
class="mb-0"
>
<p slot="append" style="margin-right: 0;" small>
µs
</p>
</va-input>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'RefractoryPeriod',
components: {
},
computed: {
...mapFields('spike', [
'refractoryPeriod',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
itemChange (val) {
this.refractoryPeriod = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,83 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<va-radio-button
v-for="(option, index) in options"
:key="index"
v-model="selectedThreshold"
:option="option"
:label="`${option}`"
/>
<va-input
v-if="selectedThreshold === 'over'"
@mouseleave.native="itemChange(upperThreshold, 'upper')"
@keyup.enter="itemChange(upperThreshold, 'upper')"
@blur="itemChange(upperThreshold, 'upper')"
v-model="upperThreshold"
class="mb-0"
>
<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"
class="mb-0"
>
<p slot="append" style="margin-right: 0;" small>
µV
</p>
</va-input>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'Threshold',
components: {
},
computed: {
...mapFields('spike', [
'selectedThreshold',
'upperThreshold',
'lowerThreshold',
]),
},
props: {
},
created () {
},
data () {
return {
options: [
// 'Auto',
'over',
'bellow',
],
}
},
mqtt: {
},
methods: {
itemChange (val, type) {
if (type === 'upper') {
this.upperThreshold = val
} else if (type === 'lower') {
this.lowerThreshold = val
}
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,57 @@
<template>
<div class="row flex sm12 xl12 md12 xs12 align--center my-0 py-0">
<div class="flex sm7 xl7 md7 xs7 px-0 mx-0">
<p>Waveform length<p/>
</div>
<div class="flex sm5 xl5 md5 xs5 px-0 mx-0">
<va-input
@mouseleave.native="itemChange(waveLength)"
@keyup.enter="itemChange(waveLength)"
@blur="itemChange(waveLength)"
v-model="waveLength"
class="mb-0"
>
<p slot="append" style="margin-right: 0;" small>
µs
</p>
</va-input>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'WaveLength',
components: {
},
computed: {
...mapFields('spike', [
'waveLength',
]),
},
props: {
},
created () {
},
data () {
return {
}
},
mqtt: {
},
methods: {
itemChange (val) {
this.waveLength = val
},
},
async mounted () {
},
destroyed () {
},
watch: {
},
}
</script>
@@ -0,0 +1,37 @@
<template>
<div>
<span> Resample: {{ currentScale }} to 1 </span>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields'
export default {
name: 'show-resample-rate',
components: {
},
props: {
},
created () {
},
data () {
return {
}
},
computed: {
...mapFields('spike', [
'currentScale',
]),
},
watch: {
},
methods: {
},
mounted () {
},
destroyed () {
},
}
</script>
+140 -539
View File
@@ -1,143 +1,26 @@
<template>
<div>
<va-modal
class="flex sm12 xl12 md12 xs12 pa-0 ma-0"
v-model="showDownload"
size="small"
:hideDefaultActions="true"
:mobileFullscreen="false"
>
<template slot="header">
<div class="row mb-2">
<va-icon name="fa fa-download mr-3 " color="primary"/>
<h5 class="mt-0 mb-0">{{'Download'}}</h5>
</div>
</template>
<div class="downloadOptionsList">
<template v-for="(downloadOptions, i) in downloadOptionsList">
<va-accordion :key="downloadOptions.filename">
<va-collapse>
<va-item :key="downloadOptions.filename" slot="header" clickable>
<va-item-section avatar>
<va-item-label>
{{ downloadOptions.filename }}
</va-item-label>
</va-item-section>
<va-item-section>
<!-- <va-item-label>
{{ downloadInfo.minTime }}
</va-item-label>
<va-item-label caption>
{{ downloadInfo.maxTime }}
</va-item-label> -->
</va-item-section>
<va-item-section v-if="downloadOptions.download.status == 'idle'" side>
<va-icon name="fa fa-play" color="gray" @click.native.stop="confirmDownload(downloadOptions.metaPath)"/>
</va-item-section>
<va-item-section v-else side class="md4 offset--md8">
<div class="row flex sm12 xl12 md12 xs12">
<div class="flex-center spinner-box">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#6c7fee'"
:size="20"
>
</fulfilling-bouncing-circle-spinner>
<span class="ml-3">{{loaded}}</span>
</div>
</div>
</va-item-section>
</va-item>
<div slot="body">
<div v-if="downloadOptions.download.status == 'downloading'" class="row flex sm12 xl12 md12 xs12">
<div 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>
</div>
<div v-else>
<div class="mb-5">
<va-item class="flex sm12 xl12 md12 xs12 mr-1 pa-0">
<va-icon
class="flex sm1 xl1 md1 xs1 start "
name="glyphicon glyphicon-filter"
size="1.0rem"
:fixedWidth = "Boolean('true')"
/>
<div class="flex sm11 xl11 md11 xs11 row">
<div v-for="(item,index) in downloadOptions.channel" :key="item.channel" class="filter-button">
<va-button
class="mx-1 px-1"
v-if="item.checked"
@click="channelUnchecked(index,downloadOptions.metaPath)"
small
>
{{item.alias}}
</va-button>
<va-button
class="mx-1 px-1"
v-else
@click="channelChecked(index,downloadOptions.metaPath)"
small outline
>
{{item.alias}}
</va-button>
</div>
</div>
</va-item>
</div>
<div class="mb-5">
<va-item class="flex sm12 xl12 md12 xs12 pa-0">
<va-icon
class="flex sm1 xl1 md1 xs1 mr-4"
:fixedWidth="true"
name="va-icon glyphicon glyphicon-time"
size="1.0rem"
/>
<div class="flex">
<va-slider
class="flex sm11 xl11 md11 xs11 filter-button"
value-visible
range
v-model="downloadOptions.timeRange"
@click.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@mouseup.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@mouseleave.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@touchend.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
:min="downloadOptions.minTime"
:max="downloadOptions.maxTime"
/>
<span> {{ transTime(downloadOptions.timeRange) }} </span>
</div>
</va-item>
</div>
</div>
</div>
</va-collapse>
</va-accordion>
<va-list-separator v-if="i < downloadOptionsList.length - 1" :key="'separator' + downloadOptions.filename" />
</template>
<div class="row flex sm12 xl12 md12 xs12" style="height: 100vh;">
<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>
</va-modal>
</div>
</div>
</template>
<script>
import { file } from '../../data/file/File'
import { download } from '../../data/download/Download'
import { exportData } from '../../data/export/export'
import api from '@/data/api/index'
import types from '@/store/modules/download/types'
import { mapActions, mapMutations } from 'vuex'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
import { mapFields } from 'vuex-map-fields'
import newDownload from '@/factories/file/downloadFactory'
import newChannel from '@/factories/file/channelFactory'
export default {
name: 'Download',
@@ -146,387 +29,17 @@ export default {
},
data: () => {
return {
showDownload: false,
controllerList: [],
downloadList: [],
downloadListRecord: [],
downloadOptionsList: [],
// downloadInfo: {
// metaPath: '',
// previousPath: '',
// filenameWithType: '',
// filename: '',
// channel: [],
// timeRange: [0, 0],
// minTime: 0,
// maxTime: 0,
// status: 'idle',
// device: '',
// },
currentlyDownloadInfo: {
metaPath: '',
previousPath: '',
filenameWithType: '',
filename: '',
channel: [],
timeRange: [0, 0],
minTime: 0,
maxTime: 0,
download: {
status: 'idle',
rate: 0,
},
device: '',
},
estimatedTime: 0,
fileSegmentIndex: 0,
fileSegmentMax: 0,
totoalFileSize: 0,
loadingPercent: 0,
loadTime: 0,
interval: null,
data: '',
}
},
computed: {
// transTime (min,max) {
// return this.getTimeInFormat(min) + ' - ' + this.getTimeInFormat(max)
// },
loaded () {
return this.loadingPercent + '%'
},
...mapFields('download', [
'metaList',
'downloadInfo',
'metaInfo',
]),
},
// mqtt: {
// async '+/file_segment/+' (data, topic) {
// console.log('file_segment')
// console.log(String.fromCharCode.apply(null, data))
// // console.log(navigator.connection.downlink)
// // console.log(this.fileNum)
// // console.log(this.downloadOptionsList.length)
// // console.log(JSON.parse(String.fromCharCode.apply(null, data)).data.file_size)
// if (this.downloadOptionsList.length > 0) {
// await download.updateFileSegmentInfo(String.fromCharCode.apply(null, data))
// this.fileSegmentMax = download.getFileSegmentMaxIndex()
// this.checkFileSegmentGetOrNot()
// }
// },
// async '+/file_segment_get/+' (data, topic) {
// console.log('file_segment_get')
// console.log(JSON.parse(new TextDecoder('utf-8').decode(data)))
// if (this.downloadOptionsList.length > 0) {
// const segmentData = atob(JSON.parse(new TextDecoder('utf-8').decode(data)).data.segment)
// this.data += segmentData
// this.fileSegmentIndex += 1
// this.checkFileSegmentGetOrNot()
// }
// },
// },
methods: {
transTime (timeRange) {
return this.getTimeInFormat(timeRange[0]) + ' - ' + this.getTimeInFormat(timeRange[1])
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
showDownloadModal: function () {
this.showDownload = true
},
channelChecked: function (channelIndex, path) {
download.getDownloadOptions(path).channel[channelIndex].checked = true
this.refresh()
},
channelUnchecked: function (channelIndex, path) {
download.getDownloadOptions(path).channel[channelIndex].checked = false
this.refresh()
},
getTimeInFormat: function (time) {
if (time < 60) {
const second = time
return `${second}s`
} else if (time < 3600) {
const second = time % 60
const minute = parseInt(time / 60)
return `${minute}m${second}s`
} else {
const hour = parseInt(time / 3600)
const hourRemain = time % 3600
const minute = parseInt(hourRemain / 60)
const second = hourRemain % 60
return `${hour}h${minute}m${second}s`
}
},
getCurrentFullPathName: function () {
return this.currentlyDownloadInfo.previousPath + '/' + this.currentlyDownloadInfo.filenameWithType
},
downloading: async function (info) {
this.$emit('download')
},
addDownloadTask: async function (info) {
this.downloading()
/*
** download all
*/
if (parseInt(info.time_duration) < 18e8) {
const rawData = await download.getAllRawDatas(info)
exportData.exportCSV(info, rawData)
} else {
for (const channelID in info.raw_data) {
const rawDataIDList = info.raw_data[channelID]
while (rawDataIDList.length > 1000) {
const _list = rawDataIDList.splice(0, 1000)
const rawData = await download.getIndexRawDatas(info, channelID, _list)
exportData.exportCSV(info, rawData)
}
const rawData = await download.getIndexRawDatas(info, channelID, rawDataIDList)
exportData.exportCSV(info, rawData, channelID)
rawData[channelID].length = 0
// const rawData = await download.getAllRawDatas(info)
// console.timeEnd('download')
}
}
// console.time('download')
// const rawData = await download.getAllRawDatas(info)
// console.timeEnd('download')
// exportData.exportCSV(info, rawData)
/*
** download split
*/
this.downloading()
},
_addDownloadTask: function (downloadPath) {
if (Array.isArray(downloadPath)) {
downloadPath.forEach(path => {
this.downloadList.push(path.path)
this.updateDownloadInfo(path.path)
})
} else {
this.downloadList.push(downloadPath)
this.updateDownloadInfo(downloadPath)
}
this.confirmDownload()
},
refresh: function () {
this.downloadOptionsList = download.getDownloadOptionsList()
console.log(this.downloadOptionsList)
},
confirmDownload: function (path) {
console.log('confirmDownload')
console.log(download.getDownloadOptionsList())
console.log(download.isFileDownloading())
console.log(download.downloadTaskRemain())
if (!download.isFileDownloading() && download.downloadTaskRemain()) {
const downloadOptionsIndex = download.getDownloadOptionsList().findIndex(downloadInfo => downloadInfo.metaPath === path)
// this.downloadBlockList[downloadInfoIndex].status = 'downloading'
this.currentlyDownloadInfo = download.getDownloadOptionsList()[downloadOptionsIndex]
this.currentlyDownloadInfo.download.status = 'downloading'
console.log(this.currentlyDownloadInfo)
console.log(download.getDownloadOptionsList())
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.calculateFileSize(this.currentlyDownloadInfo.metaPath)
console.log('getactualsize')
this.subfileListRemain()
}
// if (this.downloadList.length > 0 && this.currentlyDownloadInfo.status === 'idle') {
// this.showDownload()
// this.updateDownloadSubFileList(this.downloadList[0])
// } else {
// /* TODO If file downloading then waiting */
// console.log('no download task or file downloading')
// }
},
_confirmDownload: function () {
console.log('confirmDownload')
console.log(download.getDownloadOptionsList())
// if (this.downloadList.length > 0 && this.currentlyDownloadInfo.status === 'idle') {
// this.showDownload()
// this.updateDownloadSubFileList(this.downloadList[0])
// } else {
// /* TODO If file downloading then waiting */
// console.log('no download task or file downloading')
// }
},
downloadAllSubFileList: async function (path) {
this.currentlyDownloadInfo = this.downloadInfo
download.downloadAllSubFileList(path)
this.subfileListRemain()
},
// updateDownloadInfo: function (path) {
// console.log(path)
// const metaInfo = file.getMetaInfo(path)
// const downloadInfoIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
// if (downloadInfoIndex === -1) {
// this.downloadInfo.library = metaInfo.configuration._LIBRARY_
// this.downloadInfo.channel = metaInfo.channel.map(ch => {
// let channel = ''
// if (this.downloadInfo.library.includes('Elite')) {
// channel = (ch === 0) ? 'current' : (ch === 1) ? 'voltage' : (ch === 2) ? 'resistance' : ''
// } else {
// channel = 'channel' + ch
// }
// return {
// alias: channel,
// channel: ch,
// checked: true,
// }
// })
// this.downloadInfo.metaPath = path
// this.downloadInfo.previousPath = path.split('/').slice(0, -1).join('/')
// this.downloadInfo.filename = path.split(/[\/.]+/).slice(0, -1).pop()
// this.downloadInfo.minTime = parseInt([...new Set(metaInfo['start-time'])][0] / 1000)
// this.downloadInfo.maxTime = parseInt(metaInfo.time_duration / 1000)
// this.downloadInfo.timeRange = [this.downloadInfo.minTime, this.downloadInfo.maxTime]
// this.downloadInfo.status = 'idle'
// this.downloadBlockList.push(JSON.parse(JSON.stringify(this.downloadInfo)))
// } else {
// for (var item in this.downloadInfo) {
// this.downloadInfo[item] = this.downloadBlockList[downloadInfoIndex][item]
// }
// }
// },
updatecurrentlyDownloadInfo: function () {
const downloadInfoIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === this.currentlyDownloadInfo.metaPath)
if (downloadInfoIndex !== -1) {
for (var item in this.downloadBlockList[downloadInfoIndex]) {
this.downloadBlockList[downloadInfoIndex][item] = this.currentlyDownloadInfo[item]
}
}
},
updateMetaDownloadInfo: function () {
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
},
updateDownloadSubFileList: function (path) {
console.log('updateDownloadSubFileList')
const downloadOptionsIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
this.downloadBlockList[downloadOptionsIndex].status = 'downloading'
this.currentlyDownloadInfo = this.downloadBlockList[downloadOptionsIndex]
this.updatecurrentlyDownloadInfo()
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.subfileListRemain()
},
_updateDownloadSubFileList: function (path) {
console.log(path)
console.log(this.downloadInfo)
const downloadOptionsIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
this.downloadBlockList[downloadOptionsIndex].status = 'downloading'
this.currentlyDownloadInfo = this.downloadBlockList[downloadOptionsIndex]
// this.updatecurrentlyDownloadInfo()
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.subfileListRemain()
},
subfileListRemain: function () {
console.log('subfileListRemain')
if (download.checkDownloadSubFileList(this.currentlyDownloadInfo.metaPath)) {
this.currentlyDownloadInfo.filenameWithType = download.getFirstSubfile(this.currentlyDownloadInfo.metaPath)
} else {
this.loadingPercent = 100
console.log(this.currentlyDownloadInfo.filenameWithType + ' download complete')
console.log(performance.now())
this.exportCSV()
this.test(this.downloadDone)
// this.currentlyDownloadInfo.status = this.currentlyDownloadInfo.status = 'idle'
// this.updatecurrentlyDownloadInfo()
// this.loadingPercent = 0
}
},
checkFileSegmentGetOrNot: function () {
console.log('checkFileSegmentGetOrNot')
if (this.fileSegmentIndex >= this.fileSegmentMax) {
const subfile = file.shiftDownloadSubFileList(this.currentlyDownloadInfo.metaPath)
file.pushDownloadData(this.currentlyDownloadInfo.metaPath, subfile, this.data)
this.data = ''
this.fileSegmentIndex = 0
this.subfileListRemain()
} else {
}
},
exportCSV: function () {
exportData.exportCSV(this.currentlyDownloadInfo)
},
exportTXT: function () {
},
timeRangeChange: function (timeRange, path) {
download.getDownloadOptions(path).timeRange = timeRange
this.refresh()
},
calculateFileSize: function () {
this.totoalFileSize = file.getFileSize(this.currentlyDownloadInfo.metaPath)
// const allSubfile = download.getAllSubfile(this.currentlyDownloadInfo.metaPath)
// this.fileNum = allSubfile.length
const downlinkSpeed = this.getNetSpeed('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAABTVBMVEX////qQzU0qFNChfT7vAU/g/RZlPU5gPT7uQCxyPqXufn/vQDpNCLqQTMwp1D7uADpOCfqPS4fo0bpMiD8wADpLxwopUv0paD1q6b86ObpOiv4/Pn+9/b4xcH2ubX1r6rsXVIre/NwvoOf0qvO6NQzqUm/4cf5zMn739zrSz398O/sVEjvdWz2t7PvenP4ycb7wir96Lf+8tb8zFH93pj+9eH//PD92Yvw9v7+7cP80Xb95q6pxPnq9u1PsmigvvlunvaJyZiGrvjD1vvyl5HtamHoJgzwhH3zmpTtXlLvenHuYSzygCL2mxjsUzDwcSf0jx34phH8z2DxfVHX5P38x0KHvnDOtiGjsjJ0rkLhuRW4tCnS4PyMsDtZq0it2bhmmvbO5OE1noBft3U1pWA/jNc8lLk5nJNBieSSy58+j8t5wIo6mKY2o25FrmBHQXgmAAAIJUlEQVR4nO2b2V/bRhCAhbADxJLQYQvwgcE2PsAhEHK0SUxigoGkTdMj6ZXDTdw2JbT+/x9r+QDZllYra8da85vvKU/yfszszO5IEQQEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRDWpLfXSplbqysWq9n1Unk7HfaSmJFey6ycNipGwpDUAZKRSMxt7B6vl7fDXl5QyqvVHdVQk5o8N4osa0nJqDSOSzNrmc7sVlRVk8fl7J4dTbmRXQt7sf5JZ6onkkPknC3VxE52tiJZPtUMjcru0lI1dkthL5uaTMNI0kVvOJLGRnYmCmx2R/IXPpukVFnh3nG9IvkPn81R1bJhKxC5vWME8evHMRO2hitr1URQv65jospp81jRkgz8LJLaStgyDpSfqywC2ENWN7gL48rJpAXUGe3kVthKQ2w3DKZ+c9ZuPOWocZQqrHagHY4yNRuoBbqjaZyc444D90A35AQPmzFdlYD8uorht410Q4UTnJON0A3TOxA15pLE6nUXvOYRlI3wI9gAFZRCFxReQBaZufAjKBwDtgkeioyQZX4UtcFDIyz5PqrJsqYlk2oymdQ8hqhc7MHtih9Ba+4rVTaqp6fHZ2fHp7sNaxDuPk3lQVCo+iijmmQ0zjJr9ncx6e3Syq6WcBk6crAHhVXqTSirJ7sZ53l2unQsGQ6R5GAPCuUT2vCpz28RL7K3q+pIIDlo9B026EYWmlT1vuGVT1X707jYg8IKVauXjQbdDbZctd0wuYjgGtU7peTcOvUTM/KgcPFQZDqnNYo6Khu7fh6ZftENo8yHYCnhLajN+R3NZzsFh489KAg73jk6yZisXNE4EVz3boXG7iSjzrUKH4KC93EtcTzZkzmZAD9a/obsJ0tnYa8xEKm9SPxboqHEwZkrCPuLkUj8JSFRpdOwlxiQg0iH+PJ3boJqNewVBuTOYqSrGH/lLKjtcFIuJuZhz7Dj+L1jldHKYa8wIKnIJfEfHDJVoj+Kcsr+sk1xvG0kZ30T9uvMleNI25DV2fo2zYH7y5FhxZdDhgbfH/vQsL8YGVG0tw15J+z1BefHUcNIPPLqsvsbt8NeX2BSo35DbUPbCHt9wbkzFsJe2+iHkN/P0ai562gYiS9abUPeCHt5DDhwFOy3DZWT62sQ7u+5GVq3DY2bz3sm555zkvYUl38Ke3kM2F92N4wsPqJ8ytKNwLyGMnxAiGFk+T6t4WY0IJtLUIYPSYZ7tE9ZWpgPyMJNKMM3pCR9MD3D6FMgwftxUpLuT89wfh7KkMk2ZGG4CWR4j1RK49SPYWH4FYzh1yTDN1M1BGoXjwhZuvhwqobvYAxdzt09Q+pSysJw4VkIhnevvSHtmY2NIdChhnRom7Lh42tv+AQNJ4OjfRiG4XRraRiG0+2HQJWGnzMNVLcgnksPpmoI1PH5uVtAGXJ0PwQ6efNzx4e6PXEzpwG7AXMzawObYrCalwaftUWhDIkz72XqmffmAg0kwxtQhqT3FrH4z5RPeXeThiOC4cIRlCHh3VPsF0Wpsfyt14QgQh3aBPf3h7HYr6Jo5ln+FGm3QjV8wfXsHYv8Joqicsjyp46i7oZgzcLtPX7sd0W00IsMf4qwDcGG+oLLtxixt2IPpcXul95tuvuBvZixGP+eJhZ5Lw5gGMQj0jYEuv92GfsmKrb34VJQVNrMfmiBtA2Bzt1dRr9r6zQJ0YbeZPQ7pEoanYcrNMJIv+g2iSF0Rj2RVGZAt+Hwwa3XJIZQckx+hXh0Bd2GQ9V00CSGg8gkT28QduH8AtiXGD0ub1Cxtw6CIpOz2xPi7QPs2N2n3/RjsfcOft16mgr6E69JhRTwQ4wBB70M/eAs2DmeBu77xBwFT9JeSxxpEiNbsR7sB0jN3oKNBom98SYxohjokrFEOK/Ng1fSLo/GmwRDxWceEQS8V1whElI0aKI+I1aZTrsHu97bKepehqKem6yiegnCnkmvOPQMYqeiTtIXH296CEb/YO3iTME7iJ1M9n+V8qqinRDCzS+GyZkUivqFv0z9OO8pCDdGHKXmnaZWpio+wpi60D/d8FIEHEGNkqfIUyuMrQLd81J50RS3/vyLrDitXdiFotjQO9bybd16nmL+TVQEe+XkuCiqIHYdD5vk/Vi4UMzB32vrc9S9mi48nY5bH7o87TnquaKLZKpwruj2dNj67x/XMEancZyx0aKopzbJ1nmxVrN5pmq14nnL1EefoihfXBShPk9wpdam24qDlZu6Lh7mLur183r9Itc6FHXddLxDb/3rmKnA4xknaPr+WIDMHopC+PNsfXLqjNPOUYumf0U6ttrjbWNqp5kh6lCKytZo24B7ZUiG5vQ2GVuf5+2bcXrHtRFSfgqqT0V72wAecxMVD8EUTeXL5TwDfvpEUvTVM3wxaBvRKR64HRXBothpG91MhfvPeJSKcHuxd9sIW7CjmINqGt22Eb6gANgXLcWPYdt1aepA9UZRKK/Q4BREkM1otpl+hRSIWgsgUyedugKRZ52pSrD3HwDU2HZGU+RlC9rIk259/lDMOlcZOqCWY5Sq+iGHAexROGTgaJqsvssBoSgGdDTNcy4T1EYzQBwVU6nz0wPdKbbGpoR0fno7Pwt+FoW64jeQiuk+OeaSVDHnMhF10Zud8F3RkRSt2aiHXMdObDVnT69PoZlrm7qzpzUg1s12Ll+YpeR0IFUr5i9abd2iP/M2u/9ut+r5Ym3G7a5IpWqFYrOZt2g2i4VCLXVt3BAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAE4Yf/AZmODCZml4YgAAAAAElFTkSuQmCC', 3)
// this.totoalFileSize = (allSubfile.length) * 1000000
console.log(downlinkSpeed)
console.log(this.totoalFileSize)
this.estimatedTime = this.totoalFileSize / (downlinkSpeed * 1024)
if (this.totoalFileSize >= 1 * 1024 * 1024) {
console.log('size big')
this.estimatedTime = this.estimatedTime * 10
}
console.log(this.estimatedTime)
this.loadTime = parseInt((this.estimatedTime / 1000) % 60) * 100
console.log(this.loadTime)
this.doProgress()
// allSubfile.forEach(subfile => {
// console.log(subfile)
// this.currentlyDownloadInfo.filenameWithType = subfile
// })
},
doProgress () {
const step = this.estimatedTime / 100 * 1000
this.interval = setInterval(() => {
if (this.loadingPercent >= 99) {
console.log('complete')
clearInterval(this.interval)
this.estimatedTime = 0
} else {
this.loadingPercent++
}
}, step)
},
getNetSpeed (url, times) {
const connection = window.navigator.connection
if (connection && connection.downlink) {
return connection.downlink * 1024 / 8
}
},
test: async function (fn) {
return await setTimeout(async function () {
await fn()
}, 1000)
},
downloadDone: function () {
download.getDownloadOptionsList().shift()
this.loadingPercent = 0
this.refresh()
this.confirmDownload()
},
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
@@ -539,43 +52,131 @@ export default {
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
...mapActions('download',
[
types.raw.rawDataByID,
types.export.setHeader,
types.export.setTitle,
types.export.setData,
types.downloadStream.writerClose,
types.downloadStream.setDownloadStream,
],
),
...mapMutations('download',
[
types.download.setSampleRate,
types.download.setdownloadIdList,
],
),
},
watch: {
},
// watch: {
// loadingPercent(val) {
// if (val >= 100) {
// console.log('complete');
// clearInterval(this.interval)
// }
// }
// },
async mounted () {
await this.getControllerList()
this.pageMqttSub()
// TODO authorization
const format = this.$route.params.format
this.metaList = this.$route.params.metaList.split('-')
while (this.metaList.length !== 0) {
let meta
const metaID = this.metaList.pop()
const ret = await api.meta.getByID(metaID)
if (ret.status === 200) {
meta = ret.data[0]
}
if (meta !== undefined) {
this.metaInfo = meta
const channelArray = JSON.parse(this.metaInfo.channels)
const channelList = []
if (meta.device.library_name.indexOf('Neulive') >= 0) {
if (channelArray.some(el => el >= 256)) {
channelList.push(channelArray.filter(el => el < 256))
channelList.push(channelArray.filter(el => (el >= 256 && el <= 259)))
} else {
channelList.push(channelArray)
}
} else if (meta.device.library_name.indexOf('Elite') >= 0) {
channelList.push(channelArray)
}
while (channelList.length > 0) {
this.downloadInfo = newDownload()
this.downloadInfo.channel = channelList.pop()
// download format
this.downloadInfo.format = format
// download device and filename
if (meta.device.library_name.indexOf('Neulive') >= 0) {
this.downloadInfo.device = [meta.device.library_name.slice(0, 7), meta.device.library_name.slice(7)]
if (this.downloadInfo.channel.some(el => el >= 256 && el <= 259)) {
this.downloadInfo.filename = meta.name + '-accerlate-' + format
this.downloadInfo.size = parseInt(parseInt(meta.size) / 78)
} else {
this.downloadInfo.filename = meta.name + '-' + format
this.downloadInfo.size = parseInt(meta.size)
}
} 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)]
this.downloadInfo.filename = meta.name + '-' + format
this.downloadInfo.size = parseInt(meta.size)
}
// init
// get sampleRate && downloadList
for (const channel of this.downloadInfo.channel) {
this.downloadInfo.channelInfo[channel] = newChannel()
this.setSampleRateDownload({ channel: channel })
this.setdownloadIdListDownload({ channel: channel })
}
// create writer
this.setDownloadStream()
console.log(this.downloadInfo)
console.log(this.metaInfo)
// set Header
if (this.downloadInfo.format !== 'csv-edf') {
this.setHeaderExport()
}
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]
// console.log(rowIndex, _idx, rawID)
if (rawID !== undefined) {
await this.rawDataByIDRaw({
channel: channel,
rawID: rawID,
idx: _idx,
api: api,
})
}
}
}
this.setDataExport({ last: (rowIndex === this.downloadInfo.maxRows - 1) })
rowIndex += 1
}
this.writerCloseDownloadStream()
}
}
}
await new Promise(resolve => setTimeout(resolve, 2000)) // sleep
window.close()
},
destroyed () {
this.pageMqttUnSub()
+589
View File
@@ -0,0 +1,589 @@
<template>
<div>
<va-modal
class="flex sm12 xl12 md12 xs12 pa-0 ma-0"
v-model="showDownload"
size="small"
:hideDefaultActions="true"
:mobileFullscreen="false"
>
<template slot="header">
<div class="row mb-2">
<va-icon name="fa fa-download mr-3 " color="primary"/>
<h5 class="mt-0 mb-0">{{'Download'}}</h5>
</div>
</template>
<div class="downloadOptionsList">
<template v-for="(downloadOptions, i) in downloadOptionsList">
<va-accordion :key="downloadOptions.filename">
<va-collapse>
<va-item :key="downloadOptions.filename" slot="header" clickable>
<va-item-section avatar>
<va-item-label>
{{ downloadOptions.filename }}
</va-item-label>
</va-item-section>
<va-item-section>
<!-- <va-item-label>
{{ downloadInfo.minTime }}
</va-item-label>
<va-item-label caption>
{{ downloadInfo.maxTime }}
</va-item-label> -->
</va-item-section>
<va-item-section v-if="downloadOptions.download.status == 'idle'" side>
<va-icon name="fa fa-play" color="gray" @click.native.stop="confirmDownload(downloadOptions.metaPath)"/>
</va-item-section>
<va-item-section v-else side class="md4 offset--md8">
<div class="row flex sm12 xl12 md12 xs12">
<div class="flex-center spinner-box">
<fulfilling-bouncing-circle-spinner
:animation-duration="3000"
:color="'#6c7fee'"
:size="20"
>
</fulfilling-bouncing-circle-spinner>
<span class="ml-3">{{loaded}}</span>
</div>
</div>
</va-item-section>
</va-item>
<div slot="body">
<div v-if="downloadOptions.download.status == 'downloading'" class="row flex sm12 xl12 md12 xs12">
<div 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>
</div>
<div v-else>
<div class="mb-5">
<va-item class="flex sm12 xl12 md12 xs12 mr-1 pa-0">
<va-icon
class="flex sm1 xl1 md1 xs1 start "
name="glyphicon glyphicon-filter"
size="1.0rem"
:fixedWidth = "Boolean('true')"
/>
<div class="flex sm11 xl11 md11 xs11 row">
<div v-for="(item,index) in downloadOptions.channel" :key="item.channel" class="filter-button">
<va-button
class="mx-1 px-1"
v-if="item.checked"
@click="channelUnchecked(index,downloadOptions.metaPath)"
small
>
{{item.alias}}
</va-button>
<va-button
class="mx-1 px-1"
v-else
@click="channelChecked(index,downloadOptions.metaPath)"
small outline
>
{{item.alias}}
</va-button>
</div>
</div>
</va-item>
</div>
<div class="mb-5">
<va-item class="flex sm12 xl12 md12 xs12 pa-0">
<va-icon
class="flex sm1 xl1 md1 xs1 mr-4"
:fixedWidth="true"
name="va-icon glyphicon glyphicon-time"
size="1.0rem"
/>
<div class="flex">
<va-slider
class="flex sm11 xl11 md11 xs11 filter-button"
value-visible
range
v-model="downloadOptions.timeRange"
@click.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@mouseup.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@mouseleave.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
@touchend.native="timeRangeChange(downloadOptions.timeRange,downloadOptions.metaPath)"
:min="downloadOptions.minTime"
:max="downloadOptions.maxTime"
/>
<span> {{ transTime(downloadOptions.timeRange) }} </span>
</div>
</va-item>
</div>
</div>
</div>
</va-collapse>
</va-accordion>
<va-list-separator v-if="i < downloadOptionsList.length - 1" :key="'separator' + downloadOptions.filename" />
</template>
</div>
</va-modal>
</div>
</template>
<script>
import { file } from '../../data/file/File'
import { download } from '../../data/download/Download'
import { exportData } from '../../data/export/export'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'Download',
components: {
FulfillingBouncingCircleSpinner,
},
data: () => {
return {
showDownload: false,
controllerList: [],
downloadList: [],
downloadListRecord: [],
downloadOptionsList: [],
// downloadInfo: {
// metaPath: '',
// previousPath: '',
// filenameWithType: '',
// filename: '',
// channel: [],
// timeRange: [0, 0],
// minTime: 0,
// maxTime: 0,
// status: 'idle',
// device: '',
// },
currentlyDownloadInfo: {
metaPath: '',
previousPath: '',
filenameWithType: '',
filename: '',
channel: [],
timeRange: [0, 0],
minTime: 0,
maxTime: 0,
download: {
status: 'idle',
rate: 0,
},
device: '',
},
estimatedTime: 0,
fileSegmentIndex: 0,
fileSegmentMax: 0,
totoalFileSize: 0,
loadingPercent: 0,
loadTime: 0,
interval: null,
data: '',
}
},
computed: {
// transTime (min,max) {
// return this.getTimeInFormat(min) + ' - ' + this.getTimeInFormat(max)
// },
loaded () {
return this.loadingPercent + '%'
},
},
// mqtt: {
// async '+/file_segment/+' (data, topic) {
// console.log('file_segment')
// console.log(String.fromCharCode.apply(null, data))
// // console.log(navigator.connection.downlink)
// // console.log(this.fileNum)
// // console.log(this.downloadOptionsList.length)
// // console.log(JSON.parse(String.fromCharCode.apply(null, data)).data.file_size)
// if (this.downloadOptionsList.length > 0) {
// await download.updateFileSegmentInfo(String.fromCharCode.apply(null, data))
// this.fileSegmentMax = download.getFileSegmentMaxIndex()
// this.checkFileSegmentGetOrNot()
// }
// },
// async '+/file_segment_get/+' (data, topic) {
// console.log('file_segment_get')
// console.log(JSON.parse(new TextDecoder('utf-8').decode(data)))
// if (this.downloadOptionsList.length > 0) {
// const segmentData = atob(JSON.parse(new TextDecoder('utf-8').decode(data)).data.segment)
// this.data += segmentData
// this.fileSegmentIndex += 1
// this.checkFileSegmentGetOrNot()
// }
// },
// },
methods: {
transTime (timeRange) {
return this.getTimeInFormat(timeRange[0]) + ' - ' + this.getTimeInFormat(timeRange[1])
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
showDownloadModal: function () {
this.showDownload = true
},
channelChecked: function (channelIndex, path) {
download.getDownloadOptions(path).channel[channelIndex].checked = true
this.refresh()
},
channelUnchecked: function (channelIndex, path) {
download.getDownloadOptions(path).channel[channelIndex].checked = false
this.refresh()
},
getTimeInFormat: function (time) {
if (time < 60) {
const second = time
return `${second}s`
} else if (time < 3600) {
const second = time % 60
const minute = parseInt(time / 60)
return `${minute}m${second}s`
} else {
const hour = parseInt(time / 3600)
const hourRemain = time % 3600
const minute = parseInt(hourRemain / 60)
const second = hourRemain % 60
return `${hour}h${minute}m${second}s`
}
},
getCurrentFullPathName: function () {
return this.currentlyDownloadInfo.previousPath + '/' + this.currentlyDownloadInfo.filenameWithType
},
downloading: async function (info) {
this.$emit('download')
},
addDownloadTask: async function (info) {
this.downloading()
/*
** download all
*/
if (parseInt(info.time_duration) < 18e8) {
const rawData = await download.getAllRawDatas(info)
exportData.exportCSV(info, rawData)
} else {
for (const channelID in info.raw_data) {
const rawDataIDList = info.raw_data[channelID]
while (rawDataIDList.length > 1000) {
const _list = rawDataIDList.splice(0, 1000)
const rawData = await download.getIndexRawDatas(info, channelID, _list)
exportData.exportCSV(info, rawData)
}
const rawData = await download.getIndexRawDatas(info, channelID, rawDataIDList)
exportData.exportCSV(info, rawData, channelID)
rawData[channelID].length = 0
// const rawData = await download.getAllRawDatas(info)
// console.timeEnd('download')
}
}
// console.time('download')
// const rawData = await download.getAllRawDatas(info)
// console.timeEnd('download')
// exportData.exportCSV(info, rawData)
/*
** download split
*/
this.downloading()
},
_addDownloadTask: function (downloadPath) {
if (Array.isArray(downloadPath)) {
downloadPath.forEach(path => {
this.downloadList.push(path.path)
this.updateDownloadInfo(path.path)
})
} else {
this.downloadList.push(downloadPath)
this.updateDownloadInfo(downloadPath)
}
this.confirmDownload()
},
refresh: function () {
this.downloadOptionsList = download.getDownloadOptionsList()
console.log(this.downloadOptionsList)
},
confirmDownload: function (path) {
console.log('confirmDownload')
console.log(download.getDownloadOptionsList())
console.log(download.isFileDownloading())
console.log(download.downloadTaskRemain())
if (!download.isFileDownloading() && download.downloadTaskRemain()) {
const downloadOptionsIndex = download.getDownloadOptionsList().findIndex(downloadInfo => downloadInfo.metaPath === path)
// this.downloadBlockList[downloadInfoIndex].status = 'downloading'
this.currentlyDownloadInfo = download.getDownloadOptionsList()[downloadOptionsIndex]
this.currentlyDownloadInfo.download.status = 'downloading'
console.log(this.currentlyDownloadInfo)
console.log(download.getDownloadOptionsList())
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.calculateFileSize(this.currentlyDownloadInfo.metaPath)
console.log('getactualsize')
this.subfileListRemain()
}
// if (this.downloadList.length > 0 && this.currentlyDownloadInfo.status === 'idle') {
// this.showDownload()
// this.updateDownloadSubFileList(this.downloadList[0])
// } else {
// /* TODO If file downloading then waiting */
// console.log('no download task or file downloading')
// }
},
_confirmDownload: function () {
console.log('confirmDownload')
console.log(download.getDownloadOptionsList())
// if (this.downloadList.length > 0 && this.currentlyDownloadInfo.status === 'idle') {
// this.showDownload()
// this.updateDownloadSubFileList(this.downloadList[0])
// } else {
// /* TODO If file downloading then waiting */
// console.log('no download task or file downloading')
// }
},
downloadAllSubFileList: async function (path) {
this.currentlyDownloadInfo = this.downloadInfo
download.downloadAllSubFileList(path)
this.subfileListRemain()
},
// updateDownloadInfo: function (path) {
// console.log(path)
// const metaInfo = file.getMetaInfo(path)
// const downloadInfoIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
// if (downloadInfoIndex === -1) {
// this.downloadInfo.library = metaInfo.configuration._LIBRARY_
// this.downloadInfo.channel = metaInfo.channel.map(ch => {
// let channel = ''
// if (this.downloadInfo.library.includes('Elite')) {
// channel = (ch === 0) ? 'current' : (ch === 1) ? 'voltage' : (ch === 2) ? 'resistance' : ''
// } else {
// channel = 'channel' + ch
// }
// return {
// alias: channel,
// channel: ch,
// checked: true,
// }
// })
// this.downloadInfo.metaPath = path
// this.downloadInfo.previousPath = path.split('/').slice(0, -1).join('/')
// this.downloadInfo.filename = path.split(/[\/.]+/).slice(0, -1).pop()
// this.downloadInfo.minTime = parseInt([...new Set(metaInfo['start-time'])][0] / 1000)
// this.downloadInfo.maxTime = parseInt(metaInfo.time_duration / 1000)
// this.downloadInfo.timeRange = [this.downloadInfo.minTime, this.downloadInfo.maxTime]
// this.downloadInfo.status = 'idle'
// this.downloadBlockList.push(JSON.parse(JSON.stringify(this.downloadInfo)))
// } else {
// for (var item in this.downloadInfo) {
// this.downloadInfo[item] = this.downloadBlockList[downloadInfoIndex][item]
// }
// }
// },
updatecurrentlyDownloadInfo: function () {
const downloadInfoIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === this.currentlyDownloadInfo.metaPath)
if (downloadInfoIndex !== -1) {
for (var item in this.downloadBlockList[downloadInfoIndex]) {
this.downloadBlockList[downloadInfoIndex][item] = this.currentlyDownloadInfo[item]
}
}
},
updateMetaDownloadInfo: function () {
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
},
updateDownloadSubFileList: function (path) {
console.log('updateDownloadSubFileList')
const downloadOptionsIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
this.downloadBlockList[downloadOptionsIndex].status = 'downloading'
this.currentlyDownloadInfo = this.downloadBlockList[downloadOptionsIndex]
this.updatecurrentlyDownloadInfo()
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.subfileListRemain()
},
_updateDownloadSubFileList: function (path) {
console.log(path)
console.log(this.downloadInfo)
const downloadOptionsIndex = this.downloadBlockList.findIndex(downloadInfo => downloadInfo.metaPath === path)
this.downloadBlockList[downloadOptionsIndex].status = 'downloading'
this.currentlyDownloadInfo = this.downloadBlockList[downloadOptionsIndex]
// this.updatecurrentlyDownloadInfo()
download.updateDownloadSubFileList(
this.currentlyDownloadInfo.metaPath,
this.currentlyDownloadInfo.channel,
this.currentlyDownloadInfo.timeRange,
)
this.subfileListRemain()
},
subfileListRemain: function () {
console.log('subfileListRemain')
if (download.checkDownloadSubFileList(this.currentlyDownloadInfo.metaPath)) {
this.currentlyDownloadInfo.filenameWithType = download.getFirstSubfile(this.currentlyDownloadInfo.metaPath)
} else {
this.loadingPercent = 100
console.log(this.currentlyDownloadInfo.filenameWithType + ' download complete')
console.log(performance.now())
this.exportCSV()
this.test(this.downloadDone)
// this.currentlyDownloadInfo.status = this.currentlyDownloadInfo.status = 'idle'
// this.updatecurrentlyDownloadInfo()
// this.loadingPercent = 0
}
},
checkFileSegmentGetOrNot: function () {
console.log('checkFileSegmentGetOrNot')
if (this.fileSegmentIndex >= this.fileSegmentMax) {
const subfile = file.shiftDownloadSubFileList(this.currentlyDownloadInfo.metaPath)
file.pushDownloadData(this.currentlyDownloadInfo.metaPath, subfile, this.data)
this.data = ''
this.fileSegmentIndex = 0
this.subfileListRemain()
} else {
}
},
exportCSV: function () {
exportData.exportCSV(this.currentlyDownloadInfo)
},
exportTXT: function () {
},
timeRangeChange: function (timeRange, path) {
download.getDownloadOptions(path).timeRange = timeRange
this.refresh()
},
calculateFileSize: function () {
this.totoalFileSize = file.getFileSize(this.currentlyDownloadInfo.metaPath)
// const allSubfile = download.getAllSubfile(this.currentlyDownloadInfo.metaPath)
// this.fileNum = allSubfile.length
const downlinkSpeed = this.getNetSpeed('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAABTVBMVEX////qQzU0qFNChfT7vAU/g/RZlPU5gPT7uQCxyPqXufn/vQDpNCLqQTMwp1D7uADpOCfqPS4fo0bpMiD8wADpLxwopUv0paD1q6b86ObpOiv4/Pn+9/b4xcH2ubX1r6rsXVIre/NwvoOf0qvO6NQzqUm/4cf5zMn739zrSz398O/sVEjvdWz2t7PvenP4ycb7wir96Lf+8tb8zFH93pj+9eH//PD92Yvw9v7+7cP80Xb95q6pxPnq9u1PsmigvvlunvaJyZiGrvjD1vvyl5HtamHoJgzwhH3zmpTtXlLvenHuYSzygCL2mxjsUzDwcSf0jx34phH8z2DxfVHX5P38x0KHvnDOtiGjsjJ0rkLhuRW4tCnS4PyMsDtZq0it2bhmmvbO5OE1noBft3U1pWA/jNc8lLk5nJNBieSSy58+j8t5wIo6mKY2o25FrmBHQXgmAAAIJUlEQVR4nO2b2V/bRhCAhbADxJLQYQvwgcE2PsAhEHK0SUxigoGkTdMj6ZXDTdw2JbT+/x9r+QDZllYra8da85vvKU/yfszszO5IEQQEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRDWpLfXSplbqysWq9n1Unk7HfaSmJFey6ycNipGwpDUAZKRSMxt7B6vl7fDXl5QyqvVHdVQk5o8N4osa0nJqDSOSzNrmc7sVlRVk8fl7J4dTbmRXQt7sf5JZ6onkkPknC3VxE52tiJZPtUMjcru0lI1dkthL5uaTMNI0kVvOJLGRnYmCmx2R/IXPpukVFnh3nG9IvkPn81R1bJhKxC5vWME8evHMRO2hitr1URQv65jospp81jRkgz8LJLaStgyDpSfqywC2ENWN7gL48rJpAXUGe3kVthKQ2w3DKZ+c9ZuPOWocZQqrHagHY4yNRuoBbqjaZyc444D90A35AQPmzFdlYD8uorht410Q4UTnJON0A3TOxA15pLE6nUXvOYRlI3wI9gAFZRCFxReQBaZufAjKBwDtgkeioyQZX4UtcFDIyz5PqrJsqYlk2oymdQ8hqhc7MHtih9Ba+4rVTaqp6fHZ2fHp7sNaxDuPk3lQVCo+iijmmQ0zjJr9ncx6e3Syq6WcBk6crAHhVXqTSirJ7sZ53l2unQsGQ6R5GAPCuUT2vCpz28RL7K3q+pIIDlo9B026EYWmlT1vuGVT1X707jYg8IKVauXjQbdDbZctd0wuYjgGtU7peTcOvUTM/KgcPFQZDqnNYo6Khu7fh6ZftENo8yHYCnhLajN+R3NZzsFh489KAg73jk6yZisXNE4EVz3boXG7iSjzrUKH4KC93EtcTzZkzmZAD9a/obsJ0tnYa8xEKm9SPxboqHEwZkrCPuLkUj8JSFRpdOwlxiQg0iH+PJ3boJqNewVBuTOYqSrGH/lLKjtcFIuJuZhz7Dj+L1jldHKYa8wIKnIJfEfHDJVoj+Kcsr+sk1xvG0kZ30T9uvMleNI25DV2fo2zYH7y5FhxZdDhgbfH/vQsL8YGVG0tw15J+z1BefHUcNIPPLqsvsbt8NeX2BSo35DbUPbCHt9wbkzFsJe2+iHkN/P0ai562gYiS9abUPeCHt5DDhwFOy3DZWT62sQ7u+5GVq3DY2bz3sm555zkvYUl38Ke3kM2F92N4wsPqJ8ytKNwLyGMnxAiGFk+T6t4WY0IJtLUIYPSYZ7tE9ZWpgPyMJNKMM3pCR9MD3D6FMgwftxUpLuT89wfh7KkMk2ZGG4CWR4j1RK49SPYWH4FYzh1yTDN1M1BGoXjwhZuvhwqobvYAxdzt09Q+pSysJw4VkIhnevvSHtmY2NIdChhnRom7Lh42tv+AQNJ4OjfRiG4XRraRiG0+2HQJWGnzMNVLcgnksPpmoI1PH5uVtAGXJ0PwQ6efNzx4e6PXEzpwG7AXMzawObYrCalwaftUWhDIkz72XqmffmAg0kwxtQhqT3FrH4z5RPeXeThiOC4cIRlCHh3VPsF0Wpsfyt14QgQh3aBPf3h7HYr6Jo5ln+FGm3QjV8wfXsHYv8Joqicsjyp46i7oZgzcLtPX7sd0W00IsMf4qwDcGG+oLLtxixt2IPpcXul95tuvuBvZixGP+eJhZ5Lw5gGMQj0jYEuv92GfsmKrb34VJQVNrMfmiBtA2Bzt1dRr9r6zQJ0YbeZPQ7pEoanYcrNMJIv+g2iSF0Rj2RVGZAt+Hwwa3XJIZQckx+hXh0Bd2GQ9V00CSGg8gkT28QduH8AtiXGD0ub1Cxtw6CIpOz2xPi7QPs2N2n3/RjsfcOft16mgr6E69JhRTwQ4wBB70M/eAs2DmeBu77xBwFT9JeSxxpEiNbsR7sB0jN3oKNBom98SYxohjokrFEOK/Ng1fSLo/GmwRDxWceEQS8V1whElI0aKI+I1aZTrsHu97bKepehqKem6yiegnCnkmvOPQMYqeiTtIXH296CEb/YO3iTME7iJ1M9n+V8qqinRDCzS+GyZkUivqFv0z9OO8pCDdGHKXmnaZWpio+wpi60D/d8FIEHEGNkqfIUyuMrQLd81J50RS3/vyLrDitXdiFotjQO9bybd16nmL+TVQEe+XkuCiqIHYdD5vk/Vi4UMzB32vrc9S9mi48nY5bH7o87TnquaKLZKpwruj2dNj67x/XMEancZyx0aKopzbJ1nmxVrN5pmq14nnL1EefoihfXBShPk9wpdam24qDlZu6Lh7mLur183r9Itc6FHXddLxDb/3rmKnA4xknaPr+WIDMHopC+PNsfXLqjNPOUYumf0U6ttrjbWNqp5kh6lCKytZo24B7ZUiG5vQ2GVuf5+2bcXrHtRFSfgqqT0V72wAecxMVD8EUTeXL5TwDfvpEUvTVM3wxaBvRKR64HRXBothpG91MhfvPeJSKcHuxd9sIW7CjmINqGt22Eb6gANgXLcWPYdt1aepA9UZRKK/Q4BREkM1otpl+hRSIWgsgUyedugKRZ52pSrD3HwDU2HZGU+RlC9rIk259/lDMOlcZOqCWY5Sq+iGHAexROGTgaJqsvssBoSgGdDTNcy4T1EYzQBwVU6nz0wPdKbbGpoR0fno7Pwt+FoW64jeQiuk+OeaSVDHnMhF10Zud8F3RkRSt2aiHXMdObDVnT69PoZlrm7qzpzUg1s12Ll+YpeR0IFUr5i9abd2iP/M2u/9ut+r5Ym3G7a5IpWqFYrOZt2g2i4VCLXVt3BAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAE4Yf/AZmODCZml4YgAAAAAElFTkSuQmCC', 3)
// this.totoalFileSize = (allSubfile.length) * 1000000
console.log(downlinkSpeed)
console.log(this.totoalFileSize)
this.estimatedTime = this.totoalFileSize / (downlinkSpeed * 1024)
if (this.totoalFileSize >= 1 * 1024 * 1024) {
console.log('size big')
this.estimatedTime = this.estimatedTime * 10
}
console.log(this.estimatedTime)
this.loadTime = parseInt((this.estimatedTime / 1000) % 60) * 100
console.log(this.loadTime)
this.doProgress()
// allSubfile.forEach(subfile => {
// console.log(subfile)
// this.currentlyDownloadInfo.filenameWithType = subfile
// })
},
doProgress () {
const step = this.estimatedTime / 100 * 1000
this.interval = setInterval(() => {
if (this.loadingPercent >= 99) {
console.log('complete')
clearInterval(this.interval)
this.estimatedTime = 0
} else {
this.loadingPercent++
}
}, step)
},
getNetSpeed (url, times) {
const connection = window.navigator.connection
if (connection && connection.downlink) {
return connection.downlink * 1024 / 8
}
},
test: async function (fn) {
return await setTimeout(async function () {
await fn()
}, 1000)
},
downloadDone: function () {
download.getDownloadOptionsList().shift()
this.loadingPercent = 0
this.refresh()
this.confirmDownload()
},
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
id: 'tommy_test',
mac: '',
status: 'loading',
isScan: true,
files: [],
})
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
})
},
},
// watch: {
// loadingPercent(val) {
// if (val >= 100) {
// console.log('complete');
// clearInterval(this.interval)
// }
// }
// },
async mounted () {
await this.getControllerList()
this.pageMqttSub()
},
destroyed () {
this.pageMqttUnSub()
},
}
</script>
<style>
.filter-button {
text-align: center;
}
</style>
@@ -1,159 +0,0 @@
<template>
<div>
</div>
</template>
<script>
import api from '@/data/api/index'
import types from '@/store/modules/file/elite/csv/types'
import { mapActions, mapGetters, mapMutations } from 'vuex'
// import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'Download',
components: {
// FulfillingBouncingCircleSpinner,
},
data: () => {
return {
controllerList: [],
spliceDataNumsLimit: 20,
}
},
computed: {
getByIndex (index) {
return this.getByIndexDownload(index)
},
getDownloading () {
return this.getDownloadingDownload()
},
getRemainIDLength (index) {
return this.getRemainIDLengthDownload(index)
},
},
methods: {
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
id: 'tommy_test',
mac: '',
status: 'loading',
isScan: true,
files: [],
})
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
})
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
...mapActions('eliteCsv',
[
types.download.init,
types.download.rawDataByIDs,
types.export.createHeadersString,
types.export.createTitlesString,
types.export.createDataString,
types.export.closeFileStream,
],
),
...mapMutations('eliteCsv',
[
types.download.setDownloading,
types.download.removeDownloadBuffer,
],
),
...mapGetters('eliteCsv',
[
types.download.getByIndex,
types.download.getDownloading,
types.download.getAll,
types.download.getRemainIDLength,
],
),
},
watch: {
},
async mounted () {
await this.getControllerList()
// TODO authorization
this.pageMqttSub()
const metaIDList = this.$route.params.metaList.split('-')
const recoverData = this.$route.params.recover
await this.initDownload({ metaIDList: metaIDList, api: api })
// let first file download in download List
this.setDownloadingDownload()
const downloadingFile = this.getDownloading
downloadingFile.recoverData = recoverData
let spliceDataNums = 0
// console.log('downloadingFile', downloadingFile)
// console.log(this.getRemainIDLength(0))
// create headers and title
this.createHeadersStringExport()
this.createTitlesStringExport()
// loop until each channel's rawID List empty
while (this.getRemainIDLength(0)) {
// forEach channel in raw_data
for (const channel in downloadingFile.raw_data) {
// get spliceDataNumsLimit numbers of ID to download once time
if (downloadingFile.raw_data[channel].length >= this.spliceDataNumsLimit) {
spliceDataNums = this.spliceDataNumsLimit
} else {
spliceDataNums = downloadingFile.raw_data[channel].length
}
const rawID = downloadingFile.raw_data[channel].splice(0, spliceDataNums)
// console.log('rawID', channel, rawID)
// download data and save into dataBuffer by rawDataIDList
await this.rawDataByIDsDownload({
channel: channel,
rawDataIDList: rawID,
api: api,
})
}
// export data
this.createDataStringExport()
// this.removeDownloadBufferDownload()
}
this.closeFileStreamExport()
},
destroyed () {
this.pageMqttUnSub()
},
}
</script>
<style>
.filter-button {
text-align: center;
}
</style>
@@ -1,216 +0,0 @@
<template>
<div>
<div class="row flex sm12 xl12 md12 xs12" style="height: 100vh;">
<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>
</div>
</template>
<script>
import api from '@/data/api/index'
import types from '@/store/modules/file/elite/excel/types'
import { mapActions, mapGetters, mapMutations } from 'vuex'
// import { file } from '../../data/file/File'
// import { download } from '../../data/download/Download'
// import { exportData } from '../../data/export/export'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'Download',
components: {
FulfillingBouncingCircleSpinner,
},
data: () => {
return {
controllerList: [],
}
},
computed: {
getByIndex (index) {
return this.getByIndexDownload(index)
},
getDownloading () {
return this.getDownloadingDownload()
},
getRemainIDLength (index) {
return this.getRemainIDLengthDownload(index)
},
// transTime (min,max) {
// return this.getTimeInFormat(min) + ' - ' + this.getTimeInFormat(max)
// },
loaded () {
return this.loadingPercent + '%'
},
},
methods: {
transTime (timeRange) {
return this.getTimeInFormat(timeRange[0]) + ' - ' + this.getTimeInFormat(timeRange[1])
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
id: 'tommy_test',
mac: '',
status: 'loading',
isScan: true,
files: [],
})
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
})
},
...mapActions('eliteExcel',
[
types.download.init,
types.download.rawDataByIDs,
types.export.createHeadersString,
types.export.createTitlesString,
types.export.createDataString,
types.export.closeFileStream,
],
),
...mapMutations('eliteExcel',
[
types.download.setDownloading,
types.download.removeDownloadBuffer,
],
),
...mapGetters('eliteExcel',
[
types.download.getByIndex,
types.download.getDownloading,
types.download.getAll,
types.download.getRemainIDLength,
],
),
},
openPortal () {
this.windowRef = window.open('', '', 'width=600,height=400,left=200,top=200')
this.windowRef.document.body.appendChild(this.$el)
// copyStyles(window.document, this.windowRef.document)
this.windowRef.addEventListener('beforeunload', this.closePortal)
},
closePortal () {
if (this.windowRef) {
this.windowRef.close()
this.windowRef = null
this.$emit('close')
}
},
// watch: {
// loadingPercent(val) {
// if (val >= 100) {
// console.log('complete');
// clearInterval(this.interval)
// }
// }
// },
async mounted () {
await this.getControllerList()
// TODO authorization
this.pageMqttSub()
const metaIDList = this.$route.params.metaList.split('-')
await this.initDownload({ metaIDList: metaIDList, api: api })
this.setDownloadingDownload()
const downloadingFile = this.getDownloading
// create headers and title
this.createHeadersStringExport()
this.createTitlesStringExport()
// let spliceDataNums = 0
let rowIndex = 0
// let columnIndex = 0
// // while (this.getRemainIDLength(0)) {
while (rowIndex < downloadingFile.needRows) {
for (const channel in downloadingFile.downloadIDList) {
const rawID = downloadingFile.downloadIDList[channel][rowIndex]
// console.log('rawID', channel, rawID)
if (rawID !== undefined) {
await this.rawDataByIDsDownload({
channel: channel,
rawDataIDList: rawID,
api: api,
})
}
}
rowIndex += 1
this.createDataStringExport({ lastPartData: (rowIndex === downloadingFile.needRows) })
// this.removeDownloadBufferDownload()
// console.log('end', downloadingFile)
}
this.closeFileStreamExport()
await new Promise(resolve => setTimeout(resolve, 2000)) // sleep
window.close()
// }
// if (downloadingFile.raw_data[channel].length >= 10) {
// spliceDataNums = 10
// } else {
// spliceDataNums = downloadingFile.raw_data[channel].length
// }
// const rawID = downloadingFile.raw_data[channel].splice(0, spliceDataNums)
// console.log('rawID', channel, rawID)
// await this.rawDataByIDsDownload({
// channel: channel,
// rawDataIDList: rawID,
// api: api,
// })
// }
// // console.log('end', downloadingFile)
// // export data
// this.createDataStringExport({})
// this.removeDownloadBufferDownload()
// }
// const rawData = await download.getAllRawDatas(meta.data[0])
// exportData.exportCSV(meta.data[0], rawData)
// await this.initMeta({ metaIDList: metaIDList, api: api })
},
destroyed () {
this.pageMqttUnSub()
},
}
</script>
<style>
.filter-button {
text-align: center;
}
</style>
@@ -1,178 +0,0 @@
<template>
<div>
<div class="row flex sm12 xl12 md12 xs12" style="height: 100vh;">
<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>
</div>
</template>
<script>
import api from '@/data/api/index'
import types from '@/store/modules/file/neulive/csv/types'
import { mapActions, mapGetters, mapMutations } from 'vuex'
import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'Download',
components: {
FulfillingBouncingCircleSpinner,
},
data: () => {
return {
controllerList: [],
spliceDataNumsLimit: 1,
}
},
computed: {
getByIndex (index) {
return this.getByIndexDownload(index)
},
getDownloading () {
return this.getDownloadingDownload()
},
getRemainIDLength (index) {
return this.getRemainIDLengthDownload(index)
},
},
methods: {
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
id: 'tommy_test',
mac: '',
status: 'loading',
isScan: true,
files: [],
})
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
})
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
...mapActions('neuliveCsv',
[
types.download.init,
types.download.rawDataByIDs,
types.export.createHeadersString,
types.export.createTitlesString,
types.export.createDataString,
types.export.closeFileStream,
],
),
...mapMutations('neuliveCsv',
[
types.download.setDownloading,
types.download.removeDownloadBuffer,
],
),
...mapGetters('neuliveCsv',
[
types.download.getByIndex,
types.download.getDownloading,
types.download.getAll,
types.download.getRemainIDLength,
],
),
},
watch: {
},
async mounted () {
await this.getControllerList()
// TODO authorization
this.pageMqttSub()
const metaIDList = this.$route.params.metaList.split('-')
const recoverData = this.$route.params.recover
const rawData = this.$route.params.rawData
await this.initDownload({ metaIDList: metaIDList, api: api })
// let first file download in download List
this.setDownloadingDownload()
const downloadingFile = this.getDownloading
downloadingFile.recoverData = recoverData
let spliceDataNums = 0
// console.log(this.getRemainIDLength(0))
const channelArray = Object.keys(downloadingFile.raw_data)
const filterChannelArray = (rawData === 'acc') ? channelArray.filter(ch => parseInt(ch) >= 256) : channelArray.filter(ch => parseInt(ch) < 256)
// create headers and title
this.createHeadersStringExport()
this.createTitlesStringExport({ rawData: rawData })
// loop until each channel's rawID List empty
while (this.getRemainIDLength(0, filterChannelArray)) {
const lastPartData = ((this.getRemainIDLength(0, filterChannelArray) - this.spliceDataNumsLimit) <= 0)
// forEach channel in raw_data
for (const channel of filterChannelArray) {
// get spliceDataNumsLimit numbers of ID to download once time
if (downloadingFile.raw_data[channel].length >= this.spliceDataNumsLimit) {
spliceDataNums = this.spliceDataNumsLimit
} else {
spliceDataNums = downloadingFile.raw_data[channel].length
}
const rawID = downloadingFile.raw_data[channel].splice(0, spliceDataNums)
// download data and save into dataBuffer by rawDataIDList
await this.rawDataByIDsDownload({
channel: channel,
rawDataIDList: rawID,
api: api,
})
}
// export data
this.createDataStringExport({ lastPartData: lastPartData, rawData: rawData })
// if (this.spliceDataNumsLimit <= 10) {
// this.spliceDataNumsLimit += 1
// }
// console.log('removeNeuliveCSV')
// this.removeDownloadBufferDownload()
}
this.closeFileStreamExport()
await new Promise(resolve => setTimeout(resolve, 2000)) // sleep
window.close()
},
destroyed () {
this.pageMqttUnSub()
},
}
</script>
<style>
.filter-button {
text-align: center;
}
</style>
@@ -1,207 +0,0 @@
<template>
<div>
</div>
</template>
<script>
import api from '@/data/api/index'
import types from '@/store/modules/file/neulive/excel/types'
import { mapActions, mapGetters, mapMutations } from 'vuex'
// import { file } from '../../data/file/File'
// import { download } from '../../data/download/Download'
// import { exportData } from '../../data/export/export'
// import { FulfillingBouncingCircleSpinner } from 'epic-spinners'
export default {
name: 'Download',
components: {
// FulfillingBouncingCircleSpinner,
},
data: () => {
return {
controllerList: [],
}
},
computed: {
getByIndex (index) {
return this.getByIndexDownload(index)
},
getDownloading () {
return this.getDownloadingDownload()
},
getRemainIDLength (index) {
return this.getRemainIDLengthDownload(index)
},
// transTime (min,max) {
// return this.getTimeInFormat(min) + ' - ' + this.getTimeInFormat(max)
// },
loaded () {
return this.loadingPercent + '%'
},
},
methods: {
transTime (timeRange) {
return this.getTimeInFormat(timeRange[0]) + ' - ' + this.getTimeInFormat(timeRange[1])
},
notify (name) {
this.showToast(`done ${name}`, {
position: 'bottom-right',
})
},
getControllerList: async function () {
const update = async () => {
this.controllerList.push({
id: 'tommy_test',
mac: '',
status: 'loading',
isScan: true,
files: [],
})
}
await update()
},
mqttPub: function (topic, mes) {
this.$mqtt.publish(topic, mes)
},
mqttSub: function (val) {
this.$mqtt.subscribe(val)
},
mqttUnSub: function (val) {
this.$mqtt.unsubscribe(val)
},
pageMqttSub: function () {
this.controllerList.forEach(controller => {
// this.mqttSub(controller.id + '/broadcast')
this.mqttSub(controller.id + '/file_meta_info/+')
this.mqttSub(controller.id + '/file_segment/+')
this.mqttSub(controller.id + '/file_segment_get/+')
})
},
pageMqttUnSub: function () {
this.controllerList.forEach(controller => {
// this.mqttUnSub(controller.id + '/broadcast')
this.mqttUnSub(controller.id + '/file_meta_info/+')
this.mqttUnSub(controller.id + '/file_segment/+')
this.mqttUnSub(controller.id + '/file_segment_get/+')
})
},
...mapActions('neuliveExcel',
[
types.download.init,
types.download.rawDataByIDs,
types.export.createHeadersString,
types.export.createTitlesString,
types.export.createDataString,
types.export.closeFileStream,
],
),
...mapMutations('neuliveExcel',
[
types.download.setDownloading,
types.download.removeDownloadBuffer,
],
),
...mapGetters('neuliveExcel',
[
types.download.getByIndex,
types.download.getDownloading,
types.download.getAll,
types.download.getRemainIDLength,
],
),
},
openPortal () {
this.windowRef = window.open('', '', 'width=600,height=400,left=200,top=200')
this.windowRef.document.body.appendChild(this.$el)
// copyStyles(window.document, this.windowRef.document)
this.windowRef.addEventListener('beforeunload', this.closePortal)
},
closePortal () {
if (this.windowRef) {
this.windowRef.close()
this.windowRef = null
this.$emit('close')
}
},
// watch: {
// loadingPercent(val) {
// if (val >= 100) {
// console.log('complete');
// clearInterval(this.interval)
// }
// }
// },
async mounted () {
await this.getControllerList()
// TODO authorization
this.pageMqttSub()
const metaIDList = this.$route.params.metaList.split('-')
await this.initDownload({ metaIDList: metaIDList, api: api })
this.setDownloadingDownload()
const downloadingFile = this.getDownloading
console.log('downloadingFile', downloadingFile)
console.log(this.getRemainIDLength(0))
// create headers and title
this.createHeadersStringExport()
this.createTitlesStringExport()
// let spliceDataNums = 0
let rowIndex = 0
// let columnIndex = 0
// // while (this.getRemainIDLength(0)) {
while (rowIndex < downloadingFile.needRows) {
for (const channel in downloadingFile.downloadIDList) {
const rawID = downloadingFile.downloadIDList[channel][rowIndex]
// console.log('rawID', channel, rawID)
if (rawID !== undefined) {
await this.rawDataByIDsDownload({
channel: channel,
rawDataIDList: rawID,
api: api,
})
}
}
this.createDataStringExport()
this.removeDownloadBufferDownload()
// console.log('end', downloadingFile)
rowIndex += 1
}
this.closeFileStreamExport()
// }
// if (downloadingFile.raw_data[channel].length >= 10) {
// spliceDataNums = 10
// } else {
// spliceDataNums = downloadingFile.raw_data[channel].length
// }
// const rawID = downloadingFile.raw_data[channel].splice(0, spliceDataNums)
// console.log('rawID', channel, rawID)
// await this.rawDataByIDsDownload({
// channel: channel,
// rawDataIDList: rawID,
// api: api,
// })
// }
// // console.log('end', downloadingFile)
// // export data
// this.createDataStringExport({})
// this.removeDownloadBufferDownload()
// }
// const rawData = await download.getAllRawDatas(meta.data[0])
// exportData.exportCSV(meta.data[0], rawData)
// await this.initMeta({ metaIDList: metaIDList, api: api })
},
destroyed () {
this.pageMqttUnSub()
},
}
</script>
<style>
.filter-button {
text-align: center;
}
</style>
+3 -16
View File
@@ -1,13 +1,6 @@
<template>
<div>
<div class="row">
<div class="flex md12 xl12 xs12 sm12" :style="AreaStyle">
<file-table :ref="'file_ref'" @download="addDownloadTask"/>
</div>
<div class="flex md12 xl12 xs12 sm12" :style="AreaStyle">
<!-- <download :ref="'download_ref'" @download="downloadingFile"></download> -->
</div>
</div>
<file-table :ref="'file_ref'"/>
</div>
</template>
@@ -24,11 +17,6 @@ export default {
data () {
return {
controllerList: [],
AreaStyle: {
height: 'auto',
'overflow-y': 'hidden',
'overflow-x': 'hidden',
},
}
},
methods: {
@@ -78,7 +66,7 @@ export default {
this.$refs.file_ref.currentInfo = initCollection.data[0]
const folders = await api.collection.getByParent('folder', initCollection.data[0].id)
const files = await api.meta.getCollectionByParent('folder', initCollection.data[0].id)
const files = await api.meta.getByParent('folder', initCollection.data[0].id)
file.updateFileList(folders)
file.updateFileList(files)
@@ -92,6 +80,7 @@ export default {
async mounted () {
await this.getControllerList()
await api.auth.getJwtData()
console.log(await api.meta.getByParentWithLimit('folder', 2, 5, 0, 'DESC'))
this.pageMqttSub()
this.pageInitPub()
},
@@ -102,5 +91,3 @@ export default {
},
}
</script>
<style lang="scss">
</style>
+17 -27
View File
@@ -182,6 +182,9 @@
<va-popover :message="`replay ${props.rowData.name}`" placement="top">
<va-button flat small color="gray" icon="fa fa-eye" v-if="props.rowData.type !== 'folder'" @click.stop="replay(props.rowData)"/>
</va-popover>
<va-popover :message="`spike ${props.rowData.name}`" placement="top">
<va-button flat small color="gray" icon="fa fa-filter" v-if="props.rowData.type !== 'folder'" @click.stop="spike(props.rowData)"/>
</va-popover>
<va-popover :message="`edit ${props.rowData.name}`" placement="top">
<va-button flat small color="gray" icon="fa fa-pencil" @click.stop="editFileConfirm(props.rowData)"/>
</va-popover>
@@ -380,7 +383,7 @@ export default {
// call getfolder api and update
const folders = await api.collection.getByParent('folder', info.id)
const files = await api.meta.getCollectionByParent('folder', info.id)
const files = await api.meta.getByParent('folder', info.id)
file.updateFileList(folders)
file.updateFileList(files)
@@ -400,7 +403,7 @@ export default {
this.currentInfo = item
// call getfolder api and update
const folders = await api.collection.getByParent('folder', item.id)
const files = await api.meta.getCollectionByParent('folder', item.id)
const files = await api.meta.getByParent('folder', item.id)
file.updateFileList(folders)
file.updateFileList(files)
@@ -421,7 +424,7 @@ export default {
if (initCollection.data.length === 1) {
this.currentInfo = initCollection.data[0]
const folders = await api.collection.getByParent('folder', initCollection.data[0].id)
const files = await api.meta.getCollectionByParent('folder', initCollection.data[0].id)
const files = await api.meta.getByParent('folder', initCollection.data[0].id)
file.updateFileList(folders)
file.updateFileList(files)
this.fileLists = file.getFileList()
@@ -554,20 +557,8 @@ export default {
}
},
async deleteSingleFileByDeleteInfo () {
for (const item of this.deleteInfo.rawChannelAndIDArrays) {
const channel = item[0]
const id = item[1]
await api.raw.update(channel, id, { deleted: true })
await api.raw.clearDel(channel, id)
}
for (const item of this.deleteInfo.miniChannelAndIDArrays) {
const channel = item[0]
const id = item[1]
await api.mini.update(channel, id, { deleted: true })
await api.mini.clearDel(channel, id)
}
await api.meta.update(this.deleteInfo.metaID, { deleted: true })
await api.meta.clearDel(this.deleteInfo.metaID)
await api.meta.clearDel()
},
deleteFileConfirm: function (info) {
this.fillDeleteInfo(info)
@@ -621,7 +612,7 @@ export default {
// call getfolder api and update
const folders = await api.collection.getByParent('folder', this.currentInfo.id)
const files = await api.meta.getCollectionByParent('folder', this.currentInfo.id)
const files = await api.meta.getByParent('folder', this.currentInfo.id)
file.updateFileList(folders)
file.updateFileList(files)
@@ -654,23 +645,17 @@ export default {
downloadCsv: async function (meta) {
const href = location.href.split('/')[2]
if (meta.device.library_name.indexOf('Elite') >= 0) {
window.open('http://' + href + '/#/download/elite/csv/' + meta.id)
window.open('http://' + href + '/#/download/csv/' + meta.id)
} else if (meta.device.library_name.indexOf('Neulive') >= 0) {
window.open('http://' + href + '/#/download/neulive/csv/' + meta.id + '/raw', 'raw')
if (JSON.parse(meta.channels).some(el => el >= 256)) {
window.open('http://' + href + '/#/download/neulive/csv/' + meta.id + '/acc', 'acc')
}
window.open('http://' + href + '/#/download/csv/' + meta.id)
}
},
downloadExcel: async function (meta) {
const href = location.href.split('/')[2]
if (meta.device.library_name.indexOf('Elite') >= 0) {
window.open('http://' + href + '/#/download/elite/excel/' + meta.id)
window.open('http://' + href + '/#/download/excel/' + meta.id)
} else if (meta.device.library_name.indexOf('Neulive') >= 0) {
window.open('http://' + href + '/#/download/neulive/csv/' + meta.id + '/raw/true', 'raw')
if (JSON.parse(meta.channels).some(el => el >= 256)) {
window.open('http://' + href + '/#/download/neulive/csv/' + meta.id + '/acc/true', 'acc')
}
window.open('http://' + href + '/#/download/csv-edf/' + meta.id)
}
},
replay: function (meta) {
@@ -686,6 +671,11 @@ export default {
})
}
},
spike: function (meta) {
this.$router.push({
path: '/admin/data-spike/' + meta.id,
})
},
},
mounted () {
},
@@ -11,16 +11,16 @@
<!-- {{ controller }} -->
<va-item :key="controller.device_mac" class="pa-0 ma-0"
>
<va-item-section avatar class="ml-2 mr-2">
<va-item-section avatar class="ml-4 mr-4">
<img
:src="getImageUrl()"
:alt="'BPS-CONTROLLER'"
width="300px"
height="300px"
width="200px"
height="200px"
contain
/>
</va-item-section>
<va-item-section class="ml-4" >
<va-item-section class="ml-5 mr-4" >
<va-item-label style="font-size: 2rem;">
{{ controller.wifi_name }}
</va-item-label>
@@ -46,7 +46,7 @@
<script>
import { mapActions, mapGetters, mapMutations } from 'vuex'
import api from '@/data/api/index'
import types from '@/store/modules/about/types'
import types from '@/store/modules/setting/types'
import eliteImgUrl from '@/assets/img/elite_img.png'
export default {
@@ -92,9 +92,9 @@ export default {
getImageUrl () {
return eliteImgUrl
},
...mapActions('about', [types.controller.init]),
...mapMutations('about', [types.controller.set, types.controller.updateByIndex]),
...mapGetters('about', [types.controller.getAll]),
...mapActions('setting', [types.controller.init]),
...mapMutations('setting', [types.controller.set, types.controller.updateByIndex]),
...mapGetters('setting', [types.controller.getAll]),
},
async mounted () {
this.initController({ api: api })
@@ -106,8 +106,8 @@ export default {
</script>
<style lang="scss">
.area-style {
height: 100%;
.area-style-setting-controller {
height: auto;
overflow-y: hidden;
overflow-x: hidden;
@@ -1,10 +1,10 @@
<template>
<div class="device-info">
<div class="row flex">
<draggable class="flex d-flex sm12 xl12 md12 xs12" v-model="devices" @start="drag=true" @end="drag=false" >
<draggable class="flex d-flex sm12 xl12 md12 xs12" v-model="devicesList" @start="drag=true" @end="drag=false" >
<div
class="flex d-flex sm12 xl3 md4 xs12 ma-0 area-style-info "
v-for="(device, index) in devices" :key="device.id"
class="flex d-flex sm12 xl3 md4 xs12 ma-0 area-style-setting-device "
v-for="(device, index) in devicesList" :key="device.id"
>
<va-card
:noPadding="true"
@@ -21,7 +21,7 @@
class="mr-1"
color="#babfc2"
style="cursor: pointer;"
@click.native.stop="removeCheck(index, device)"
@click.native.stop="remove(index, device)"
/>
</div>
</div>
@@ -58,8 +58,9 @@
</template>
<script>
import { mapActions, mapGetters, mapMutations } from 'vuex'
import { mapFields } from 'vuex-map-fields'
import api from '@/data/api/index'
import types from '@/store/modules/about/types'
import types from '@/store/modules/setting/types'
import neuliveImgUrl from '@/assets/img/neulive_img.png'
import eliteImgUrl from '@/assets/img/elite_img.png'
import draggable from 'vuedraggable'
@@ -76,6 +77,9 @@ export default {
}
},
computed: {
...mapFields('setting', [
'devicesList',
]),
devices: {
get () {
return this.getAllDevice()
@@ -94,7 +98,7 @@ export default {
timeStampToLocaleTime: function (time) {
return this.GLOBAL.dateToStr(time)
},
getImageUrl (libraryName) {
getImageUrl: function (libraryName) {
if (libraryName !== null) {
if (libraryName.indexOf('Neulive') >= 0) {
return neuliveImgUrl
@@ -104,40 +108,36 @@ export default {
}
return null
},
autoConnect (index, device) {
autoConnect: async function (index, device) {
device.auto_connect = !device.auto_connect
this.updateDatabaseByIDDevice({
id: device.id,
request: { auto_connect: device.auto_connect },
api: api,
})
this.updateByIndexDevice({ index: index, item: { auto_connect: device.auto_connect } })
await api.device.updateByID(device.id, { auto_connect: device.auto_connect })
},
removeCheck (index, device) {
/**
* TODO remove modal
*/
this.removeDevice({ id: device.id, index: index, device, deviceList: this.devices, api: api })
remove (index, device) {
console.log('removeDevcie', index, device)
this.removeDevice({ id: device.id, index: index, api: api })
},
...mapActions('about', [types.device.init, types.device.remove, types.device.reorder, types.device.updateDatabaseByID]),
...mapMutations('about', [types.device.set, types.device.updateByIndex]),
...mapGetters('about', [types.device.getAll]),
...mapActions('setting', [types.device.init, types.device.remove, types.device.reorder]),
...mapMutations('setting', [types.device.set, types.device.updateByIndex]),
...mapGetters('setting', [types.device.getAll]),
},
async mounted () {
this.initDevice({ api: api })
const devices = await api.device.getAll()
if (devices.status === 200) {
this.devicesList = devices.data
}
},
destroyed () {
},
}
</script>
<style lang="scss">
.area-style-info {
.area-style-setting-device {
height: auto;
overflow-y: hidden;
overflow-x: hidden;
@include media-breakpoint-only(xl) {
height: 48vh;
height: auto;
overflow-y: scroll;
overflow-x: hidden;
}
+70 -59
View File
@@ -21,7 +21,7 @@
/>
<div class="row row-equal">
<div v-if="showDeviceSetting" class="flex md12 xl4 xs12 sm12 area-style">
<div v-if="showDeviceSetting" class="flex md12 xl4 xs12 sm12">
<div class="row">
<div class="flex sm12 xl12 md12 xs12">
<task-devices :ref="'devices_ref'" @add="addDevice($event)" @remove="removeDevice($event)" @refreshSetting="refreshChartSetting($event)" class="pa-0" />
@@ -33,7 +33,7 @@
</div>
<div :class="chartAreaClass" class="area-style">
<div :class="chartAreaClass">
<div class="row" >
<div class="flex sm12 xl12 md12 xs12">
<div class="flex sm12 xl12 md12 xs12" style="text-align: center;">
@@ -130,6 +130,8 @@ export default {
showDeviceSetting: true,
chartAreaClass: ['flex', 'xs12', 'sm12', 'md12', 'xl8', 'pa-0'],
getParmDone: false,
deviceList: {},
}
},
@@ -141,6 +143,7 @@ export default {
async '+/get_device_info/+' (data, topic) {
await taskInfo.deviceInfo(String.fromCharCode.apply(null, data))
this.refreshDevicesWindow()
this.getParmDone = true
},
async '+/device_parameter/+' (data, topic) {
await taskInfo.deviceParameter(String.fromCharCode.apply(null, data))
@@ -152,47 +155,47 @@ export default {
},
async '+/hardware_device/+' (data, topic) {
await taskInfo.deviceInfo(String.fromCharCode.apply(null, data))
console.log(this.deviceList)
for (const d1 in taskInfo.getTaskInfo().deviceList) {
if (taskInfo.getTaskInfo().deviceList[d1].info.device_address.length > 0) {
const addr1 = taskInfo.getTaskInfo().deviceList[d1].info.device_address.join()
let parameter, content
for (const d2 in this.deviceList) {
const addr2 = this.deviceList[d2].info.device_address.join()
if (addr1 === addr2) {
console.log(this.deviceList[d2].parameterSet)
for (const p in this.deviceList[d2].parameterSet) {
if (p !== 'STIMULATING') {
if (parameter == null) {
parameter = p
} else {
parameter = parameter + '|' + p
}
let c
if (Array.isArray(this.deviceList[d2].parameterSet[p])) {
c = '0:' + this.deviceList[d2].parameterSet[p].length + '=' + this.deviceList[d2].parameterSet[p]
} else {
c = this.deviceList[d2].parameterSet[p]
}
// console.log(this.deviceList)
// for (const d1 in taskInfo.getTaskInfo().deviceList) {
// if (taskInfo.getTaskInfo().deviceList[d1].info.device_address.length > 0) {
// const addr1 = taskInfo.getTaskInfo().deviceList[d1].info.device_address.join()
// let parameter, content
// for (const d2 in this.deviceList) {
// const addr2 = this.deviceList[d2].info.device_address.join()
// if (addr1 === addr2) {
// console.log(this.deviceList[d2].parameterSet)
// for (const p in this.deviceList[d2].parameterSet) {
// if (p !== 'STIMULATING') {
// if (parameter == null) {
// parameter = p
// } else {
// parameter = parameter + '|' + p
// }
// let c
// if (Array.isArray(this.deviceList[d2].parameterSet[p])) {
// c = '0:' + this.deviceList[d2].parameterSet[p].length + '=' + this.deviceList[d2].parameterSet[p]
// } else {
// c = this.deviceList[d2].parameterSet[p]
// }
if (content == null) {
content = c
} else {
content = content + '|' + c
}
}
}
console.log('****', parameter, content)
// this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
// header: 'device_parameter/0',
// device: parseInt(d1),
// parameter: parameter,
// content: content,
// }))
}
}
}
}
// if (content == null) {
// content = c
// } else {
// content = content + '|' + c
// }
// }
// }
// console.log('****', parameter, content)
// // this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
// // header: 'device_parameter/0',
// // device: parseInt(d1),
// // parameter: parameter,
// // content: content,
// // }))
// }
// }
// }
// }
if (this.$refs.devices_ref != null) {
await this.$refs.devices_ref.loadingDone()
}
@@ -200,7 +203,7 @@ export default {
// listen datastream [ topic: controllerID/data_server/device_data_stream/deviceID/channel ]
async '+/data_server/device_data_stream/+/+' (data, topic) {
const topicSplit = topic.split('/')
if (!taskInfo.getDeviceInfoByRawID(parseInt(topicSplit[3])).info.isSync) {
if (this.getParmDone & !taskInfo.getDeviceInfoByRawID(parseInt(topicSplit[3])).info.isSync) {
if (chartData.defaultChartGenerator(taskInfo.getDeviceIDByRaw(parseInt(topicSplit[3])), null, 'device')) {
taskInfo.deviceStart(parseInt(topicSplit[3]))
}
@@ -230,6 +233,10 @@ export default {
break
case 'disconnected':
taskInfo.deviceDisconnected(mes[1])
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
header: 'get_device_info/0',
device: parseInt(mes[1]),
}))
break
case 'connected':
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
@@ -243,7 +250,12 @@ export default {
this.showShutdownModal = true
break
case 'start':
taskInfo.deviceStart(parseInt(mes[1]))
if (!taskInfo.getDeviceInfoByRawID(parseInt(parseInt(mes[1]))).info.isSync) {
taskInfo.deviceStart(parseInt(mes[1]))
if (chartData.defaultChartGenerator(taskInfo.getDeviceIDByRaw(parseInt(parseInt(mes[1]))), null, 'device')) {
taskInfo.deviceStart(parseInt(parseInt(mes[1])))
}
}
break
case 'stop':
if (mes[1] === 'all') {
@@ -438,7 +450,6 @@ export default {
// if have chart with device ID
for (const chart in chartData.getAllChartsData()) {
if (chartData.getAllChartsData()[chart].mappingID === deviceID) {
console.log('000')
return false
}
}
@@ -455,10 +466,10 @@ export default {
},
addDevice: async function (deviceID) {
this.mqttSub(taskInfo.getTaskInfo().controllerID + '/data_server/device_data_stream/' + taskInfo.getRawDeviceID(deviceID) + '/+')
const chart = this.addMappingChart(deviceID)
if (taskInfo.getDeviceInfo(deviceID).info.isSync) {
chartData.defaultChartGenerator(deviceID, chart, 'device')
}
this.addMappingChart(deviceID)
// if (taskInfo.getDeviceInfo(deviceID).info.isSync) {
// chartData.defaultChartGenerator(deviceID, chart, 'device')
// }
// console.log(chartData.getAllChartsData())
// console.log(taskInfo.getTaskInfo())
// await api.task.update({
@@ -599,17 +610,17 @@ export default {
</script>
<style lang="scss">
.area-style {
height: 100%;
overflow-y: hidden;
overflow-x: hidden;
// .area-style {
// height: 100%;
// overflow-y: hidden;
// overflow-x: hidden;
@include media-breakpoint-only(xl) {
height: 90vh;
overflow-y: scroll;
overflow-x: hidden;
}
}
// @include media-breakpoint-only(xl) {
// height: 94vh;
// overflow-y: scroll;
// overflow-x: hidden;
// }
// }
.no-dark-style-va-button {
&:focus {
@@ -268,6 +268,8 @@ export default {
this.nameCard = chartData.getAllChartsData()[this.chartID].cardName
await this.refreshMappingSelect()
this.initDone = true
this.timeIntervalInput = '500'
this.timeIntervalChange('500')
},
destroyed () {
clearInterval(this.resizeTimer)
@@ -1,5 +1,58 @@
<template>
<div style="height: 350px; overflow-y: scroll; overflow-x: hidden;">
<div>
<div class="flex row align--center">
<div class="flex md3 xl3 xs3 sm3">
<va-select
small
:label="'X SOURCE'"
v-model="deviceSelectX"
textBy="name"
:options="deviceOptions"
/>
</div>
<div class="flex md3 xl3 xs3 sm3">
<va-select
:on-change="channelSelectXChange()"
:label="'X CHANNEL'"
v-model="channelSelectX"
textBy="channel"
:options="channelOptionsX"
:disabled="channelSelectDisableX"
/>
</div>
<div class="flex md3 xl3 xs3 sm3">
<va-select
:label="'Y SOURCE'"
v-model="deviceSelectY"
textBy="name"
:options="deviceOptions"
/>
</div>
<div class="flex md3 xl3 xs3 sm3">
<va-select
:on-change="channelSelectYChange()"
:label="'Y CHANNEL'"
v-model="channelSelectY"
textBy="channel"
:options="channelOptionsY"
:disabled="channelSelectDisableY"
/>
</div>
<div class="flex md3 xl3 xs3 sm3">
<va-select
:label="'SubPlot'"
v-model="gridSelection"
textBy="value"
:options="gridOptions"
@click.enter="updateGridOptions()"
/>
</div>
<va-button :disabled="sourceAddStatus" class="flex md3 xl3 xs3 sm3 pa-1" @click="addRegisterDeviceChannel(gridSelection.id)" >
Add
</va-button><span class="ml-2" style="color: red;">{{error}}</span>
</div>
</div>
<va-tree-root>
<va-tree-category
style="font-size: 20px;"
@@ -7,64 +60,20 @@
:key="grid.index"
:label="'Subplot ' + grid.index">
<va-tree-node
v-for="series in grid.series"
v-for="series in gridSource[grid.index]"
:key="series.name"
>
<div class="flex row align--center">
<va-badge class="mb-0" style="font-size: 14px;" color="primary">{{series.name}}</va-badge>
<va-badge class="mb-0" style="font-size: 14px;" color="primary" outline>{{series.xAxis}}</va-badge>
<va-badge class="mb-0" style="font-size: 14px;" color="primary" outline>{{series.yAxis}}</va-badge>
<va-icon
<!-- <va-icon
name="ion ion-md-close"
color="info"
class="ml-2 pa-1 shrink"
style="cursor: pointer;"
@click.native="removeProduct(product)"
/>
</div>
</va-tree-node>
<va-tree-node>
<div class="flex row align--center">
<div class="flex md6 xl6 xs6 sm6">
<va-select
small
:label="'X SOURCE'"
v-model="deviceSelectX"
textBy="name"
:options="deviceOptions"
/>
</div>
<div class="flex md6 xl6 xs6 sm6">
<va-select
:on-change="channelSelectXChange()"
:label="'X CHANNEL'"
v-model="channelSelectX"
textBy="channel"
:options="channelOptionsX"
:disabled="channelSelectDisableX"
/>
</div>
<div class="flex md6 xl6 xs6 sm6">
<va-select
:label="'Y SOURCE'"
v-model="deviceSelectY"
textBy="name"
:options="deviceOptions"
/>
</div>
<div class="flex md6 xl6 xs6 sm6">
<va-select
:on-change="channelSelectYChange()"
:label="'Y CHANNEL'"
v-model="channelSelectY"
textBy="channel"
:options="channelOptionsY"
:disabled="channelSelectDisableY"
/>
</div>
<va-button :disabled="sourceAddStatus" class="flex md6 xl6 xs6 sm6 pa-1" @click="addRegisterDeviceChannel(grid.index)" >
Add
</va-button>
/> -->
</div>
</va-tree-node>
</va-tree-category>
@@ -96,17 +105,16 @@ export default {
},
data () {
return {
error: '',
gridSelection: '',
gridOptions: [
{
id: 0,
value: 0,
},
],
grids: this.gridInfo,
electronics: [
{ id: 1, name: 'Cellphones' },
{ id: 2, name: 'Camera Body Kits' },
{ id: 3, name: 'External HDDs' },
],
products: [
{ id: 4, name: 'Cables' },
{ id: 5, name: 'Monitors' },
{ id: 6, name: 'Keyboards' },
],
gridSource: chartData.getAllChartsData()[this.chartID].gridSource,
channelSelectX: '',
channelOptionsX: [],
channelSelectY: '',
@@ -117,6 +125,7 @@ export default {
deviceSelectY: '',
deviceOptions: taskInfo.getDeviceList(),
sourceAddStatus: true,
sourceList: [],
}
},
watch: {
@@ -248,6 +257,15 @@ export default {
},
},
methods: {
updateGridOptions () {
this.gridOptions = []
for (let i = 0; i < this.grids.length; i++) {
this.gridOptions.push({
id: i,
value: String(i),
})
}
},
channelSelectYChange: function () {
this.checkSourceAddStatus()
},
@@ -261,6 +279,7 @@ export default {
this.channelOptionsY = []
this.deviceSelectX = ''
this.deviceSelectY = ''
this.gridSelection = ''
},
addRegisterDeviceChannel: function (gridIndex) {
let deviceNameX
@@ -284,7 +303,14 @@ export default {
deviceNameY = 'Time'
}
chartData.register(this.chartID, gridIndex, deviceNameX, deviceIDX, channelX, deviceNameY, deviceIDY, channelY, null)
console.log(this.chartID, gridIndex, deviceNameX, deviceIDX, channelX, deviceNameY, deviceIDY, channelY, null)
console.log(this.deviceSelectX, this.channelSelectX, this.deviceSelectY, this.channelSelectY)
if (deviceNameX !== 'Time') {
this.sourceList.push([this.deviceSelectX.name, deviceIDX, channelX].toString())
}
if (deviceNameY !== 'Time') {
this.sourceList.push([this.deviceSelectY.name, deviceIDY, channelY].toString())
}
this.sourceAddStatus = true
this.clearSelectOptions()
},
@@ -292,8 +318,31 @@ export default {
chartData.updateGridList(this.chartID, chartData.getAllChartsData()[this.chartID].grid.length + 1)
this.grids = chartData.getAllChartsData()[this.chartID].grid
this.treeKey++
this.gridOptions = []
for (let i = 0; i < this.grids.length; i++) {
this.gridOptions.push({
id: i,
value: String(i),
})
}
console.log(chartData.getAllChartsData()[this.chartID])
},
checkSourceAddStatus () {
if (this.sourceList.indexOf([this.deviceSelectX.name, this.deviceSelectX.id, this.channelSelectX.id].toString()) >= 0) {
this.error = 'some source have been registered'
this.sourceAddStatus = true
return
}
if (this.sourceList.indexOf([this.deviceSelectY.name, this.deviceSelectY.id, this.channelSelectY.id].toString()) >= 0) {
this.sourceAddStatus = true
this.error = 'some source have been registered'
return
}
this.error = ''
if (this.gridSelection.id == null) {
this.sourceAddStatus = true
return
}
if (this.channelSelectX.id != null && this.channelSelectY.id != null) {
this.sourceAddStatus = false
} else if (this.channelSelectX.id != null && this.deviceSelectY.id < 0) {
@@ -313,6 +362,8 @@ export default {
removeProduct (product) {
this.products = this.products.filter(productToFilter => productToFilter !== product)
},
mounted () {
},
},
}
</script>
@@ -14,13 +14,13 @@
<span slot="header" >
<va-item :key="device.id" class="pa-0 ma-0"
>
<va-icon
<!-- <va-icon
name="ion ion-md-close"
color="danger"
class="ml-0 mr-3 pa-0 shrink"
style="cursor: pointer;"
@click.native.stop="removeCheck(device)"
/>
/> -->
<va-item-section avatar class="ml-0">
<va-avatar style="border-radius: 0;">
@@ -45,7 +45,7 @@
<va-item-section class="ml-3">
<va-item-label>
<va-button @click.stop="start(device)" v-if="(device.type == 'Neulive' && !device.isSync && !device.isStimulate) || (device.type == 'Elite' && !device.isSync)" class="px-2 py-0 mr-0" color="success" small>START</va-button>
<va-button @click.stop="stop(device)" v-if="(device.type == 'Neulive' && (device.isSync || device.isStimulate)) || (device.type == 'Elite' && device.isSync)" :disabled="device.stopping" class="px-2 py-0 mr-0" color="danger" small>STOP</va-button>
<va-button @click.stop="stop(device)" v-if="(device.type == 'Neulive' && (device.isSync || device.isStimulate)) || (device.type == 'Elite' && device.isSync)" class="px-2 py-0 mr-0" color="danger" small>STOP</va-button>
<va-button id="detectButton" @click.stop="detect(device)" v-if="device.type == 'Elite'" color="primary" class="px-2 py-0 mr-0" small>DETECT</va-button>
<va-button @click.stop="record(device)" v-if="device.type == 'Neulive' && !device.isSync" color="primary" class="px-2 py-0 mr-0 no-dark-style-va-button" small>RECORD</va-button>
<va-button @click.stop="stopRecord(device)" v-if="device.type == 'Neulive' && device.isSync" color="danger" class="px-2 py-0 mr-0 no-dark-style-va-button" small>STOP REC</va-button>
@@ -82,7 +82,7 @@
import EliteParameterWindow from '../parameter/EliteParameterWindow'
import NeuliveParameterWindow from '../parameter/NeuliveParameterWindow'
import FileControlWindow from '../meta/FileControlWindow'
import { mapGetters } from 'vuex'
import { mapActions } from 'vuex'
import types from '@/store/modules/task/content/types'
export default {
@@ -115,8 +115,8 @@ export default {
remove: function () {
this.$emit('remove', this.selectDevice)
},
start: function (device) {
const checkParam = this.getIsParamSetDoneParam()(device.type)
start: async function (device) {
const checkParam = await this.checkIfDeviceSetDoneParam({ device: device })
const status = checkParam.status
const message = checkParam.message
if (status === true) {
@@ -131,11 +131,13 @@ export default {
}
},
stop: function (device) {
this.$emit('stop', device)
this.pageToast('The device ' + device.name + ' has been stopped.')
if (!device.stopping) {
this.$emit('stop', device)
this.pageToast('The device ' + device.name + ' has been stopped.')
}
},
record: function (device) {
const checkParam = this.getIsRecordingParamSetDoneParam()(device.type)
record: async function (device) {
const checkParam = await this.checkIfRecordingSetDoneParam({ device: device })
const status = checkParam.status
const message = checkParam.message
if (status === true) {
@@ -150,9 +152,10 @@ export default {
}
},
stopRecord: function (device) {
device.stopping = true
this.$emit('stopRecord', device)
this.pageToast('The device ' + device.name + ' has been stopped.')
if (!device.stopping) {
this.$emit('stopRecord', device)
this.pageToast('The device ' + device.name + ' has been stopped.')
}
},
generateChart: function (device) {
this.$emit('generate', device)
@@ -195,8 +198,8 @@ export default {
this.$emit('detect', device)
this.pageToast('The device ' + device.name + ' has been detect.')
},
stimulate: function (device) {
const checkParam = this.getIsStimulationParamSetDoneParam()
stimulate: async function (device) {
const checkParam = await this.checkIfStimulationSetDoneParam({ device: device })
const status = checkParam.status
const message = checkParam.message
if (status === true) {
@@ -217,7 +220,7 @@ export default {
recoverDetectButton () {
document.getElementById('detectButton').className += ' no-dark-style-va-button'
},
...mapGetters('taskContent', [types.param.getIsRecordingParamSetDone, types.param.getIsStimulationParamSetDone, types.param.getIsParamSetDone]),
...mapActions('taskContent', [types.param.checkIfRecordingSetDone, types.param.checkIfStimulationSetDone, types.param.checkIfDeviceSetDone]),
},
destroyed () {
this.deviceList.length = 0
@@ -1,6 +1,6 @@
<template>
<div class="flex" :class="cardclass">
<va-card>
<va-card style="height: 88vh; overflow: auto;">
<template slot="header">
<va-icon name="fa fa-bluetooth-b mr-3" color="primary"/>
<h5 class="mt-0 mb-0">DEVICES</h5>
@@ -18,7 +18,6 @@
<task-device-list
:ref="'pdlist_ref'"
:deviceList="deviceList"
@remove="removeDevice($event)"
@start="startDevice($event)"
@stop="stopDevice($event)"
@record="record($event)"
@@ -29,7 +28,7 @@
@detect="detectDevice($event)"
/>
<div class="row flex xs12 md12 xl12 sm12 mt-2" style="text-align: center;">
<!-- <div class="row flex xs12 md12 xl12 sm12 mt-2" style="text-align: center;">
<div class="xs10 md10 xl10 sm10">
<va-select
small
@@ -43,7 +42,7 @@
REGISTER
</va-button>
</div>
</div>
</div> -->
</div>
</va-card>
@@ -106,11 +105,30 @@ export default {
// }
// }
// }
this.clearDevice()
console.log(this.deviceOptions)
for (let i = 4; i <= 7; i++) {
await this.addDevice(i)
if (taskInfo.getDeviceInfoByRawID(i).info.status === 'connected') {
const index = this.deviceList.findIndex(d => taskInfo.getRawDeviceID(d.id) === i)
if (index < 0) {
await this.addDevice(i)
}
} else {
console.log(this.deviceList)
const index = this.deviceList.findIndex(d => taskInfo.getRawDeviceID(d.id) === i)
console.log(index)
if (index > 0) {
await this.removeDevice(this.deviceList[index])
}
}
}
taskInfo.getDeviceList().length = 0
taskInfo.getDeviceList().push({
id: -1,
name: 'Time',
})
taskInfo.getDeviceList().push(...this.deviceList)
console.log(taskInfo.getDeviceList())
// for (let i = this.deviceOptions.length - 1; i >= 0; i--) {
// this.deviceSelect = this.deviceOptions[i]
// await this.addDevice(this.deviceOptions[i].id)
@@ -225,16 +243,18 @@ export default {
}
},
stopRecord: function (device) {
dataStreamBuffer.removeByDevice(taskInfo.getRawDeviceID(device.id))
if (taskInfo.getDeviceInfo(device.id).info.isSync) {
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
header: 'device_instruction/0',
device: taskInfo.getRawDeviceID(device.id),
instruction: 'stop_record',
}))
if (!device.stopping) {
dataStreamBuffer.removeByDevice(taskInfo.getRawDeviceID(device.id))
if (taskInfo.getDeviceInfo(device.id).info.isSync) {
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
header: 'device_instruction/0',
device: taskInfo.getRawDeviceID(device.id),
instruction: 'stop_record',
}))
}
device.isSync = false
device.stopping = true
}
device.isSync = false
device.stopping = true
},
sti: function (device) {
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
@@ -315,6 +335,9 @@ export default {
if (addr.length === 0) {
return false
}
if (taskInfo.getTaskInfo().taskDeviceID.indexOf(selectID) > 0) {
return false
}
await taskInfo.getTaskInfo().deviceAddrList.push(taskInfo.getDeviceInfoByRawID(selectID).info.device_address)
await taskInfo.getTaskInfo().taskDeviceID.push(selectID)
const _address = this.GLOBAL.transMac(taskInfo.getDeviceInfoByRawID(selectID).info.device_address)
@@ -249,7 +249,6 @@
<script>
import { taskInfo } from '../../../../data/task/TaskInfo'
import { chartData } from '../../../../data/task/ChartsManagement'
import { mapFields } from 'vuex-map-fields'
export default {
name: 'NeuliveParameterRecording',
@@ -267,6 +266,7 @@ export default {
},
data () {
return {
channelList: [],
ampGainSelect: null,
ampGainOptions: [
// {
@@ -457,11 +457,6 @@ export default {
axisChannelList: [],
}
},
computed: {
...mapFields('taskContent', [
'channelList',
]),
},
mqtt: {
},
methods: {
@@ -649,8 +644,13 @@ export default {
for (const chartID in charts) {
for (const seriesIndex in charts[chartID].series) {
if (charts[chartID].series[seriesIndex].yAxisSource[1] >= 256 && charts[chartID].series[seriesIndex].yAxisSource[1] <= 260) {
charts[chartID].yAxis[seriesIndex].min = -scaleRange
charts[chartID].yAxis[seriesIndex].max = scaleRange
if (parseInt(charts[chartID].series[seriesIndex].yAxisSource[1]) === 260) {
charts[chartID].yAxis[seriesIndex].min = 0
charts[chartID].yAxis[seriesIndex].max = Math.ceil(Math.sqrt(Math.pow(scaleRange, 2) * 3))
} else {
charts[chartID].yAxis[seriesIndex].min = -scaleRange
charts[chartID].yAxis[seriesIndex].max = scaleRange
}
}
}
}
@@ -361,9 +361,6 @@
<script>
import { taskInfo } from '../../../../data/task/TaskInfo'
import { mapActions, mapMutations } from 'vuex'
import { mapFields } from 'vuex-map-fields'
import types from '@/store/modules/task/content/types'
export default {
name: 'NeuliveParameterStimulation',
@@ -383,6 +380,9 @@ export default {
return {
showGuideLineModal: { ELECTRODE: false, PATTERN: false },
stimulationOn: false,
posChannelList: [2],
negChannelList: [1],
stimulateElectrodeSelect: null,
stimulateElectrodeOptions: [
{
label: 'Monopolar',
@@ -393,6 +393,7 @@ export default {
value: 1,
},
],
TRangeErrorMsgList: [{ id: 1, showError: false, errorMsg: [''] }, { id: 2, showError: false, errorMsg: [''] }, { id: 3, showError: false, errorMsg: [''] }, { id: 4, showError: false, errorMsg: [''] }],
cycleNumberSelect: null,
cycleNumberOptions: [
{
@@ -456,12 +457,6 @@ export default {
}
},
computed: {
...mapFields('taskContent', [
'posChannelList',
'negChannelList',
'stimulateElectrodeSelect',
'TRangeErrorMsgList',
]),
stimulateDisable () {
// posChannel and negChannel at least one && t1 ~ t4 > 0
const totalStiTime = (this.t1Range + this.t2Range + this.t3Range + this.t4Range) * 10
@@ -471,6 +466,20 @@ export default {
return true
}
},
TRangeStatusCheck () {
let flag = true
for (let i = 0; i < this.TRangeErrorMsgList.length; i++) {
if (this.TRangeErrorMsgList[i].showError === true) {
flag = false
break
}
}
if (flag === true) {
return true
} else {
return false
}
},
},
mqtt: {
},
@@ -487,24 +496,22 @@ export default {
},
refreshPattern () {
this.patternModeSelect = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_PATTERN ? 1 : 0
this.t1Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T1[0]
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t1RangeArray.fill(this.t1Range, 0)
this.t2Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T2[0]
this.t2RangeInput = String((this.t2Range * 10) / this.unitMultiply)
this.t2RangeArray.fill(this.t2Range, 0)
this.t3Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T3[0]
this.t3RangeInput = String((this.t3Range * 10) / this.unitMultiply)
this.t3RangeArray.fill(this.t3Range, 0)
this.t4Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T4[0]
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
this.t4RangeArray.fill(this.t4Range, 0)
if (this.patternModeSelect === 0) {
this.periodNumber = (this.t1Range + this.t4Range) * 10
} else {
this.periodNumber = (this.t1Range + this.t2Range + this.t3Range + this.t4Range) * 10
if (this.TRangeStatusCheck === true) { // if t1~t4 is not valid input, not need to refresh
this.t1Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T1[0]
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t1RangeArray.fill(this.t1Range, 0)
this.t2Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T2[0]
this.t2RangeInput = String((this.t2Range * 10) / this.unitMultiply)
this.t2RangeArray.fill(this.t2Range, 0)
this.t3Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T3[0]
this.t3RangeInput = String((this.t3Range * 10) / this.unitMultiply)
this.t3RangeArray.fill(this.t3Range, 0)
this.t4Range = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T4[0]
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
this.t4RangeArray.fill(this.t4Range, 0)
}
this.periodNumber = (this.t1Range + this.t2Range + this.t3Range + this.t4Range) * 10
this.periodNumberInput = String((this.periodNumber) / this.unitMultiply)
if (this.periodNumber !== 0) {
@@ -531,11 +538,6 @@ export default {
}
}
})
// }
// console.log('cha',stiChannelSet)
// console.log('pol',stiPolaritySet)
// console.log('pos',this.posChannelList)
// console.log('neg',this.negChannelList)
this.stimulateElectrodeSelect = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_H_BRIDGE ? 1 : 0
this.cycleNumberSelect = taskInfo.getDeviceInfo(this.deviceID).parameterSet.CONTINUOUS_STI ? 1 : 0
this.cycleNumber = taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_CYCLE_LIST[0]
@@ -545,17 +547,6 @@ export default {
this.amplitudeNumberInput = String(this.amplitudeNumber)
this.refreshPattern()
// console.log('STI_CONTINUOUS', this.cycleNumberSelect)
// console.log('STI_H_BRIDGE', this.stimulateElectrodeSelect)
// console.log('STI_PATTERN', this.patternModeSelect)
// console.log('STI_T1', taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T1[0])
// console.log('STI_T2', taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T2[0])
// console.log('STI_T3', taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T3[0])
// console.log('STI_T4', taskInfo.getDeviceInfo(this.deviceID).parameterSet.STI_T4[0])
// console.log(this.periodNumber)
// console.log(this.periodNumberInput)
// console.log(this.frequencyNumber)
// console.log(this.frequencyNumberInput)
},
transUnit () {
if (this.unitSelect === 0) { // s
@@ -677,6 +668,8 @@ export default {
}))
}
},
// [Region] Neulive sti pattern block
patternModeSelectChange () {
if (this.patternModeSelect == null) {
return false
@@ -689,11 +682,21 @@ export default {
content: this.patternModeSelect,
}))
if (this.patternModeSelect === 0) {
this.t2Range = 0
this.t3Range = 0
} else if (this.patternModeSelect === 1) {
this.t2Range = 0
this.t3Range = 10
this.t3RangeInput = String((this.t3Range * 10) / this.unitMultiply)
}
this.autoComputeT4()
const check4 = this.checkIndividualTRange(4, this.t4Range)
if (check4) {
this.updateTimeRange()
}
this.updateStiTRangeCheckParam()
},
frequencyNumberChange (val) {
if (this.frequencyNumberInput == null || isNaN(val)) {
@@ -726,6 +729,7 @@ export default {
if (check1 && check2 && check3 && check4) {
this.updateTimeRange()
}
this.updateStiTRangeCheckParam()
},
periodNumberChange (val) {
const usVal = parseFloat(val * this.unitMultiply) // us
@@ -759,23 +763,7 @@ export default {
if (check1 && check2 && check3 && check4) {
this.updateTimeRange()
}
},
autoResetAllTRange () {
if (this.patternModeSelect === 0) { // Monophasic
this.t1Range = 10
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t4Range = this.periodNumber / 10 - this.t1Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
} else if (this.patternModeSelect === 1) { // Biphasic
this.t1Range = 10
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t2Range = 10
this.t2RangeInput = String((this.t2Range * 10) / this.unitMultiply)
this.t3Range = 10
this.t3RangeInput = String((this.t3Range * 10) / this.unitMultiply)
this.t4Range = this.periodNumber / 10 - this.t1Range - this.t2Range - this.t3Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
}
this.updateStiTRangeCheckParam()
},
timeRangeChange (stiIimeSelect, inputVal) {
inputVal = parseFloat(inputVal)
@@ -802,90 +790,106 @@ export default {
break
}
const overallStatus = this.checkOverallTRange()
const status1 = this.checkIndividualTRange(1, this.t1Range)
const status2 = this.checkIndividualTRange(2, this.t2Range)
const status3 = this.checkIndividualTRange(3, this.t3Range)
const status4 = this.checkIndividualTRange(4, this.t4Range)
if (overallStatus && status1 && status2 && status3 && status4) {
this.resetAllTRangeErrorMsgParam({ totalT: 4 })
if (status1 && status2 && status3 && status4) {
this.resetAllTRangeErrorMsg({ totalT: 4 })
this.updateTimeRange()
}
this.updateStiTRangeCheckParam()
},
updateStiTRangeCheckParam () {
this.mqttPub(taskInfo.getTaskInfo().controllerID + '_user', JSON.stringify({
header: 'device_parameter/0',
device: taskInfo.getRawDeviceID(this.deviceID),
parameter: 'sti_trange_set_done',
content: this.TRangeStatusCheck,
}))
},
autoResetAllTRange () {
if (this.patternModeSelect === 0) { // Monophasic
this.t1Range = 10
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t4Range = this.periodNumber / 10 - this.t1Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
} else if (this.patternModeSelect === 1) { // Biphasic
this.t1Range = 10
this.t1RangeInput = String((this.t1Range * 10) / this.unitMultiply)
this.t2Range = 10
this.t2RangeInput = String((this.t2Range * 10) / this.unitMultiply)
this.t3Range = 10
this.t3RangeInput = String((this.t3Range * 10) / this.unitMultiply)
this.t4Range = this.periodNumber / 10 - this.t1Range - this.t2Range - this.t3Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
}
},
autoComputeT4 () {
if (this.patternModeSelect === 0) {
this.t4Range = (this.periodNumber / 10) - this.t1Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
this.pageToast('Auto complete T4 value')
} else if (this.patternModeSelect === 1) {
this.t4Range = (this.periodNumber / 10) - this.t1Range - this.t2Range - this.t3Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
this.pageToast('Auto complete T4 value')
}
this.t4Range = (this.periodNumber / 10) - this.t1Range - this.t2Range - this.t3Range
this.t4RangeInput = String((this.t4Range * 10) / this.unitMultiply)
this.pageToast('Auto complete T4 value')
},
checkOverallTRange () {
let status = true
if (this.t1Range > this.periodNumber) {
this.updateTRangeErrorMsgParam({ stiIimeSelect: 1, status: true, msg: 'T1 > Period' })
status = false
// [SubRegion] check sti T range setting helper function
// update individual t range error msg
updateTRangeErrorMsg (payload) {
const index = payload.stiIimeSelect - 1
this.TRangeErrorMsgList[index].showError = payload.status
this.TRangeErrorMsgList[index].errorMsg[0] = payload.msg
},
resetAllTRangeErrorMsg (payload) {
for (let i = 1; i <= payload.totalT; i++) {
this.updateTRangeErrorMsg({ stiIimeSelect: i, status: false, msg: '' })
}
if (this.t2Range > this.periodNumber) {
this.updateTRangeErrorMsgParam({ stiIimeSelect: 2, status: true, msg: 'T2 > Period' })
status = false
}
if (this.t3Range > this.periodNumber) {
this.updateTRangeErrorMsgParam({ stiIimeSelect: 3, status: true, msg: 'T3 > Period' })
status = false
}
if (this.t4Range > this.periodNumber) {
this.updateTRangeErrorMsgParam({ stiIimeSelect: 4, status: true, msg: 'T4 > Period' })
status = false
}
return status
},
checkIndividualTRange (stiIimeSelect, val) {
// val unit: us
val = parseFloat(val) * 10 // UI us
if (val < 0 || isNaN(val)) {
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: true, msg: 'T > 0' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: true, msg: 'T > 0' })
return false
}
switch (stiIimeSelect) {
case 1:
if (val < 100 || val > 10230) { // us
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: true, msg: '0.1ms <= T1 <= 10.23ms' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: true, msg: '0.1ms <= T1 <= 10.23ms' })
return false
} else {
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
return true
}
case 2:
if (val < 0 || val > 10230) { // us
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: true, msg: '0ms <= T2 <= 10.23ms' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: true, msg: '0ms <= T2 <= 10.23ms' })
return false
} else {
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
return true
}
case 3:
if (val < 100 || val > 10230) { // us
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: true, msg: '0.1ms <= T3 <= 10.23ms' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: true, msg: '0.1ms <= T3 <= 10.23ms' })
return false
} else {
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
return true
}
case 4:
if (val < 0 || val > 1310710) { // us
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: true, msg: '0ms <= T4 <= 1310.71ms' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: true, msg: '0ms <= T4 <= 1310.71ms' })
return false
} else {
this.updateTRangeErrorMsgParam({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
this.updateTRangeErrorMsg({ stiIimeSelect: stiIimeSelect, status: false, msg: '' })
return true
}
}
},
// [EndSubRegion]
// [EndRegion]
updateStiChannel () {
// stiChannelSet (+,-), stiPolaritySet (+)
let stiChannelSet = ''
@@ -960,12 +964,6 @@ export default {
},
)
},
...mapActions('taskContent', [types.param.resetAllTRangeErrorMsg]),
...mapMutations('taskContent', [types.param.initTRangeErrorMsgList, types.param.updateTRangeErrorMsg]),
},
created () {
this.initTRangeErrorMsgListParam({ totalT: 4 })
},
mounted () {
this.refresh()
@@ -99,21 +99,3 @@ export default {
},
}
</script>
<style lang="scss">
.flex-container {
display: flex;
}
.flex-child {
flex: 2;
}
.flex-child2 {
flex: 0;
}
.flex-child:first-child {
margin-right: 1px;
}
</style>
@@ -77,7 +77,7 @@ export default {
this.itemInputMax = val
this.inputeKey++
if (parseFloat(this.itemInputMax).toFixed(1) !== parseFloat(Math.round(parseFloat((taskInfo.getDeviceInfo(this.deviceID).parameterSet.FREQ_STOP * 0.0149) * 100)) / 100).toFixed(1)) {
if (parseFloat(this.itemInputMin) < 0.1) {
if (parseFloat(this.itemInputMax) < 0.1) {
this.$emit('parameterChange', [this.parameterName + '_START', 1])
} else {
this.$emit('parameterChange', [this.parameterName + '_STOP', Math.round((parseFloat(this.itemInputMax)) / 0.0149)])
@@ -111,16 +111,4 @@ export default {
.block:hover {
background-color: #d3d3d3;
}
.flex-container {
display: flex;
}
.flex-child {
flex: 1;
}
.flex-child:first-child {
margin-right: 20px;
}
</style>
+18
View File
@@ -0,0 +1,18 @@
import api from './index'
import auth from './auth'
async function spikeDetect (attrs) {
const _header = auth.getHeader()
try {
return await api.axios.post('/api/analysis/spike_detect', attrs, { headers: _header })
} catch (e) {
console.error(e)
return false
}
}
const analysis = {
spikeDetect,
}
export default analysis
+6 -6
View File
@@ -11,7 +11,7 @@ async function create (attrs) {
}
try {
return await api.axios.post('/api/collection/create', _data, { headers: _header })
return await api.axios.post('/api/file/collection/create', _data, { headers: _header })
} catch (e) {
console.error(e)
return false
@@ -21,7 +21,7 @@ async function create (attrs) {
async function getByID (id) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/collection/get_by_id/${id}`, { headers: _header })
return await api.axios.get(`/api/file/collection/get_by_id/${id}`, { headers: _header })
} catch (e) {
console.error(e)
return false
@@ -31,7 +31,7 @@ async function getByID (id) {
async function getByName (name) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/collection/get_by_name/${name}`, { headers: _header })
return await api.axios.get(`/api/file/collection/get_by_name/${name}`, { headers: _header })
} catch (e) {
console.error(e)
return false
@@ -41,7 +41,7 @@ async function getByName (name) {
async function getByParent (type, parent) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/collection/get_by_parent/${type}/${parent}`, { headers: _header })
return await api.axios.get(`/api/file/collection/get_by_parent/${type}/${parent}`, { headers: _header })
} catch (e) {
console.error(e)
return false
@@ -51,7 +51,7 @@ async function getByParent (type, parent) {
async function getAll () {
const _header = auth.getHeader()
try {
return await api.axios.get('/api/collection/get/all', { headers: _header })
return await api.axios.get('/api/file/collection/get/all', { headers: _header })
} catch (e) {
console.error(e)
return false
@@ -62,7 +62,7 @@ async function getByParentName (collectionType, parent, name) {
console.log(collectionType, parent, name)
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/collection/get_by_parent_name/${collectionType}/${parent}/${name}`, { headers: _header })
return await api.axios.get(`/api/file/collection/get_by_parent_name/${collectionType}/${parent}/${name}`, { headers: _header })
} catch (e) {
console.error(e)
return false
+2
View File
@@ -7,6 +7,7 @@ import device from './device'
import meta from './meta'
import raw from './raw'
import mini from './mini'
import analysis from './analysis'
// axios.defaults.baseURL = 'http://192.168.3.53:3000'
axios.defaults.baseURL = 'http://' + location.href.split('/')[2].split(':')[0] + ':3000'
@@ -21,6 +22,7 @@ const api = {
meta,
raw,
mini,
analysis,
}
export default api
+15 -4
View File
@@ -21,18 +21,28 @@ async function update (id, attrs) {
}
}
async function clearDel (id) {
async function clearDel () {
console.log('clearDelete')
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/file/meta/clear_deleted/${id}`, { headers: _header })
return await api.axios.get('/api/file/meta/clear_deleted', { headers: _header })
} catch (e) {
console.error(e)
return false
}
}
async function getCollectionByParent (type, parent) {
async function getByParentWithLimit (type, parent, limit, offset, order) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/file/meta/get_by_parent_with_limit/${type}/${parent}/${limit}/${offset}/${order}`, { headers: _header })
} catch (e) {
console.error(e)
return false
}
}
async function getByParent (type, parent) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/file/meta/get_by_parent/${type}/${parent}`, { headers: _header })
@@ -66,7 +76,8 @@ const meta = {
create,
update,
clearDel,
getCollectionByParent,
getByParent,
getByParentWithLimit,
getByID,
getByName,
}
+11
View File
@@ -31,6 +31,16 @@ async function getByID (channel, id) {
}
}
async function getAttrByID (channel, id, attr) {
const _header = auth.getHeader()
try {
return await api.axios.get(`/api/file/raw/get_attr_by_id/${channel}/${id}/${attr}`, { responseType: 'json', headers: _header })
} catch (e) {
console.error(e)
return false
}
}
async function getByIDs (channel, ids) {
const _header = auth.getHeader()
try {
@@ -75,6 +85,7 @@ const raw = {
update,
clearDel,
getByID,
getAttrByID,
getByIDs,
getAttrByIDs,
getByParent,
+16 -7
View File
@@ -762,7 +762,7 @@ export const chartData = {
_chartData[chartID].xAxis[_chartData[chartID].series[i].xAxisIndex].axisLabel.show = false
}
_chartData[chartID].chartDevice = 'neulive'
_chartData[chartID].chartYUnit = 'mV'
_chartData[chartID].chartYUnit = 'uV'
}
}
@@ -777,6 +777,8 @@ export const chartData = {
// TODO remove old buffer register
if (_chartID != null) {
// update only one
console.log(_chartID, _chartData)
chartData.reset(_chartID, _chartData[_chartID].mappingID, _chartData[_chartID].cardName)
_chartData[_chartID].mappingID = parseInt(item.id)
switch (_type) {
@@ -1014,17 +1016,22 @@ const chartRegister = (chartID, gridIndex, deviceNameX, deviceIDX, channelX, dev
}
if (parseInt(sourceList[1].channel) >= 257 && parseInt(sourceList[1].channel) <= 260) {
_chartData[chartID].yAxis[_yAxisIndex].min = -scaleRange
_chartData[chartID].yAxis[_yAxisIndex].max = scaleRange
if (parseInt(sourceList[1].channel) === 260) {
_chartData[chartID].yAxis[_yAxisIndex].min = 0
_chartData[chartID].yAxis[_yAxisIndex].max = Math.ceil(Math.sqrt(Math.pow(scaleRange, 2) * 3))
} else {
_chartData[chartID].yAxis[_yAxisIndex].min = -scaleRange
_chartData[chartID].yAxis[_yAxisIndex].max = scaleRange
}
_chartData[chartID].yAxis[_yAxisIndex].axisLabel.formatter = function (val) {
return parseFloat(val / 100) + 'g'
}
} else {
_chartData[chartID].yAxis[_yAxisIndex].axisLabel.formatter = function (val) {
return parseFloat(val / 1000).toFixed(2) + 'mV'
return parseFloat(val).toFixed(2) + 'uV'
}
}
_chartData[chartID].chartYUnit = 'mV' // nA * 1000
_chartData[chartID].chartYUnit = 'uV' // nA * 1000
} else if (sourceList[1].name === 'Elite') {
switch (sourceList[1].channel) {
case 1:
@@ -1104,8 +1111,10 @@ const chartRegister = (chartID, gridIndex, deviceNameX, deviceIDX, channelX, dev
xAxis: _axisList[0],
yAxis: _axisList[1],
}
_chartData[chartID].grid[gridIndex].series.push(_gridSeries)
if (_chartData[chartID].gridSource[gridIndex] == null) {
_chartData[chartID].gridSource[gridIndex] = []
}
_chartData[chartID].gridSource[gridIndex].push(_gridSeries)
// console.log(_chartData[chartID])
return _chartData[chartID]
+13 -13
View File
@@ -214,22 +214,22 @@ export const taskInfo = {
deviceDisconnected: async (deviceID) => {
const update = async () => {
taskInfo.getTaskInfo().deviceAddrList.splice(_taskInfo.taskDeviceID.indexOf(parseInt(deviceID)), 1)
deviceListRegister.forEach(device => {
if (device.id === _taskInfo.taskDeviceID.indexOf(parseInt(deviceID))) {
deviceListRegister.splice(deviceListRegister.indexOf(device), 1)
}
})
deviceListRegister.forEach(device => {
if (device.id > _taskInfo.taskDeviceID.indexOf(parseInt(deviceID))) {
device.id--
}
})
_taskInfo.taskDeviceID.splice(_taskInfo.taskDeviceID.indexOf(parseInt(deviceID)), 1)
// taskInfo.getTaskInfo().deviceAddrList.splice(_taskInfo.taskDeviceID.indexOf(parseInt(deviceID)), 1)
// deviceListRegister.forEach(device => {
// if (device.id === _taskInfo.taskDeviceID.indexOf(parseInt(deviceID))) {
// deviceListRegister.splice(deviceListRegister.indexOf(device), 1)
// }
// })
// deviceListRegister.forEach(device => {
// if (device.id > _taskInfo.taskDeviceID.indexOf(parseInt(deviceID))) {
// device.id--
// }
// })
// _taskInfo.taskDeviceID.splice(_taskInfo.taskDeviceID.indexOf(parseInt(deviceID)), 1)
_taskInfo.deviceList[parseInt(deviceID)].info.status = 'disconnected'
}
await update()
await updateAllList()
// await updateAllList()
},
controllerShutdown: async () => {
+1
View File
@@ -25,6 +25,7 @@ function newChart (chartID) {
snap: true,
},
grid: [],
gridSource: [],
xAxis: [],
yAxis: [],
dataZoom: null,
+1
View File
@@ -6,6 +6,7 @@ function newSeries (type, legendName, xAxisIndex, yAxisIndex, xAxisSource, yAxis
showSymbol: false,
showAllSymbol: false,
symbolSize: 2,
// [modified 2021/08/10] sampling method managed by vuex
// sampling: 'average',
// sampling: function (frame) {
// return frame[0]
+24 -11
View File
@@ -1,9 +1,10 @@
function getTime (val) {
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)
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 '
}
@@ -16,7 +17,9 @@ function getTime (val) {
if (millisecond > 0) {
label += millisecond + ' ms '
}
// const label = parseInt(parseInt(val / 1000000) / 60) + ' m ' + parseInt((val % 60000000) / 1000000) + ' s'
if (microsecond > 0 && hour === 0 && minute === 0 && second === 0) {
label += microsecond + ' µs '
}
return label
}
@@ -54,22 +57,32 @@ function newTooltip (chartID) {
if (param.seriesName === 'Time') {
value = param.value[1]
} else if (param.seriesName.split('_')[1] === 'T' && param.seriesName.split('_')[0] !== 'T') {
if (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'V') {
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 (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'I') {
} else if (physicalQuantity === 'I') {
value = parseFloat(param.value[0]).toFixed(3) + ' nA'
} else if (param.seriesName.split('_')[0][param.seriesName.split('_')[0].length - 1] === 'R') {
} 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') {
if (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'V') {
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 (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'I') {
} else if (physicalQuantity === 'I') {
value = parseFloat(param.value[1]).toFixed(3) + ' nA'
} else if (param.seriesName.split('_')[1][param.seriesName.split('_')[1].length - 1] === 'R') {
} 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)
}
+12
View File
@@ -0,0 +1,12 @@
function newChannel () {
return {
sampleRate: -1,
timeInterval: -1,
prevTime: -1,
prevData: -1,
downloadDataBuffer: [],
downloadIDList: [],
}
}
export default newChannel
+10 -6
View File
@@ -1,11 +1,15 @@
function newDownload () {
return {
_downloadIDList: [],
_downloadBuffer: [],
downloadIDList: {},
downloadBuffer: {},
progress: 0,
status: '',
device: 'Neulive or Elite',
format: 'csv or csv-edf or excel',
filename: '',
size: 0,
downloadStream: null,
meta: {},
channel: [],
channelInfo: {},
maxRows: 0,
maxColumns: 0,
}
}
@@ -0,0 +1,8 @@
function newDownloadStream () {
return {
fileStream: null,
writer: null,
}
}
export default newDownloadStream
+9 -42
View File
@@ -75,14 +75,9 @@ export default new Router({
component: EmptyParentComponent,
children: [
{
name: 'neulive-csv-download',
path: 'neulive/csv/:metaList?/:rawData?/:recover?',
component: () => import('../components/download/neulive/csv/NeuliveDownloadCsv.vue'),
},
{
name: 'elite-excel-download',
path: 'elite/excel/:metaList?',
component: () => import('../components/download/elite/excel/EliteDownloadExcel.vue'),
name: 'download',
path: ':format/:metaList',
component: () => import('../components/download/Download.vue'),
},
],
},
@@ -97,11 +92,6 @@ export default new Router({
component: () => import('../components/dashboard/Dashboard.vue'),
default: true,
},
{
name: 'about',
path: 'about',
component: () => import('../components/about/aboutContent.vue'),
},
{
name: 'devices',
path: 'devices',
@@ -117,6 +107,11 @@ export default new Router({
path: 'data-replay/elite/:metaList?',
component: () => import('../components/analysis/replay/elite/EliteDataReplay.vue'),
},
{
name: 'data-spike',
path: 'data-spike/:metaList?',
component: () => import('../components/analysis/spike/Spike.vue'),
},
{
name: 'task-content',
path: 'task-content/:taskID?',
@@ -130,38 +125,10 @@ export default new Router({
// keepAlive: true,
// },
},
// {
// name: 'download',
// path: 'download',
// component: () => import('../components/download/Download.vue'),
// // meta: {
// // keepAlive: true,
// // },
// },
// {
// name: 'neulive-csv-download',
// path: 'download/neulive/csv/:metaList?/:recover?',
// component: () => import('../components/download/neulive/csv/NeuliveDownloadCsv.vue'),
// },
{
name: 'neulive-excel-download',
path: 'download/neulive/excel/:metaList?',
component: () => import('../components/download/neulive/excel/NeuliveDownloadExcel.vue'),
},
{
name: 'elite-csv-download',
path: 'download/elite/csv/:metaList?/:recover?',
component: () => import('../components/download/elite/csv/EliteDownloadCsv.vue'),
},
// {
// name: 'elite-excel-download',
// path: 'download/elite/excel/:metaList?',
// component: () => import('../components/download/elite/excel/EliteDownloadExcel.vue'),
// },
{
name: 'settings',
path: 'settings',
component: () => import('../components/about/aboutContent.vue'),
component: () => import('../components/setting/settingContent.vue'),
// meta: {
// keepAlive: true,
// },
+12 -14
View File
@@ -2,18 +2,17 @@ import Vue from 'vue'
import Vuex from 'vuex'
import VuexI18n from 'vuex-i18n' // load vuex i18n module
// import app from './modules/app'
import state from './state'
import setting from './modules/setting'
import taskContent from './modules/task/content'
import replay from './modules/analysis/replay'
import neuliveCsv from './modules/file/neulive/csv'
import neuliveExcel from './modules/file/neulive/excel'
import eliteCsv from './modules/file/elite/csv'
import eliteExcel from './modules/file/elite/excel'
// import eliteFile from './modules/file/elite'
// import file from './modules/file'
import about from './modules/about'
import state from './state'
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 * as getters from './getters'
Vue.use(Vuex)
@@ -24,11 +23,10 @@ const store = new Vuex.Store({
modules: {
taskContent,
replay,
neuliveCsv,
neuliveExcel,
eliteCsv,
eliteExcel,
about,
spike,
setting,
download,
file,
},
state: state,
mutations: mutations,
@@ -1,66 +0,0 @@
import types from '@/store/modules/about/types'
const typePath = types.device
const deviceActs = {
/**
* init devices list
*
* @param {object} api
*/
[typePath.init]: async ({ state, getters, commit, dispatch }, payload) => {
const devices = await payload.api.device.getAll()
commit(typePath.set, { deviceList: devices.data })
},
/**
* reorder devices list
*
* @param {object} api
* @param {object} deviceList
*/
[typePath.reorder]: async ({ state, getters, commit, dispatch }, payload) => {
if (payload.deviceList.length > 0) {
// update connect_priority according to id
payload.deviceList.map((v, idx) => {
v.connect_priority = idx
})
// update database device's info
for (const device of payload.deviceList) {
dispatch(typePath.updateDatabaseByID, { id: device.id, request: { connect_priority: device.connect_priority }, api: payload.api })
}
commit(typePath.set, { deviceList: payload.deviceList })
}
},
/**
* update database device table data by ID with request
*
* @param {number} id
* @param {object} request
* @param {object} api
*/
[typePath.updateDatabaseByID]: async ({ state, getters, commit, dispatch }, payload) => {
await payload.api.device.updateByID(payload.id, payload.request)
},
/**
* delete device
*
* @param {number} id
* @param {number} index
* @param {Array} deviceList
* @param {object} api
*/
[typePath.remove]: async ({ state, getters, commit, dispatch }, payload) => {
if (payload.deviceList.length > 0) {
payload.deviceList.splice(payload.index, 1)
dispatch(typePath.reorder, { deviceList: payload.deviceList, api: payload.api })
await dispatch(typePath.updateDatabaseByID, { id: payload.id, request: { deleted: true }, api: payload.api })
await payload.api.device.del(payload.id)
}
},
}
export default deviceActs
@@ -261,62 +261,6 @@ const actions = {
}
},
/**
* set time format to xAxis by chart ID
*
* @param {object} chartAllData
* @param {int} chartID
* @param {int} xAxisIndex
* @param {object} series : chart series (echarts series)
*/
[typePath.setTimeXAxis]: (state, payload) => {
payload.chartAllData[payload.chartID].xAxis[payload.xAxisIndex].axisLabel.formatter = function (val) {
const label = parseInt(parseInt(val / 1000000) / 60) + ' m ' + parseInt((val % 60000000) / 1000000) + ' s'
return label
}
},
/**
* set value format by chart ID
*
* @param {object} chartAllData
* @param {int} chartID
* @param {string} axis ('x' or 'y')
* @param {int} axisIndex
* @param {optional, object} format
*/
[typePath.setAxisValueFormat]: (state, payload) => {
let name = '' // string
let unit = '' // string
let unitScale = 1 // int
if (payload.format !== undefined) {
name = payload.format.name // string
unit = payload.format.unit // string
unitScale = payload.format.unitScale // int
}
if (payload.axis === 'x') {
payload.chartAllData[payload.chartID].xAxis[payload.axisIndex].axisLabel.formatter = function (val) {
if (name !== 'time') {
const label = (val / unitScale).toFixed(2) + unit
return label
} else {
var hr = Math.floor(val / 3600000000)
var min = Math.floor((val - (hr * 3600000000)) / 60000000)
var sec = parseInt((val - (hr * 3600000000) - (min * 60000000)) / 1000000)
while (min.length < 2) { min = '0' + min }
while (sec.length < 2) { sec = '0' + min }
if (hr) hr += 'h'
return hr + min + 'm' + sec + 's'
}
}
} else if (payload.axis === 'y') {
payload.chartAllData[payload.chartID].yAxis[payload.axisIndex].axisLabel.formatter = function (val) {
const label = (val / unitScale).toFixed(2) + unit
return label
}
}
},
/**
* register device-channel to chartData
*
@@ -472,19 +416,6 @@ const actions = {
return chartData
},
/**
* reset chart axis label
*
* @param {object} chartData
*/
[typePath.resetChartDataAxisLabel]: async (state, payload) => {
const axisArray = payload.chartData.yAxis
for (let index = 0; index < axisArray.length; ++index) {
payload.chartData.yAxis[index].max = null
payload.chartData.yAxis[index].min = null
}
},
// /**
// * remove device-channel from chart TODO
// *
@@ -20,8 +20,6 @@ const types = {
pushSeries: 'pushSeriesChart',
pushAxis: 'pushAxisChart',
pushLegendName: 'pushLegendNameChart',
setTimeXAxis: 'setTimeXAxisChart',
setAxisValueFormat: 'setAxisValueFormatChart',
pushGridSeries: 'pushGridSeriesChart',
deleteByID: 'deleteByIDChart',
update: 'updateChart',
@@ -34,8 +32,6 @@ const types = {
updateRegistered: 'updateRegisteredChart',
updateAxisSplitNumber: 'updateAxisSplitNumberChart',
updateChartSampling: 'updateChartSamplingChart',
resetChartDataAxisLabel: 'resetChartDataAxisLabelChart',
},
meta: {
init: 'initMeta',
@@ -0,0 +1,506 @@
import newSeries from '@/factories/chart/seriesFactory'
import newChart from '@/factories/chart/chartFactory'
import newTooltip from '@/factories/chart/tooltipFactory'
import newDataZoom from '@/factories/chart/dataZoomFactory'
import types from '@/store/modules/analysis/spike/types'
const typePath = types.chart
// generate x axis format
function makeXAxis (gridIndex, type) {
const xAxis = {
type: type,
gridIndex: gridIndex,
axisLine: { show: false, onZero: false, lineStyle: { color: '#aaa' } },
axisTick: { show: false },
axisLabel: {
show: false,
showMinLabel: true,
showMaxLabel: true,
color: '#aaa',
},
splitLine: { show: true, lineStyle: { color: '#eee' } },
splitNumber: 5,
scale: true,
min: null,
max: null,
}
return xAxis
}
// generate y axis format
function makeYAxis (gridIndex, type) {
return {
type: type,
gridIndex: gridIndex,
axisTick: { show: false },
axisLine: { show: false }, // color: '#ccc' } },
axisLabel: { show: true, color: '#aaa', showMinLabel: true, showMaxLabel: true },
splitLine: { show: true, lineStyle: { color: '#eee' } },
splitNumber: 1,
animation: false,
scale: true,
min: 'dataMin',
max: 'dataMax',
}
}
// generate x axis format
function makeXAxisOverview (gridIndex, type) {
return {
type: type,
gridIndex: gridIndex,
axisLine: { show: false, onZero: false, lineStyle: { color: '#aaa' } },
axisTick: { show: false },
axisLabel: { show: true, color: '#aaa', showMinLabel: true, showMaxLabel: true },
splitLine: { show: false, lineStyle: { color: '#eee' } },
scale: true,
min: null,
max: null,
}
}
// generate y axis format
function makeYAxisOverview (gridIndex, type) {
return {
type: type,
gridIndex: gridIndex,
axisTick: { show: false },
axisLine: { show: false }, // color: '#ccc' } },
axisLabel: { show: true, color: '#aaa', showMinLabel: true, showMaxLabel: true },
splitLine: { show: true, lineStyle: { color: '#eee' } },
splitNumber: 1,
animation: false,
scale: true,
min: 'dataMin',
max: 'dataMax',
}
}
// get legend name
function getLegendName (sourceList) {
let legendName = ''
// source only 2 device
sourceList.forEach(source => {
switch (source.name) {
case 'Time':
legendName += 'T'
break
case 'Neulive':
switch (source.channel) {
case 256:
legendName += '' + source.id + '-X'
break
case 257:
legendName += '' + source.id + '-Y'
break
case 258:
legendName += '' + source.id + '-Z'
break
case 259:
legendName += '' + source.id + '-Mag'
break
default:
legendName += '' + source.id + 'CH' + String(parseInt(source.channel) + 1)
break
}
break
case 'Elite':
switch (source.channel) {
case 1:
legendName += '' + source.id + '-I'
break
case 2:
legendName += '' + source.id + '-V'
break
case 3:
legendName += '' + source.id + '-R'
break
default:
legendName += '' + source.id + 'CH' + source.channel
break
}
break
default:
legendName += '' + source.id + 'CH' + source.channel
break
}
legendName += '_'
})
legendName = legendName.substr(0, legendName.length - 1)
return legendName
}
// generate grid
function makeGrid (index, length, chartID) {
const _height = (82 / length) - 2 + '%'
let _top
if (chartID === 0) {
_top = index * (82 / length) + 8 + '%'
} else {
_top = index * (82 / length) + 6 + '%'
}
const _left = '8%'
const _right = '4%'
return {
index: index,
top: _top,
height: _height,
left: _left,
right: _right,
series: [],
}
}
const actions = {
/**
* init data to chartData via chartID
*
* @param {object} chartAllData
* @param {int} chartID : chart id
* @param {int} gridLength : length of chart grid
* @param {string} type
* @param {string} device
* @param {string} mode
* @return {object} : chart data
*/
[typePath.init]: ({ state, getters, commit, dispatch }, payload) => {
if (payload.chartAllData[payload.chartID] == null) {
payload.chartAllData[payload.chartID] = newChart(payload.chartID)
payload.chartAllData[payload.chartID].tooltip = newTooltip()
payload.chartAllData[payload.chartID].chartType = payload.type
payload.chartAllData[payload.chartID].chartDevice = payload.device
payload.chartAllData[payload.chartID].chartMode = payload.mode
dispatch(typePath.updateGridList, { chartAllData: payload.chartAllData, chartID: payload.chartID, gridLength: payload.gridLength })
return payload.chartAllData[payload.chartID]
} else {
return payload.chartAllData[payload.chartID]
}
},
/**
* init selectedChannel by metaIndex
*
* @param {int} metaIndex : chart id
*/
[typePath.initSelectedChannel]: ({ state, getters, commit, dispatch }, payload) => {
const channelList = JSON.parse(state.metaList[payload.metaIndex].channels)
state.selectedChannel = String(channelList[0])
state.selectedChannelOptions = []
channelList.forEach(function (item) {
if (item < 256) {
state.selectedChannelOptions.push({ label: String(item + 1), value: String(item) })
} else if (item === 256) {
state.selectedChannelOptions.push({ label: 'x', value: String(item) })
} else if (item === 257) {
state.selectedChannelOptions.push({ label: 'y', value: String(item) })
} else if (item === 258) {
state.selectedChannelOptions.push({ label: 'z', value: String(item) })
} else if (item === 259) {
state.selectedChannelOptions.push({ label: 'mag', value: String(item) })
}
})
},
/**
* update grid length by chart ID
*
* @param {object} chartAllData
* @param {int} chartID
* @param {int} gridLength : chart data (echarts options)
*/
[typePath.updateGridList]: async ({ state, getters, commit }, payload) => {
payload.chartAllData[payload.chartID].grid = []
for (let i = 0; i < payload.gridLength; i++) {
payload.chartAllData[payload.chartID].grid.push(makeGrid(i, payload.gridLength, payload.chartID))
}
},
/**
* reset data from chartData via chartID
*
* @param {object} chartAllData
* @param {int} chartID : chart id
* @param {int} gridLength : length of chart grid
* @return {object} : chart data
*/
[typePath.reset]: ({ state, getters, commit, dispatch }, payload) => {
// delete
for (const _series of payload.chartAllData[payload.chartID].series) {
_series.data.length = 0
_series.length = 0
}
delete payload.chartAllData[payload.chartID]
dispatch(typePath.init, { chartAllData: payload.chartAllData, chartID: payload.chartID, gridLength: payload.gridLength })
},
/**
* clear all chart data
*
* @param {int} chartAllData
*/
[typePath.clear]: ({ state, getters, commit }, payload) => {
for (const id in payload.chartAllData) {
for (const _series of payload.chartAllData[id].series) {
_series.data.length = 0
_series.length = 0
}
delete payload.chartAllData[id]
}
},
/**
* update datazoom by chart ID
*
* @param {object} chartAllData
* @param {int} chartID
*/
[typePath.updateDataZoom]: (state, payload) => {
// update dataZoom AxisIndex list
const dataZoomXAxisIndex = Array.apply(null, { length: payload.chartAllData[payload.chartID].xAxis.length }).map(Number.call, Number)
const dataZoomYAxisIndex = Array.apply(null, { length: payload.chartAllData[payload.chartID].yAxis.length }).map(Number.call, Number)
if (payload.chartAllData[payload.chartID].dataZoom == null) {
payload.chartAllData[payload.chartID].dataZoom = newDataZoom()
}
if (payload.chartAllData[payload.chartID].dataZoom != null) {
payload.chartAllData[payload.chartID].dataZoom[0].xAxisIndex = dataZoomXAxisIndex
payload.chartAllData[payload.chartID].dataZoom[1].xAxisIndex = dataZoomXAxisIndex
payload.chartAllData[payload.chartID].dataZoom[2].yAxisIndex = dataZoomYAxisIndex
}
},
/**
* register device-channel to chartData
*
* @param {object} chartAllData
* @param {int} chartID : chart id
* @param {int} gridIndex : index of grid
* @param {int} deviceNameX
* @param {int} deviceIDX
* @param {int} channelX
* @param {int} deviceNameY
* @param {int} deviceIDY
* @param {int} channelY
* @return {object} : chart data
*/
[typePath.register]: ({ state, getters, commit, dispatch }, payload) => {
const chartData = payload.chartAllData[payload.chartID]
const sourceList = [
{
name: payload.deviceNameX,
id: payload.deviceIDX,
channel: payload.channelX,
},
{
name: payload.deviceNameY,
id: payload.deviceIDY,
channel: payload.channelY,
},
]
if (chartData == null) {
return false
}
let xAxisType
let yAxisType
let xAxisSource
let yAxisSource
const legendName = getLegendName(sourceList)
switch (sourceList[0].name) {
case 'Time':
xAxisType = 'time'
xAxisSource = [-1, -1]
break
default:
xAxisType = 'value'
xAxisSource = [sourceList[0].id, sourceList[0].channel]
break
}
switch (sourceList[1].name) {
case 'Time':
yAxisType = 'time'
yAxisSource = [-1, -1]
break
default:
yAxisType = 'value'
yAxisSource = [sourceList[1].id, sourceList[1].channel]
break
}
// if AxisIndex != -1 then add new axis into list
let _xAxisIndex = -1
let _yAxisIndex = -1
// the grid have exist, so just use the axis in grid
for (let i = 0; i < chartData.xAxis.length; i++) {
if (chartData.xAxis[i].gridIndex === payload.gridIndex) {
_xAxisIndex = i
}
}
for (let i = 0; i < chartData.yAxis.length; i++) {
if (chartData.yAxis[i].gridIndex === payload.gridIndex) {
_yAxisIndex = i
}
}
// create new axis
if (_xAxisIndex === -1) {
if (payload.chartID === 0) {
chartData.xAxis.push(makeXAxisOverview(payload.gridIndex, xAxisType))
} else {
chartData.xAxis.push(makeXAxis(payload.gridIndex, xAxisType))
}
_xAxisIndex = chartData.xAxis.length - 1
}
if (_yAxisIndex === -1) {
if (payload.chartID === 0) {
chartData.yAxis.push(makeYAxisOverview(payload.gridIndex, yAxisType))
} else {
chartData.yAxis.push(makeYAxis(payload.gridIndex, yAxisType))
}
_yAxisIndex = chartData.yAxis.length - 1
}
// dispatch(typePath.updateDataZoom, { chartAllData: payload.chartAllData, chartID: payload.chartID })
// register series
const _series = newSeries('line', legendName, _xAxisIndex, _yAxisIndex, xAxisSource, yAxisSource, null, null)
chartData.series.push(_series)
// register series into grid
const _axisList = []
sourceList.forEach((source, index) => {
let _axis = 'X'
if (index !== 0) {
_axis = 'Y'
}
switch (source.name) {
case 'Time':
_axisList.push(_axis + ' AXIS = TIME')
break
case 'Neulive':
_axisList.push(_axis + ' AXIS = NEULIVE ' + source.id + ' CHANNEL ' + source.channel)
break
case 'Elite':
switch (source.channel) {
case 1:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' CURRENT')
break
case 2:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' VOLTAGE')
break
case 3:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' RESISTANCE')
break
default:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' CHANNEL ' + source.channel)
break
}
break
default:
_axisList.push(_axis + ' AXIS = DEVICE ' + source.id + ' CHANNEL ' + source.channel)
break
}
})
const _gridSeries = {
name: legendName,
xAxis: _axisList[0],
yAxis: _axisList[1],
}
chartData.grid[payload.gridIndex].series.push(_gridSeries)
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
@@ -0,0 +1,9 @@
import chartAct from './chartAct'
import metaAct from './metaAct'
import recAct from './recAct'
export default {
...chartAct,
...metaAct,
...recAct,
}
@@ -0,0 +1,23 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.meta
const metaActs = {
/**
* init meta
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : meta list info
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const promises = payload.metaIDList.map(async id => {
const metaRes = await payload.api.meta.getByID(id)
return metaRes.data[0]
})
const list = await Promise.all(promises)
commit(typePath.set, { list: list })
},
}
export default metaActs
@@ -0,0 +1,131 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.rec
const recActs = {
/**
* get rec by list index
*
* @param {objuct} api
* @return {object} : rec info
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const metaList = getters.getAllMeta
const _rec = {}
metaList.forEach(meta => {
_rec[meta.id] = {}
JSON.parse(meta.channels).forEach(ch => {
_rec[meta.id][ch] = {
1: [],
10: [],
100: [],
1000: [],
}
})
})
const getRawInfo = metaList.map(async meta => {
const getData = JSON.parse(meta.channels).map(async ch => {
const res = await payload.api.raw.getAttrByParent(ch, meta.id, 'id-start_time-end_time')
res.data.forEach(_data => {
_data.load = false
_data.data = []
if (parseInt(_data.end_time) <= parseInt(meta.time_duration)) {
if (parseInt(_data.end_time) < 0) {
_data.end_time = parseInt(meta.time_duration)
}
_rec[meta.id][ch][1].push(_data)
}
})
return res.data
})
return await Promise.all(getData)
})
await Promise.all(getRawInfo)
const getMiniInfo = metaList.map(async meta => {
const getData = JSON.parse(meta.channels).map(async ch => {
const miniIDs = Object.values(meta.mini_data[ch])
const combineMiniIDs = [].concat.apply([], miniIDs)
for (const id of combineMiniIDs) {
const res = await payload.api.mini.getAttrByID(ch, id, 'id-start_time-end_time-scale')
res.data.forEach(_data => {
_data.load = false
_data.data = []
if (parseInt(_data.end_time) <= parseInt(meta.time_duration)) {
if (parseInt(_data.end_time) < 0) {
_data.end_time = parseInt(meta.time_duration)
}
_rec[meta.id][ch][parseInt(_data.scale)].push(_data)
}
})
}
// const res = await payload.api.mini.getAttrByParent(ch, meta.id, 'id-start_time-end_time-scale')
// res.data.forEach(_data => {
// _data.load = false
// _data.data = []
// if (_data.end_time < 0) {
// _data.end_time = meta.time_duration
// }
// _rec[meta.id][ch][parseInt(_data.scale)].push(_data)
// })
// return res.data
})
return await Promise.all(getData)
})
await Promise.all(getMiniInfo)
commit(typePath.set, { data: _rec })
},
/**
* into data to rec
*
* @param {int} index
* @param {int} metaID
* @param {int} channel
* @param {int} scale
* @param {object} data
*/
[typePath.intoData]: async ({ state, getters, commit }, payload) => {
commit(typePath.updateDataByIndex, {
index: payload.index,
metaID: payload.metaID,
channel: payload.channel,
scale: payload.scale,
data: payload.data,
})
},
/**
* into data to rec
*
* @param {int} meta
* @param {str} device
*/
[typePath.initDisplaySetting]: async ({ state, getters, commit }, payload) => {
switch (payload.device) {
case 'Neulive':
commit(typePath.initDisplaySettingNeulive, {
meta: payload.meta,
})
break
case 'Elite':
commit(typePath.initDisplaySettingElite, {
meta: payload.meta,
})
break
default:
break
}
},
[typePath.updateDisplaySetting]: async ({ state, getters, commit }, payload) => {
commit(typePath.updateDisplaySetting, {
updateObject: payload.updateObject,
})
},
}
export default recActs
@@ -0,0 +1,9 @@
import metaGet from './metaGet'
import recGet from './recGet'
import { getField } from 'vuex-map-fields'
export default {
...metaGet,
...recGet,
getField,
}
@@ -0,0 +1,44 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.meta
const metaGets = {
/**
* get meta by list index
*
* @param {int} index
* @return {object} : meta info
*/
[typePath.getByIndex]: (state) => (index) => {
if (index == null || state.chartData[index] == null) {
return null
}
return state.metaList[index]
},
/**
* get meta by id
*
* @param {int} id
* @return {object} : meta info
*/
[typePath.getByID]: (state) => (id) => {
state.metaList.forEach(meta => {
if (meta.id === id) {
return meta
}
})
return null
},
/**
* get all meta info
*
* @return {Array} : meta list
*/
[typePath.getAll]: (state) => {
return state.metaList
},
}
export default metaGets
@@ -0,0 +1,61 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.rec
const recGets = {
/**
* get rec by meta
*
* @return {object} : rec data
*/
[typePath.getByMeta]: (state) => (meta) => {
return state.recInfo[meta]
},
/**
* get all rec data
* @return {Object} : rec data
*/
[typePath.getAll]: (state) => {
return state.recInfo
},
/**
* get cycle data mapping
* @param {string} : meta ID
* @param {int} : cycle channel index
* @param {object} : cycleDataList, list value: object
* @return {object} : cycle mapping
*/
[typePath.getCycleDict]: (state) => (cycleDataList) => {
// get cycle data
// const cycleDataList = state.recInfo[metaID][cycleDataID][state.currentScale]
const cycleDataListNum = cycleDataList.length
var cycleDict = {}
var cycleStartTime = 0
var cycleEndTime = 0
var currentCycleIndex = 0
for (let i = 0; i < cycleDataListNum; i++) {
const cycleDataArray = cycleDataList[i].data
for (let index = 0; index < cycleDataArray.length; index++) {
const value = cycleDataArray[index]
const time = parseInt(value[0])
const cycleIndex = parseInt(value[1])
if (cycleIndex === currentCycleIndex) {
if (currentCycleIndex in cycleDict) {
cycleEndTime = time
cycleDict[cycleIndex][1] = cycleEndTime
} else {
cycleDict[cycleIndex] = [cycleStartTime, cycleEndTime]
}
} else {
cycleStartTime = time
currentCycleIndex++
}
}
}
return cycleDict
},
}
export default recGets
@@ -0,0 +1,310 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.chart
// get legend name
function getLegendName (sourceList) {
let legendName = ''
// source only 2 device
sourceList.forEach(source => {
switch (source.name) {
case 'Time':
legendName += 'T'
break
case 'Neulive':
switch (source.channel) {
case 256:
legendName += '' + source.id + '-X'
break
case 257:
legendName += '' + source.id + '-Y'
break
case 258:
legendName += '' + source.id + '-Z'
break
case 259:
legendName += '' + source.id + '-Mag'
break
default:
legendName += '' + source.id + 'CH' + String(parseInt(source.channel) + 1)
break
}
break
case 'Elite':
switch (source.channel) {
case 1:
legendName += '' + source.id + '-I'
break
case 2:
legendName += '' + source.id + '-V'
break
case 3:
legendName += '' + source.id + '-R'
break
default:
legendName += '' + source.id + 'CH' + source.channel
break
}
break
default:
legendName += '' + source.id + 'CH' + source.channel
break
}
legendName += '_'
})
legendName = legendName.substr(0, legendName.length - 1)
return legendName
}
const chartMutations = {
/**
* update label formatter
*
* @param {int} chartID
* @param {string} axis ('x' or 'y')
* @param {int} axisIndex
* @param {int} channel // use for checking whether it is 3-axis
*/
[typePath.updateAxisLabel]: (state, payload) => {
if (payload.axis === 'x') {
const axisLabel = state.chartData[payload.chartID].xAxis[payload.axisIndex].axisLabel
axisLabel.formatter = function (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
}
} else if (payload.axis === 'y') {
const axisLabel = state.chartData[payload.chartID].yAxis[payload.axisIndex].axisLabel
axisLabel.formatter = function (val) {
let label
if (payload.channel < 256) {
const num = String(val)
label = num + 'µV'
} else {
label = String(val.toFixed(2))
}
return label
}
}
},
/**
* turn on/off axis event handler
*
* @param {int} chartID
* @param {string} axis ('x' or 'y')
* @param {bool} status (on/off)
*/
[typePath.updateAxisEventHandler]: (state, payload) => {
let _axis
if (payload.axis === 'x') {
_axis = state.chartData[payload.chartID].xAxis
} else if (payload.axis === 'y') {
_axis = state.chartData[payload.chartID].yAxis
}
const len = _axis.length
if (payload.status === 'on') {
for (let i = 0; i < len; i++) {
_axis[i].triggerEvent = true
}
} else if (payload.status === 'off') {
for (let i = 0; i < len; i++) {
_axis[i].triggerEvent = false
}
}
},
/**
* update axis value min & max
*
* @param {int} chartID
* @param {string} axis ('x' or 'y')
* @param {int} axisIndex
* @param {float} minVal
* @param {float} maxVal
*/
[typePath.updateAxisValMinMax]: (state, payload) => {
let grid
if (payload.axis === 'x') {
grid = state.chartData[payload.chartID].xAxis[payload.axisIndex]
} else if (payload.axis === 'y') {
grid = state.chartData[payload.chartID].yAxis[payload.axisIndex]
}
if (payload.minVal !== '') {
grid.min = payload.minVal
} else {
grid.min = 'dataMin'
}
if (payload.maxVal !== '') {
grid.max = payload.maxVal
} else {
grid.max = 'dataMax'
}
},
/**
* update series by cycle
*
* @param {int} chartID
* @param {int} seriesIndex
* @param {array} data
*/
[typePath.updateSeries]: (state, payload) => {
state.chartData[payload.chartID].series[payload.seriesIndex].data = payload.data
},
/**
* update registered chartData
*
* @param {object} chartAllData
* @param {int} chartID : chart id
* @param {int} gridIndex : index of grid
* @param {int} deviceNameX
* @param {int} deviceIDX
* @param {int} channelX
* @param {int} deviceNameY
* @param {int} deviceIDY
* @param {int} channelY
* @return {object} : chart data
*/
[typePath.updateRegistered]: ({ state, getters, commit, dispatch }, payload) => {
const chartData = payload.chartAllData[payload.chartID]
const sourceList = [
{
name: payload.deviceNameX,
id: payload.deviceIDX,
channel: payload.channelX,
},
{
name: payload.deviceNameY,
id: payload.deviceIDY,
channel: payload.channelY,
},
]
if (chartData == null) {
return false
}
const legendName = getLegendName(sourceList)
// register legendName
if (chartData.legend.data.indexOf(legendName) >= 0) {
// have been registered
return false
}
// push legendName
chartData.legend.data[payload.gridIndex] = legendName
// register series into grid
const _axisList = []
sourceList.forEach((source, index) => {
let _axis = 'X'
if (index !== 0) {
_axis = 'Y'
}
switch (source.name) {
case 'Time':
_axisList.push(_axis + ' AXIS = TIME')
break
case 'Neulive':
_axisList.push(_axis + ' AXIS = NEULIVE ' + source.id + ' CHANNEL ' + source.channel)
break
case 'Elite':
switch (source.channel) {
case 1:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' CURRENT')
break
case 2:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' VOLTAGE')
break
case 3:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' RESISTANCE')
break
default:
_axisList.push(_axis + ' AXIS = ELITE ' + source.id + ' CHANNEL ' + source.channel)
break
}
break
default:
_axisList.push(_axis + ' AXIS = DEVICE ' + source.id + ' CHANNEL ' + source.channel)
break
}
})
const _gridSeries = {
name: legendName,
xAxis: _axisList[0],
yAxis: _axisList[1],
}
chartData.grid[payload.gridIndex].series[payload.gridIndex] = _gridSeries
chartData.series[payload.gridIndex].name = legendName
return chartData
},
/**
* update axis 間距刻度數量
*
* @param {int} chartID
* @param {string} axis ('x' or 'y')
*/
[typePath.updateAxisSplitNumber]: (state, payload) => {
if (payload.axis === 'x') {
const axis = state.chartData[payload.chartID].xAxis
const len = axis.length
for (let i = 0; i < len; i++) {
axis[i].splitNumber = 5
}
} else if (payload.axis === 'y') {
const axis = state.chartData[payload.chartID].yAxis
const len = axis.length
let splitNum
if (len === 1) {
splitNum = 5
} else if (len > 1 && len <= 4) {
splitNum = 3
} else if (len > 4) {
splitNum = 1
}
for (let i = 0; i < len; i++) {
axis[i].splitNumber = splitNum
}
}
},
/**
* update chart sampling method
*
* @param {int} chartID
* @param {int} seriesIndex
* @param {object} method
*/
[typePath.updateChartSampling]: (state, payload) => {
state.chartData[payload.chartID].series[payload.seriesIndex].sampling = payload.method
},
}
export default chartMutations
@@ -0,0 +1,11 @@
import chartMut from './chartMut'
import metaMut from './metaMut'
import recMut from './recMut'
import { updateField } from 'vuex-map-fields'
export default {
...chartMut,
...metaMut,
...recMut,
updateField,
}
@@ -0,0 +1,31 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.meta
const metaMutations = {
/**
* set meta list
*
*/
[typePath.set]: async (state, payload) => {
state.metaList.length = 0
state.metaList = payload.list
},
/**
* Updates the meta info by index
*
* @param {number} index
* @param {any} item
*/
[typePath.updateByIndex]: function (state, payload) {
for (const item in payload) {
if (item !== 'index') {
state.metaList[payload.index][item] = payload[item]
}
}
},
}
export default metaMutations
@@ -0,0 +1,41 @@
import types from '@/store/modules/analysis/spike/types'
const typePath = types.rec
const recMutations = {
/**
* set rec list
*
* @param {object} data
*/
[typePath.set]: async (state, payload) => {
state.recInfo = payload.data
},
/**
* Updates the rec info by index
*
* @param {any} item
*/
[typePath.update]: function (state, payload) {
for (const item in payload) {
state.recInfo[item] = payload[item]
}
},
/**
* update data rec
*
* @param {int} index
* @param {int} metaID
* @param {int} channel
* @param {int} scale
* @param {object} data
*/
[typePath.updateDataByIndex]: function (state, payload) {
state.recInfo[payload.metaID][payload.channel][payload.scale][payload.index].data = payload.data
state.recInfo[payload.metaID][payload.channel][payload.scale][payload.index].load = true
},
}
export default recMutations
+22
View File
@@ -0,0 +1,22 @@
function newState () {
return {
recInfo: {},
metaList: [],
chartData: {},
chartOverviewData: {},
chartDetailData: {},
selectedChannel: '',
selectedChannelOptions: [],
cutoffFreq: 300,
filterOrder: 4,
selectedThreshold: 'over',
upperThreshold: 68,
lowerThreshold: 0,
waveLength: 1600,
preThreshold: 800,
refractoryPeriod: 800,
}
}
export default newState
+66
View File
@@ -0,0 +1,66 @@
const types = {
chart: {
init: 'initChart',
initSelectedChannel: 'initSelectedChannelChart',
reset: 'resetChart',
register: 'registerChart',
remove: 'removeChart',
clear: 'clearChart',
getDataByID: 'getDataByIDChart',
getAllData: 'getAllDataChart',
setChartData: 'setChartDataChart',
setTooltip: 'setTooltipChart',
setMarkArea: 'setMarkAreaChart',
setMarkAreaData: 'setMarkAreaDataChart',
setDataZoom: 'setDataZoomChart',
updateDataZoom: 'updateDataZoomChart',
updateGridList: 'updateGridListChart',
pushSeries: 'pushSeriesChart',
pushAxis: 'pushAxisChart',
pushLegendName: 'pushLegendNameChart',
pushGridSeries: 'pushGridSeriesChart',
deleteByID: 'deleteByIDChart',
update: 'updateChart',
updateSeries: 'updateSeriesChart',
updateAxis: 'updateAxisChart',
updateSeriesData: 'updateSeriesDataChart',
updateAxisLabel: 'updateAxisLabelChart',
updateAxisEventHandler: 'updateAxisEventHandlerChart',
updateAxisValMinMax: 'updateAxisValMinMaxChart',
updateRegistered: 'updateRegisteredChart',
updateAxisSplitNumber: 'updateAxisSplitNumberChart',
updateChartSampling: 'updateChartSamplingChart',
},
meta: {
init: 'initMeta',
set: 'setMeta',
setByApi: 'setByApiMeta',
updateByIndex: 'updateByIndexMeta',
getByIndex: 'getByIndexMeta',
getByID: 'getByIDMeta',
getAll: 'getAllMeta',
},
rec: {
init: 'initRec',
intoData: 'intoDataRec',
initDisplaySetting: 'initDisplaySettingRec',
initDisplaySettingNeulive: 'initDisplaySettingNeuliveRec',
initDisplaySettingElite: 'initDisplaySettingEliteRec',
set: 'setRec',
update: 'updateRec',
updateDataByIndex: 'updateDataByIndexRec',
updateDisplaySetting: 'updateDisplaySettingRec',
getAll: 'getAllRec',
getCycleDict: 'getCycleDictRec',
},
}
export default types
@@ -0,0 +1,37 @@
import types from '@/store/modules/download/types'
import streamSaver from 'streamsaver'
import newDownloadStream from '@/factories/file/downloadStreamFactory'
const typePath = types.downloadStream
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
const downloadStreamActs = {
[typePath.setDownloadStream]: function ({ state, getters, commit, dispatch }, payload) {
const downloadInfo = state.downloadInfo
if (downloadInfo.downloadStream === null) {
downloadInfo.downloadStream = newDownloadStream()
if (downloadInfo.downloadStream.fileStream === null) {
downloadInfo.downloadStream.fileStream = streamSaver.createWriteStream(downloadInfo.filename + '.csv', {
size: downloadInfo.size,
})
downloadInfo.downloadStream.writer = downloadInfo.downloadStream.fileStream.getWriter()
window.isSecureContext && window.addEventListener('beforeunload', evt => {
downloadInfo.downloadStream.writer.close()
})
}
}
},
[typePath.writeData]: function ({ state, getters, commit }, payload) {
const data = encode(payload.data)
data && state.downloadInfo.downloadStream.writer.write(data)
},
[typePath.writerClose]: function ({ state, getters, commit }, payload) {
state.downloadInfo.downloadStream.writer.close()
},
}
export default downloadStreamActs
@@ -0,0 +1,242 @@
import golbalMethods from '@/data/global/global'
import types from '@/store/modules/download/types'
const typePath = types.export
const typePathDownload = types.download
const typePathDownloadStream = types.downloadStream
const NeuliveParameterOrder = ['RECORDING_CH', 'AXIS_CH', 'AMP_GAIN', 'ADC_CLOCK']
const NeuliveAmpGain = [100, 400, 800]
const exportActs = {
[typePath.setHeader]: function ({ state, getters, commit, dispatch }, payload) {
const meta = state.metaInfo
const device = state.downloadInfo.device
const deviceHeader = ['Device', meta.device.device_name, golbalMethods.transMac(meta.device.device_address)]
const libraryHeader = ['Libaray', meta.device.library_name, meta.device.library_version]
const parameter = []
if (device[0] === 'Neulive') {
NeuliveParameterOrder.forEach(p => {
const value = meta.parameter_set[p]
if (p === 'RECORDING_CH') {
let result = '"'
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
result = result.slice(0, -1) + '"'
parameter.push(['Record channels', result])
} else if (p === 'AXIS_CH') {
let result = '"'
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
if (_value === '1') {
if (_index === 0) {
result = result + 'x,'
} else if (_index === 1) {
result = result + 'y,'
} else if (_index === 2) {
result = result + 'z,'
} else if (_index === 3) {
result = result + 'm,'
}
}
})
if (result.length !== 1) {
result = result.slice(0, -1)
}
result += '"'
parameter.push(['Accelrator channels', result])
} else if (p === 'AMP_GAIN') {
parameter.push(['Amp gain', NeuliveAmpGain[value]])
} else if (p === 'ADC_CLOCK') {
const recordChannel = JSON.parse(meta.channels).filter(ch => parseInt(ch) < 256)
parameter.push(['Sample Rate', parseInt(8e5 / (value * recordChannel.length))])
}
})
} else if (device[0] === 'Elite') {
const sortedParameterSet = {}
Object.keys(meta.parameter_set).sort().forEach(key => {
sortedParameterSet[key] = meta.parameter_set[key]
})
for (const p in sortedParameterSet) {
parameter.push([p, meta.parameter_set[p]])
}
}
const headersArray = [deviceHeader, libraryHeader, ...parameter]
let headersString = ''
headersArray.forEach(header => {
headersString += header.join(',') + '\r\n'
})
console.log(headersString)
dispatch(typePathDownloadStream.writeData, { data: headersString })
},
[typePath.setTitle]: function ({ state, getters, commit, dispatch }, payload) {
const downloadInfo = state.downloadInfo
let channelString = ''
let titleString = ''
let cycle = -1
for (const channel in downloadInfo.channelInfo) {
cycle = Math.max(cycle, downloadInfo.channelInfo[channel].downloadIDList.length)
}
if (downloadInfo.format === 'csv-edf') {
channelString += 'Time,'
}
while (cycle > 0) {
for (const channel of downloadInfo.channel) {
if (downloadInfo.device[0] === 'Neulive') {
if (downloadInfo.format === 'csv-edf') {
switch (channel) {
case 256:
channelString += 'x,'
break
case 257:
channelString += 'y,'
break
case 258:
channelString += 'z,'
break
case 259:
channelString += 'm,'
break
default:
channelString += `${parseInt(channel) + 1},`
break
}
} else {
switch (channel) {
case 256:
channelString += 'channel,x,'
titleString += 'Time[us],value[g],'
break
case 257:
channelString += 'channel,y,'
titleString += 'Time[us],value[g],'
break
case 258:
channelString += 'channel,z,'
titleString += 'Time[us],value[g],'
break
case 259:
channelString += 'channel,m,'
titleString += 'Time[us],value[g],'
break
default:
channelString += `channel,${parseInt(channel) + 1},`
titleString += 'Time[us],value[uV],'
break
}
}
} else if (downloadInfo.device[0] === 'Elite') {
let unitTitle = ''
switch (channel) {
case 0:
unitTitle = 'Current[nA]'
break
case 1:
unitTitle = 'Voltage[uV]'
break
case 2:
unitTitle = 'Resister[mohm]'
break
case 3:
unitTitle = 'Cycle[round]'
break
default:
unitTitle = ''
break
}
// channelString += `channel ${_ch + 1},,`
titleString += 'Time[us],' + unitTitle + ','
}
}
cycle -= 1
}
if (downloadInfo.device[0] === 'Neulive') {
channelString = channelString.slice(0, -1) + '\r\n'
titleString = titleString.slice(0, -1) + '\r\n'
if (downloadInfo.format === 'csv-edf') {
dispatch(typePathDownloadStream.writeData, { data: channelString })
} else {
dispatch(typePathDownloadStream.writeData, { data: channelString + titleString })
}
} else if (downloadInfo.device[0] === 'Elite') {
titleString = titleString.slice(0, -1) + '\r\n'
dispatch(typePathDownloadStream.writeData, { data: titleString })
}
console.log(channelString, titleString)
},
[typePath.setData]: function ({ state, getters, commit, dispatch }, payload) {
const downloadInfo = state.downloadInfo
const channels = state.downloadInfo.channel
let dataLength = null
for (const channel of channels) {
for (const data of downloadInfo.channelInfo[channel].downloadDataBuffer) {
if (dataLength === null) {
dataLength = data.length
}
if (!payload.last) {
dataLength = Math.min(dataLength, data.length)
}
if (payload.last) {
dataLength = Math.max(dataLength, data.length)
}
}
}
let dataString = ''
for (let row = 0; row < dataLength; row++) {
const rowData = []
for (const [chIndex, channel] of channels.entries()) {
const dataBuffer = downloadInfo.channelInfo[channel].downloadDataBuffer
for (const index in dataBuffer) {
if (dataBuffer[index][row] === undefined) {
if (downloadInfo.format === 'csv-edf' && payload.last) {
dataBuffer[index][row] = []
if (chIndex === 0) {
dataBuffer[index][row].push(((dataBuffer[index][row - 1][0] + downloadInfo.channelInfo[channel].timeInterval) / 1e6).toFixed(6))
}
dataBuffer[index][row].push(dataBuffer[index][row - 1][1])
rowData.push(...dataBuffer[index][row])
} else {
rowData.push(...['', ''])
}
} else {
if (downloadInfo.format === 'csv-edf') {
if (chIndex === 0) {
dataBuffer[index][row][0] = (dataBuffer[index][row][0] / 1e6).toFixed(6)
rowData.push(...dataBuffer[index][row])
} else {
rowData.push(dataBuffer[index][row][1])
}
} else {
rowData.push(...dataBuffer[index][row])
}
}
}
}
dataString = dataString + rowData.join(',') + '\r\n'
rowData.length = 0
}
dispatch(typePathDownloadStream.writeData, { data: dataString }).then(response => {
// for (const channel of state.downloadInfo.channel) {
// for (const data of state.downloadInfo.channelInfo[channel].downloadDataBuffer) {
// data.splice(0, dataLength)
// }
// }
commit(typePathDownload.delDownloadBuffer, { dataLength: dataLength })
})
},
}
export default exportActs
@@ -0,0 +1,9 @@
import rawAct from './rawAct'
import exportAct from './exportAct'
import downloadStreamAct from './downloadStreamAct'
export default {
...rawAct,
...exportAct,
...downloadStreamAct,
}
@@ -0,0 +1,159 @@
import types from '@/store/modules/download/types'
const typePath = types.raw
const rawActs = {
/**
* get raw data by single ID from database
*
* @param {Number} channel
* @param {Array} rawDataIDList
* @param {Number} time_duration
* @param {objuct} api
* @return {object} : raw data
*/
[typePath.rawDataByID]: async ({ state, getters, commit, dispatch }, payload) => {
const { channel, rawID, idx, api } = payload
const dataArray = []
const rawDataRes = await api.raw.getAttrByID(channel, rawID, 'id-channel-size-serial_number-start_time-end_time-data')
if (rawDataRes.data !== null) {
dispatch(typePath.parseRawData, { channel: channel, dataArray: dataArray, rawData: rawDataRes }).then(response => {
if (state.downloadInfo === undefined) {
return response
} else {
if (state.downloadInfo.channelInfo[channel].downloadDataBuffer[idx] === undefined) {
state.downloadInfo.channelInfo[channel].downloadDataBuffer[idx] = []
}
state.downloadInfo.channelInfo[channel].downloadDataBuffer[idx].push(...response)
}
})
}
},
/**
* get raw data by multi ID from database
*
* @param {Number} channel
* @param {Array} rawDataIDList
* @param {Number} time_duration
* @param {objuct} api
* @return {object} : raw data
*/
[typePath.rawDataByIDs]: async ({ state, getters, commit, dispatch }, payload) => {
const { channel, rawDataIDList, api } = payload
const rawDataRes = await api.raw.getAttrByIDs(channel, rawDataIDList, 'id-channel-size-serial_number-start_time-end_time-data')
if (rawDataRes.data !== null) {
dispatch(typePath.parseRawData, { rawData: rawDataRes, channel: channel }).then(response => {
}).catch(e => {
console.log(e)
})
}
},
[typePath.parseRawData]: function ({ state, getters, commit, dispatch }, payload) {
const { channel, rawData } = payload
const dataArray = []
for (const raw of rawData.data) {
if (raw.data !== null && raw.data.length > 0) {
const encodedData = raw.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) {
if (state.downloadInfo === undefined) {
dispatch(typePath.parseData, { channel: channel, rawData: unzipDatas[i] }).then(response => {
dataArray.push(...response)
}).catch(e => {
console.log(e)
})
} else {
if (state.downloadInfo.format === 'csv-edf') {
dispatch(typePath.parseFixedData, { channel: channel, rawData: unzipDatas[i] }).then(response => {
dataArray.push(...response)
}).catch(e => {
console.log(e)
})
} else {
dispatch(typePath.parseData, { channel: channel, rawData: unzipDatas[i] }).then(response => {
dataArray.push(...response)
}).catch(e => {
console.log(e)
})
}
}
}
}
encodedData.length = 0
unzipDatas.length = 0
}
}
delete payload.rawData
return dataArray
},
[typePath.parseData]: function ({ state, getters, commit }, payload) {
const { channel, rawData } = payload
const meta = state.metaInfo
const dataArray = []
for (let i = 0; i <= rawData.length - 1; i += 2) {
if (rawData[i].length !== 0) {
const time = parseInt(rawData[i])
const data = (channel >= 256 && channel <= 259) ? (rawData[i + 1] / 100).toFixed(3) : rawData[i + 1]
if (time <= parseInt(meta.time_duration)) {
const tmpData = [time, data]
dataArray.push(tmpData)
}
}
}
delete payload.rawData
return dataArray
},
[typePath.parseFixedData]: function ({ state, getters, commit }, payload) {
const { channel, rawData } = payload
const meta = state.metaInfo
const downloadInfo = state.downloadInfo
const timeInterval = state.downloadInfo.channelInfo[channel].timeInterval
const dataArray = []
for (let i = 0; i <= rawData.length - 1; i += 2) {
if (rawData[i].length !== 0) {
const time = Math.floor(parseInt(rawData[i]) / timeInterval) * timeInterval
const data = (channel >= 256 && channel <= 259) ? (rawData[i + 1] / 100).toFixed(3) : rawData[i + 1]
let timeDiff = time - downloadInfo.channelInfo[channel].prevTime
const timeDiffOrigin = timeDiff
const dataDiff = parseInt(data) - parseInt(downloadInfo.channelInfo[channel].prevData)
if (timeDiff >= 1.5 * timeInterval && downloadInfo.channelInfo[channel].prevTime > 0) {
console.log('data-recover', timeDiff)
let timeCorrection = 0
let dataIndex = 1
while (timeDiff > 0) {
timeCorrection += timeInterval
timeDiff -= timeInterval
if (timeDiff > 0) {
const _time = parseInt(downloadInfo.channelInfo[channel].prevTime) + parseInt(timeCorrection)
const _data = (parseInt(downloadInfo.channelInfo[channel].prevData) + (timeCorrection / timeDiffOrigin) * (dataDiff)).toFixed(3)
dataArray.push([_time, _data])
dataIndex = dataIndex + 1
}
}
}
if (time <= parseInt(meta.time_duration) && time !== downloadInfo.channelInfo[channel].prevTime) {
dataArray.push([time, data])
}
downloadInfo.channelInfo[channel].prevTime = time
downloadInfo.channelInfo[channel].prevData = data
}
}
delete payload.rawData
return dataArray
},
}
export default rawActs
@@ -0,0 +1,5 @@
import { getField } from 'vuex-map-fields'
export default {
getField,
}
@@ -0,0 +1,108 @@
import types from '@/store/modules/download/types'
const typePath = types.download
function chunkArray (rawDataIDArray, chunkSize) {
const arrayLength = rawDataIDArray.length
const results = []
for (let index = 0; index < arrayLength; index += chunkSize) {
results.push(rawDataIDArray.slice(index, index + chunkSize))
}
return results
}
const downloadMutations = {
/**
* set sample rate
*
*/
[typePath.setSampleRate]: function (state, payload) {
const meta = state.metaInfo
const device = state.downloadInfo.device
const channel = payload.channel
const timeRatio = 1e6
let sampleRate = null
let sampleRateDivideNum = 1
if (device[0] === 'Neulive') {
if (channel < 256) {
sampleRate = parseInt(800000 / parseInt(meta.configuration.ADC_CLOCK))
sampleRateDivideNum = state.downloadInfo.channel.length
} else if (channel >= 256 && channel <= 259) {
sampleRate = parseInt(800000 / parseInt(meta.configuration.ADC_CLOCK) / 78)
}
} else if (device[0] === 'Elite') {
if (device[1] === 'ZM15') {
if (parseInt(meta.configuration.MODE) === 0 || parseInt(meta.configuration.MODE) === 1) {
if (parseInt(meta.configuration.STEP_TIME) === 0) {
sampleRate = 2
} else if (parseInt(meta.configuration.STEP_TIME) === 1) {
sampleRate = 1
} else if (parseInt(meta.configuration.STEP_TIME) === 2) {
sampleRate = 0.5
}
} else {
sampleRate = parseInt(meta.configuration.SAMPLE_RATE) / 10
}
} else if (device[1] === 'EIS') {
if (parseInt(meta.configuration.MODE) === 0) {
sampleRate = 1
} else {
sampleRate = parseInt(meta.configuration.SAMPLE_RATE) / 10
}
} else {
sampleRate = parseInt(meta.configuration.SAMPLE_RATE) / 10
}
}
if (sampleRate !== null) {
state.downloadInfo.channelInfo[channel].sampleRate = sampleRate
state.downloadInfo.channelInfo[channel].timeInterval = parseInt((1 / (sampleRate / sampleRateDivideNum)) * timeRatio)
}
},
/**
* set download id list
*
*/
[typePath.setdownloadIdList]: function (state, payload) {
const channel = payload.channel
const meta = state.metaInfo
const downloadInfo = state.downloadInfo
if (downloadInfo.format === 'csv' || downloadInfo.format === 'csv-edf') {
downloadInfo.channelInfo[channel].downloadIDList.push(JSON.parse(JSON.stringify(meta.raw_data[channel])))
downloadInfo.maxColumns = 1
downloadInfo.maxRows = downloadInfo.channelInfo[channel].downloadIDList[0].length
} else if (downloadInfo.format === 'excel') {
// get sample rate
const sampleRate = downloadInfo.channelInfo[channel].sampleRate
// estimate total sample nums
const sampleNums = parseInt((parseInt(meta.time_duration) * sampleRate) / 1e6)
const idNums = meta.raw_data[channel].length
// estimate per raw_data contains x of samples
const samplePerRawData = parseInt(sampleNums / idNums)
// estimate 5e5 rows need how many ID
const rawDataPerColumnTemp = parseInt((5e5 / parseInt(samplePerRawData)) + 1)
const rawDataPerColumn = (rawDataPerColumnTemp > idNums) ? idNums : rawDataPerColumnTemp
const rawDataIDDoubleArray = chunkArray(meta.raw_data[channel], rawDataPerColumn)
downloadInfo.maxColumns = rawDataIDDoubleArray.length
for (const rawDataArray of rawDataIDDoubleArray) {
downloadInfo.maxRows = Math.max(downloadInfo.maxRows, rawDataArray.length)
downloadInfo.channelInfo[channel].downloadIDList.push(rawDataArray)
}
}
},
[typePath.delDownloadBuffer]: function (state, payload) {
for (const channel of state.downloadInfo.channel) {
for (const data of state.downloadInfo.channelInfo[channel].downloadDataBuffer) {
data.splice(0, payload.dataLength)
}
}
},
}
export default downloadMutations
@@ -1,7 +1,7 @@
import downloadMut from './downloadMut'
import exportMut from './exportMut'
import { updateField } from 'vuex-map-fields'
export default {
...downloadMut,
...exportMut,
updateField,
}
+9
View File
@@ -0,0 +1,9 @@
function newState () {
return {
metaList: [],
metaInfo: {},
downloadInfo: {},
}
}
export default newState
+26
View File
@@ -0,0 +1,26 @@
const types = {
download: {
setSampleRate: 'setSampleRateDownload',
setdownloadIdList: 'setdownloadIdListDownload',
delDownloadBuffer: 'delDownloadBufferDownload',
},
export: {
setHeader: 'setHeaderExport',
setTitle: 'setTitleExport',
setData: 'setDataExport',
},
raw: {
rawDataByID: 'rawDataByIDRaw',
parseRawData: 'parseRawDataRaw',
parseData: 'parseDataRaw',
parseFixedData: 'parseFixedDataRaw',
},
downloadStream: {
setDownloadStream: 'setDownloadStream',
writeData: 'writeDataDownloadStream',
writerClose: 'writerCloseDownloadStream',
},
}
export default types
+6
View File
@@ -0,0 +1,6 @@
import tableAct from './tableAct'
export default {
...tableAct,
}
@@ -0,0 +1,17 @@
import types from '@/store/modules/file/types'
const typePath = types.table
const tableActs = {
/**
* init meta
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : meta list info
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
},
}
export default tableActs
@@ -1,145 +0,0 @@
import newDownload from '@/factories/file/downloadFactory'
import types from '@/store/modules/file/elite/csv/types'
const typePath = types.download
// function getExcleNeedColumns (downloadInfo, excelFormat) {
// let maxIDNums = 1
// const meta = downloadInfo.meta
// const sampleRate = 800000 / meta.configuration.ADC_CLOCK
// const sampleNums = parseInt((parseInt(meta.time_duration) * sampleRate) / 1e6)
// for (const channelID in meta.raw_data) {
// maxIDNums = (meta.raw_data[channelID].length > maxIDNums) ? meta.raw_data[channelID].length : maxIDNums
// }
// const RowPerID = parseInt(sampleNums / maxIDNums)
// const IDPerColumn = (excelFormat) ? RowPerID / (parseInt(RowPerID / 5e5) + 1) : RowPerID
// for (const channelID in meta.raw_data) {
// downloadInfo.downloadIDList[channelID] = chunkArray(meta.raw_data[channelID], IDPerColumn)
// }
// }
// function chunkArray (rawDataIDArray, chunkSize) {
// const arrayLength = rawDataIDArray.length
// const results = []
// for (let index = 0; index < arrayLength; index += chunkSize) {
// results.push(rawDataIDArray.slice(index, index + chunkSize))
// }
// return results
// }
const downloadActs = {
/**
* init downloadList info
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : download list
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const promises = payload.metaIDList.map(async id => {
const metaRes = await payload.api.meta.getByID(String(id))
const downloadInfo = Object.assign(newDownload(), metaRes.data[0])
for (const channel of JSON.parse(downloadInfo.channels)) {
downloadInfo.downloadBuffer[channel] = []
}
// downloadInfo.downloadIDList = JSON.parse(JSON.stringify(downloadInfo.raw_data))
// getExcleNeedColumns(downloadInfo, true)
return downloadInfo
})
const list = await Promise.all(promises)
commit(typePath.set, { list: list })
return list
},
// [typePath.start]: async ({ state, getters, commit }, payload) => {
// },
/**
* get raw data by multi ID from database
*
* @param {Number} channel
* @param {Array} rawDataIDList
* @param {Number} time_duration
* @param {objuct} api
* @return {object} : raw data
*/
[typePath.rawDataByIDs]: async ({ state, getters, commit, dispatch }, payload) => {
const { channel, rawDataIDList, api } = payload
const rawDataRes = await api.raw.getAttrByIDs(channel, rawDataIDList, 'id-channel-size-serial_number-start_time-end_time-data')
dispatch(typePath.parseRawData, { rawData: rawDataRes, channel: channel })
},
[typePath.parseRawData]: function ({ state, getters, commit, dispatch }, payload) {
const { channel, rawData } = payload
for (const raw of rawData.data) {
if (raw.data !== null && raw.data.length > 0) {
const encodedData = raw.data.split('"***"')
// console.log(encodedData)
const unzipDatas = encodedData.map(ctx => {
return ctx.split(' ')
})
// console.log(unzipDatas)
for (let i = 0; i <= unzipDatas.length - 1; i++) {
if (unzipDatas[i].length > 0) {
dispatch(typePath.parseEachData, { rawData: unzipDatas[i], channel: channel })
}
}
encodedData.length = 0
// unzipDatas.length = 0
}
}
delete payload.rawData
},
[typePath.parseEachData]: function ({ state, getters, commit }, payload) {
const { channel, rawData } = payload
const meta = state.downloadingFile
const sampleRate = 800000 / meta.configuration.ADC_CLOCK
const averageSampleRate = sampleRate / JSON.parse(meta.channels).length
const timeInterval = parseInt((1 / averageSampleRate) * 1e6)
let prevTime = -1
let prevData = 0
for (let i = 0; i <= rawData.length - 1; i += 2) {
if (rawData[i].length !== 0) {
const time = parseInt(rawData[i])
const data = rawData[i + 1]
if (state.downloadingFile.recoverData) {
let timeDiff = time - prevTime
const timeDiffOrigin = timeDiff
if (timeDiff >= 1.8 * timeInterval && prevTime !== -1) {
console.log('data-recover', timeDiff)
let timeCorrection = 0
let dataIndex = 1
while (timeDiff > 0) {
timeCorrection += timeInterval
timeDiff -= timeInterval
if (timeDiff > 0) {
const _time = parseInt(prevTime) + parseInt(timeCorrection)
const _data = parseInt(prevData) + (timeCorrection / timeDiffOrigin) * (parseInt(data) - parseInt(prevData))
const tmpData = [_time, _data]
state.downloadingFile.downloadBuffer[channel].push(tmpData)
dataIndex = dataIndex + 1
}
}
}
}
if (time <= parseInt(meta.time_duration)) {
const tmpData = [time, data]
state.downloadingFile.downloadBuffer[channel].push(tmpData)
}
prevTime = time
prevData = data
}
}
delete payload.rawData
},
}
export default downloadActs
@@ -1,191 +0,0 @@
import streamSaver from 'streamsaver'
import types from '@/store/modules/file/elite/csv/types'
import golbalMethods from '@/data/global/global'
const typePath = types.export
const typePathDownload = types.download
const NeuliveParameterOrder = ['RECORDING_CH', 'AMP_GAIN', 'ADC_CLOCK']
const NeuliveAmpGain = [100, 400, 800]
let fileStream, writer
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
function intoFileStream (data, filename, size, close) {
if (!fileStream) {
if (filename !== undefined) {
fileStream = streamSaver.createWriteStream(filename + '.csv', {
size: size,
})
writer = fileStream.getWriter()
}
}
data = encode(data)
data && writer.write(data)
}
function closeWriter () {
writer.close()
}
const exportActs = {
/**
* init meta
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : meta list info
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const promises = payload.metaIDList.map(async id => {
const metaRes = await payload.api.meta.getByID(id)
return metaRes.data[0]
})
const list = await Promise.all(promises)
commit(typePath.set, { list: list })
},
// createHeadersString: function (headers, channel) {
[typePath.createHeadersString]: function ({ state, getters, commit, dispatch }, payload) {
const headers = state.downloadingFile
const device = ['Device', headers.device.device_name, golbalMethods.transMac(headers.device.device_address)]
const library = ['Libaray', headers.device.library_name, headers.device.library_version]
const parameter = []
if (headers.device.library_name.indexOf('Neulive') >= 0) {
NeuliveParameterOrder.forEach(p => {
const value = headers.parameter_set[p]
if (p === 'RECORDING_CH') {
let result = '"'
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
result = result.slice(0, -1) + '"'
parameter.push(['Record channels', result])
} else if (p === 'AMP_GAIN') {
parameter.push(['Amp gain', NeuliveAmpGain[value]])
} else if (p === 'ADC_CLOCK') {
parameter.push(['Sample Rate', parseInt(8e5 / (value * JSON.parse(headers.channels).length))])
}
})
} else if (headers.device.library_name.indexOf('Elite') >= 0) {
const sortedParameterSet = {}
Object.keys(headers.parameter_set).sort().forEach(key => {
sortedParameterSet[key] = headers.parameter_set[key]
})
for (const p in sortedParameterSet) {
parameter.push([p, headers.parameter_set[p]])
}
}
const headersArray = [device, library, ...parameter]
let headersString = ''
headersArray.forEach(header => {
headersString += header.join(',') + '\r\n'
})
dispatch(typePath.writeDataToFileStream, { data: headersString })
},
// createTitlesString: function (headers, cycle, channel) {
[typePath.createTitlesString]: function ({ state, getters, commit, dispatch }, payload) {
let channelString = ''
let titleString = ''
for (const _ch of JSON.parse(state.downloadingFile.channels)) {
if (state.downloadingFile.device.library_name.indexOf('Neulive') >= 0) {
switch (_ch) {
case 256:
channelString += 'channel,X,'
break
case 257:
channelString += 'channel,Y,'
break
case 258:
channelString += 'channel,Z,'
break
case 259:
channelString += 'channel,M,'
break
default:
channelString += `channel,${parseInt(_ch) + 1},`
break
}
titleString += 'Time[us],value[uV],'
} else if (state.downloadingFile.device.library_name.indexOf('Elite') >= 0) {
const _time = 'Time[us]'
let _title = ''
switch (_ch) {
case 0:
_title = 'Current[nA]'
break
case 1:
_title = 'Voltage[uV]'
break
case 2:
_title = 'Resister[mohm]'
break
default:
_title = ''
break
}
// channelString += `channel ${_ch + 1},,`
titleString += _time + ',' + _title + ','
}
}
if (state.downloadingFile.device.library_name.indexOf('Neulive') >= 0) {
channelString = channelString.slice(0, -1) + '\r\n'
titleString = titleString.slice(0, -1) + '\r\n'
dispatch(typePath.writeDataToFileStream, { data: channelString + titleString })
} else if (state.downloadingFile.device.library_name.indexOf('Elite') >= 0) {
titleString = titleString.slice(0, -1) + '\r\n'
dispatch(typePath.writeDataToFileStream, { data: titleString })
}
},
[typePath.createDataString]: function ({ state, getters, commit, dispatch }, payload) {
const data = state.downloadingFile.downloadBuffer
let dataLength
for (const channel in data) {
if (dataLength === undefined) {
dataLength = data[channel].length
}
if (data[channel].length < dataLength) {
dataLength = data[channel].length
}
}
let dataString = ''
for (let idx = 0; idx < dataLength; idx++) {
const rowData = []
for (const channel in data) {
if (data[channel][idx] === undefined) {
rowData.push(...['', ''])
} else {
rowData.push(...data[channel][idx])
}
}
dataString = dataString + rowData.join(',') + '\r\n'
rowData.length = 0
}
dispatch(typePath.writeDataToFileStream, { data: dataString })
commit(typePathDownload.removeDownloadBuffer, { dataLength: dataLength })
dataString = ''
// return dataString
},
[typePath.writeDataToFileStream]: function ({ state, getters, commit }, payload) {
intoFileStream(payload.data, state.downloadingFile.name, state.downloadingFile.size)
payload.data = null
},
[typePath.closeFileStream]: function ({ state, getters, commit }, payload) {
closeWriter()
},
}
export default exportActs
@@ -1,7 +0,0 @@
import downloadAct from './downloadAct'
import exportAct from './exportAct'
export default {
...downloadAct,
...exportAct,
}
@@ -1,70 +0,0 @@
import types from '@/store/modules/file/elite/csv/types'
const typePath = types.download
const downloadGets = {
/**
* get download info by list index
*
* @param {int} index
* @return {object} : download info
*/
[typePath.getByIndex]: (state) => (index) => {
if (index == null || state.downloadList[index] == null) {
return null
}
return state.downloadList[index]
},
/**
* get download by id
*
* @param {int} id
* @return {object} : download info
*/
[typePath.getByID]: (state) => (id) => {
state.downloadList.forEach(meta => {
if (meta.id === id) {
return meta
}
})
return null
},
/**
* get downloading info
*
* @return {object} : download info
*/
[typePath.getDownloading]: (state) => {
return state.downloadingFile
},
/**
* get all download info
*
* @return {Array} : meta list
*/
[typePath.getAll]: (state) => {
return state.downloadList
},
/**
* get raw data ID remaining length
*
* @return {Array} : meta list
*/
[typePath.getRemainIDLength]: (state) => (index) => {
let IDListLength = 0
const downloadIDList = state.downloadList[index].raw_data
for (const channel in downloadIDList) {
if (downloadIDList[channel].length > IDListLength) {
IDListLength = downloadIDList[channel].length
}
}
return IDListLength
},
}
export default downloadGets
@@ -1,32 +0,0 @@
import types from '@/store/modules/file/elite/csv/types'
const typePath = types.export
const exportGets = {
/**
* get rec by meta
*
* @return {object} : rec data
*/
[typePath.getByMeta]: (state) => (meta) => {
return state.recInfo[meta]
},
/**
* get all rec data
* @return {Object} : rec data
*/
[typePath.getAll]: (state) => {
return state.recInfo
},
[typePath.getAllDisplaySetting]: (state) => {
return JSON.parse(JSON.stringify(state.displaySettingInfo))
},
[typePath.getDisplaySettingByKey]: (state) => (key) => {
return JSON.parse(JSON.stringify(state.displaySettingInfo[key]))
},
}
export default exportGets
@@ -1,7 +0,0 @@
import downloadGet from './downloadGet'
import exportGet from './exportGet'
export default {
...downloadGet,
...exportGet,
}
@@ -1,59 +0,0 @@
import types from '@/store/modules/file/elite/csv/types'
const typePath = types.download
const downloadMutations = {
/**
* set download list
*
*/
[typePath.set]: async (state, payload) => {
state.downloadList.length = 0
state.downloadList = payload.list
},
/**
* set downloading file
*
*/
[typePath.setDownloading]: async (state, payload) => {
state.downloadingFile = state.downloadList[0]
},
/**
* update donwload data
*
*/
[typePath.updatedownloadBuffer]: (state, payload) => {
},
/**
* remove download data
*
*/
[typePath.removeDownloadBuffer]: (state, payload) => {
// console.log('removeDownloadBuffer', payload.dataLength)
for (const channel of JSON.parse(state.downloadingFile.channels)) {
state.downloadingFile.downloadBuffer[channel].splice(0, payload.dataLength)
// delete state.downloadingFile.downloadBuffer[channel]
// state.downloadingFile.downloadBuffer[channel] = []
}
},
// /**
// * Updates the meta info by index
// *
// * @param {number} index
// * @param {any} item
// */
// [typePath.updateByIndex]: function (state, payload) {
// for (const item in payload) {
// if (item !== 'index') {
// state.metaList[payload.index][item] = payload[item]
// }
// }
// },
}
export default downloadMutations
@@ -1,165 +0,0 @@
import types from '@/store/modules/file/elite/csv/types'
const typePath = types.export
const exportMutations = {
/**
* set rec list
*
* @param {object} data
*/
[typePath.set]: async (state, payload) => {
state.recInfo = payload.data
},
/**
* Updates the rec info by index
*
* @param {any} item
*/
[typePath.update]: function (state, payload) {
for (const item in payload) {
state.recInfo[item] = payload[item]
}
},
/**
* update data rec
*
* @param {int} index
* @param {int} metaID
* @param {int} channel
* @param {int} scale
* @param {object} data
*/
[typePath.updateDataByIndex]: function (state, payload) {
state.recInfo[payload.metaID][payload.channel][payload.scale][payload.index].data = payload.data
state.recInfo[payload.metaID][payload.channel][payload.scale][payload.index].load = true
},
/**
* init rec display setting
*
* @param {object} meta
*/
[typePath.initDisplaySetting]: function (state, payload) {
state.displaySettingInfo.valueRanges = []
state.displaySettingInfo.visibleGridIndexes = Array.from({ length: Object.keys(payload.meta.mini_data).length }, (v, i) => i.toString()) // gives [0, 1, ... , length-1]
for (const key of Object.keys(payload.meta.mini_data)) {
if (parseInt(key) < 255) {
state.displaySettingInfo.valueRanges.push({ id: parseInt(key), name: 'ch ' + (parseInt(key) + 1), xMin: '', xMax: '', yMin: '', yMax: '', showXMin: true, showXMax: true, showYMin: true, showYMax: true })
} else if (parseInt(key) === 256) {
state.displaySettingInfo.valueRanges.push({ id: parseInt(key), name: 'x', xMin: '', xMax: '', yMin: '', yMax: '', showXMin: true, showXMax: true, showYMin: true, showYMax: true })
} else if (parseInt(key) === 257) {
state.displaySettingInfo.valueRanges.push({ id: parseInt(key), name: 'y', xMin: '', xMax: '', yMin: '', yMax: '', showXMin: true, showXMax: true, showYMin: true, showYMax: true })
} else if (parseInt(key) === 258) {
state.displaySettingInfo.valueRanges.push({ id: parseInt(key), name: 'z', xMin: '', xMax: '', yMin: '', yMax: '', showXMin: true, showXMax: true, showYMin: true, showYMax: true })
} else if (parseInt(key) === 259) {
state.displaySettingInfo.valueRanges.push({ id: parseInt(key), name: 'magnitude', xMin: '', xMax: '', yMin: '', yMax: '', showXMin: true, showXMax: true, showYMin: true, showYMax: true })
}
}
state.displaySettingInfo.valueFormatOptions = {
0: [ // Voltage
{
id: 0,
unit: 'uV',
unitScale: 1,
},
{
id: 1,
unit: 'mV',
unitScale: 1000,
},
],
1: [ // Time
{
id: 0,
unit: 'us',
unitScale: 1,
},
{
id: 1,
unit: 'ms',
unitScale: 1000,
},
{
id: 2,
unit: 's',
unitScale: 1000000,
},
],
}
state.displaySettingInfo.valueFormatX = { id: 2, unit: 's', unitScale: 1000000 } // [todo] init by meta mode
state.displaySettingInfo.valueFormatY = { id: 0, unit: 'uV', unitScale: 1 } // [todo] init by meta mode
state.displaySettingInfo.channelXSelect = { id: 1, description: 'Time' }
state.displaySettingInfo.channelYSelect = { id: 0, description: 'Voltage' }
state.displaySettingInfo.ignoreGridIndexes = []
state.displaySettingInfo.filename = payload.meta.name
const params = payload.meta.parameter_set
state.displaySettingInfo.parameterInfo = { recording: '', stimulation: '' }
state.displaySettingInfo.parameterInfo.recording += 'Duration: ' + parseInt(parseInt(payload.meta.time_duration / 1000000) / 60) + ' m ' + parseInt((payload.meta.time_duration % 60000000) / 1000000) + ' s<br/>'
state.displaySettingInfo.parameterInfo.recording += '<br/>Parameter: <br/>'
for (const [key, value] of Object.entries(params)) {
if (!(key.includes('sti') || key.includes('STI'))) {
state.displaySettingInfo.parameterInfo.recording += ' ' + key + ': ' + value + '<br/>'
}
}
state.displaySettingInfo.parameterInfo.stimulation += 'Duration: ' + parseInt(parseInt(payload.meta.time_duration / 1000000) / 60) + ' m ' + parseInt((payload.meta.time_duration % 60000000) / 1000000) + ' s<br/>'
state.displaySettingInfo.parameterInfo.stimulation += '<br/>Parameter: <br/>'
for (const [key, value] of Object.entries(params)) {
if (key.includes('sti') || key.includes('STI')) {
state.displaySettingInfo.parameterInfo.stimulation += ' ' + key + ': ' + value + '<br/>'
}
}
state.displaySettingInfo.editInfo = {
info: { id: payload.meta.id },
changedName: '',
success: false,
error: false,
errorMessagesAll: ['File name contains illegal character', 'File name empty', 'File name exist'],
errorMessages: [],
}
state.displaySettingInfo.deleteInfo = {
path: '',
name: '',
metaID: payload.meta.id,
rawChannelAndIDArrays: [], // list of lists
miniChannelAndIDArrays: [], // list of lists
}
},
/**
* update rec display setting
*
* @param {object} updateObject { settingInfoKey, value (array or object) }
*/
[typePath.updateDisplaySetting]: function (state, payload) {
const infoNames = Object.keys(payload.updateObject)
for (const infoName of infoNames) {
const updateObject = payload.updateObject[infoName]
if (typeof updateObject === 'object' && updateObject !== null) { // is object
if (Object.prototype.hasOwnProperty.call(updateObject, 'id')) {
const id = updateObject.id
const index = state.displaySettingInfo[infoName].findIndex(x => x.id === id)
for (const [key, value] of Object.entries(updateObject)) {
state.displaySettingInfo[infoName][index][key] = value
}
} else {
for (const [key, value] of Object.entries(updateObject)) {
state.displaySettingInfo[infoName][key] = value
}
}
} else if (Array.isArray(updateObject)) { // is array
state.displaySettingInfo[infoName] = updateObject
} else { // e.g. string
state.displaySettingInfo[infoName] = updateObject
}
}
},
}
export default exportMutations
@@ -1,9 +0,0 @@
function newState () {
return {
downloadList: [],
downloadingFile: null,
exportingFile: null,
}
}
export default newState
-57
View File
@@ -1,57 +0,0 @@
const types = {
download: {
init: 'initDownload',
reset: 'resetDownload',
register: 'registerDownload',
remove: 'removeDownload',
clear: 'clearDownload',
start: 'startDownload',
pause: 'pauseDonwload',
delete: 'deleteDonload',
allRawDatas: 'allRawDatasDownload',
rawDataByIDs: 'rawDataByIDsDownload',
rawDataByID: 'rawDataByIDDownload',
getMiniDataByIDs: 'getMiniDataByIDsDownload',
getMiniDataByID: 'getMiniDataByIDDownload',
parseRawData: 'parseRawDataDownload',
parseEachData: 'parseEachDataDownload',
parseEachDataWithHeaderTime: 'parseEachDataWithHeaderTimeDownload',
set: 'setDownload',
setDownloading: 'setDownloadingDownload',
removeDownloadBuffer: 'removeDownloadBufferDownload',
getByIndex: 'getByIndexDownload',
getDownloading: 'getDownloadingDownload',
getAll: 'getAllDownload',
getRemainIDLength: 'getRemainIDLengthDownload',
// setByApi: 'setByApiMeta',
// updateByIndex: 'updateByIndexMeta',
// getByID: 'getByIDMeta',
},
export: {
init: 'initExport',
reset: 'resetExport',
register: 'registerExport',
remove: 'removeExport',
clear: 'clearExport',
createHeadersString: 'createHeadersStringExport',
createTitlesString: 'createTitlesStringExport',
createDataString: 'createDataStringExport',
writeDataToFileStream: '.writeDataToFileStreamExport',
closeFileStream: 'closeFileStreamExport',
csvFormat: 'csvFormatExport',
csvMillionFormat: 'csvMillionFormatExport',
edfFormat: 'edfFormatExport',
},
}
export default types
@@ -1,179 +0,0 @@
import newDownload from '@/factories/file/downloadFactory'
import types from '@/store/modules/file/elite/excel/types'
const typePath = types.download
function getExcleNeedColumns (downloadInfo, excelFormat) {
let maxIDNums = 1
const meta = downloadInfo
const channelNums = JSON.parse(downloadInfo.channels).length
let sampleRate = 0
if (meta.configuration.MODE === 0 || meta.configuration.MODE === 1) {
if (meta.configuration.STEP_TIME === 0) {
sampleRate = 2
} else if (meta.configuration.STEP_TIME === 1) {
sampleRate = 1
} else if (meta.configuration.STEP_TIME === 2) {
sampleRate = 0.5
}
} else {
sampleRate = parseInt(meta.configuration.SAMPLE_RATE) / 10
}
console.log('sampleRatePerChannel', sampleRate)
// estimate total sample nums
const sampleNums = parseInt((parseInt(meta.time_duration) * sampleRate) / 1e6)
for (const channelID in meta.raw_data) {
console.log('channelIDNums', channelID, meta.raw_data[channelID].length)
maxIDNums = (meta.raw_data[channelID].length > maxIDNums) ? meta.raw_data[channelID].length : maxIDNums
}
// estimate per raw_data contains ? of samples
const rowPerRawData = parseInt(sampleNums / maxIDNums)
// console.log(sampleRatePerChannel, meta.time_duration, maxIDNums, sampleNums, rowPerRawData)
// estimate 5e5 rows need how many ID
const needRows = (excelFormat) ? parseInt((5e5 / parseInt(rowPerRawData)) + 1) : rowPerRawData
const needRealRows = (needRows > maxIDNums) ? maxIDNums : needRows
downloadInfo.needRows = needRealRows
for (const channelID in meta.raw_data) {
let channelIndex = parseInt(channelID)
const rawDataIDDoubleArray = chunkArray(meta.raw_data[channelID], needRealRows)
downloadInfo.needColumns = rawDataIDDoubleArray.length
for (const rawDataArray of rawDataIDDoubleArray) {
downloadInfo.downloadIDList[channelIndex] = rawDataArray
channelIndex += channelNums
}
}
}
function chunkArray (rawDataIDArray, chunkSize) {
const arrayLength = rawDataIDArray.length
const results = []
for (let index = 0; index < arrayLength; index += chunkSize) {
results.push(rawDataIDArray.slice(index, index + chunkSize))
}
return results
}
const downloadActs = {
/**
* init downloadList info
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : download list
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const promises = payload.metaIDList.map(async id => {
const metaRes = await payload.api.meta.getByID(String(id))
const downloadInfo = Object.assign(newDownload(), metaRes.data[0])
// for (const channel of JSON.parse(downloadInfo.channels)) {
// downloadInfo._downloadBuffer[channel] = []
// }
console.log('downloadInfo.raw_data', downloadInfo.raw_data)
downloadInfo.downloadIDList = JSON.parse(JSON.stringify(downloadInfo.raw_data))
getExcleNeedColumns(downloadInfo, true)
return downloadInfo
})
const list = await Promise.all(promises)
commit(typePath.set, { list: list })
return list
},
// [typePath.start]: async ({ state, getters, commit }, payload) => {
// },
/**
* get raw data by multi ID from database
*
* @param {Number} channel
* @param {Array} rawDataIDList
* @param {Number} time_duration
* @param {objuct} api
* @return {object} : raw data
*/
[typePath.rawDataByIDs]: async ({ state, getters, commit, dispatch }, payload) => {
const { channel, rawDataIDList, api } = payload
const channelNums = JSON.parse(state.downloadingFile.channels).length
const rawDataRes = await api.raw.getAttrByIDs(channel % channelNums, rawDataIDList, 'id-channel-size-serial_number-start_time-end_time-data')
dispatch(typePath.parseRawData, { rawData: rawDataRes, channel: channel })
},
[typePath.parseRawData]: function ({ state, getters, commit, dispatch }, payload) {
const { channel, rawData } = payload
for (const raw of rawData.data) {
if (raw.data !== null && raw.data.length > 0) {
const encodedData = raw.data.split('"***"')
// console.log(encodedData)
const unzipDatas = encodedData.map(ctx => {
return ctx.split(' ')
})
// console.log(unzipDatas)
for (let i = 0; i <= unzipDatas.length - 1; i++) {
if (unzipDatas[i].length > 0) {
dispatch(typePath.parseEachData, { rawData: unzipDatas[i], channel: channel })
}
}
encodedData.length = 0
// unzipDatas.length = 0
}
}
delete payload.rawData
},
[typePath.parseEachData]: function ({ state, getters, commit }, payload) {
const { channel, rawData } = payload
const meta = state.downloadingFile
// const sampleRate = 800000 / meta.configuration.ADC_CLOCK
// const averageSampleRate = sampleRate / JSON.parse(meta.channels).length
// const timeInterval = parseInt((1 / averageSampleRate) * 1e6)
// let prevTime = -1
// let prevData = 0
for (let i = 0; i <= rawData.length - 1; i += 2) {
if (rawData[i].length !== 0) {
const time = parseInt(rawData[i])
const data = rawData[i + 1]
// let timeDiff = time - prevTime
// const timeDiffOrigin = timeDiff
// if (timeDiff >= 1.8 * timeInterval && prevTime !== -1) {
// let timeCorrection = 0
// let dataIndex = 1
// while (timeDiff > 0) {
// timeCorrection += timeInterval
// timeDiff -= timeInterval
// if (timeDiff > 0) {
// const _time = parseInt(prevTime) + parseInt(timeCorrection)
// const _data = parseInt(prevData) + (timeCorrection / timeDiffOrigin) * (parseInt(data) - parseInt(prevData))
// const tmpData = [_time, _data]
// if (state.downloadingFile.downloadBuffer[channel] === undefined) {
// state.downloadingFile.downloadBuffer[channel] = []
// }
// state.downloadingFile.downloadBuffer[channel].push(tmpData)
// dataIndex = dataIndex + 1
// }
// }
// }
if (time <= parseInt(meta.time_duration)) {
const tmpData = [time, data]
if (state.downloadingFile.downloadBuffer[channel] === undefined) {
state.downloadingFile.downloadBuffer[channel] = []
}
state.downloadingFile.downloadBuffer[channel].push(tmpData)
}
// prevTime = time
// prevData = data
}
}
// console.log(channel, state.downloadingFile.downloadBuffer[channel].length)
delete payload.rawData
},
}
export default downloadActs
@@ -1,202 +0,0 @@
import streamSaver from 'streamsaver'
import types from '@/store/modules/file/elite/excel/types'
import golbalMethods from '@/data/global/global'
const typePath = types.export
const typePathDownload = types.download
const NeuliveParameterOrder = ['RECORDING_CH', 'AMP_GAIN', 'ADC_CLOCK']
const NeuliveAmpGain = [100, 400, 800]
let fileStream, writer
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
function intoFileStream (data, filename, size, close) {
if (!fileStream) {
if (filename !== undefined) {
fileStream = streamSaver.createWriteStream(filename + '.csv', {
size: size,
})
writer = fileStream.getWriter()
window.isSecureContext && window.addEventListener('beforeunload', evt => {
writer.close()
})
}
}
data = encode(data)
data && writer.write(data)
}
function closeWriter () {
writer.close()
}
const exportActs = {
/**
* init meta
*
* @param {Array} metaIDList
* @param {objuct} api
* @return {object} : meta list info
*/
[typePath.init]: async ({ state, getters, commit }, payload) => {
const promises = payload.metaIDList.map(async id => {
const metaRes = await payload.api.meta.getByID(id)
return metaRes.data[0]
})
const list = await Promise.all(promises)
commit(typePath.set, { list: list })
},
// createHeadersString: function (headers, channel) {
[typePath.createHeadersString]: function ({ state, getters, commit, dispatch }, payload) {
const headers = state.downloadingFile
const device = ['Device', headers.device.device_name, golbalMethods.transMac(headers.device.device_address)]
const library = ['Libaray', headers.device.library_name, headers.device.library_version]
const parameter = []
if (headers.device.library_name.indexOf('Neulive') >= 0) {
NeuliveParameterOrder.forEach(p => {
const value = headers.parameter_set[p]
if (p === 'RECORDING_CH') {
let result = '"'
const channelList = value.toString(2).split('').reverse()
channelList.forEach((_value, _index) => {
if (_value === '1') {
result = result + `${_index + 1},`
}
})
result = result.slice(0, -1) + '"'
parameter.push(['Record channels', result])
} else if (p === 'AMP_GAIN') {
parameter.push(['Amp gain', NeuliveAmpGain[value]])
} else if (p === 'ADC_CLOCK') {
parameter.push(['Sample Rate', parseInt(8e5 / (value * JSON.parse(headers.channels).length))])
}
})
} else if (headers.device.library_name.indexOf('Elite') >= 0) {
const sortedParameterSet = {}
Object.keys(headers.parameter_set).sort().forEach(key => {
sortedParameterSet[key] = headers.parameter_set[key]
})
for (const p in sortedParameterSet) {
parameter.push([p, headers.parameter_set[p]])
}
}
const headersArray = [device, library, ...parameter]
let headersString = ''
headersArray.forEach(header => {
headersString += header.join(',') + '\r\n'
})
dispatch(typePath.writeDataToFileStream, { data: headersString })
},
// createTitlesString: function (headers, cycle, channel) {
[typePath.createTitlesString]: function ({ state, getters, commit, dispatch }, payload) {
let channelString = ''
let titleString = ''
let cycle = state.downloadingFile.needColumns
while (cycle > 0) {
for (const _ch of JSON.parse(state.downloadingFile.channels)) {
if (state.downloadingFile.device.library_name.indexOf('Neulive') >= 0) {
switch (_ch) {
case 256:
channelString += 'channel,X,'
break
case 257:
channelString += 'channel,Y,'
break
case 258:
channelString += 'channel,Z,'
break
case 259:
channelString += 'channel,M,'
break
default:
channelString += `channel,${parseInt(_ch) + 1},`
break
}
titleString += 'Time[us],value[uV],'
} else if (state.downloadingFile.device.library_name.indexOf('Elite') >= 0) {
const _time = 'Time[us]'
let _title = ''
switch (_ch) {
case 0:
_title = 'Current[nA]'
break
case 1:
_title = 'Voltage[uV]'
break
case 2:
_title = 'Resister[mohm]'
break
default:
_title = ''
break
}
// channelString += `channel ${_ch + 1},,`
titleString += _time + ',' + _title + ','
}
}
cycle -= 1
}
if (state.downloadingFile.device.library_name.indexOf('Neulive') >= 0) {
channelString = channelString.slice(0, -1) + '\r\n'
titleString = titleString.slice(0, -1) + '\r\n'
dispatch(typePath.writeDataToFileStream, { data: channelString + titleString })
} else if (state.downloadingFile.device.library_name.indexOf('Elite') >= 0) {
titleString = titleString.slice(0, -1) + '\r\n'
dispatch(typePath.writeDataToFileStream, { data: titleString })
}
},
[typePath.createDataString]: function ({ state, getters, commit, dispatch }, payload) {
const data = state.downloadingFile.downloadBuffer
let dataLength = 0
for (const channel in data) {
if (dataLength === undefined) {
dataLength = data[channel].length
}
if (data[channel].length < dataLength && !payload.lastPartData) {
dataLength = data[channel].length
}
if (data[channel].length > dataLength && payload.lastPartData) {
dataLength = data[channel].length
}
}
let dataString = ''
for (let idx = 0; idx < dataLength; idx++) {
const rowData = []
for (const channel in data) {
if (data[channel][idx] === undefined) {
rowData.push(...['', ''])
} else {
rowData.push(...data[channel][idx])
}
}
dataString = dataString + rowData.join(',') + '\r\n'
rowData.length = 0
}
dispatch(typePath.writeDataToFileStream, { data: dataString })
commit(typePathDownload.removeDownloadBuffer, { dataLength: dataLength })
dataString = ''
// return dataString
},
[typePath.writeDataToFileStream]: function ({ state, getters, commit }, payload) {
intoFileStream(payload.data, state.downloadingFile.name, state.downloadingFile.size)
payload.data = null
},
[typePath.closeFileStream]: function ({ state, getters, commit }, payload) {
closeWriter()
},
}
export default exportActs

Some files were not shown because too many files have changed in this diff Show More