monitor.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. import { getSensors } from '@/api/external'
  2. import {
  3. publish,
  4. subscribe,
  5. unsubscribe
  6. } from '@/utils/mqtt'
  7. import {
  8. parseTime,
  9. parseByte
  10. } from '@/utils'
  11. let productId = null
  12. let deviceId = null
  13. export const Type = {
  14. LOAD: 1,
  15. LOOP: 2,
  16. CACHE: 3
  17. }
  18. const RESET_MESSAGE = '__reset__'
  19. const types = new Map()
  20. export function start (device) {
  21. if (productId) {
  22. stop()
  23. }
  24. productId = device.productId
  25. deviceId = device.id
  26. subscribe([
  27. `${productId}/${deviceId}/online`,
  28. `${productId}/${deviceId}/offline`,
  29. `${productId}/${deviceId}/status/reply`,
  30. `${productId}/${deviceId}/resource/progress`,
  31. `${productId}/${deviceId}/screen`
  32. ], onMessage)
  33. createType('online', { parser: onlineParser })
  34. createType('status', { type: Type.LOAD, parser: statusParser, value: '未知', reset: true })
  35. createType('download', { type: Type.CACHE, parser: downloadParser, value: [], reset: true, mark: {} })
  36. createType('topology', { type: Type.CACHE, parser: topologyParser, reset: true })
  37. }
  38. export function stop () {
  39. if (productId) {
  40. unsubscribe([
  41. `${productId}/${deviceId}/online`,
  42. `${productId}/${deviceId}/offline`,
  43. `${productId}/${deviceId}/status/reply`,
  44. `${productId}/${deviceId}/resource/progress`,
  45. `${productId}/${deviceId}/screen`
  46. ], onMessage)
  47. productId = null
  48. deviceId = null
  49. types.forEach(inst => {
  50. if (inst.type === Type.LOOP) {
  51. inst.running = false
  52. }
  53. })
  54. types.clear()
  55. }
  56. }
  57. export function addListener (type, cb, options) {
  58. let inst = types.get(type)
  59. if (!inst && options) {
  60. inst = createType(type, options)
  61. }
  62. if (inst && !inst.cbs.has(cb)) {
  63. inst.cbs.add(cb)
  64. invoke(inst, cb)
  65. }
  66. }
  67. export function removeListener (type, cb) {
  68. const inst = types.get(type)
  69. if (inst) {
  70. inst.cbs.delete(cb)
  71. }
  72. }
  73. function getTypeBySend (topic) {
  74. switch (topic) {
  75. case '/status/ask':
  76. return 'status'
  77. default:
  78. return null
  79. }
  80. }
  81. export function send (topic, message) {
  82. return publish(`${productId}/${deviceId}${topic}`, JSON.stringify(message || { timestamp: Date.now() })).then(() => {
  83. const inst = types.get(getTypeBySend(topic))
  84. if (inst && inst.type === Type.LOAD) {
  85. inst.loading = true
  86. inst.cbs.forEach(cb => {
  87. cb(inst.value, inst)
  88. })
  89. }
  90. })
  91. }
  92. export function reset (type) {
  93. const inst = types.get(type)
  94. if (inst?.reset) {
  95. onUpdate(inst, RESET_MESSAGE)
  96. }
  97. }
  98. function getType (topic) {
  99. switch (topic) {
  100. case 'online':
  101. case 'offline':
  102. return 'online'
  103. case 'status/reply':
  104. return 'status'
  105. case 'resource/progress':
  106. return 'download'
  107. case 'screen':
  108. return 'topology'
  109. default:
  110. return null
  111. }
  112. }
  113. function onMessage (topic, message) {
  114. const result = topic.match(/^(\d+)\/(\d+)\/(.+)$/)
  115. if (result) {
  116. if (productId === result[1] && deviceId === result[2]) {
  117. onUpdate(types.get(getType(result[3])), message, result[3])
  118. }
  119. }
  120. }
  121. function onUpdate (inst, message, topic) {
  122. if (inst) {
  123. try {
  124. const value = inst.parser ? inst.parser(inst, message, topic) : message
  125. updateType(inst, value)
  126. inst.cbs.forEach(cb => {
  127. cb(value, inst)
  128. })
  129. } catch (e) {
  130. console.warn(e)
  131. }
  132. }
  133. }
  134. function updateType (inst, value) {
  135. console.log()
  136. switch (inst.type) {
  137. case Type.LOAD:
  138. inst.loading = false
  139. break
  140. case Type.LOOP:
  141. case Type.CACHE:
  142. break
  143. default:
  144. return
  145. }
  146. inst.value = value
  147. }
  148. function invoke (inst, cb) {
  149. switch (inst.type) {
  150. case Type.LOAD:
  151. case Type.LOOP:
  152. case Type.CACHE:
  153. cb(inst.value, inst)
  154. break
  155. default:
  156. break
  157. }
  158. }
  159. function createType (unique, options = {}) {
  160. let defaultOptions = null
  161. switch (options.type) {
  162. case Type.LOAD:
  163. defaultOptions = getLoadTypeOptions()
  164. break
  165. case Type.LOOP:
  166. defaultOptions = getLoopTypeOptions()
  167. break
  168. default:
  169. break
  170. }
  171. const inst = {
  172. cbs: new Set(),
  173. ...defaultOptions,
  174. ...options
  175. }
  176. types.set(unique, inst)
  177. return inst
  178. }
  179. function getLoadTypeOptions () {
  180. return {
  181. loading: false
  182. }
  183. }
  184. function getLoopTypeOptions () {
  185. return {
  186. running: true
  187. }
  188. }
  189. function onlineParser (inst, message, topic) {
  190. if (topic === 'online') {
  191. return true
  192. }
  193. reset('status')
  194. reset('download')
  195. return false
  196. }
  197. function statusParser (inst, message) {
  198. let status
  199. try {
  200. status = message === RESET_MESSAGE ? void 0 : JSON.parse(message).status
  201. } catch (e) {
  202. console.warn(e)
  203. status = -1
  204. }
  205. return status
  206. }
  207. function downloadParser (inst, message) {
  208. if (message === RESET_MESSAGE) {
  209. inst.mark = {}
  210. inst.value = []
  211. } else {
  212. const { ossUrl, complete, errorReason, progress, rate, success } = JSON.parse(message)
  213. const file = {
  214. id: ossUrl,
  215. name: ossUrl,
  216. complete,
  217. success,
  218. status: complete
  219. ? success
  220. ? 'is-success'
  221. : 'is-exception'
  222. : 'is-processing',
  223. tip: complete
  224. ? success
  225. ? '下载成功'
  226. : errorReason || '下载失败'
  227. : `${parseByte(rate)}/s`,
  228. percentage: complete ? 100 : progress
  229. }
  230. if (inst.mark[file.id]) {
  231. Object.assign(inst.value[inst.mark[file.id] - 1], file)
  232. } else {
  233. inst.value.push(file)
  234. inst.mark[file.id] = inst.value.length
  235. }
  236. }
  237. return inst.value
  238. }
  239. function transformValue (type, value, unit) {
  240. switch (type) {
  241. case 'temperature':
  242. return `${((value * 10) | 0) / 10}℃`
  243. case 'flooding':
  244. return value ? '是' : '否'
  245. default:
  246. return `${value}${unit}`
  247. }
  248. }
  249. function transform (type, arr) {
  250. return arr.map(({ sensorAddr, sensorValue, sensorValueUnit, logDate }) => {
  251. return {
  252. key: sensorAddr,
  253. name: `传感器${sensorAddr}`,
  254. sensorValue,
  255. value: transformValue(type, sensorValue, sensorValueUnit),
  256. time: parseTime(logDate * 1000, '{y}.{m}.{d} {h}:{i}:{s}')
  257. }
  258. })
  259. }
  260. export function startSensor () {
  261. let sensor = types.get('sensor')
  262. if (sensor) {
  263. if (sensor.running) {
  264. return
  265. }
  266. sensor.running = true
  267. } else {
  268. sensor = createType('sensor', { type: Type.LOOP, parser: sensorParser })
  269. }
  270. updateSensors(sensor)
  271. }
  272. export function stopSensor () {
  273. const sensor = types.get('sensor')
  274. if (sensor) {
  275. sensor.running = false
  276. }
  277. }
  278. function updateSensors (inst) {
  279. inst.running && getSensors({ custom: true }).then(({ data }) => {
  280. inst.running && onUpdate(inst, data)
  281. return 5000
  282. }, () => 2000).then(delay => {
  283. if (inst.running) {
  284. setTimeout(updateSensors, delay, inst)
  285. }
  286. })
  287. }
  288. function sensorParser (inst, data) {
  289. Object.keys(data).forEach(type => {
  290. onUpdate(types.get(type) || createType(type, { type: Type.CACHE, value: [] }), transform(type, data[type]))
  291. })
  292. }
  293. function topologyParser (inst, message) {
  294. if (message === RESET_MESSAGE) {
  295. return null
  296. }
  297. return JSON.parse(message)
  298. }