鸿蒙跨端智能农场灌溉控制系统开发指南

鸿蒙跨端智能农场灌溉控制系统开发指南

一、项目概述

本文基于HarmonyOS的分布式能力和物联网技术,开发一套智能农场灌溉控制系统。该系统整合土壤墒情预测模型、阀门开关时序优化和多云天气策略调整,并借鉴《鸿蒙跨端U同步》中的多设备同步技术,实现农场灌溉设备的智能控制和多终端协同管理。

二、系统架构

+---------------------+       +---------------------+       +---------------------+
|   控制终端          |<----->|   分布式数据总线    |<----->|   灌溉设备          |
| (手机/平板/PC)      |       | (Distributed Bus)   |       | (阀门/传感器节点)   |
+----------+----------+       +----------+----------+       +----------+----------+
           |                              |                              |
+----------v----------+       +----------v----------+       +----------v----------+
|  预测模型模块       |       |  灌溉控制模块       |       |  设备同步模块       |
| (墒情预测/天气)     |       | (阀门时序/策略)      |       | (状态同步/控制)     |
+---------------------+       +---------------------+       +---------------------+

三、核心代码实现

1. 灌溉控制服务实现

// src/main/ets/service/IrrigationService.ts
import { distributedData } from '@ohos.data.distributedData';
import { BusinessError } from '@ohos.base';
import { weather } from '@ohos.weather';
import { sensor } from '@ohos.sensor';
import { taskpool } from '@ohos.taskpool';
import { fileIo } from '@ohos.fileio';
import { zlib } from '@ohos.zlib';

interface SoilData {
  timestamp: number;
  moisture: number;    // 土壤湿度 0-100%
  temperature: number; // 土壤温度 ℃
  salinity: number;    // 盐分含量 %
  deviceId: string;
  isSynced: boolean;
}

interface WeatherData {
  timestamp: number;
  temperature: number; // 气温 ℃
  humidity: number;    // 空气湿度 %
  precipitation: number; // 降水量 mm
  cloudCover: number;   // 云量 %
  isSynced: boolean;
}

interface ValveControl {
  valveId: string;
  openTime: number;    // 开启时间(分钟)
  startTime: number;   // 开始时间戳
  endTime: number;     // 结束时间戳
  waterAmount: number; // 用水量 m³
  isCompleted: boolean;
  isSynced: boolean;
}

interface IrrigationPlan {
  planId: string;
  valveIds: string[];
  predictedMoisture: number; // 预测墒情 %
  requiredWater: number;    // 需水量 m³
  plannedTime: number;      // 计划执行时间戳
  actualTime?: number;      // 实际执行时间戳
  status: 'pending' | 'executing' | 'completed' | 'canceled';
  isSynced: boolean;
}

export class IrrigationService {
  private static instance: IrrigationService;
  private kvStore: distributedData.KVStore | null = null;
  private readonly STORE_ID = 'irrigation_store';
  private soilSensorId: number = -1;
  private soilData: SoilData[] = [];
  private weatherData: WeatherData[] = [];
  private valveControls: ValveControl[] = [];
  private irrigationPlans: IrrigationPlan[] = [];
  private lastSyncTime: number = 0;
  private readonly SYNC_INTERVAL = 30 * 60 * 1000; // 30分钟同步一次
  
  private constructor() {
    this.initKVStore();
    this.loadLocalData();
    this.initWeatherUpdates();
  }

  public static getInstance(): IrrigationService {
    if (!IrrigationService.instance) {
      IrrigationService.instance = new IrrigationService();
    }
    return IrrigationService.instance;
  }

