Просмотр исходного кода

feat: 增加DTU心跳间隔调整并新增资源监控展示

1.  更新DTU心跳间隔改为60秒,新增离线超时配置
2.  前端环境传感器页面新增CPU和内存使用率展示
3.  DTU配置页面新增温度颜色适配,新增固件版本、CPU和内存使用率展示
4.  为DTU状态面板新增定时刷新逻辑
wenhongquan 2 дней назад
Родитель
Сommit
e3a5e3f45f
4 измененных файлов с 653 добавлено и 166 удалено
  1. 570 163
      backend/app.py
  2. 2 1
      backend/config.py
  3. 37 1
      frontend/src/components/DTUConfig.vue
  4. 44 1
      frontend/src/views/EnvSensor.vue

Разница между файлами не показана из-за своего большого размера
+ 570 - 163
backend/app.py


+ 2 - 1
backend/config.py

@@ -99,7 +99,8 @@ ERROR_MESSAGES = {
 # DTU配置
 # DTU配置
 DEFAULT_CUSTOMER_ID = os.getenv('CUSTOMER_ID', 'default_customer')
 DEFAULT_CUSTOMER_ID = os.getenv('CUSTOMER_ID', 'default_customer')
 DEFAULT_DTU_ID = os.getenv('DTU_ID', 'dtu_001')
 DEFAULT_DTU_ID = os.getenv('DTU_ID', 'dtu_001')
-DTU_HEARTBEAT_INTERVAL = 30  # DTU状态上报间隔(秒)
+DTU_HEARTBEAT_INTERVAL = 60  # DTU状态上报间隔(秒) - 协议 7.13 要求
+DTU_OFFLINE_TIMEOUT = 180    # DTU 离线判定超时(秒) - 协议 7.13 要求
 
 
 # MQTT主题前缀(可配置 - 通过web界面设置)
 # MQTT主题前缀(可配置 - 通过web界面设置)
 DEFAULT_MQTT_TOPIC_PREFIX = os.getenv('MQTT_TOPIC_PREFIX', '线架系统')
 DEFAULT_MQTT_TOPIC_PREFIX = os.getenv('MQTT_TOPIC_PREFIX', '线架系统')

+ 37 - 1
frontend/src/components/DTUConfig.vue

@@ -130,6 +130,33 @@
           <a-descriptions-item label="主题前缀">
           <a-descriptions-item label="主题前缀">
             {{ dataStore.dtuConfig.topic_prefix || '未配置' }}
             {{ dataStore.dtuConfig.topic_prefix || '未配置' }}
           </a-descriptions-item>
           </a-descriptions-item>
+          <a-descriptions-item label="主板温度">
+            <span :style="{ color: getTempColor(dataStore.dtuStatus.temperature) }">
+              {{ dataStore.dtuStatus.temperature !== null && dataStore.dtuStatus.temperature !== undefined
+                  ? `${dataStore.dtuStatus.temperature}°C` : '--' }}
+            </span>
+          </a-descriptions-item>
+          <a-descriptions-item label="固件版本">
+            {{ dataStore.dtuStatus.firmware_version || dataStore.dtuConfig.firmware_version || '--' }}
+          </a-descriptions-item>
+          <a-descriptions-item label="CPU 使用率">
+            <a-progress
+              v-if="dataStore.dtuStatus.cpu_usage !== null && dataStore.dtuStatus.cpu_usage !== undefined"
+              :percent="Math.round(dataStore.dtuStatus.cpu_usage)"
+              :status="dataStore.dtuStatus.cpu_usage > 80 ? 'exception' : 'normal'"
+              style="width: 100%"
+            />
+            <span v-else>--</span>
+          </a-descriptions-item>
+          <a-descriptions-item label="内存使用率">
+            <a-progress
+              v-if="dataStore.dtuStatus.memory_usage !== null && dataStore.dtuStatus.memory_usage !== undefined"
+              :percent="Math.round(dataStore.dtuStatus.memory_usage)"
+              :status="dataStore.dtuStatus.memory_usage > 80 ? 'exception' : 'normal'"
+              style="width: 100%"
+            />
+            <span v-else>--</span>
+          </a-descriptions-item>
         </a-descriptions>
         </a-descriptions>
 
 
         <div style="display: flex; justify-content: flex-end; margin-top: 16px">
         <div style="display: flex; justify-content: flex-end; margin-top: 16px">
@@ -209,7 +236,7 @@
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { onMounted, ref, computed, watch } from 'vue'
+import { onMounted, onUnmounted, ref, computed, watch } from 'vue'
 import { useDataStore } from '../stores/dataStore'
 import { useDataStore } from '../stores/dataStore'
 import apiService from '../api/apiService'
 import apiService from '../api/apiService'
 import StatusIndicator from './StatusIndicator.vue'
 import StatusIndicator from './StatusIndicator.vue'
@@ -252,6 +279,13 @@ const canRegister = computed(() => {
   return formData.value.topic_prefix && formData.value.customer_id && formData.value.dtu_id
   return formData.value.topic_prefix && formData.value.customer_id && formData.value.dtu_id
 })
 })
 
 
