onlineTest.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. <template>
  2. <a-row>
  3. <a-col :span="13">
  4. <a-card title="调试输出" style="background-color: #eef0f5;">
  5. <template #extra >
  6. <a-button type="primary" @click="clearLog" >清空日志</a-button>
  7. </template>
  8. <a-row>
  9. <a-col :span="6" class="app-imitate" >应用模拟器</a-col>
  10. <a-col :span="3" class="app-to-platform" >
  11. <div class="equipment-platform" >
  12. <div class="border-dot-top">命令下发</div>
  13. <div class="border-dot-bottom">数据上报</div>
  14. </div>
  15. </a-col>
  16. <a-col :span="6" class="IOT" >IOT平台</a-col>
  17. <a-col :span="3" class="app-to-platform" >
  18. <div class="equipment-platform" >
  19. <div class="border-dot-top">命令下发</div>
  20. <div class="border-dot-bottom">数据上报</div>
  21. </div>
  22. </a-col>
  23. <a-col :span="6" class="device-imitate" >真实设备</a-col>
  24. </a-row>
  25. <a-row style="height: 505px;width: 100%" justify="space-between" >
  26. <a-col :span="11" style="height: 100%;background-color: #fff" >
  27. <div v-for="(item, index) in state.eventList" :key="index" >{{JSON.stringify(item)}}</div>
  28. </a-col>
  29. <a-col :span="11" style='background-color: #fff;eight: 100%' ></a-col>
  30. </a-row>
  31. </a-card>
  32. </a-col>
  33. <a-col :span="9">
  34. <a-row justify='end' >
  35. <a-col>
  36. <a-button type="link" >{{state.device.deviceLabel ? state.device.deviceLabel : "尚未选择设备"}}</a-button>
  37. <a-button type="primary" @click="state.drawerVisible = true" >选择设备</a-button>
  38. </a-col>
  39. </a-row>
  40. <!-- :tab-list="tabListNoTitle"
  41. :active-tab-key="state.activeKey"
  42. @tabChange="onTabChange" -->
  43. <a-card
  44. title="应用模拟器"
  45. style="width: 100%;height: 100%;margin-top: 10px;"
  46. >
  47. <a-row :gutter="[8, 8]">
  48. <a-col span="24" ><a-tag color="blue" style="scale: 1.2;" >命令下发</a-tag></a-col>
  49. <a-col class="subtitle" span="24" >
  50. 应用模拟器可根据产品定义向设备下发命令。<br/>
  51. 若您使用了图形化开发的编解码插件,为获得正确的编码结果,下发命令时,请携带所有在插件中定义的字段,且每个命令的长度需小于512个字节
  52. </a-col>
  53. <a-col span="24" v-if="false" >
  54. <a-form
  55. :labelCol="{span: 2}"
  56. :wrapperCol="{span: 14}"
  57. >
  58. <a-form-item label="产品" >
  59. <a-select v-model:value="state.modelId" >
  60. <a-select-option
  61. v-for="item in state.modelSouce"
  62. :key="item.id"
  63. :value="item.id"
  64. >
  65. {{item.modelLabel}}
  66. </a-select-option>
  67. </a-select>
  68. </a-form-item>
  69. <a-form-item label="命令" >
  70. <a-select v-model:value="state.cmdId" >
  71. <a-select-option
  72. v-for="item in state.modelCmdList"
  73. :key="item.id"
  74. :value="item.id"
  75. >
  76. {{item.cmdLabel}}
  77. </a-select-option>
  78. </a-select>
  79. </a-form-item>
  80. <span v-if="cmdDetail?.cmdParams.length" >
  81. <a-form-item :label="item.paramLabel" v-for="(item, index) in cmdDetail?.cmdParams" :key="item.id" >
  82. <a-input v-model:value="item.dataUnit" ></a-input>
  83. </a-form-item>
  84. </span>
  85. </a-form>
  86. </a-col>
  87. </a-row>
  88. <template #extra >
  89. <a-button type="primary" @click="addEventList">发送</a-button>
  90. </template>
  91. </a-card>
  92. </a-col>
  93. </a-row>
  94. <a-drawer
  95. v-model:visible="state.drawerVisible"
  96. size="large"
  97. class="custom-class"
  98. title="选择设备"
  99. placement="right"
  100. >
  101. <SelectDevice ref="selectDeviceRef" />
  102. <template #footer >
  103. <a-row justify="end" >
  104. <a-col>
  105. <a-space>
  106. <a-button @click="state.drawerVisible = false">取消</a-button>
  107. <a-button type="primary" @click="handleSelectDevice">确定</a-button>
  108. </a-space>
  109. </a-col>
  110. </a-row>
  111. </template>
  112. </a-drawer>
  113. </template>
  114. <script lang='ts' setup >
  115. import { computed, onMounted, reactive, ref, watch } from 'vue'
  116. import SelectDevice from '@/pages/iot/rule/components/selectDevice.vue'
  117. import { message } from 'ant-design-vue'
  118. import { DeviceContriller, EventController, ModelCmdController, ModelController } from '@/controller'
  119. import { useRoute } from 'vue-router'
  120. import dayjs from 'dayjs'
  121. import { useScheduler } from '@/hooks'
  122. const route = useRoute()
  123. const tabListNoTitle = [
  124. {
  125. key: 'app',
  126. tab: '应用模拟器'
  127. }
  128. // {
  129. // key: 'device',
  130. // tab: '设备模拟器'
  131. // }
  132. ]
  133. const selectDeviceRef = ref()
  134. const state = reactive<{
  135. activeKey: 'app',
  136. drawerVisible: boolean,
  137. modelSouce: IOT.API.MODEL.Model[],
  138. modelId: string,
  139. cmdId: string,
  140. modelCmdList: IOT.API.CMD.Cmd[],
  141. eventList: any,
  142. startTime: number,
  143. lastId: string
  144. device: {
  145. id: string
  146. deviceLabel: string
  147. }
  148. }>({
  149. activeKey: 'app',
  150. drawerVisible: false,
  151. modelSouce: [],
  152. modelId: '',
  153. cmdId: '',
  154. modelCmdList: [],
  155. eventList: [],
  156. startTime: new Date().getTime(),
  157. lastId: '',
  158. device: {
  159. id: '1',
  160. deviceLabel: ''
  161. }
  162. })
  163. watch(() => state.modelId, () => getModelCmdList())
  164. const cmdDetail = computed(() => {
  165. const detail = state.modelCmdList.find(item => item.id === state.cmdId)
  166. return detail
  167. })
  168. const clearLog = () => {
  169. state.eventList = []
  170. message.success('清除成功')
  171. }
  172. const handleSelectDevice = () => {
  173. const _device = selectDeviceRef.value.getSelectDevice()
  174. if (_device) {
  175. state.device = _device
  176. state.drawerVisible = false
  177. } else {
  178. message.warn('请选择产品')
  179. }
  180. }
  181. const getModelCmdList = async () => {
  182. const { data } = await ModelCmdController.list({ modelId: state.modelId })
  183. state.modelCmdList = data
  184. }
  185. const getEventList = async () => {
  186. const { data, sum } = await EventController.list({ deviceId: state.device.id, startTime: state.startTime, lastId: state.lastId })
  187. state.eventList.push(data)
  188. }
  189. const { start, stop } = useScheduler(getEventList, 2000)
  190. const getEventTraceList = async () => {
  191. const data = await EventController.listTrace({ deviceId: state.device.id }) as unknown as Number
  192. // state.formatStartTime = data ? dayjs(data).format('YYYY-MM-DD hh:mm:ss') : ''
  193. start()
  194. }
  195. const addEventList = async () => {
  196. stop()
  197. await EventController.addTrace({ deviceId: state.device.id })
  198. getEventTraceList()
  199. }
  200. const getDeviceById = async () => {
  201. const data = await DeviceContriller.byId(state.device.id)
  202. state.device = data
  203. }
  204. onMounted(() => {
  205. // const deviceId = route.query.id as string
  206. const deviceId = 1
  207. if (deviceId) {
  208. state.device.id = deviceId
  209. getDeviceById()
  210. }
  211. })
  212. </script>
  213. <style lang='less' scoped >
  214. @import "~@/styles/theme.less";
  215. .subtitle {
  216. font-size: 12px;
  217. color: @label-color;
  218. }
  219. .app-imitate {
  220. width: 250px;
  221. height: 80px;
  222. background-color: #fff;
  223. display: flex;
  224. justify-content: center;
  225. align-items: center;
  226. font-size: 20px;
  227. }
  228. .IOT {
  229. width: 250px;
  230. height: 64px;
  231. background-color: #fff;
  232. display: flex;
  233. justify-content: center;
  234. align-items: center;
  235. font-size: 20px;
  236. }
  237. .app-to-platform {
  238. height: 100%;
  239. // width: 12.5%;
  240. // float: left;
  241. // height: 100%;
  242. }
  243. .device-imitate {
  244. width: 250px;
  245. height: 80px;
  246. background-color: #fff;
  247. display: flex;
  248. justify-content: center;
  249. align-items: center;
  250. font-size: 20px;
  251. }
  252. .equipment-platform {
  253. position: absolute;
  254. display: inline-block;
  255. vertical-align: middle;
  256. width: 100%;
  257. line-height: 1;
  258. color: #4d4d4d;
  259. text-align: center;
  260. font-size: 12px;
  261. .border-dot-top {
  262. position: relative;
  263. padding-bottom: 0.6rem;
  264. margin-bottom: 1rem;
  265. border-bottom: 2px #999 solid;
  266. }
  267. .border-dot-top::before {
  268. left: 0;
  269. top: 100%;
  270. margin-top: -0.15rem;
  271. content: "";
  272. display: block;
  273. width: 0.4rem;
  274. height: 0.4rem;
  275. border-radius: 0.2rem;
  276. border: 2px #999 solid;
  277. background-color: #ebedf0;
  278. position: absolute;
  279. }
  280. .border-dot-top::after {
  281. content: "";
  282. display: block;
  283. border: 4px #999 solid;
  284. width: 0;
  285. height: 0;
  286. transform: rotate(45deg);
  287. position: absolute;
  288. right: 0;
  289. top: 97%;
  290. margin-top: -0.15rem;
  291. border-color: #999 #999 transparent transparent;
  292. }
  293. .border-dot-bottom {
  294. border-color: #ccc;
  295. position: relative;
  296. padding-top: 0.6rem;
  297. border-top: 2px #999 dashed;
  298. }
  299. .border-dot-bottom::before {
  300. content: "";
  301. display: block;
  302. border: 4px #999 solid;
  303. width: 0;
  304. height: 0;
  305. transform: rotate(-135deg);
  306. position: absolute;
  307. top: -11%;
  308. margin-top: -0.15rem;
  309. border-color: #999 #999 transparent transparent;
  310. }
  311. .border-dot-bottom::after {
  312. right: 0;
  313. top: -1px;
  314. margin-top: -0.15rem;
  315. content: "";
  316. display: block;
  317. width: 0.4rem;
  318. height: 0.4rem;
  319. border-radius: 0.2rem;
  320. border: 2px #999 solid;
  321. background-color: #ebedf0;
  322. position: absolute;
  323. }
  324. }
  325. </style>