Compare commits

...

30 Commits

Author SHA1 Message Date
warren30815 bb21e58582 sd delete 2022-03-07 23:08:46 +08:00
Jordan Hsu 8eeca803ba add missing column 2022-02-24 16:31:50 +08:00
peterlu14 5ad715a2a6 [add] sd upload check 2022-02-23 19:18:49 +08:00
Jordan Hsu 8d31ba4924 typo 2022-02-23 18:19:52 +08:00
Jordan Hsu b2792207a5 [add] sd upload check 2022-02-23 18:16:01 +08:00
Jordan Hsu b4236b2eb7 [add] trigger download format 2022-02-15 16:48:45 +08:00
Jordan Hsu 4ff3d76dac typo 2022-02-15 16:34:04 +08:00
Jordan Hsu 0a6fcc8940 bug fix 2022-02-15 16:29:44 +08:00
Jordan Hsu b4087527a1 typo 2022-02-15 16:26:07 +08:00
Jordan Hsu 5233131f49 [modify] trigger download format 2022-02-15 16:20:27 +08:00
Jordan Hsu 6451cca01a add missing code 2022-02-15 14:52:29 +08:00
Jordan Hsu dbfa941474 fix wrong data type 2022-02-15 14:46:24 +08:00
Jordan Hsu 0f262eb440 delete redundant column 2022-02-15 14:20:28 +08:00
Jordan Hsu dcdb32ae9c add missing change 2022-02-15 11:10:22 +08:00
Jordan Hsu a1373e6fe4 [modify] raw trigger path 2022-02-15 11:04:04 +08:00
Jordan Hsu bb73e4b02b [add] missing column 2022-02-11 14:52:13 +08:00
Jordan Hsu 765e70a0e0 get meta all api 2022-02-11 10:52:49 +08:00
Jordan Hsu 62591f9039 sd model format 2022-02-10 12:22:54 +08:00
Jordan Hsu 29c3cb6af0 sd card return equation 2022-02-10 10:34:03 +08:00
tommy830509 9ad0b9590c sd card bug fix 2022-02-09 19:51:49 +08:00
Tommy Huang d6b17671c4 sd download 2022-02-09 18:33:38 +08:00
Jordan Hsu 7795d96aad sd new db format 2022-02-09 17:24:35 +08:00
Jordan Hsu a220885f56 bug fix 2022-02-08 14:12:14 +08:00
Jordan Hsu ff882328d8 merge trigger api 2022-02-08 11:15:10 +08:00
Jordan Hsu 6e98a7749a Merge branch 'feature/trigger_data' into feature/sd_merge_test 2022-02-08 10:57:13 +08:00
Jordan Hsu 9c016dceb0 [fix] sd api bug 2022-01-29 11:08:47 +08:00
Jordan Hsu ec02f84381 [fix] sd api bug 2022-01-28 15:07:09 +08:00
Jordan Hsu 24d7138888 [add] sd download api 2022-01-27 16:10:05 +08:00
Jordan Hsu 14f987a49d [add] get meta all api 2022-01-26 16:24:08 +08:00
Jordan Hsu 074e163c95 [add] trigger raw & meta download 2022-01-26 10:24:00 +08:00
19 changed files with 350 additions and 36 deletions
@@ -1,7 +1,9 @@
import RecordingDataMeta from '../../models/file/recording_data_meta'
import * as auth from '../../controllers/auth'
import RecordingMeta from '../../models/file/bps_recording_meta'
const recordingDataMeta = new RecordingDataMeta()
const recordingMeta = new RecordingMeta() // control system 3.0
export const create = async (ctx, next) => {
const data = ctx.request.body
@@ -120,3 +122,19 @@ export const getAll = async (ctx, next) => {
next()
}
// control system 3.0
export const getMetaByUUID = async (ctx, next) => {
const uuid = ctx.params.uuid
const result = await recordingMeta.getMetaByUUID(uuid)
ctx.body = result
next()
}
export const getMetaAll = async (ctx, next) => {
const result = await recordingMeta.getMetaAll()
ctx.body = result
next()
}
+15 -4
View File
@@ -80,10 +80,21 @@ export const getDataByID = async (ctx, next) => {
next()
}
export const getSdDataByID = async (ctx, next) => {
const index = ctx.params.channel
const id = ctx.params.id
const result = await recordingDataRaw.getSdDataByID(id, index)
export const getSdDataByUUID = async (ctx, next) => {
const uuid = ctx.params.uuid
const channel = ctx.params.channel
const startTime = ctx.params.startTime
const endTime = ctx.params.endTime
const result = await recordingDataRaw.getSdDataByUUID(uuid, channel, startTime, endTime)
ctx.body = result
next()
}
export const getSdDataAllTimeslotByUUID = async (ctx, next) => {
const uuid = ctx.params.uuid
const channel = ctx.params.channel
const result = await recordingDataRaw.getSdDataAllTimeslotByUUID(uuid, channel)
ctx.body = result
next()
+17 -12
View File
@@ -1,10 +1,12 @@
import RecordingDataMeta from '../../../models/file/recording_data_meta'
import RecordingDataRaw from '../../../models/file/recording_data_raw'
import Controller from '../../../models/product/controller'
import childProcess from 'child_process'
import path from 'path'
import { homedir } from 'os'
const recordingDataMeta = new RecordingDataMeta()
const recordingDataRaw = new RecordingDataRaw()
const controller = new Controller()
const sdPath = [homedir(), 'bioproanalysis', 'sdcard']
@@ -61,13 +63,11 @@ const getUuidPathStringPromise = async () => {
})
}
function uploadPromise (uuidPath, uuid, ampGain) {
function uploadPromise (uuid) {
return new Promise((resolve, reject) => {
const subprocess = childProcess.spawn('python3', [
path.join(...sdUploadPath),
uuidPath,
uuid,
ampGain
])
let dataString = ''
@@ -130,8 +130,6 @@ export const uploadThenDelete = async (ctx, next) => {
const controllerID = request.controllerID
const metaID = request.metaID
const uuid = request.uuid
const uuidPath = await getPath(uuid)
const ampGain = await getParameter(metaID, 'AMP_GAIN')
// timeout defualt 120s to no-limit
ctx.req.setTimeout(0)
@@ -141,14 +139,21 @@ export const uploadThenDelete = async (ctx, next) => {
sd_uploading: true
}, controllerID)
const uploadResult = await uploadPromise(uuidPath, uuid, ampGain)
let uploadResult = await uploadPromise(uuid)
if (uploadResult === 'true') {
// meta sd_card_uploaded true
await recordingDataMeta.update({
sd_data_uploaded: true
}, metaID)
await deletePromise(uuidPath)
// [Region] then doubly check if sd raw data table actually has data
const meta = await recordingDataMeta.getByUUID(uuid)
const channel = JSON.parse(meta[0].channels)[0]
const sdDataRes = await recordingDataRaw.getSdDataAllTimeslotByUUID(uuid, channel)
if (sdDataRes.length > 0) {
// [EndRegion]
await recordingDataMeta.update({
sd_data_uploaded: true
}, metaID)
// await deletePromise(uuidPath)
} else {
uploadResult = 'false'
}
}
// controller column sd_uploading close
+6 -6
View File
@@ -1,25 +1,25 @@
import SDRawNeulive from '../../models/file/sd_raw_neulive'
import * as auth from '../auth'
const sd_raw_Neulive = new SDRawNeulive()
const sdRawNeulive = new SDRawNeulive()
export const create = async (ctx, next) => {
const data = ctx.request.body
const result = await sd_raw_Neulive.create(data)
const result = await sdRawNeulive.create(data)
ctx.body = result
next()
}
export const getByParentUUID = async (ctx, next) => {
const parent_uuid = ctx.params.parent_uuid
const result = await sd_raw_Neulive.getByParentUUID(parent_uuid)
const result = await sdRawNeulive.getByParentUUID(parent_uuid)
ctx.body = result
next()
}
export const deleteByParentUUID = async (ctx, next) => {
export const clearDeleted = async (ctx, next) => {
const parent_uuid = ctx.params.parent_uuid
const result = await sd_raw_Neulive.deleteByParentUUID(parent_uuid)
const result = await sdRawNeulive.clearDeleted(parent_uuid)
ctx.body = result
next()
}
}
+11
View File
@@ -0,0 +1,11 @@
import RawTrigger from '../../../models/file/bps_raw_trigger'
const rawTrigger = new RawTrigger()
export const getRawTriggerByUUID = async (ctx, next) => {
const parent_uuid = ctx.params.parent_uuid
const result = await rawTrigger.getDataByUUID(parent_uuid)
ctx.body = result
next()
}
+3 -1
View File
@@ -10,6 +10,7 @@ import * as recordingDataRaw from './file/recording_data_raw'
import * as recordingDataMini from './file/recording_data_mini'
import * as analysis from './analysis'
import * as sdCard from './file/sd_card'
import * as trigger from './file/trigger'
export default {
upload,
@@ -23,5 +24,6 @@ export default {
recordingDataRaw,
recordingDataMini,
analysis,
sdCard
sdCard,
trigger,
}
+2
View File
@@ -40,6 +40,8 @@ for (let i = 256; i < 288; i++) {
for (let i = 256; i < 288; i++) {
db[i + '_recording_data_minis'] = require('./schema/recording_data_mini.model')(sequelize, Sequelize, i)
}
db.recording_meta = require('./schema/bps_recording_meta.model')(sequelize, Sequelize)
db.raw_trigger = require('./schema/bps_raw_trigger.model')(sequelize, Sequelize)
db.sd_raw_neulive = require('./schema/sd_raw_neulive.model')(sequelize, Sequelize)
+34
View File
@@ -0,0 +1,34 @@
module.exports = (sequelize, Sequelize) => {
const TriggerDataRaw = sequelize.define('bps_raw_trigger', {
channel: {
type: Sequelize.INTEGER,
defaultValue: -1
},
parent_uuid: {
type: Sequelize.STRING,
defaultValue: ''
},
data: {
type: Sequelize.TEXT,
defaultValue: ''
},
timestamp: {
type: Sequelize.TEXT,
defaultValue: ''
},
start_time: {
type: Sequelize.STRING,
defaultValue: '-1'
},
end_time: {
type: Sequelize.STRING,
defaultValue: '-1'
},
},
{
freezeTableName: true
}
)
return TriggerDataRaw
}
+78
View File
@@ -0,0 +1,78 @@
module.exports = (sequelize, Sequelize) => {
const RecordingMeta = sequelize.define('bps_recording_meta', {
uuid: {
type: Sequelize.STRING,
defaultValue: '',
primaryKey: true
},
name: {
type: Sequelize.STRING
},
series: {
type: Sequelize.STRING
},
folder_id: {
type: Sequelize.INTEGER,
defaultValue: -1
},
task_uuid: {
type: Sequelize.STRING,
defaultValue: ''
},
device: {
type: Sequelize.JSONB,
defaultValue: {}
},
description: {
type: Sequelize.STRING,
defaultValue: ''
},
size: {
type: Sequelize.INTEGER,
defaultValue: -1
},
sd_size: {
type: Sequelize.INTEGER,
defaultValue: -1
},
time_duration_sec: {
type: Sequelize.INTEGER,
defaultValue: 0
},
time_duration_micro: {
type: Sequelize.INTEGER,
defaultValue: 0
},
sample_rate: {
type: Sequelize.INTEGER,
defaultValue: -1
},
deleted: {
type: Sequelize.BOOLEAN,
defaultValue: false
},
tag: {
type: Sequelize.STRING,
defaultValue: '-1'
},
parameter_set: {
type: Sequelize.ARRAY(Sequelize.JSONB),
defaultValue: []
},
create_date: {
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
channel: {
type: Sequelize.ARRAY(Sequelize.INTEGER),
defaultValue: []
}
},
{
timestamps: false
}
)
return RecordingMeta
}
+4
View File
@@ -10,6 +10,10 @@ module.exports = (sequelize, Sequelize, i) => {
type: Sequelize.TEXT,
defaultValue: ''
},
data_time: {
type: Sequelize.TEXT,
defaultValue: ''
},
start_time_sec: {
type: Sequelize.INTEGER,
defaultValue: 0
+42
View File
@@ -0,0 +1,42 @@
const db = require('../../db/database')
const { Transform } = require("stream")
class RawTrigger {
async getDataByUUID (parent_uuid) {
const ret = await db.raw_trigger.findAllWithStream(
{
where: {
parent_uuid: parent_uuid
},
attributes: [
'data'
],
}
)
const transformer = new Transform({
transform(chunk, encoding, callback) {
const chunkDataStr = chunk.toString('utf8')
// replace: ref https://dmitripavlutin.com/replace-all-string-occurrences-javascript/#2-replace-with-a-global-regular-expression
// slice: remove last ','
const transformDataArr = JSON.parse(chunkDataStr)[0].data.replace(/[@#$]/g, '').slice(0, -1).split(',')
const step = 3
let transformData = ''
for (let startPos = 0; startPos < transformDataArr.length; startPos += step) {
// array chunking algo. comparison: https://www.better.dev/javascript-algorithms-array-chunking
let temp = transformDataArr.slice(startPos, startPos + step)
const date = new Date(parseInt(temp[0]))
temp[0] = date
temp[1] = parseInt(temp[1]) + 1
temp.splice(1, 0, [date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()].join(':'))
transformData += (temp.join() + '\n') // join: https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/join
}
callback(null, transformData)
},
})
return ret.pipe(transformer)
}
}
export default RawTrigger
+37
View File
@@ -0,0 +1,37 @@
const db = require('../../db/database')
class RecordingMeta {
async getMetaByUUID (uuid) {
const ret = await db.recording_meta.findAll(
{
where: {
uuid: uuid
}
}
)
return ret
}
async getMetaAll () {
const ret = await db.recording_meta.findAll({
order: [
['create_date', 'DESC']
]
})
return ret
}
async clearDeleted (uuid) {
const ret = await db.recording_meta.destroy(
{
where: {
uuid: uuid
}
}
)
return ret
}
}
export default RecordingMeta
+11 -2
View File
@@ -1,10 +1,15 @@
import RecordingDataRaw from './recording_data_raw'
import RecordingDataMini from './recording_data_mini'
import SDRawNeulive from './sd_raw_neulive'
import RecordingMeta from '../../models/file/bps_recording_meta'
const db = require('../../db/database')
// const RecordingDataMini = require('./recording_data_mini')
const recordingDataRaw = new RecordingDataRaw()
const recordingDataMini = new RecordingDataMini()
const sdRawNeulive = new SDRawNeulive()
const recordingMeta = new RecordingMeta() // control system 3.0
const Op = db.Sequelize.Op
@@ -47,7 +52,8 @@ class RecordingDataMeta {
attributes: [
'id',
'raw_data',
'mini_data'
'mini_data',
'uuid'
],
where: {
deleted: true
@@ -68,6 +74,8 @@ class RecordingDataMeta {
recordingDataMini.clearDeleted(channel)
}
}
recordingMeta.clearDeleted(meta.uuid) // control system 3.0
sdRawNeulive.clearDeleted(meta.uuid)
}
ret = await db.recording_data_metas.destroy(
{
@@ -175,7 +183,8 @@ class RecordingDataMeta {
{
where: {
uuid: uuid
}
},
raw: true
}
)
return ret
+52 -7
View File
@@ -103,21 +103,66 @@ class RecordingDataRaw {
return ret
}
async getSdDataByID (id, index) {
const ret = await db[index + '_recording_data_raws'].findAllWithStream(
// ref: https://stackoverflow.com/questions/47498083/find-the-closest-smaller-value-of-an-array
findClosestSmalllerValIndex (arr, val) {
const targetVal = arr.slice().reverse().find(e => e <= val) // why additionally use slice: see https://stackoverflow.com/questions/30610523/reverse-array-in-javascript-without-mutating-original-array
if (targetVal !== undefined) {
return arr.findIndex(e => e === targetVal)
} else {
return 0
}
}
async getSdDataByUUID (uuid, channel, startTime, endTime) {
const timeSlots = await this.getSdDataAllTimeslotByUUID(uuid, channel)
const startTimeArr = timeSlots.map(x => parseInt(x.start_time_sec) * 1e6 + parseInt(x.start_time_micro))
const endTimeArr = timeSlots.map(x => parseInt(x.end_time_sec) * 1e6 + parseInt(x.end_time_micro))
const startIndex = this.findClosestSmalllerValIndex(startTimeArr, parseInt(startTime))
let endIndex
const findEndIndex = endTimeArr.findIndex(el => el >= parseInt(endTime))
findEndIndex !== -1 ? endIndex = findEndIndex : endIndex = timeSlots.length - 1
const idList = timeSlots.slice(startIndex, endIndex + 1).map(x => x.id)
const ret = await db['sd_raw_neulive'].findAll(
{
batchSize: 1000,
where: {
id: id
id: idList
},
attributes: [
'sd_data'
]
order: [
['start_time_sec', 'ASC'],
['start_time_micro', 'ASC']
],
raw: true,
}
)
return ret
}
async getSdDataAllTimeslotByUUID (uuid, channel) {
const ret = await db['sd_raw_neulive'].findAll(
{
where: {
parent_uuid: uuid,
channel: channel,
},
attributes: [
'id',
'start_time_sec',
'end_time_sec',
'start_time_micro',
'end_time_micro',
],
order: [
['start_time_sec', 'ASC'],
['start_time_micro', 'ASC']
],
raw: true,
}
)
return ret
}
async getByAttrID (id, attr, index) {
const attrList = attr.split('-')
+1 -1
View File
@@ -27,7 +27,7 @@ class SDRawNeulive {
return ret
}
async deleteByParentUUID (parent_uuid) {
async clearDeleted (parent_uuid) {
const ret = await db['sd_raw_neulive'].destroy(
{
where: {
+3 -1
View File
@@ -3,6 +3,7 @@ import meta from './meta'
import mini from './mini'
import raw from './raw'
import sdcard from './sdcard'
import trigger from './trigger'
export default function (_prefix) {
const prefix = _prefix + '/file'
@@ -11,6 +12,7 @@ export default function (_prefix) {
...meta(prefix),
...mini(prefix),
...raw(prefix),
...sdcard(prefix)
...sdcard(prefix),
...trigger(prefix)
]
}
+4 -1
View File
@@ -17,6 +17,9 @@ export default function (_prefix) {
['GET', prefix + '/get_by_parent_with_limit/:type/:parent/:limit/:offset/:order', controllers.recordingDataMeta.getByParentWithLimit],
['GET', prefix + '/get_by_attr_parent_with_limit/:type/:parent/:limit/:offset/:order/:attr', controllers.recordingDataMeta.getByAttrsParentWithLimit],
['GET', prefix + '/get_or_create_by_path/:path', controllers.recordingDataMeta.getOrCreateByPath],
['GET', prefix + '/get/all', controllers.recordingDataMeta.getAll]
['GET', prefix + '/get/all', controllers.recordingDataMeta.getAll],
// control system 3.0
['GET', prefix + '/get_meta_by_uuid/:uuid', controllers.recordingDataMeta.getMetaByUUID],
['GET', prefix + '/get_meta_all', controllers.recordingDataMeta.getMetaAll],
]
}
+2 -1
View File
@@ -12,7 +12,8 @@ export default function (_prefix) {
['GET', prefix + '/get_by_id/:channel/:id', controllers.recordingDataRaw.getByID],
['GET', prefix + '/get_by_ids/:channel/:id', controllers.recordingDataRaw.getByIDs],
['GET', prefix + '/get_data_by_id/:channel/:id', controllers.recordingDataRaw.getDataByID],
['GET', prefix + '/get_sd_data_by_id/:channel/:id', controllers.recordingDataRaw.getSdDataByID],
['GET', prefix + '/get_sd_data_by_uuid/:uuid/:channel/:startTime/:endTime', controllers.recordingDataRaw.getSdDataByUUID],
['GET', prefix + '/get_sd_data_all_timeslot_by_uuid/:uuid/:channel', controllers.recordingDataRaw.getSdDataAllTimeslotByUUID],
['GET', prefix + '/get_by_parent/:channel/:parent', controllers.recordingDataRaw.getByParent],
['GET', prefix + '/get_attr_by_id/:channel/:id/:attr', controllers.recordingDataRaw.getAttrByID],
['GET', prefix + '/get_attr_by_ids/:channel/:id/:attr', controllers.recordingDataRaw.getAttrByIDs],
+10
View File
@@ -0,0 +1,10 @@
import controllers from '../../../controllers'
// '/api/file/trigger'
export default function (_prefix) {
const prefix = _prefix + '/trigger'
return [
['GET', prefix + '/get_raw_by_uuid/:parent_uuid', controllers.trigger.getRawTriggerByUUID],
]
}