鸿蒙跨端智能农场灌溉控制系统开发指南
一、项目概述
本文基于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 });
}
}
四、与游戏同步技术的结合点
- 实时状态同步:借鉴游戏中玩家状态实时同步机制,优化灌溉设备状态的跨设备同步
- 冲突解决策略:采用类似游戏中的"时间戳优先"策略解决多设备控制指令冲突
- 设备角色分配:类似游戏中的主机/客户端模式,确定主控制设备和从属设备
- 网络传输优化:使用类似游戏中的网络优化技术,对控制指令进行批量压缩传输
- 状态一致性保障:参考游戏中的状态同步机制,确保多设备间控制状态一致
五、关键特性实现
-
土壤墒情预测模型:
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)); }
-
阀门开关时序优化:
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 })); }
-
多云天气策略调整:
// 计算需水量 (简化模型) 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; }
-
分布式数据同步:
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); } }); }
六、性能优化策略
-
智能同步调度:
// 只有新数据时才触发同步 if (now - this.lastSyncTime > this.SYNC_INTERVAL) { await this.syncData(); this.lastSyncTime = now; }
-
本地缓存优先:
public getCurrentSoilStatus(): SoilData | null { // 先从内存缓存读取 if (this.soilData.length === 0) return null; return this.soilData[this.soilData.length - 1]; }
-
并行计算优化:
// 使用任务池并行计算预测模型 const task = new taskpool.Task(this.predictSoilMoistureTask, this.soilData, this.weatherData); const predictedMoisture = await taskpool.execute(task) as number;
-
资源释放管理:
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(); }
七、项目扩展方向
- 作物生长模型:结合不同作物生长阶段的需水特性优化灌溉策略
- 智能预警系统:当土壤参数异常或设备故障时自动发送提醒
- 多语言支持:增加多语言界面和语音提示
- 云端大数据分析:结合历史数据进行长期趋势分析和优化
- 无人机巡检:集成无人机进行农田巡检和墒情监测
八、总结
本文实现的智能农场灌溉控制系统具有以下特点:
- 采用土壤墒情预测模型,实现精准灌溉决策
- 优化阀门开关时序,提高灌溉效率和水资源利用率
- 根据多云天气动态调整灌溉策略,避免水资源浪费
- 基于分布式数据同步技术,实现多终端协同控制
- 提供直观的数据可视化和完整的农场灌溉管理功能
该应用展示了HarmonyOS在农业物联网领域的强大能力,特别是在传感器数据处理、分布式控制和节能优化方面的优势。通过借鉴游戏同步技术,实现了高效可靠的设备控制机制,为智能农业灌溉系统开发提供了完整解决方案。