  private async initKVStore(): Promise<void> {
    try {
      const options: distributedData.KVManagerConfig = {
        bundleName: 'com.example.irrigation',
        userInfo: {
          userId: '0',
          userType: distributedData.UserType.SAME_USER_ID
        }
      };
      
      const kvManager = distributedData.createKVManager(options);
      this.kvStore = await kvManager.getKVStore({
        storeId: this.STORE_ID,
        options: {
          createIfMissing: true,
          encrypt: false,
          backup: false,
          autoSync: true,
          kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
        }
      });
      
      this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
        this.handleRemoteDataChange(data);
      });
    } catch (e) {
      console.error(`Failed to initialize KVStore. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private async loadLocalData(): Promise<void> {
    try {
      // 加载土壤数据
      const soilFile = await fileIo.open('data/soil_data.bin', 0o666);
      const soilData = await fileIo.read(soilFile.fd, new ArrayBuffer(0));
      await fileIo.close(soilFile.fd);
      
      if (soilData) {
        const decompressed = await zlib.deflateSync(soilData);
        this.soilData = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
      }
      
      // 加载天气数据
      const weatherFile = await fileIo.open('data/weather_data.bin', 0o666);
      const weatherData = await fileIo.read(weatherFile.fd, new ArrayBuffer(0));
      await fileIo.close(weatherFile.fd);
      
      if (weatherData) {
        const decompressed = await zlib.deflateSync(weatherData);
        this.weatherData = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
      }
      
      // 加载阀门控制记录
      const valveFile = await fileIo.open('data/valve_controls.bin', 0o666);
      const valveData = await fileIo.read(valveFile.fd, new ArrayBuffer(0));
      await fileIo.close(valveFile.fd);
      
      if (valveData) {
        const decompressed = await zlib.deflateSync(valveData);
        this.valveControls = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
      }
      
      // 加载灌溉计划
      const planFile = await fileIo.open('data/irrigation_plans.bin', 0o666);
      const planData = await fileIo.read(planFile.fd, new ArrayBuffer(0));
      await fileIo.close(planFile.fd);
      
      if (planData) {
        const decompressed = await zlib.deflateSync(planData);
        this.irrigationPlans = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
      }
    } catch (e) {
      console.log('No local data found or error reading file');
    }
  }

  private async saveLocalData(): Promise<void> {
    try {
      // 确保目录存在
      await fileIo.mkdir('data');
      
      // 保存土壤数据
      const soilStr = JSON.stringify(this.soilData);
      const soilCompressed = await zlib.inflateSync(new Uint8Array(soilStr.split('').map(c => c.charCodeAt(0))));
      
      const soilFile = await fileIo.open('data/soil_data.bin', 0o666 | fileIo.OpenMode.CREATE);
      await fileIo.write(soilFile.fd, soilCompressed.buffer);
      await fileIo.close(soilFile.fd);
      
      // 保存天气数据
      const weatherStr = JSON.stringify(this.weatherData);
      const weatherCompressed = await zlib.inflateSync(new Uint8Array(weatherStr.split('').map(c => c.charCodeAt(0))));
      
      const weatherFile = await fileIo.open('data/weather_data.bin', 0o666 | fileIo.OpenMode.CREATE);
      await fileIo.write(weatherFile.fd, weatherCompressed.buffer);
      await fileIo.close(weatherFile.fd);
      
      // 保存阀门控制记录
      const valveStr = JSON.stringify(this.valveControls);
      const valveCompressed = await zlib.inflateSync(new Uint8Array(valveStr.split('').map(c => c.charCodeAt(0))));
      
      const valveFile = await fileIo.open('data/valve_controls.bin', 0o666 | fileIo.OpenMode.CREATE);
      await fileIo.write(valveFile.fd, valveCompressed.buffer);
      await fileIo.close(valveFile.fd);
      
      // 保存灌溉计划
      const planStr = JSON.stringify(this.irrigationPlans);
      const planCompressed = await zlib.inflateSync(new Uint8Array(planStr.split('').map(c => c.charCodeAt(0))));
      
      const planFile = await fileIo.open('data/irrigation_plans.bin', 0o666 | fileIo.OpenMode.CREATE);
      await fileIo.write(planFile.fd, planCompressed.buffer);
      await fileIo.close(planFile.fd);
    } catch (e) {
      console.error(`Failed to save local data. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private async initWeatherUpdates(): Promise<void> {
    try {
      // 注册天气变化监听
      weather.on('weatherChange', (data) => {
        this.handleWeatherUpdate(data);
      });
      
      // 立即获取一次天气数据
      const currentWeather = await weather.getWeatherInfo();
      this.handleWeatherUpdate(currentWeather);
    } catch (e) {
      console.error(`Failed to initialize weather updates. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private async handleWeatherUpdate(weatherInfo: weather.WeatherInfo): Promise<void> {
    const now = Date.now();
    const newData: WeatherData = {
      timestamp: now,
      temperature: weatherInfo.temperature,
      humidity: weatherInfo.humidity,
      precipitation: weatherInfo.precipitation,
      cloudCover: weatherInfo.cloudCover,
      isSynced: false
    };
    
    this.weatherData.push(newData);
    
    // 天气变化时重新评估灌溉计划
    await this.evaluateIrrigationPlans();
    
    // 保存并同步数据
    await this.saveLocalData();
    
    if (now - this.lastSyncTime > this.SYNC_INTERVAL) {
      await this.syncData();
      this.lastSyncTime = now;
    }
  }

  public async startSoilMonitoring(): Promise<boolean> {
    try {
      this.soilSensorId = await sensor.on(sensor.SensorId.SOIL_MOISTURE, {
        interval: sensor.SensorFrequency.SENSOR_DELAY_NORMAL,
        callback: (data) => {
          this.handleSoilData(data);
        }
      });
      return true;
    } catch (e) {
      console.error(`Failed to start soil monitoring. Code: ${e.code}, message: ${e.message}`);
      return false;
    }
  }

  private async handleSoilData(data: sensor.SoilMoistureResponse): Promise<void> {
    const now = Date.now();
    const newData: SoilData = {
      timestamp: now,
      moisture: data.moisture,
      temperature: data.temperature,
      salinity: data.salinity,
      deviceId: this.getDeviceId(),
      isSynced: false
    };
    
    this.soilData.push(newData);
    
    // 新土壤数据时重新评估灌溉计划
    await this.evaluateIrrigationPlans();
    
    // 保存并同步数据
    await this.saveLocalData();
    
    if (now - this.lastSyncTime > this.SYNC_INTERVAL) {
      await this.syncData();
      this.lastSyncTime = now;
    }
  }

  private async evaluateIrrigationPlans(): Promise<void> {
    // 使用任务池并行计算预测模型
    const task = new taskpool.Task(this.predictSoilMoistureTask, this.soilData, this.weatherData);
    const predictedMoisture = await taskpool.execute(task) as number;
    
    // 获取最新的土壤数据
    const latestSoil = this.soilData.length > 0 ? 
      this.soilData[this.soilData.length - 1] : null;
    
    // 获取最新的天气数据
    const latestWeather = this.weatherData.length > 0 ? 
      this.weatherData[this.weatherData.length - 1] : null;
    
    if (!latestSoil || !latestWeather) return;
    
    // 计算需水量 (简化模型)
    let requiredWater = 0;
    if (predictedMoisture < 30) {
      requiredWater = 10; // 重度缺水,需10m³
    } else if (predictedMoisture < 50) {
      requiredWater = 5;  // 中度缺水,需5m³
    } else if (predictedMoisture < 70) {
      requiredWater = 2;  // 轻度缺水,需2m³
    }
    
    // 多云天气调整策略
    if (latestWeather.cloudCover > 70) {
      // 多云天气减少30%的灌溉量
      requiredWater *= 0.7;
    }
    
    if (requiredWater > 0) {
      // 创建新的灌溉计划
      const planId = 'plan_' + Date.now();
      const valveIds = ['valve_1', 'valve_2', 'valve_3']; // 实际应用中应获取真实阀门ID
      
      const newPlan: IrrigationPlan = {
        planId,
        valveIds,
        predictedMoisture,
        requiredWater,
        plannedTime: Date.now() + 30 * 60 * 1000, // 30分钟后执行
        status: 'pending',
        isSynced: false
      };
      
      this.irrigationPlans.push(newPlan);
      await this.saveLocalData();
      await this.syncData();
      
      // 调度计划执行
      setTimeout(() => {
        this.executeIrrigationPlan(newPlan);
      }, 30 * 60 * 1000);
    }
  }

  private predictSoilMoistureTask(soilData: SoilData[], weatherData: WeatherData[]): number {
    // 简化的土壤墒情预测模型
    // 实际应用中应使用更复杂的机器学习模型
    
    if (soilData.length === 0 || weatherData.length === 0) return 50; // 默认值
    
    // 计算过去24小时平均土壤湿度
    const now = Date.now();
    const oneDayAgo = now - 24 * 60 * 60 * 1000;
    
    const recentSoilData = soilData.filter(d => d.timestamp >= oneDayAgo);
    const avgMoisture = recentSoilData.reduce((sum, d) => sum + d.moisture, 0) / recentSoilData.length;
    
    // 考虑天气因素
    const recentWeatherData = weatherData.filter(d => d.timestamp >= oneDayAgo);
    const avgPrecipitation = recentWeatherData.reduce((sum, d) => sum + d.precipitation, 0) / recentWeatherData.length;
    const avgTemp = recentWeatherData.reduce((sum, d) => sum + d.temperature, 0) / recentWeatherData.length;
    
    // 预测公式 (简化版)
    let predicted = avgMoisture 
      - (avgTemp - 20) * 0.5  // 温度每高于20℃,湿度降低0.5%
      + avgPrecipitation * 2  // 降水每1mm增加2%湿度
      - 2;                    // 自然蒸发
    
    // 限制在合理范围内
    return Math.max(0, Math.min(100, predicted));
  }

  private async executeIrrigationPlan(plan: IrrigationPlan): Promise<void> {
    // 检查计划状态
    const existingPlan = this.irrigationPlans.find(p => p.planId === plan.planId);
    if (!existingPlan || existingPlan.status !== 'pending') return;
    
    // 更新计划状态
    existingPlan.status = 'executing';
    existingPlan.isSynced = false;
    
    // 优化阀门开关时序
    const valveTimings = this.optimizeValveTiming(plan.valveIds, plan.requiredWater);
    
    // 执行阀门控制
    for (const timing of valveTimings) {
      const control: ValveControl = {
        valveId: timing.valveId,
        openTime: timing.duration,
        startTime: Date.now(),
        endTime: Date.now() + timing.duration * 60 * 1000,
        waterAmount: timing.waterAmount,
        isCompleted: false,
        isSynced: false
      };
      
      this.valveControls.push(control);
      
      // 实际应用中应发送控制指令到硬件
      console.log(`Opening valve ${timing.valveId} for ${timing.duration} minutes`);
      
      // 模拟阀门操作
      setTimeout(() => {
        control.isCompleted = true;
        control.isSynced = false;
        this.saveLocalData();
        this.syncData();
      }, timing.duration * 60 * 1000);
    }
    
    // 更新计划状态
    existingPlan.actualTime = Date.now();
    existingPlan.status = 'completed';
    existingPlan.isSynced = false;
    
    await this.saveLocalData();
    await this.syncData();
  }

  private optimizeValveTiming(valveIds: string[], totalWater: number): Array<{
    valveId: string;
    duration: number;
    waterAmount: number;
  }> {
    // 阀门时序优化算法
    // 简化版:平均分配水量到各阀门
    
    const valveCount = valveIds.length;
    const waterPerValve = totalWater / valveCount;
    const durationPerValve = Math.ceil(waterPerValve * 10); // 假设1分钟灌溉0.1m³
    
    return valveIds.map(valveId => ({
      valveId,
      duration: durationPerValve,
      waterAmount: waterPerValve
    }));
  }

  private getDeviceId(): string {
    // 实际应用中应获取真实设备ID
    return 'irrigation_' + Math.random().toString(36).substr(2, 9);
  }

  private async syncData(): Promise<void> {
    if (!this.kvStore) return;
    
    try {
      // 同步土壤数据
      const unsyncedSoil = this.soilData.filter(d => !d.isSynced);
      if (unsyncedSoil.length > 0) {
        await this.kvStore.put('soil_data', { value: unsyncedSoil });
        this.soilData.forEach(d => {
          if (!d.isSynced) d.isSynced = true;
        });
      }
      
      // 同步天气数据
      const unsyncedWeather = this.weatherData.filter(d => !d.isSynced);
      if (unsyncedWeather.length > 0) {
        await this.kvStore.put('weather_data', { value: unsyncedWeather });
        this.weatherData.forEach(d => {
          if (!d.isSynced) d.isSynced = true;
        });
      }
      
      // 同步阀门控制
      const unsyncedValves = this.valveControls.filter(v => !v.isSynced);
      if (unsyncedValves.length > 0) {
        await this.kvStore.put('valve_controls', { value: unsyncedValves });
        this.valveControls.forEach(v => {
          if (!v.isSynced) v.isSynced = true;
        });
      }
      
      // 同步灌溉计划
      const unsyncedPlans = this.irrigationPlans.filter(p => !p.isSynced);
      if (unsyncedPlans.length > 0) {
        await this.kvStore.put('irrigation_plans', { value: unsyncedPlans });
        this.irrigationPlans.forEach(p => {
          if (!p.isSynced) p.isSynced = true;
        });
      }
    } catch (e) {
      console.error(`Failed to sync data. Code: ${e.code}, message: ${e.message}`);
    }
  }

  private handleRemoteDataChange(data: distributedData.ChangeData): void {
    data.insertEntries.forEach((entry: distributedData.Entry) => {
      if (entry.key === 'soil_data') {
        const remoteData = entry.value.value as SoilData[];
        this.mergeSoilData(remoteData);
      } else if (entry.key === 'weather_data') {
        const remoteData = entry.value.value as WeatherData[];
        this.mergeWeatherData(remoteData);
      } else if (entry.key === 'valve_controls') {
        const remoteData = entry.value.value as ValveControl[];
        this.mergeValveControls(remoteData);
      } else if (entry.key === 'irrigation_plans') {
        const remoteData = entry.value.value as IrrigationPlan[];
        this.mergeIrrigationPlans(remoteData);
      }
    });
  }

  private mergeSoilData(remoteData: SoilData[]): void {
    remoteData.forEach(remote => {
      const existing = this.soilData.find(local => 
        local.timestamp === remote.timestamp && 
        local.deviceId === remote.deviceId
      );
      
      if (!existing) {
        this.soilData.push(remote);
      } else {
        // 合并策略:保留更精确的数据
        existing.moisture = remote.moisture;
        existing.temperature = remote.temperature;
        existing.salinity = remote.salinity;
      }
    });
    
    // 按时间排序
    this.soilData.sort((a, b) => a.timestamp - b.timestamp);
  }

  private mergeWeatherData(remoteData: WeatherData[]): void {
    remoteData.forEach(remote => {
      const existing = this.weatherData.find(local => 
        local.timestamp === remote.timestamp
      );
      
      if (!existing) {
        this.weatherData.push(remote);
      } else {
        // 合并策略:保留更新的数据
        existing.temperature = remote.temperature;
        existing.humidity = remote.humidity;
        existing.precipitation = remote.precipitation;
        existing.cloudCover = remote.cloudCover;
      }
    });
    
    // 按时间排序
    this.weatherData.sort((a, b) => a.timestamp - b.timestamp);
  }

  private mergeValveControls(remoteData: ValveControl[]): void {
    remoteData.forEach(remote => {
      const existing = this.valveControls.find(local => 
        local.valveId === remote.valveId &&
        local.startTime === remote.startTime
      );
      
      if (!existing) {
        this.valveControls.push(remote);
      } else {
        // 合并策略:保留执行状态
        existing.isCompleted = remote.isCompleted;
      }
    });
    
    // 按开始时间排序
    this.valveControls.sort((a, b) => a.startTime - b.startTime);
  }

  private mergeIrrigationPlans(remoteData: IrrigationPlan[]): void {
    remoteData.forEach(remote => {
      const existing = this.irrigationPlans.find(local => 
        local.planId === remote.planId
      );
      
      if (!existing) {
        this.irrigationPlans.push(remote);
      } else {
        // 合并策略:保留状态和实际执行时间
        existing.status = remote.status;
        if (remote.actualTime) {
          existing.actualTime = remote.actualTime;
        }
      }
    });
    
    // 按计划时间排序
    this.irrigationPlans.sort((a, b) => (a.plannedTime || 0) - (b.plannedTime || 0));
  }

  public getCurrentSoilStatus(): SoilData | null {
    if (this.soilData.length === 0) return null;
    return this.soilData[this.soilData.length - 1];
  }

  public getCurrentWeatherStatus(): WeatherData | null {
    if (this.weatherData.length === 0) return null;
    return this.weatherData[this.weatherData.length - 1];
  }

  public getActivePlans(): IrrigationPlan[] {
    return this.irrigationPlans.filter(p => 
      p.status === 'pending' || p.status === 'executing'
    );
  }

  public getRecentValveControls(): ValveControl[] {
    const now = Date.now();
    const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;
    return this.valveControls.filter(v => v.startTime >= oneWeekAgo);
  }

  public async cancelPlan(planId: string): Promise<boolean> {
    const plan = this.irrigationPlans.find(p => p.planId === planId);
    if (!plan || plan.status !== 'pending') return false;
    
    plan.status = 'canceled';
    plan.isSynced = false;
    
    await this.saveLocalData();
    await this.syncData();
    
    return true;
  }

  public async destroy(): Promise<void> {
    if (this.soilSensorId !== -1) {
      await sensor.off(this.soilSensorId);
      this.soilSensorId = -1;
    }
    
    weather.off('weatherChange');
    
    if (this.kvStore) {
      this.kvStore.off('dataChange');
    }
    
    await this.saveLocalData();
  }
}

2. 灌溉控制组件实现

// src/main/ets/components/IrrigationControl.ets
@Component
export struct IrrigationControl {
  private irrigationService = IrrigationService.getInstance();
  @State soilStatus: SoilData | null = null;
  @State weatherStatus: WeatherData | null = null;
  @State activePlans: IrrigationPlan[] = [];
  @State recentValves: ValveControl[] = [];
  private timer: number = 0;
  
  aboutToAppear(): void {
    this.loadData();
    this.startAutoRefresh();
  }
  
  aboutToDisappear(): void {
    this.stopAutoRefresh();
  }

  private loadData(): void {
    this.soilStatus = this.irrigationService.getCurrentSoilStatus();
    this.weatherStatus = this.irrigationService.getCurrentWeatherStatus();
    this.activePlans = this.irrigationService.getActivePlans();
    this.recentValves = this.irrigationService.getRecentValveControls();
  }

  private startAutoRefresh(): void {
    this.timer = setInterval(() => {
      this.loadData();
    }, 60000); // 每分钟刷新一次
  }

  private stopAutoRefresh(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }

  build() {
    Column() {
      // 标题
      Text('智能灌溉控制')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 });
      
      // 状态卡片
      this.buildStatusCard();
      
      // 活动计划
      Text('活动灌溉计划')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 });
      
      if (this.activePlans.length > 0) {
        this.buildActivePlans();
      } else {
        Text('暂无活动灌溉计划')
          .fontSize(14)
          .fontColor('#666666');
      }
      
      // 近期阀门操作
      Text('近期灌溉记录')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 });
      
      if (this.recentValves.length > 0) {
        this.buildValveHistory();
      } else {
        Text('暂无近期灌溉记录')
          .fontSize(14)
          .fontColor('#666666');
      }
    }
    .width('100%')
    .height('100%')
    .padding(20);
  }

  @Builder
  private buildStatusCard() {
    Column() {
      // 土壤状态
      Row() {
        Image($r('app.media.ic_soil'))
          .width(24)
          .height(24)
          .margin({ right: 10 });
        
        Column() {
          Text('土壤湿度')
            .fontSize(14)
            .fontColor('#666666');
          
          Text(this.soilStatus ? `${this.soilStatus.moisture}%` : '--')
            .fontSize(18)
            .fontWeight(FontWeight.Bold);
        }
        .layoutWeight(1);
        
        // 湿度状态指示
        Circle()
          .width(20)
          .height(20)
          .fill(this.getMoistureColor(this.soilStatus?.moisture || 50));
      }
      .margin({ bottom: 10 });
      
      // 天气状态
      Row() {
        Image($r('app.media.ic_weather'))
          .width(24)
          .height(24)
          .margin({ right: 10 });
        
        Column() {
          Text('天气状况')
            .fontSize(14)
            .fontColor('#666666');
          
          if (this.weatherStatus) {
            Text(`${this.weatherStatus.temperature}℃ / ${this.weatherStatus.cloudCover}%云量`)
              .fontSize(16);
            
            Text(`降水 ${this.weatherStatus.precipitation}mm / 湿度 ${this.weatherStatus.humidity}%`)
              .fontSize(14)
              .fontColor('#666666');
          } else {
            Text('--')
              .fontSize(16);
          }
        }
        .layoutWeight(1);
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(10)
    .shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
  }

  private getMoistureColor(moisture: number | undefined): string {
    if (moisture === undefined) return '#9E9E9E';
    if (moisture < 30) return '#F44336'; // 干燥
    if (moisture < 60) return '#4CAF50'; // 适宜
    return '#2196F3'; // 湿润
  }

  @Builder
  private buildActivePlans() {
    Column() {
      ForEach(this.activePlans, (plan) => {
        Column() {
          Row() {
            Text(`计划 #${plan.planId.substring(0, 6)}`)
              .fontSize(16)
              .fontWeight(FontWeight.Bold)
              .layoutWeight(1);
            
            Text(this.getPlanStatusText(plan.status))
              .fontSize(14)
              .fontColor(this.getPlanStatusColor(plan.status));
          }
          
          Row() {
            Text(`需水量: ${plan.requiredWater.toFixed(1)}m³`)
              .fontSize(14)
              .fontColor('#666666');
            
            Text(`预测墒情: ${plan.predictedMoisture.toFixed(1)}%`)
              .fontSize(14)
              .fontColor('#666666')
              .margin({ left: 10 });
          }
          .margin({ top: 5 });
          
          Row() {
            Text(`计划时间: ${new Date(plan.plannedTime).toLocaleTimeString()}`)
              .fontSize(14)
              .fontColor('#666666');
            
            if (plan.status === 'executing' && plan.actualTime) {
              Text(`开始时间: ${new Date(plan.actualTime).toLocaleTimeString()}`)
                .fontSize(14)
                .fontColor('#666666')
                .margin({ left: 10 });
            }
          }
          .margin({ top: 5 });
          
          if (plan.status === 'pending') {
            Button('取消计划')
              .type(ButtonType.Capsule)
              .width('40%')
              .height(30)
              .backgroundColor('#F44336')
              .fontColor('#FFFFFF')
              .margin({ top: 10 })
              .onClick(() => {
                this.cancelPlan(plan.planId);
              });
          }
        }
        .width('100%')
        .padding(10)
        .backgroundColor('#F5F5F5')
        .borderRadius(8)
        .margin({ bottom: 10 });
      })
    }
  }

  private getPlanStatusText(status: string): string {
    switch (status) {
      case 'pending': return '等待执行';
      case 'executing': return '执行中';
      case 'completed': return '已完成';
      case 'canceled': return '已取消';
      default: return status;
    }
  }

  private getPlanStatusColor(status: string): string {
    switch (status) {
      case 'pending': return '#FF9800';
      case 'executing': return '#2196F3';
      case 'completed': return '#4CAF50';
      case 'canceled': return '#F44336';
      default: return '#9E9E9E';
    }
  }

  @Builder
  private buildValveHistory() {
    Column() {
      ForEach(this.recentValves.slice(0, 5), (valve) => {
        Row() {
          Text(`阀门 ${valve.valveId}`)
            .fontSize(14)
            .layoutWeight(1);
          
          Text(`${valve.openTime}分钟`)
            .fontSize(14)
            .fontColor('#2196F3');
          
          Text(`${valve.waterAmount.toFixed(1)}m³`)
            .fontSize(14)
            .fontColor('#4CAF50')
            .margin({ left: 10 });
          
          Text(valve.isCompleted ? '已完成' : '进行中')
            .fontSize(14)
            .fontColor(valve.isCompleted ? '#4CAF50' : '#FF9800')
            .margin({ left: 10 });
        }
        .width('100%')
        .padding(5)
        .margin({ bottom: 5 });
      })
    }
  }

  private async cancelPlan(planId: string): Promise<void> {
    const success = await this.irrigationService.cancelPlan(planId);
    if (success) {
      prompt.showToast({ message: '计划已取消', duration: 2000 });
      this.loadData();
    } else {
      prompt.showToast({ message: '取消计划失败', duration: 2000 });
    }
  }
}

