|
|
@@ -11,19 +11,13 @@ import {
|
|
|
|
|
|
let productId = null
|
|
|
let deviceId = null
|
|
|
-let cbs = null
|
|
|
|
|
|
-let running = '未知'
|
|
|
-let screenshotPic = null
|
|
|
-let download = null
|
|
|
-let sensor = null
|
|
|
+const types = new Map()
|
|
|
|
|
|
export function start (device) {
|
|
|
if (productId) {
|
|
|
stop()
|
|
|
}
|
|
|
- cbs = {}
|
|
|
- download = { files: [] }
|
|
|
productId = device.productId
|
|
|
deviceId = device.id
|
|
|
subscribe([
|
|
|
@@ -33,8 +27,12 @@ export function start (device) {
|
|
|
`${productId}/${deviceId}/screenshot/reply`,
|
|
|
`${productId}/${deviceId}/resource/progress`
|
|
|
], onMessage)
|
|
|
+ createImmediateType('online', { parser: onlineParser })
|
|
|
+ createLoadType('status', { defaults: '未知', parser: statusParser })
|
|
|
+ createLoadType('screenshot', { parser: screenshotParser })
|
|
|
+ createLoopType('download', { parser: downloadParser })
|
|
|
if (__SENSOR__) {
|
|
|
- updateSensors(sensor = { running: true })
|
|
|
+ updateSensors(createLoopType('sensor', { parser: sensorParser }))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -49,158 +47,201 @@ export function stop () {
|
|
|
], onMessage)
|
|
|
productId = null
|
|
|
deviceId = null
|
|
|
- running = '未知'
|
|
|
- screenshotPic = null
|
|
|
- download = null
|
|
|
- if (__SENSOR__) {
|
|
|
- sensor.running = false
|
|
|
- sensor = null
|
|
|
- }
|
|
|
+ types.forEach(inst => {
|
|
|
+ if (inst.type === 1) {
|
|
|
+ inst.running = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ types.clear()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-export function addListener (type, cb) {
|
|
|
- if (type && cbs) {
|
|
|
- let cbSet = cbs[type]
|
|
|
- if (!cbSet) {
|
|
|
- cbs[type] = (cbSet = new Set())
|
|
|
- }
|
|
|
- cbSet.add(cb)
|
|
|
- invoke(type, cb)
|
|
|
+export function addListener (type, cb, inject = false) {
|
|
|
+ let inst = types.get(type)
|
|
|
+ if (!inst && inject) {
|
|
|
+ inst = createLoopType(type)
|
|
|
+ }
|
|
|
+ if (inst) {
|
|
|
+ inst.cbs.add(cb)
|
|
|
+ invoke(inst, cb)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export function removeListener (type, cb) {
|
|
|
- if (type && cbs && cbs[type]) {
|
|
|
- cbs[type].delete(cb)
|
|
|
+ const inst = types.get(type)
|
|
|
+ if (inst) {
|
|
|
+ inst.cbs.delete(cb)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export function send (topic, message) {
|
|
|
- return publish(`${productId}/${deviceId}${topic}`, JSON.stringify(message || { timestamp: Date.now() }))
|
|
|
-}
|
|
|
-
|
|
|
-function invoke (type, cb) {
|
|
|
- switch (type) {
|
|
|
- case 'running':
|
|
|
- cb(running)
|
|
|
- break
|
|
|
- case 'screenshot':
|
|
|
- cb(screenshotPic)
|
|
|
- break
|
|
|
- case 'download':
|
|
|
- cb(download.files)
|
|
|
- break
|
|
|
- case 'temperature':
|
|
|
- case 'smoke':
|
|
|
- case 'flooding':
|
|
|
- case 'light':
|
|
|
- sensor[type] && cb(sensor[type])
|
|
|
- break
|
|
|
- default:
|
|
|
- break
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function emit (type, val) {
|
|
|
- if (cbs && cbs[type]) {
|
|
|
- if (val == null) {
|
|
|
- cbs[type].forEach(cb => invoke(type, cb))
|
|
|
- } else {
|
|
|
- cbs[type].forEach(cb => cb(val))
|
|
|
+ return publish(`${productId}/${deviceId}/${topic}`, JSON.stringify(message || { timestamp: Date.now() })).then(() => {
|
|
|
+ const inst = types.get(getTypeBySend(topic))
|
|
|
+ if (inst && inst.type === 2) {
|
|
|
+ inst.loading = true
|
|
|
+ inst.cbs.forEach(cb => {
|
|
|
+ cb(inst.value, inst)
|
|
|
+ })
|
|
|
}
|
|
|
- }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function onMessage (topic, message) {
|
|
|
const result = topic.match(/^(\d+)\/(\d+)\/(.+)$/)
|
|
|
if (result) {
|
|
|
if (productId === result[1] && deviceId === result[2]) {
|
|
|
- onUpdate(result[3], message)
|
|
|
+ onUpdate(types.get(getType(result[3])), message, result[3])
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function onUpdate (invoke, message) {
|
|
|
- switch (invoke) {
|
|
|
+function getTypeBySend (topic) {
|
|
|
+ switch (topic) {
|
|
|
+ case 'status/ask':
|
|
|
+ return 'status'
|
|
|
+ case 'screenshot/ask':
|
|
|
+ return 'screenshot'
|
|
|
+ default:
|
|
|
+ return null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getType (topic) {
|
|
|
+ switch (topic) {
|
|
|
case 'online':
|
|
|
- emit('online', true)
|
|
|
- break
|
|
|
case 'offline':
|
|
|
- emit('online', false)
|
|
|
- break
|
|
|
+ return 'online'
|
|
|
case 'status/reply':
|
|
|
- onUpdateStatus(message)
|
|
|
- break
|
|
|
+ return 'status'
|
|
|
case 'screenshot/reply':
|
|
|
- onUpdateScreenShot(message)
|
|
|
- break
|
|
|
+ return 'screenshot'
|
|
|
case 'resource/progress':
|
|
|
- onUpdateFile(message)
|
|
|
+ return 'download'
|
|
|
+ default:
|
|
|
+ return null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onUpdate (inst, message, topic) {
|
|
|
+ if (inst) {
|
|
|
+ const value = inst.parser ? inst.parser(inst, message, topic) : inst.value
|
|
|
+ if (inst.type === 2) {
|
|
|
+ inst.loading = false
|
|
|
+ }
|
|
|
+ inst.cbs.forEach(cb => {
|
|
|
+ cb(value, inst)
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function invoke (inst, cb) {
|
|
|
+ switch (inst.type) {
|
|
|
+ case 1:
|
|
|
+ case 2:
|
|
|
+ cb(inst.value, inst)
|
|
|
break
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function onUpdateStatus (message) {
|
|
|
+function createType (type, inst) {
|
|
|
+ types.set(type, inst)
|
|
|
+ return inst
|
|
|
+}
|
|
|
+
|
|
|
+function createImmediateType (type, options = {}) {
|
|
|
+ return createType(type, {
|
|
|
+ type: 0,
|
|
|
+ parser: options.parser,
|
|
|
+ cbs: new Set()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function createLoopType (type, options = {}) {
|
|
|
+ return createType(type, {
|
|
|
+ type: 1,
|
|
|
+ parser: options.parser,
|
|
|
+ value: [],
|
|
|
+ running: true,
|
|
|
+ cbs: new Set()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function createLoadType (type, options = {}) {
|
|
|
+ return createType(type, {
|
|
|
+ type: 2,
|
|
|
+ parser: options.parser,
|
|
|
+ value: options.defaults,
|
|
|
+ loading: false,
|
|
|
+ cbs: new Set()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function onlineParser (inst, message, topic) {
|
|
|
+ if (topic === 'online') {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ onUpdate(types.get('status'), '{}')
|
|
|
+ onUpdate(types.get('screenshot'))
|
|
|
+ onUpdate(types.get('download'), 'clear')
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+function statusParser (inst, message) {
|
|
|
+ let status
|
|
|
try {
|
|
|
- message = JSON.parse(message)
|
|
|
- switch (message.status) {
|
|
|
- case 1:
|
|
|
- running = '未播放节目,处于默认状态'
|
|
|
- break
|
|
|
- case 2:
|
|
|
- running = '正在播放节目'
|
|
|
- break
|
|
|
- case 3:
|
|
|
- running = '解析节目异常,请重新发布'
|
|
|
- break
|
|
|
- default:
|
|
|
- running = '未知'
|
|
|
- break
|
|
|
- }
|
|
|
- } catch {
|
|
|
- running = '未知'
|
|
|
+ status = JSON.parse(message).status
|
|
|
+ } catch (e) {
|
|
|
+ console.warn(e)
|
|
|
+ status = -1
|
|
|
}
|
|
|
- emit('running')
|
|
|
+ return (inst.value = status)
|
|
|
}
|
|
|
|
|
|
-function onUpdateScreenShot (message) {
|
|
|
- screenshotPic = `data:image/jpeg;base64,${message.replace(/\s/g, '')}`
|
|
|
- emit('screenshot')
|
|
|
+function screenshotParser (inst, message) {
|
|
|
+ inst.value = message ? `data:image/jpeg;base64,${message.replace(/\s/g, '')}` : null
|
|
|
+ return inst.value
|
|
|
}
|
|
|
|
|
|
-function onUpdateFile (message) {
|
|
|
+function downloadParser (inst, message) {
|
|
|
try {
|
|
|
- const { ossUrl, complete, errorReason, progress, rate, success } = JSON.parse(message)
|
|
|
- const file = {
|
|
|
- id: ossUrl,
|
|
|
- name: ossUrl,
|
|
|
- complete,
|
|
|
- success,
|
|
|
- status: complete
|
|
|
- ? success
|
|
|
- ? 'is-success'
|
|
|
- : 'is-exception'
|
|
|
- : 'is-processing',
|
|
|
- tip: complete
|
|
|
- ? success
|
|
|
- ? '下载成功'
|
|
|
- : errorReason || '下载失败'
|
|
|
- : `${parseByte(rate)}/s`,
|
|
|
- percentage: complete ? 100 : progress
|
|
|
- }
|
|
|
- if (download[file.id]) {
|
|
|
- Object.assign(download.files[download[file.id] - 1], file)
|
|
|
+ if (message === 'clear') {
|
|
|
+ inst.mark = {}
|
|
|
+ inst.value = []
|
|
|
} else {
|
|
|
- download.files.push(file)
|
|
|
- download[file.id] = download.files.length
|
|
|
+ const { ossUrl, complete, errorReason, progress, rate, success } = JSON.parse(message)
|
|
|
+ const file = {
|
|
|
+ id: ossUrl,
|
|
|
+ name: ossUrl,
|
|
|
+ complete,
|
|
|
+ success,
|
|
|
+ status: complete
|
|
|
+ ? success
|
|
|
+ ? 'is-success'
|
|
|
+ : 'is-exception'
|
|
|
+ : 'is-processing',
|
|
|
+ tip: complete
|
|
|
+ ? success
|
|
|
+ ? '下载成功'
|
|
|
+ : errorReason || '下载失败'
|
|
|
+ : `${parseByte(rate)}/s`,
|
|
|
+ percentage: complete ? 100 : progress
|
|
|
+ }
|
|
|
+ if (!inst.mark) {
|
|
|
+ inst.mark = {}
|
|
|
+ }
|
|
|
+ if (inst.mark[file.id]) {
|
|
|
+ Object.assign(inst.value[inst.mark[file.id] - 1], file)
|
|
|
+ } else {
|
|
|
+ inst.value.push(file)
|
|
|
+ inst.mark[file.id] = inst.value.length
|
|
|
+ }
|
|
|
}
|
|
|
- emit('download')
|
|
|
} catch (e) {
|
|
|
console.warn(e)
|
|
|
}
|
|
|
+ return inst.value
|
|
|
}
|
|
|
|
|
|
function transformValue (type, value, unit) {
|
|
|
@@ -227,8 +268,8 @@ function transform (type, arr) {
|
|
|
}
|
|
|
|
|
|
function updateSensors (inst) {
|
|
|
- getSensors({ custom: true }).then(({ data }) => {
|
|
|
- inst.running && onUpdateSensors(inst, data)
|
|
|
+ inst.running && getSensors({ custom: true }).then(({ data }) => {
|
|
|
+ inst.running && onUpdate(inst, data)
|
|
|
}).finally(() => {
|
|
|
if (inst.running) {
|
|
|
setTimeout(updateSensors, 5000, inst)
|
|
|
@@ -236,9 +277,13 @@ function updateSensors (inst) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-function onUpdateSensors (inst, map) {
|
|
|
+function sensorParser (inst, map) {
|
|
|
Object.keys(map).forEach(type => {
|
|
|
- inst[type] = transform(type, map[type])
|
|
|
- emit(type)
|
|
|
+ let inst = types.get(type)
|
|
|
+ if (!inst) {
|
|
|
+ inst = createLoopType(type)
|
|
|
+ }
|
|
|
+ inst.value = transform(type, map[type])
|
|
|
+ onUpdate(inst)
|
|
|
})
|
|
|
}
|