826220679@qq.com
2 天以前 48ee74129bb09a817a0bbbabe860c4007b74c66b
src/gecaoji/Device.java
@@ -1,8 +1,12 @@
package gecaoji;
import baseStation.BaseStation;
import set.Setsys;
import zhuye.MowerLocationData;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Properties;
@@ -72,6 +76,20 @@
    // 卫星可数
    private String differentialAge;
    // 差分时间
    private String selfCheckStatus = "-1";
    // 割草机自检状态
    private String mowerStartStatus = "-1";
    // 割草机启动状态:1开启,0熄火,-1未知
    private String mowerLightStatus = "-1";
    // 割草机灯开关状态:1开启,0关闭,-1未知
    private String mowerBladeHeight = "-1";
    // 割草机刀盘高度:-1未知
    private String mowerWidth;
    // 割草机宽度,单位米
    private String mowerLength;
    // 割草机长度,单位米
    private String mowingSafetyDistance;
    // 割草安全距离,单位米
    private static final double METERS_PER_DEGREE_LAT = 111320.0d;
    
@@ -102,6 +120,61 @@
        applyDefaults(gecaoji);
    }
    /**
     * 保存所有属性到device.properties文件
     */
    public void saveToProperties() {
        Properties properties = new Properties();
        // 加载现有属性(保留其他属性)
        try (FileInputStream input = new FileInputStream("device.properties")) {
            properties.load(input);
        } catch (IOException e) {
            // 如果文件不存在,继续创建新文件
        }
        // 设置所有设备属性
        if (mowerName != null) properties.setProperty("mowerName", mowerName);
        if (mowerModel != null) properties.setProperty("mowerModel", mowerModel);
        if (mowerNumber != null) properties.setProperty("mowerNumber", mowerNumber);
        if (mowingWidth != null) properties.setProperty("mowingWidth", mowingWidth);
        if (mowingHeight != null) properties.setProperty("mowingHeight", mowingHeight);
        if (baseStationNumber != null) properties.setProperty("baseStationNumber", baseStationNumber);
        if (baseStationCardNumber != null) properties.setProperty("baseStationCardNumber", baseStationCardNumber);
        if (baseStationCoordinates != null) properties.setProperty("baseStationCoordinates", baseStationCoordinates);
        if (deviceCardnumber != null) properties.setProperty("deviceCardnumber", deviceCardnumber);
        if (createTime != null) properties.setProperty("createTime", createTime);
        if (GupdateTime != null) properties.setProperty("GupdateTime", GupdateTime);
        if (BupdateTime != null) properties.setProperty("BupdateTime", BupdateTime);
        if (realtimeLatitude != null) properties.setProperty("realtimeLatitude", realtimeLatitude);
        if (realtimeLongitude != null) properties.setProperty("realtimeLongitude", realtimeLongitude);
        if (realtimeAltitude != null) properties.setProperty("realtimeAltitude", realtimeAltitude);
        if (realtimeX != null) properties.setProperty("realtimeX", realtimeX);
        if (realtimeY != null) properties.setProperty("realtimeY", realtimeY);
        if (realtimeSpeed != null) properties.setProperty("realtimeSpeed", realtimeSpeed);
        if (heading != null) properties.setProperty("heading", heading);
        if (pitch != null) properties.setProperty("pitch", pitch);
        if (battery != null) properties.setProperty("battery", battery);
        if (positioningStatus != null) properties.setProperty("positioningStatus", positioningStatus);
        if (satelliteCount != null) properties.setProperty("satelliteCount", satelliteCount);
        if (differentialAge != null) properties.setProperty("differentialAge", differentialAge);
        if (selfCheckStatus != null) properties.setProperty("selfCheckStatus", selfCheckStatus);
        if (mowerStartStatus != null) properties.setProperty("mowerStartStatus", mowerStartStatus);
        if (mowerLightStatus != null) properties.setProperty("mowerLightStatus", mowerLightStatus);
        if (mowerBladeHeight != null) properties.setProperty("mowerBladeHeight", mowerBladeHeight);
        if (mowerWidth != null) properties.setProperty("mowerWidth", mowerWidth);
        if (mowerLength != null) properties.setProperty("mowerLength", mowerLength);
        if (mowingSafetyDistance != null) properties.setProperty("mowingSafetyDistance", mowingSafetyDistance);
        // 保存到文件
        try (FileOutputStream output = new FileOutputStream("device.properties");
             OutputStreamWriter writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
            properties.store(writer, "Updated device properties");
        } catch (IOException ex) {
            System.err.println("无法保存 device.properties: " + ex.getMessage());
        }
    }
    private void loadPropertiesInto(Device target, Properties properties) {
        if (target == null) {
            return;
@@ -131,6 +204,13 @@
        target.positioningStatus = properties.getProperty("positioningStatus", "-1");
        target.satelliteCount = properties.getProperty("satelliteCount", "-1");
        target.differentialAge = properties.getProperty("differentialAge", "-1");
        target.selfCheckStatus = properties.getProperty("selfCheckStatus", "-1");
        target.mowerStartStatus = properties.getProperty("mowerStartStatus", "-1");
        target.mowerLightStatus = properties.getProperty("mowerLightStatus", "-1");
        target.mowerBladeHeight = properties.getProperty("mowerBladeHeight", "-1");
        target.mowerWidth = properties.getProperty("mowerWidth", "-1");
        target.mowerLength = properties.getProperty("mowerLength", "-1");
        target.mowingSafetyDistance = properties.getProperty("mowingSafetyDistance", "-1");
    }
    private void applyDefaults(Device target) {
@@ -162,6 +242,13 @@
        target.positioningStatus = "-1";
        target.satelliteCount = "-1";
        target.differentialAge = "-1";
        target.selfCheckStatus = "-1";
        target.mowerStartStatus = "-1";
        target.mowerLightStatus = "-1";
        target.mowerBladeHeight = "-1";
        target.mowerWidth = "-1";
        target.mowerLength = "-1";
        target.mowingSafetyDistance = "-1";
    }
    public static synchronized Device initializeActiveDevice(String mowerId) { // 根据设备ID初始化活跃设备
@@ -260,6 +347,27 @@
            case "differentialAge":
                this.differentialAge = value;
                return true;
            case "selfCheckStatus":
                this.selfCheckStatus = value;
                return true;
            case "mowerStartStatus":
                this.mowerStartStatus = value;
                return true;
            case "mowerLightStatus":
                this.mowerLightStatus = value;
                return true;
            case "mowerBladeHeight":
                this.mowerBladeHeight = value;
                return true;
            case "mowerWidth":
                this.mowerWidth = value;
                return true;
            case "mowerLength":
                this.mowerLength = value;
                return true;
            case "mowingSafetyDistance":
                this.mowingSafetyDistance = value;
                return true;
            default:
                System.err.println("未知字段: " + fieldName);
                return false;
@@ -274,6 +382,14 @@
        device.applyGNGGAUpdate(gnggaData, deviceId);
    }
    public static synchronized void updateFromSerialGNGGA(String gnggaData) { // 串口数据更新路径(无需设备编号匹配)
        Device device = gecaoji;
        if (device == null) {
            return;
        }
        device.chuankouGNGGAUpdate(gnggaData);
    }
    private void applyGNGGAUpdate(String gnggaData, String deviceId) { // 执行GNGGA更新逻辑
        if (gnggaData == null) {
            return;
@@ -306,11 +422,27 @@
        String longitudeValue = sanitizeField(fields, 4);
        String longitudeHemisphere = sanitizeField(fields, 5);
        realtimeLatitude = defaultIfEmpty(combineCoordinate(latitudeValue, latitudeHemisphere));
        realtimeLongitude = defaultIfEmpty(combineCoordinate(longitudeValue, longitudeHemisphere));
        realtimeAltitude = defaultIfEmpty(sanitizeField(fields, 9));
        String combinedLatitude = combineCoordinate(latitudeValue, latitudeHemisphere);
        if (hasMeaningfulValue(combinedLatitude)) {
            realtimeLatitude = combinedLatitude;
        }
        String combinedLongitude = combineCoordinate(longitudeValue, longitudeHemisphere);
        if (hasMeaningfulValue(combinedLongitude)) {
            realtimeLongitude = combinedLongitude;
        }
        String altitudeValue = sanitizeField(fields, 9);
        if (hasMeaningfulValue(altitudeValue)) {
            realtimeAltitude = altitudeValue;
        }
        positioningStatus = defaultIfEmpty(sanitizeField(fields, 6));
        // 同步到绘制模块的数据源,保证往返绘制定时器能识别定位质量
        try {
            MowerLocationData.updateProperty("positioningQuality", positioningStatus);
        } catch (Throwable ignored) {
            // 防御式:即使更新失败也不影响设备数据处理
        }
        satelliteCount = defaultIfEmpty(sanitizeField(fields, 7));
        differentialAge = defaultIfEmpty(sanitizeField(fields, 13));
        battery = defaultIfEmpty(sanitizeField(fields, 16));
@@ -321,28 +453,98 @@
        updateRelativeCoordinates(latitudeValue, latitudeHemisphere, longitudeValue, longitudeHemisphere);
    }
    /**串口更新GNGGA数据*/
    private void chuankouGNGGAUpdate(String gnggaData) { // 执行GNGGA更新逻辑
        if (gnggaData == null) {
            return;
        }
        String trimmed = gnggaData.trim();
        if (trimmed.isEmpty() || !trimmed.startsWith("$GNGGA")) {
            return;
        }
        String[] fields = trimmed.split(",");
        if (fields.length < 15) {
            System.err.println("GNGGA字段数量不足: " + fields.length);
            return;
        }
        String latitudeValue = sanitizeField(fields, 2);
        String latitudeHemisphere = sanitizeField(fields, 3);
        String longitudeValue = sanitizeField(fields, 4);
        String longitudeHemisphere = sanitizeField(fields, 5);
        String combinedLatitude = combineCoordinate(latitudeValue, latitudeHemisphere);
        if (hasMeaningfulValue(combinedLatitude)) {
            realtimeLatitude = combinedLatitude;
        }
        String combinedLongitude = combineCoordinate(longitudeValue, longitudeHemisphere);
        if (hasMeaningfulValue(combinedLongitude)) {
            realtimeLongitude = combinedLongitude;
        }
        String altitudeValue = sanitizeField(fields, 9);
        if (hasMeaningfulValue(altitudeValue)) {
            realtimeAltitude = altitudeValue;
        }
        positioningStatus = defaultIfEmpty(sanitizeField(fields, 6));
        // 同步到绘制模块的数据源,保证往返绘制定时器能识别定位质量
        try {
            MowerLocationData.updateProperty("positioningQuality", positioningStatus);
        } catch (Throwable ignored) {
            // 防御式:即使更新失败也不影响设备数据处理
        }
        satelliteCount = defaultIfEmpty(sanitizeField(fields, 7));
        differentialAge = defaultIfEmpty(sanitizeField(fields, 13));
        realtimeSpeed ="0";
        GupdateTime = String.valueOf(System.currentTimeMillis());
        updateRelativeCoordinates(latitudeValue, latitudeHemisphere, longitudeValue, longitudeHemisphere);
        // 串口收到GNGGA数据后,触发拖尾更新
        notifyMowerTrailUpdate();
    }
    /**
     * 通知地图渲染器更新割草机拖尾
     * 当串口收到GNGGA数据并更新位置后调用
     */
    private void notifyMowerTrailUpdate() {
        try {
            // 通过Shouye.getInstance()获取实例,避免循环依赖
            zhuye.Shouye shouye = zhuye.Shouye.getInstance();
            if (shouye != null) {
                zhuye.MapRenderer mapRenderer = shouye.getMapRenderer();
                if (mapRenderer != null) {
                    // 调用更新拖尾方法
                    mapRenderer.forceUpdateIdleMowerTrail();
                }
            }
        } catch (Exception e) {
            // 如果调用失败,静默处理(不影响主要功能)
            // System.err.println("通知拖尾更新失败: " + e.getMessage());
        }
    }
    private void updateRelativeCoordinates(String latValue, String latHemisphere,
                                           String lonValue, String lonHemisphere) { // 计算相对坐标
        if (!hasMeaningfulValue(latValue) || !hasMeaningfulValue(lonValue)
                || !hasMeaningfulValue(latHemisphere) || !hasMeaningfulValue(lonHemisphere)) {
            realtimeX = "-1";
            realtimeY = "-1";
            return;
        }
        double mowerLat = toDecimalDegrees(latValue, latHemisphere);
        double mowerLon = toDecimalDegrees(lonValue, lonHemisphere);
        if (Double.isNaN(mowerLat) || Double.isNaN(mowerLon)) {
            realtimeX = "-1";
            realtimeY = "-1";
            return;
        }
        double[] baseLatLon = resolveBaseStationLatLon();
        if (baseLatLon == null) {
            realtimeX = "-1";
            realtimeY = "-1";
            return;
        }
@@ -354,8 +556,13 @@
        double eastMeters = deltaLonDeg * metersPerLon;
        double northMeters = deltaLatDeg * METERS_PER_DEGREE_LAT;
        realtimeX = formatMeters(eastMeters);
        realtimeY = formatMeters(northMeters);
        if (Double.isFinite(eastMeters) && Double.isFinite(northMeters)) {
            realtimeX = formatMeters(eastMeters);
            realtimeY = formatMeters(northMeters);
            // 保存坐标到工具类
            lujing.SavaXyZuobiao.addCoordinate(eastMeters, northMeters);
        }
    }
    private double[] resolveBaseStationLatLon() { // 解析基站经纬度
@@ -636,6 +843,62 @@
        this.differentialAge = differentialAge;
    }
    public String getSelfCheckStatus() { // 获取自检状态
        return selfCheckStatus;
    }
    public void setSelfCheckStatus(String selfCheckStatus) { // 设置自检状态
        this.selfCheckStatus = selfCheckStatus;
    }
    public String getMowerStartStatus() { // 获取割草机启动状态
        return mowerStartStatus;
    }
    public void setMowerStartStatus(String mowerStartStatus) { // 设置割草机启动状态
        this.mowerStartStatus = mowerStartStatus;
    }
    public String getMowerLightStatus() { // 获取割草机灯开关状态
        return mowerLightStatus;
    }
    public void setMowerLightStatus(String mowerLightStatus) { // 设置割草机灯开关状态
        this.mowerLightStatus = mowerLightStatus;
    }
    public String getMowerBladeHeight() { // 获取割草机刀盘高度
        return mowerBladeHeight;
    }
    public void setMowerBladeHeight(String mowerBladeHeight) { // 设置割草机刀盘高度
        this.mowerBladeHeight = mowerBladeHeight;
    }
    public String getMowerWidth() { // 获取割草机宽度
        return mowerWidth;
    }
    public void setMowerWidth(String mowerWidth) { // 设置割草机宽度
        this.mowerWidth = mowerWidth;
    }
    public String getMowerLength() { // 获取割草机长度
        return mowerLength;
    }
    public void setMowerLength(String mowerLength) { // 设置割草机长度
        this.mowerLength = mowerLength;
    }
    public String getMowingSafetyDistance() { // 获取割草安全距离
        return mowingSafetyDistance;
    }
    public void setMowingSafetyDistance(String mowingSafetyDistance) { // 设置割草安全距离
        this.mowingSafetyDistance = mowingSafetyDistance;
    }
    @Override
    public String toString() { // 输出对象信息
        return "Device{" +
@@ -663,6 +926,13 @@
                ", positioningStatus='" + positioningStatus + '\'' +
                ", satelliteCount='" + satelliteCount + '\'' +
                ", differentialAge='" + differentialAge + '\'' +
                ", selfCheckStatus='" + selfCheckStatus + '\'' +
                ", mowerStartStatus='" + mowerStartStatus + '\'' +
                ", mowerLightStatus='" + mowerLightStatus + '\'' +
                ", mowerBladeHeight='" + mowerBladeHeight + '\'' +
                ", mowerWidth='" + mowerWidth + '\'' +
                ", mowerLength='" + mowerLength + '\'' +
                ", mowingSafetyDistance='" + mowingSafetyDistance + '\'' +
                '}';
    }
}