3. 主界面实现

// src/main/ets/pages/FarmPage.ets
import { IrrigationService } from '../service/IrrigationService';
import { IrrigationControl } from '../components/IrrigationControl';

@Entry
@Component
struct FarmPage {
  @State activeTab: number = 0;
  private irrigationService = IrrigationService.getInstance();
  
  build() {
    Column() {
      // 标题
      Text('智能农场灌溉系统')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 });
      
      // 标签页
      Tabs({ barPosition: BarPosition.Start }) {
        TabContent() {
          // 灌溉控制标签页
          IrrigationControl()
        }
        .tabBar('灌溉控制');
        
        TabContent() {
          // 数据分析标签页
          this.buildAnalyticsTab()
        }
        .tabBar('数据分析');
      }
      .barWidth('100%')
      .barHeight(50)
      .width('100%')
      .height('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20);
  }

  @Builder
  private buildAnalyticsTab() {
    Column() {
      Text('土壤墒情趋势')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 15 });
      
      this.buildSoilChart();
      
      Text('灌溉用水统计')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 15 });
      
      this.buildWaterUsageChart();
    }
    .width('100%')
    .height('100%')
    .padding(20);
  }

  @Builder
  private buildSoilChart() {
    // 获取最近7天数据
    const now = Date.now();
    const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
    const soilData = this.irrigationService.getSoilData().filter(d => d.timestamp >= sevenDaysAgo);
    
    // 按天分组
    const dailyData: Record<string, SoilData[]> = {};
    soilData.forEach(data => {
      const date = new Date(data.timestamp).toLocaleDateString();
      if (!dailyData[date]) {
        dailyData[date] = [];
      }
      dailyData[date].push(data);
    });
    
    // 计算每天平均湿度
    const chartData = Object.entries(dailyData).map(([date, data]) => {
      const avgMoisture = data.reduce((sum, d) => sum + d.moisture, 0) / data.length;
      return { date, moisture: avgMoisture };
    });
    
    // 构建图表
    Column() {
      Stack() {
        // 网格线
        ForEach(Array.from({ length: 5 }), (_, i) => {
          Line()
            .width('100%')
            .height(1)
            .backgroundColor('#E0E0E0')
            .position({ x: 0, y: i * 40 });
        });
        
        // 数据线
        Polyline()
          .width('100%')
          .height(160)
          .fillOpacity(0)
          .stroke('#4CAF50')
          .strokeWidth(2)
          .points(this.getChartPoints(chartData));
        
        // 数据点
        ForEach(chartData, (data, i) => {
          Circle()
            .width(8)
            .height(8)
            .fill('#4CAF50')
            .position({
              x: i * (100 / (chartData.length - 1)) + '%',
              y: (100 - data.moisture) * 1.6 + '%'
            });
        });
      }
      .width('100%')
      .height(160);
      
      // X轴标签
      Row() {
        ForEach(chartData, (data) => {
          Text(data.date.split('/')[2]) // 只显示日
            .fontSize(10)
            .width(`${100 / chartData.length}%`)
            .textAlign(TextAlign.Center);
        });
      }
      .width('100%')
      .margin({ top: 5 });
    }
  }

  private getChartPoints(data: Array<{moisture: number}>): Point[] {
    return data.map((d, i) => ({
      x: i * (100 / (data.length - 1)),
      y: (100 - d.moisture) * 1.6
    }));
  }

  @Builder
  private buildWaterUsageChart() {
    // 获取最近7天阀门控制记录
    const now = Date.now();
    const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
    const valveData = this.irrigationService.getValveControls().filter(v => v.startTime >= sevenDaysAgo);
    
    // 按天分组计算用水量
    const dailyUsage: Record<string, number> = {};
    valveData.forEach(data => {
      const date = new Date(data.startTime).toLocaleDateString();
      dailyUsage[date] = (dailyUsage[date] || 0) + data.waterAmount;
    });
    
    // 构建图表数据
    const chartData = Object.entries(dailyUsage).map(([date, amount]) => ({
      date,
      amount
    }));
    
    // 构建柱状图
    Row({ space: 20 }) {
      ForEach(chartData, (data) => {
        Column() {
          // 柱状图
          Column() {
            Blank()
              .height(150 * (data.amount / 20)) // 假设最大20m³
              .width(30)
              .backgroundColor('#2196F3')
          }
          .height(150)
          .justifyContent(FlexAlign.End);
          
          // 日期标签
          Text(data.date.split('/')[2]) // 只显示日
            .fontSize(12)
            .margin({ top: 5 });
          
          // 用水量标签
          Text(`${data.amount.toFixed(1)}m³`)
            .fontSize(10)
            .fontColor('#666666')
            .margin({ top: 5 });
        }
      })
    }
    .width('100%')
    .height(200)
    .margin({ top: 20 });
  }
}

