set.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. <template>
  2. <view class="container">
  3. <!-- 头部区域 -->
  4. <BluetoothHeader
  5. @ble-data-received="handleBleDataReceived"
  6. />
  7. <!-- 主要内容区域 -->
  8. <view class="main-content">
  9. <!-- 日期时间显示 -->
  10. <view class="datetime-box">
  11. <text class="datetime-text">{{ currentDateTime }}</text>
  12. </view>
  13. <view class="status-indicators">
  14. <!-- 状态指示器 -->
  15. <view class="indicator-row">
  16. <view class="indicator-item" v-for="(item, index) in indicators1" :key="index">
  17. <view class="status-indicator" :class="{ 'active': item.value }"></view>
  18. <text class="indicator-text">{{ item.label }}</text>
  19. </view>
  20. </view>
  21. <view class="indicator-row">
  22. <view class="indicator-item" v-for="(item, index) in indicators2" :key="index">
  23. <view class="status-indicator" :class="{ 'active': item.value }"></view>
  24. <text class="indicator-text">{{ item.label }}</text>
  25. </view>
  26. </view>
  27. </view>
  28. <!-- 传感器读数和控制 -->
  29. <view class="sensor-controls">
  30. <view class="control-item" v-for="(item, index) in sensorControls" :key="index">
  31. <view class="control-label">{{ item.label }}</view>
  32. <view class="control-value">
  33. <u--input
  34. border="none"
  35. v-model="item.value"
  36. ></u--input>
  37. </view>
  38. <view class="control-buttons">
  39. <u-button
  40. v-for="(btn, btnIndex) in item.buttons"
  41. :key="btnIndex"
  42. :plain="true" :hairline="true"
  43. :class="getButtonClass(btn)"
  44. size="mini"
  45. @click="handleButtonClick(btn.text,btn.action)"
  46. >
  47. {{ btn.text }}
  48. </u-button>
  49. </view>
  50. </view>
  51. <!-- 单电机控制器按键组 -->
  52. <view class="motor-control-section">
  53. <view class="section-title">单电机控制</view>
  54. <view class="motor-control-buttons">
  55. <!-- 第一行 -->
  56. <view class="button-row">
  57. <u-button
  58. :plain="true" :hairline="true"
  59. class="motor-control-btn"
  60. type="primary"
  61. size="medium"
  62. @click="handleMotorControl('stop')"
  63. >
  64. 停止
  65. </u-button>
  66. <u-button
  67. :plain="true" :hairline="true"
  68. class="motor-control-btn"
  69. type="primary"
  70. size="medium"
  71. @click="handleMotorControl('motor1East')"
  72. >
  73. 电机1向东
  74. </u-button>
  75. <u-button
  76. :plain="true" :hairline="true"
  77. class="motor-control-btn"
  78. type="primary"
  79. size="medium"
  80. @click="handleMotorControl('motor1West')"
  81. >
  82. 电机1向西
  83. </u-button>
  84. </view>
  85. <!-- 第二行 -->
  86. <view class="button-row">
  87. <u-button
  88. :plain="true" :hairline="true"
  89. class="motor-control-btn"
  90. type="primary"
  91. size="medium"
  92. @click="handleMotorControl('motor2East')"
  93. >
  94. 电机2向东
  95. </u-button>
  96. <u-button
  97. :plain="true" :hairline="true"
  98. class="motor-control-btn"
  99. type="primary"
  100. size="medium"
  101. @click="handleMotorControl('motor2West')"
  102. >
  103. 电机2向西
  104. </u-button>
  105. <view class="empty-button-space"></view> <!-- 空白占位 -->
  106. </view>
  107. </view>
  108. </view>
  109. </view>
  110. <DeviceStatusInfo/>
  111. </view>
  112. <u-modal :show="confirmShow" title="提示" :content='confirmContent' :showCancelButton=true @cancel="cancel" @confirm="confirm" ></u-modal>
  113. </view>
  114. </template>
  115. <script>
  116. import BluetoothHeader from '@/pages/components/header.vue';
  117. import DeviceStatusInfo from '@/pages/components/DeviceStatusInfo.vue';
  118. import {writeRegister,heartbeat,getAgreement} from '@/utils/modbus.js';
  119. export default {
  120. components: {
  121. BluetoothHeader,
  122. DeviceStatusInfo
  123. },
  124. data() {
  125. return {
  126. agreementTypeName: '',
  127. communicationLink_set: false,
  128. communicationTimer_set: null,
  129. selectAction: '',
  130. currentDateTime: '0000-00-00 00:00:00',
  131. currentTab: 0,
  132. rxCount: 0,
  133. txCount: 0,
  134. confirmShow: false,
  135. confirmContent: '是否确定执行该操作?',
  136. workMode: '', // 添加工作模式变量
  137. indicators1: [
  138. {name: 'tilt', label: '倾角', value: false},
  139. {name: 'limit', label: '限位', value: false},
  140. {name: 'overcurrent', label: '过流', value: false}
  141. ],
  142. indicators2: [
  143. {name: 'rtc', label: 'RTC', value: false},
  144. {name: 'wind', label: '大风', value: false},
  145. {name: 'undervoltage', label: '欠压', value: false}
  146. ],
  147. sensorControls: [
  148. {
  149. name: 'sunAltitude',
  150. label: '太阳高度角',
  151. value: '',
  152. buttons: [
  153. {text: '自动', action: 'auto', type: 'primary'}
  154. ]
  155. },
  156. {
  157. name: 'sunAzimuth',
  158. label: '太阳方位角',
  159. value: '',
  160. buttons: [
  161. {text: '手动', action: 'manual', type: 'primary'},
  162. {text: '标定', action: 'calibrate', type: 'primary'}
  163. ]
  164. },
  165. {
  166. name: 'targetAngle',
  167. label: '目标角度',
  168. value: '',
  169. buttons: [
  170. {text: '向东', action: 'east', type: 'primary'},
  171. {text: '放平', action: 'level', type: 'primary'}
  172. ]
  173. },
  174. {
  175. name: 'angle1',
  176. label: '角度1',
  177. value: '',
  178. buttons: [
  179. {text: '向西', action: 'west', type: 'primary'},
  180. {text: '雪', action: 'snow', type: 'primary'}
  181. ]
  182. },
  183. {
  184. name: 'angle2',
  185. label: '角度2',
  186. value: '',
  187. buttons: [
  188. {text: '雨', action: 'rain', type: 'primary'},
  189. {text: '风', action: 'wind', type: 'primary'}
  190. ]
  191. },
  192. {
  193. name: 'motor1Current',
  194. label: '电机1电流(A)',
  195. value: '',
  196. buttons: []
  197. },
  198. {
  199. name: 'motor2Current',
  200. label: '电机2电流(A)',
  201. value: '',
  202. buttons: []
  203. },
  204. {
  205. name: 'temperature',
  206. label: '温度(°)',
  207. value: '',
  208. buttons: []
  209. },
  210. {
  211. name: 'voltage',
  212. label: '电压(V)',
  213. value: '',
  214. buttons: [
  215. {text: '校正时间', action: 'calibrateTime', type: 'primary', number: 1}
  216. ]
  217. }
  218. ]
  219. }
  220. },
  221. onShow() {
  222. this.agreementTypeName = getAgreement();
  223. },
  224. methods: {
  225. // 处理电机控制按钮点击
  226. handleMotorControl(action) {
  227. // 检查是否处于手动模式
  228. if (this.workMode !== '手动') {
  229. uni.showToast({
  230. title: '请先切换到手动模式',
  231. icon: 'none',
  232. duration: 2000
  233. });
  234. return;
  235. }
  236. this.motorControlAction = action;
  237. // 设置确认对话框内容
  238. let actionText = '';
  239. switch(action) {
  240. case 'stop':
  241. actionText = '停止';
  242. break;
  243. case 'motor1East':
  244. actionText = '电机1向东';
  245. break;
  246. case 'motor1West':
  247. actionText = '电机1向西';
  248. break;
  249. case 'motor2East':
  250. actionText = '电机2向东';
  251. break;
  252. case 'motor2West':
  253. actionText = '电机2向西';
  254. break;
  255. }
  256. this.confirmContent = `是否确定执行${actionText}操作?`;
  257. this.confirmShow = true;
  258. },
  259. // 处理从子组件传递过来的蓝牙数据
  260. handleBleDataReceived(data) {
  261. console.log('在父组件中接收到蓝牙数据:', data);
  262. this.updateSensorData(data);
  263. },
  264. cancel(){
  265. this.confirmShow = false
  266. this.motorControlAction = ''; // 重置电机控制动作
  267. },
  268. confirm() {
  269. if (this.motorControlAction === ''){
  270. switch (this.selectAction) {
  271. case 'auto': // 自动
  272. writeRegister("READ_AUTO",null);
  273. break
  274. case 'manual': // 手动
  275. writeRegister("READ_MANUAL",null)
  276. break
  277. case 'calibrate': // 标定
  278. // writeRegister("WRITE_REGISTER",null)
  279. break
  280. case 'east': // 向东
  281. writeRegister("READ_DOWN",null)
  282. break
  283. case 'level': // 放平
  284. writeRegister("FLATTEN",null)
  285. break
  286. case 'west': // 向西
  287. writeRegister("READ_UP",null)
  288. break
  289. case 'snow': // 雪
  290. writeRegister("SNOW",null)
  291. break
  292. case 'rain': // 雨
  293. writeRegister("RAIN",null)
  294. break
  295. case 'wind': // 风
  296. writeRegister("WIND",null)
  297. break
  298. case 'calibrateTime': // 校正时间
  299. writeRegister("READ_TIME",this.getCurrentTimeValues())
  300. break
  301. case 'cancel': //取消
  302. writeRegister("READ_CANCEL",null)
  303. }
  304. }
  305. // 添加电机控制处理
  306. switch(this.motorControlAction) {
  307. case 'stop':
  308. writeRegister("READ_MOTOR_STOP", null);
  309. break;
  310. case 'motor1East':
  311. writeRegister("READ_MOTOR1_EAST", null);
  312. break;
  313. case 'motor1West':
  314. writeRegister("READ_MOTOR1_WEST", null);
  315. break;
  316. case 'motor2East':
  317. writeRegister("READ_MOTOR2_EAST", null);
  318. break;
  319. case 'motor2West':
  320. writeRegister("READ_MOTOR2_WEST", null);
  321. break;
  322. }
  323. // 重置电机控制动作
  324. this.motorControlAction = '';
  325. this.confirmShow = false;
  326. uni.showToast({
  327. title: '操作执行成功!',
  328. icon: 'success',
  329. duration: 2000
  330. });
  331. },
  332. handleButtonClick(text,action) {
  333. console.log(action)
  334. this.selectAction = action;
  335. this.confirmContent = `是否确定执行`+text+`相关的操作?`;
  336. this.confirmShow = true;
  337. },
  338. // 获取按钮的class
  339. getButtonClass(btn) {
  340. // 建立action与工作模式的映射关系
  341. const actionToModeMap = {
  342. 'auto': '自动',
  343. 'manual': '手动',
  344. 'level': '放平',
  345. 'snow': '雪',
  346. 'rain': '雨',
  347. 'wind': '风',
  348. 'east':'向东',
  349. 'west':'向西',
  350. };
  351. let classes = ['btn-broder'];
  352. // 添加宽度类
  353. if (btn.number == 1) {
  354. classes.push('one-btn');
  355. }
  356. // 如果是工作模式按钮且与当前工作模式匹配,则添加绿色背景类
  357. if (actionToModeMap[btn.action] && this.workMode === actionToModeMap[btn.action]) {
  358. classes.push('active-work-mode');
  359. }
  360. return classes;
  361. },
  362. updateSensorData(data) {
  363. // 根据蓝牙数据更新传感器值
  364. this.sensorControls.forEach(control => {
  365. switch (control.name) {
  366. case 'sunAltitude': // 太阳高度角
  367. if (data.qEleAngle_53 !== undefined) {
  368. control.value = data.qEleAngle_53
  369. }
  370. break
  371. case 'sunAzimuth': // 太阳方位角
  372. if (data.qAzimuth_54 !== undefined) {
  373. control.value = data.qAzimuth_54
  374. }
  375. break
  376. case 'targetAngle': // 目标角度
  377. if (data.qTargetAngle_42 !== undefined) {
  378. control.value = data.qTargetAngle_42
  379. }
  380. break
  381. case 'angle1': // 角度1
  382. if (data.qRealAngle_43 !== undefined) {
  383. control.value = data.qRealAngle_43
  384. }
  385. break
  386. case 'angle2': // 角度2
  387. if (data.qRealAngle_31 !== undefined) {
  388. control.value = data.qRealAngle_31
  389. }
  390. break
  391. case 'motor1Current': // 电机1电流
  392. if (data.MotorCurrent_30 !== undefined) {
  393. control.value = data.MotorCurrent_30
  394. }
  395. break
  396. case 'motor2Current': // 电机2电流
  397. if (data.MotorCurrent_35 !== undefined) {
  398. control.value = data.MotorCurrent_35
  399. }
  400. break
  401. case 'temperature': // 温度
  402. if (data.Temperature_33 !== undefined) {
  403. control.value = data.Temperature_33
  404. }
  405. break
  406. case 'voltage': // 电压
  407. if (data.Battery_32 !== undefined) {
  408. control.value = data.Battery_32
  409. }
  410. break
  411. }
  412. })
  413. console.log('WorkModle_41:',data.WorkModle_41);
  414. // 根据C++中的工作模式解析逻辑更新workMode
  415. if (data.WorkModle_41 !== undefined) {
  416. const workModeBits = data.WorkModle_41.toString();
  417. switch (workModeBits) {
  418. case "100000": // 自动
  419. this.workMode = '自动'
  420. break
  421. case "10000": // 手动
  422. this.workMode = '手动'
  423. break
  424. case "1000000": // 放平
  425. this.workMode = '放平'
  426. break
  427. case "1000000000": // 雨
  428. this.workMode = '雨'
  429. break
  430. case "100000000": // 雪
  431. this.workMode = '雪'
  432. break
  433. case "10000000000": // 风
  434. this.workMode = '风'
  435. break
  436. case "10000000": // 指定
  437. this.workMode = '指定'
  438. break
  439. case "11000": // 向东
  440. this.workMode = '向东'
  441. break
  442. case "10100": // 向西
  443. this.workMode = '向西'
  444. break
  445. default:
  446. this.workMode = '未知模式'
  447. break
  448. }
  449. }
  450. if (data.Message_40 !== undefined) {
  451. // 根据二进制字符串更新指示器状态
  452. const messageBits = data.Message_40.toString()
  453. console.log('messageBits:',messageBits)
  454. // 清除所有状态
  455. this.indicators1.forEach(item => item.value = false)
  456. this.indicators2.forEach(item => item.value = false)
  457. // 根据实际协议确定位的含义
  458. if(messageBits === "100") {
  459. this.indicators1[0].value = true // 倾角
  460. } else if(messageBits === "10") {
  461. this.indicators1[2].value = true // 过流
  462. } else if(messageBits === "1") {
  463. this.indicators1[1].value = true // 限位
  464. } else if(messageBits === "110") {
  465. this.indicators1[2].value = true // 过流
  466. this.indicators1[1].value = true // 限位
  467. } else if(messageBits === "101") {
  468. this.indicators1[0].value = true // 倾角
  469. this.indicators1[2].value = true // 过流
  470. } else if(messageBits === "11") {
  471. this.indicators1[1].value = true // 限位
  472. this.indicators1[2].value = true // 过流
  473. } else if(messageBits === "111") {
  474. this.indicators1[0].value = true // 倾角
  475. this.indicators1[1].value = true // 限位
  476. this.indicators1[2].value = true // 过流
  477. }
  478. }
  479. // 更新时间显示
  480. if (data.nowtime !== undefined) {
  481. this.currentDateTime = data.nowtime
  482. }
  483. // 设置6秒后将 communicationLink_set 设置为 false 的定时器
  484. this.communicationTimer_set = setTimeout(() => {
  485. this.communicationLink_set = false
  486. this.communicationTimer_set = null
  487. }, 5000)
  488. },
  489. getCurrentTimeValues(){
  490. const now = new Date();
  491. // 获取各个时间组件
  492. const year = now.getFullYear(); // 年份 (例如: 2024)
  493. const month = now.getMonth() + 1; // 月份 (0-11, 需要+1变为1-12)
  494. const day = now.getDate(); // 日期 (1-31)
  495. const hours = now.getHours(); // 小时 (0-23)
  496. const minutes = now.getMinutes(); // 分钟 (0-59)
  497. const seconds = now.getSeconds(); // 秒 (0-59)
  498. // 返回时间值数组,每个值都是16位整数
  499. return [
  500. year, // 年
  501. month, // 月
  502. day, // 日
  503. hours, // 时
  504. minutes, // 分
  505. seconds // 秒
  506. ];
  507. }
  508. }
  509. }
  510. </script>
  511. <style lang="scss" scoped>
  512. .container {
  513. background-color: #f5f5f5;
  514. height: calc(100vh - 120px);
  515. display: flex;
  516. flex-direction: column;
  517. }
  518. .main-content {
  519. flex: 1;
  520. padding: 20rpx;
  521. height: 100%;
  522. }
  523. .work-mode-display {
  524. background-color: #fff;
  525. padding: 20rpx;
  526. border-radius: 16rpx;
  527. margin-bottom: 20rpx;
  528. text-align: center;
  529. .work-mode-text {
  530. font-size: 28rpx;
  531. font-weight: bold;
  532. color: #333;
  533. }
  534. }
  535. .datetime-box {
  536. background-color: #fff;
  537. padding: 30rpx;
  538. border-radius: 16rpx;
  539. margin-bottom: 20rpx;
  540. text-align: center;
  541. .datetime-text {
  542. font-size: 32rpx;
  543. font-weight: bold;
  544. color: #333;
  545. }
  546. }
  547. .status-indicators {
  548. background-color: #fff;
  549. padding: 20rpx;
  550. border-radius: 16rpx;
  551. margin-bottom: 20rpx;
  552. .indicator-row {
  553. display: flex;
  554. justify-content: space-around;
  555. align-items: center;
  556. margin-bottom: 20rpx;
  557. &:last-child {
  558. margin-bottom: 0;
  559. }
  560. }
  561. .indicator-item {
  562. display: flex;
  563. align-items: center;
  564. justify-content: center;
  565. flex: 1;
  566. text-align: center;
  567. gap: 10rpx;
  568. }
  569. .indicator-text {
  570. font-size: 24rpx;
  571. color: #666;
  572. }
  573. }
  574. .sensor-controls {
  575. background-color: #fff;
  576. border-radius: 16rpx;
  577. overflow: auto;
  578. height: calc(100% - 390rpx);
  579. .control-item {
  580. display: flex;
  581. align-items: center;
  582. padding: 20rpx;
  583. border-bottom: 1rpx solid #f0f0f0;
  584. &:last-child {
  585. border-bottom: none;
  586. }
  587. }
  588. .control-label {
  589. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  590. color: #fff;
  591. padding: 10rpx 20rpx;
  592. border-radius: 8rpx;
  593. font-size: 24rpx;
  594. width: 240rpx;
  595. text-align: center;
  596. margin-right: 20rpx;
  597. }
  598. .control-value {
  599. background-color: #f8f9fa;
  600. padding: 10rpx 20rpx;
  601. border-radius: 8rpx;
  602. font-size: 28rpx;
  603. font-weight: bold;
  604. color: #333;
  605. width: 140rpx;
  606. height: 60rpx;
  607. text-align: center;
  608. margin-right: 20rpx;
  609. }
  610. .control-buttons {
  611. display: flex;
  612. gap: 20rpx;
  613. }
  614. .u-button--mini {
  615. width: 120rpx;
  616. }
  617. }
  618. .one-btn {
  619. width: 240rpx !important;
  620. }
  621. .btn-broder {
  622. border: 1rpx solid #dadbde;
  623. }
  624. // 活跃的工作模式按钮样式
  625. .active-work-mode {
  626. background-color: #00ff00 !important; // 绿色背景
  627. color: #000 !important;
  628. border-color: #00ff00 !important;
  629. }
  630. .status-indicator {
  631. width: 20rpx;
  632. height: 20rpx;
  633. border-radius: 50%;
  634. background-color: #ccc;
  635. margin-right: 10rpx;
  636. &.active {
  637. background-color: red;
  638. }
  639. }
  640. .typeSelect{
  641. font-size: 16px;
  642. color: #fff;
  643. font-weight: 600;
  644. display: flex;
  645. }
  646. .text-lable{
  647. color: #fff;
  648. font-size: 12px;
  649. }
  650. .motor-control-section {
  651. background-color: #fff;
  652. border-radius: 16rpx;
  653. padding: 20rpx;
  654. margin-top: 20rpx;
  655. }
  656. .section-title {
  657. font-size: 28rpx;
  658. font-weight: bold;
  659. color: #333;
  660. margin-bottom: 20rpx;
  661. text-align: center;
  662. }
  663. .motor-control-buttons {
  664. .button-row {
  665. display: flex;
  666. justify-content: space-between;
  667. margin-bottom: 20rpx;
  668. &:last-child {
  669. margin-bottom: 0;
  670. }
  671. }
  672. .motor-control-btn {
  673. flex: 1;
  674. margin: 0 10rpx;
  675. &:first-child {
  676. margin-left: 0;
  677. }
  678. &:last-child {
  679. margin-right: 0;
  680. }
  681. }
  682. .empty-button-space {
  683. flex: 1;
  684. margin: 0 10rpx;
  685. }
  686. }
  687. </style>