+const getTempColor = (t) => {
+  if (t === null || t === undefined) return '#999'
+  if (t >= 70) return '#ff4d4f'
+  if (t >= 60) return '#faad14'
+  return '#52c41a'
+}
+
 watch(() => formData.value.topic_prefix, () => {
 watch(() => formData.value.topic_prefix, () => {
   if (formData.value.topic_prefix) {
   if (formData.value.topic_prefix) {
     showErrors.value = false
     showErrors.value = false
@@ -380,6 +414,8 @@ const sendConfigBroadcast = async () => {
 
 
 onMounted(() => {
 onMounted(() => {
   refreshStatus()
   refreshStatus()
+  const timer = setInterval(refreshStatus, 30000)
+  onUnmounted(() => clearInterval(timer))
 })
 })
 </script>
 </script>
 
 

+ 44 - 1
frontend/src/views/EnvSensor.vue

@@ -56,6 +56,45 @@
       </a-col>
       </a-col>
     </a-row>
     </a-row>
 
 
+    <a-row :gutter="[24, 24]" style="margin-top: 24px">
+      <a-col :xs="24" :sm="12">
+        <a-card :bordered="false" class="sensor-card">
+          <div class="sensor-content">
+            <div class="sensor-icon">
+              <DashboardOutlined />
+            </div>
+            <div class="sensor-info" style="width: 100%">
+              <div class="sensor-label">CPU 使用率</div>
+              <a-progress
+                v-if="sensorData.cpu_usage !== null && sensorData.cpu_usage !== undefined"
+                :percent="Math.round(sensorData.cpu_usage)"
+                :status="sensorData.cpu_usage > 80 ? 'exception' : 'normal'"
+              />
+              <div v-else class="sensor-value">--</div>
+            </div>
+          </div>
+        </a-card>
+      </a-col>
+      <a-col :xs="24" :sm="12">
+        <a-card :bordered="false" class="sensor-card">
+          <div class="sensor-content">
+            <div class="sensor-icon">
+              <DatabaseOutlined />
+            </div>
+            <div class="sensor-info" style="width: 100%">
+              <div class="sensor-label">内存使用率</div>
+              <a-progress
+                v-if="sensorData.memory_usage !== null && sensorData.memory_usage !== undefined"
+                :percent="Math.round(sensorData.memory_usage)"
+                :status="sensorData.memory_usage > 80 ? 'exception' : 'normal'"
+              />
+              <div v-else class="sensor-value">--</div>
+            </div>
+          </div>
+        </a-card>
+      </a-col>
+    </a-row>
+
     <!-- 详细信息卡片 -->
     <!-- 详细信息卡片 -->
     <a-card :bordered="false" class="detail-card">
     <a-card :bordered="false" class="detail-card">
       <template #title>
       <template #title>
@@ -199,7 +238,9 @@ import {
   InfoCircleOutlined,
   InfoCircleOutlined,
   WarningOutlined,
   WarningOutlined,
   LineChartOutlined,
   LineChartOutlined,
-  ReloadOutlined
+  ReloadOutlined,
+  DashboardOutlined,
+  DatabaseOutlined
 } from '@ant-design/icons-vue'
 } from '@ant-design/icons-vue'
 
 
 const loading = ref(false)
 const loading = ref(false)
@@ -278,6 +319,8 @@ const fetchSensorData = async () => {
         dtu_temperature: result.data?.dtu_temperature,
         dtu_temperature: result.data?.dtu_temperature,
         update_time: result.data?.update_time,
         update_time: result.data?.update_time,
         sensor_update_time: result.data?.sensor_update_time,
         sensor_update_time: result.data?.sensor_update_time,
+        cpu_usage: result.data?.cpu_usage,
+        memory_usage: result.data?.memory_usage,
         connected: result.data?.connected ?? (result.data?.temperature !== null)
         connected: result.data?.connected ?? (result.data?.temperature !== null)
       }
       }
     }
     }

Некоторые файлы не были показаны из-за большого количества измененных файлов