四、与游戏同步技术的结合点

  1. ​实时状态同步​​:借鉴游戏中玩家状态实时同步机制,优化灌溉设备状态的跨设备同步
  2. ​冲突解决策略​​:采用类似游戏中的"时间戳优先"策略解决多设备控制指令冲突
  3. ​设备角色分配​​:类似游戏中的主机/客户端模式,确定主控制设备和从属设备
  4. ​网络传输优化​​:使用类似游戏中的网络优化技术,对控制指令进行批量压缩传输
  5. ​状态一致性保障​​:参考游戏中的状态同步机制,确保多设备间控制状态一致

五、关键特性实现

  1. ​土壤墒情预测模型​​:

    private predictSoilMoistureTask(soilData: SoilData[], weatherData: WeatherData[]): number {
      // 计算过去24小时平均土壤湿度
      const now = Date.now();
      const oneDayAgo = now - 24 * 60 * 60 * 1000;
      
      const recentSoilData = soilData.filter(d => d.timestamp >= oneDayAgo);
      const avgMoisture = recentSoilData.reduce((sum, d) => sum + d.moisture, 0) / recentSoilData.length;
      
      // 考虑天气因素
      const recentWeatherData = weatherData.filter(d => d.timestamp >= oneDayAgo);
      const avgPrecipitation = recentWeatherData.reduce((sum, d) => sum + d.precipitation, 0) / recentWeatherData.length;
      const avgTemp = recentWeatherData.reduce((sum, d) => sum + d.temperature, 0) / recentWeatherData.length;
      
      // 预测公式 (简化版)
      let predicted = avgMoisture 
        - (avgTemp - 20) * 0.5  // 温度每高于20℃,湿度降低0.5%
        + avgPrecipitation * 2  // 降水每1mm增加2%湿度
        - 2;                    // 自然蒸发
      
      return Math.max(0, Math.min(100, predicted));
    }
  2. ​阀门开关时序优化​​:

    private optimizeValveTiming(valveIds: string[], totalWater: number): Array<{
      valveId: string;
      duration: number;
      waterAmount: number;
    }> {
      // 阀门时序优化算法
      // 简化版:平均分配水量到各阀门
      
      const valveCount = valveIds.length;
      const waterPerValve = totalWater / valveCount;
      const durationPerValve = Math.ceil(waterPerValve * 10); // 假设1分钟灌溉0.1m³
      
      return valveIds.map(valveId => ({
        valveId,
        duration: durationPerValve,
        waterAmount: waterPerValve
      }));
    }
  3. ​多云天气策略调整​​:

    // 计算需水量 (简化模型)
    let requiredWater = 0;
    if (predictedMoisture < 30) {
      requiredWater = 10; // 重度缺水,需10m³
    } else if (predictedMoisture < 50) {
      requiredWater = 5;  // 中度缺水,需5m³
    } else if (predictedMoisture < 70) {
      requiredWater = 2;  // 轻度缺水,需2m³
    }
    
    // 多云天气调整策略
    if (latestWeather.cloudCover > 70) {
      // 多云天气减少30%的灌溉量
      requiredWater *= 0.7;
    }
  4. ​分布式数据同步​​:

    private handleRemoteDataChange(data: distributedData.ChangeData): void {
      data.insertEntries.forEach((entry: distributedData.Entry) => {
        if (entry.key === 'soil_data') {
          const remoteData = entry.value.value as SoilData[];
          this.mergeSoilData(remoteData);
        } else if (entry.key === 'weather_data') {
          const remoteData = entry.value.value as WeatherData[];
          this.mergeWeatherData(remoteData);
        } else if (entry.key === 'valve_controls') {
          const remoteData = entry.value.value as ValveControl[];
          this.mergeValveControls(remoteData);
        } else if (entry.key === 'irrigation_plans') {
          const remoteData = entry.value.value as IrrigationPlan[];
          this.mergeIrrigationPlans(remoteData);
        }
      });
    }

六、性能优化策略

  1. ​智能同步调度​​:

    // 只有新数据时才触发同步
    if (now - this.lastSyncTime > this.SYNC_INTERVAL) {
      await this.syncData();
      this.lastSyncTime = now;
    }
  2. ​本地缓存优先​​:

    public getCurrentSoilStatus(): SoilData | null {
      // 先从内存缓存读取
      if (this.soilData.length === 0) return null;
      return this.soilData[this.soilData.length - 1];
    }
  3. ​并行计算优化​​:

    // 使用任务池并行计算预测模型
    const task = new taskpool.Task(this.predictSoilMoistureTask, this.soilData, this.weatherData);
    const predictedMoisture = await taskpool.execute(task) as number;
  4. ​资源释放管理​​:

    public async destroy(): Promise<void> {
      if (this.soilSensorId !== -1) {
        await sensor.off(this.soilSensorId);
        this.soilSensorId = -1;
      }
      
      weather.off('weatherChange');
      
      if (this.kvStore) {
        this.kvStore.off('dataChange');
      }
      
      await this.saveLocalData();
    }

七、项目扩展方向

  1. ​作物生长模型​​:结合不同作物生长阶段的需水特性优化灌溉策略
  2. ​智能预警系统​​:当土壤参数异常或设备故障时自动发送提醒
  3. ​多语言支持​​:增加多语言界面和语音提示
  4. ​云端大数据分析​​:结合历史数据进行长期趋势分析和优化
  5. ​无人机巡检​​:集成无人机进行农田巡检和墒情监测

八、总结

本文实现的智能农场灌溉控制系统具有以下特点:

  1. 采用土壤墒情预测模型,实现精准灌溉决策
  2. 优化阀门开关时序,提高灌溉效率和水资源利用率
  3. 根据多云天气动态调整灌溉策略,避免水资源浪费
  4. 基于分布式数据同步技术,实现多终端协同控制
  5. 提供直观的数据可视化和完整的农场灌溉管理功能

该应用展示了HarmonyOS在农业物联网领域的强大能力,特别是在传感器数据处理、分布式控制和节能优化方面的优势。通过借鉴游戏同步技术,实现了高效可靠的设备控制机制,为智能农业灌溉系统开发提供了完整解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值