张世豪
3 天以前 0930bed760105b81e2e5055801bec6d6e8d57358
新增了功能
已修改7个文件
已添加15个文件
1201 ■■■■ 文件已修改
.classpath 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Obstacledge.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
dikuai.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/jackson-annotations-2.15.2.jar 补丁 | 查看 | 原始文档 | blame | 历史
lib/jackson-core-2.15.2.jar 补丁 | 查看 | 原始文档 | blame | 历史
lib/jackson-databind-2.15.2.jar 补丁 | 查看 | 原始文档 | blame | 历史
lib/lombok-1.18.36.jar 补丁 | 查看 | 原始文档 | blame | 历史
lib/org.eclipse.paho.client.mqttv3-1.2.2.jar 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Client.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/BasestationData.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/CoordinatePoint.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/GPSData.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/HxzkPathMessage.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/PathData.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/ResponseData.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Entity/StatusData.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/PushCallback.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Util/DeviceMessageParser.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/AoxinglujingNoObstacle.java 459 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/MowingPathGenerationPage.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/udpdell/UDPServer.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.classpath
@@ -6,10 +6,15 @@
            <attribute name="module" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-annotations-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-core-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-databind-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jSerialComm-2.10.4.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jts-core-1.19.0.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/lombok-1.18.36.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/MQTT-1.0-SNAPSHOT.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/org.eclipse.paho.client.mqttv3-1.2.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/slf4j-api-1.7.30.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/slf4j-simple-1.7.30.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/MQTT-1.0-SNAPSHOT.jar"/>
    <classpathentry kind="output" path="bin"/>
</classpath>
Obstacledge.properties
@@ -1,5 +1,5 @@
# å‰²è‰æœºåœ°å—障碍物配置文件
# ç”Ÿæˆæ—¶é—´ï¼š2025-12-23T17:37:19.839482400
# ç”Ÿæˆæ—¶é—´ï¼š2025-12-23T18:32:39.445241900
# åæ ‡ç³»ï¼šWGS84(度分格式)
# ============ åœ°å—基准站配置 ============
dikuai.properties
@@ -1,8 +1,8 @@
#Dikuai Properties
#Tue Dec 23 17:37:19 CST 2025
#Tue Dec 23 18:32:39 CST 2025
LAND1.angleThreshold=-1
LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
LAND1.boundaryCoordinates=4.30,87.65;-2.36,-65.51;44.25,-66.72;49.70,-14.05;98.13,-15.87;99.34,-69.75;137.48,-67.93;134.45,90.07;4.30,87.65
LAND1.boundaryCoordinates=-99.64,193.56;185.77,182.30;61.84,424.89;237.59,415.88;235.34,539.80;-26.03,544.31
LAND1.boundaryOriginalCoordinates=39.831522,116.279873,49.25;39.831524,116.279878,49.25;39.831525,116.279878,49.24;39.831524,116.279912,49.30;39.831524,116.279911,49.29;39.831523,116.279911,49.23;39.831521,116.279915,49.31;39.831517,116.279925,49.34;39.831514,116.279940,49.30;39.831514,116.279957,49.28;39.831516,116.279974,49.28;39.831518,116.279991,49.29;39.831521,116.280008,49.24;39.831524,116.280025,49.30;39.831526,116.280042,49.24;39.831529,116.280059,49.29;39.831529,116.280076,49.26;39.831530,116.280093,49.32;39.831531,116.280110,49.28;39.831533,116.280127,49.28;39.831535,116.280144,49.26;39.831539,116.280161,49.27;39.831544,116.280175,49.25;39.831551,116.280190,49.24;39.831558,116.280204,49.26;39.831566,116.280219,49.26;39.831574,116.280234,49.22;39.831583,116.280248,49.24;39.831591,116.280260,49.24;39.831600,116.280272,49.23;39.831608,116.280285,49.18;39.831615,116.280298,49.12;39.831618,116.280312,49.11;39.831618,116.280328,49.12;39.831615,116.280342,49.15;39.831610,116.280356,49.21;39.831602,116.280369,49.23;39.831592,116.280379,49.25;39.831581,116.280388,49.25;39.831569,116.280394,49.19;39.831559,116.280395,49.23;39.831552,116.280387,49.28;39.831547,116.280373,49.32;39.831544,116.280357,49.33;39.831541,116.280340,49.29;39.831539,116.280324,49.27;39.831536,116.280307,49.24;39.831534,116.280290,49.25;39.831531,116.280273,49.26;39.831527,116.280257,49.28;39.831522,116.280242,49.21;39.831514,116.280232,49.28;39.831504,116.280229,49.24;39.831491,116.280230,49.33;39.831478,116.280233,49.34;39.831466,116.280236,49.31;39.831454,116.280239,49.31;39.831441,116.280242,49.26;39.831429,116.280244,49.23;39.831416,116.280247,49.25;39.831402,116.280250,49.22;39.831389,116.280253,49.25;39.831376,116.280256,49.26;39.831364,116.280258,49.24;39.831351,116.280261,49.25;39.831338,116.280265,49.26;39.831324,116.280268,49.20;39.831311,116.280271,49.16;39.831298,116.280274,49.17;39.831285,116.280277,49.22;39.831271,116.280278,49.16;39.831261,116.280273,49.23
LAND1.boundaryOriginalXY=-1
LAND1.boundaryPointInterval=-1
@@ -17,9 +17,9 @@
LAND1.mowingSafetyDistance=0.53
LAND1.mowingTrack=-1
LAND1.mowingWidth=200
LAND1.plannedPath=136.940221,-67.425155;133.930254,89.530244;4.807861,87.129352;-1.807069,-64.994176;43.773311,-66.177447;49.223902,-13.501734;98.648661,-15.359117;99.857663,-69.194695;136.940221,-67.425155;134.941682,-67.520523;131.930599,89.493063;129.930944,89.455881;132.943143,-67.615891;130.944605,-67.711259;127.931289,89.418700;125.931635,89.381519;128.946066,-67.806627;126.947527,-67.901995;123.931980,89.344337;121.932325,89.307156;124.948988,-67.997363;122.950449,-68.092732;119.932671,89.269974;117.933016,89.232793;120.951910,-68.188100;118.953371,-68.283468;115.933361,89.195611;113.933707,89.158430;116.954833,-68.378836;114.956294,-68.474204;111.934052,89.121248;109.934397,89.084067;112.957755,-68.569572;110.959216,-68.664940;107.934743,89.046886;105.935088,89.009704;108.960677,-68.760308;106.962138,-68.855677;103.935433,88.972523;101.935778,88.935341;104.963600,-68.951045;102.965061,-69.046413;99.936124,88.898160;97.936469,88.860978;100.966522,-69.141781;97.934238,-15.332269;95.936814,88.823797;93.937160,88.786615;95.932427,-15.257041;93.930617,-15.181813;91.937505,88.749434;89.937850,88.712253;91.928806,-15.106585;89.926996,-15.031357;87.938196,88.675071;85.938541,88.637890;87.925186,-14.956129;85.923375,-14.880901;83.938886,88.600708;81.939231,88.563527;83.921565,-14.805673;81.919754,-14.730445;79.939577,88.526345;77.939922,88.489164;79.917944,-14.655217;77.916134,-14.579989;75.940267,88.451983;73.940613,88.414801;75.914323,-14.504760;73.912513,-14.429532;71.940958,88.377620;69.941303,88.340438;71.910702,-14.354304;69.908892,-14.279076;67.941649,88.303257;65.941994,88.266075;67.907082,-14.203848;65.905271,-14.128620;63.942339,88.228894;61.942685,88.191712;63.903461,-14.053392;61.901651,-13.978164;59.943030,88.154531;57.943375,88.117350;59.899840,-13.902936;57.898030,-13.827708;55.943720,88.080168;53.944066,88.042987;55.896219,-13.752480;53.894409,-13.677252;51.944411,88.005805;49.944756,87.968624;51.892599,-13.602024;49.890788,-13.526796;47.945102,87.931442;45.945447,87.894261;48.098512,-24.377753;46.410911,-40.687091;43.945792,87.857079;41.946138,87.819898;44.723311,-56.996429;42.898575,-66.154738;39.946483,87.782717;37.946828,87.745535;40.897210,-66.102783;38.895846,-66.050827;35.947173,87.708354;33.947519,87.671172;36.894482,-65.998872;34.893118,-65.946916;31.947864,87.633991;29.948209,87.596809;32.891754,-65.894960;30.890390,-65.843005;27.948555,87.559628;25.948900,87.522447;28.889026,-65.791049;26.887662,-65.739094;23.949245,87.485265;21.949591,87.448084;24.886298,-65.687138;22.884934,-65.635182;19.949936,87.410902;17.950281,87.373721;20.883570,-65.583227;18.882205,-65.531271;15.950627,87.336539;13.950972,87.299358;16.880841,-65.479315;14.879477,-65.427360;11.951317,87.262176;9.951662,87.224995;12.878113,-65.375404;10.876749,-65.323449;7.952008,87.187814;5.952353,87.150632;8.875385,-65.271493;6.874021,-65.219537;4.214206,73.477072;2.826044,41.553502;4.872657,-65.167582;2.871293,-65.115626;1.437881,9.629932;0.049718,-22.293638;0.869929,-65.063671
LAND1.plannedPath=-25.601252,543.772523;-98.992501,194.064867;184.886062,182.865285;60.950435,425.466300;237.049774,416.438391;234.819374,539.278905;-25.601252,543.772523;-23.565057,543.737388;-96.965713,193.984906;-94.938925,193.904945;-21.528862,543.702253;-19.492667,543.667118;-92.912138,193.824985;-90.885350,193.745024;-17.456472,543.631983;-15.420277,543.596848;-88.858562,193.665063;-86.831775,193.585102;-13.384082,543.561713;-11.347887,543.526578;-84.804987,193.505141;-82.778200,193.425180;-9.311692,543.491443;-7.275497,543.456308;-80.751412,193.345219;-78.724624,193.265259;-5.239302,543.421173;-3.203107,543.386038;-76.697837,193.185298;-74.671049,193.105337;-1.166912,543.350903;0.869283,543.315768;-72.644261,193.025376;-70.617474,192.945415;2.905478,543.280633;4.941673,543.245498;-68.590686,192.865454;-66.563898,192.785493;6.977868,543.210363;9.014063,543.175228;-64.537111,192.705533;-62.510323,192.625572;11.050258,543.140093;13.086453,543.104958;-60.483536,192.545611;-58.456748,192.465650;15.122648,543.069823;17.158843,543.034688;-56.429960,192.385689;-54.403173,192.305728;19.195038,542.999553;21.231233,542.964418;-52.376385,192.225767;-50.349597,192.145807;23.267428,542.929283;25.303623,542.894148;-48.322810,192.065846;-46.296022,191.985885;27.339818,542.859013;29.376013,542.823877;-44.269235,191.905924;-42.242447,191.825963;31.412208,542.788742;33.448403,542.753607;-40.215659,191.746002;-38.188872,191.666041;35.484598,542.718472;37.520793,542.683337;-36.162084,191.586081;-34.135296,191.506120;39.556988,542.648202;41.593183,542.613067;-32.108509,191.426159;-30.081721,191.346198;43.629378,542.577932;45.665573,542.542797;-28.054933,191.266237;-26.028146,191.186276;47.701768,542.507662;49.737963,542.472527;-24.001358,191.106315;-21.974571,191.026355;51.774158,542.437392;53.810353,542.402257;-19.947783,190.946394;-17.920995,190.866433;55.846548,542.367122;57.882743,542.331987;-15.894208,190.786472;-13.867420,190.706511;59.918938,542.296852;61.955133,542.261717;-11.840632,190.626550;-9.813845,190.546589;63.991328,542.226582;66.027523,542.191447;-7.787057,190.466628;-5.760269,190.386668;68.063718,542.156312;70.099913,542.121177;-3.733482,190.306707;-1.706694,190.226746;72.136108,542.086042;74.172303,542.050907;0.320093,190.146785;2.346881,190.066824;76.208498,542.015772;78.244693,541.980637;4.373669,189.986863;6.400456,189.906902;80.280888,541.945502;82.317083,541.910367;8.427244,189.826942;10.454032,189.746981;84.353278,541.875232;86.389473,541.840097;61.955917,425.414753;61.670804,424.056196;12.480819,189.667020;14.507607,189.587059;63.119317,421.220767;63.977733,425.311103;88.425668,541.804962;90.461863,541.769827;65.999549,425.207453;64.567829,418.385338;16.534394,189.507098;18.561182,189.427137;66.016342,415.549910;68.021365,425.103802;92.498058,541.734692;94.534253,541.699557;70.043181,425.000152;67.464854,412.714481;20.587970,189.347176;22.614757,189.267216;68.913367,409.879053;72.064997,424.896501;96.570448,541.664422;98.606643,541.629287;74.086814,424.792851;70.361880,407.043624;24.641545,189.187255;26.668333,189.107294;71.810392,404.208195;76.108630,424.689200;100.642838,541.594152;102.679033,541.559017;78.130446,424.585550;73.258905,401.372767;28.695120,189.027333;30.721908,188.947372;74.707417,398.537338;80.152262,424.481900;104.715228,541.523882;106.751423,541.488747;82.174078,424.378249;76.155930,395.701910;32.748696,188.867411;34.775483,188.787450;77.604442,392.866481;84.195894,424.274599;108.787618,541.453612;110.823813,541.418477;86.217710,424.170948;79.052955,390.031053;36.802271,188.707490;38.829058,188.627529;80.501468,387.195624;88.239526,424.067298;112.860008,541.383342;114.896203,541.348207;90.261342,423.963647;81.949980,384.360195;40.855846,188.547568;42.882634,188.467607;83.398493,381.524767;92.283158,423.859997;116.932398,541.313072;118.968593,541.277937;94.304974,423.756347;84.847005,378.689338;44.909421,188.387646;46.936209,188.307685;86.295518,375.853910;96.326790,423.652696;121.004788,541.242802;123.040983,541.207667;98.348606,423.549046;87.744030,373.018481;48.962997,188.227724;50.989784,188.147764;89.192543,370.183052;100.370422,423.445395;125.077177,541.172532;127.113372,541.137397;102.392238,423.341745;90.641056,367.347624;53.016572,188.067803;55.043360,187.987842;92.089568,364.512195;104.414054,423.238094;129.149567,541.102262;131.185762,541.067127;106.435870,423.134444;93.538081,361.676767;57.070147,187.907881;59.096935,187.827920;94.986593,358.841338;108.457686,423.030794;133.221957,541.031992;135.258152,540.996857;110.479502,422.927143;96.435106,356.005910;61.123722,187.747959;63.150510,187.667998;97.883618,353.170481;112.501318,422.823493;137.294347,540.961722;139.330542,540.926587;114.523134,422.719842;99.332131,350.335052;65.177298,187.588038;67.204085,187.508077;100.780644,347.499624;116.544950,422.616192;141.366737,540.891452;143.402932,540.856317;118.566766,422.512541;102.229156,344.664195;69.230873,187.428116;71.257661,187.348155;103.677669,341.828767;120.588582,422.408891;145.439127,540.821182;147.475322,540.786047;122.610398,422.305241;105.126181,338.993338;73.284448,187.268194;75.311236,187.188233;106.574694,336.157910;124.632214,422.201590;149.511517,540.750911;151.547712,540.715776;126.654030,422.097940;108.023206,333.322481;77.338023,187.108272;79.364811,187.028312;109.471719,330.487052;128.675846,421.994289;153.583907,540.680641;155.620102,540.645506;130.697662,421.890639;110.920231,327.651624;81.391599,186.948351;83.418386,186.868390;112.368744,324.816195;132.719478,421.786988;157.656297,540.610371;159.692492,540.575236;134.741294,421.683338;113.817257,321.980767;85.445174,186.788429;87.471962,186.708468;115.265769,319.145338;136.763111,421.579688;161.728687,540.540101;163.764882,540.504966;138.784927,421.476037;116.714282,316.309909;89.498749,186.628507;91.525537,186.548546;118.162794,313.474481;140.806743,421.372387;165.801077,540.469831;167.837272,540.434696;142.828559,421.268736;119.611307,310.639052;93.552325,186.468586;95.579112,186.388625;121.059819,307.803624;144.850375,421.165086;169.873467,540.399561;171.909662,540.364426;146.872191,421.061435;122.508332,304.968195;97.605900,186.308664;99.632687,186.228703;123.956845,302.132767;148.894007,420.957785;173.945857,540.329291;175.982052,540.294156;150.915823,420.854135;125.405357,299.297338;101.659475,186.148742;103.686263,186.068781;126.853870,296.461909;152.937639,420.750484;178.018247,540.259021;180.054442,540.223886;154.959455,420.646834;128.302382,293.626481;105.713050,185.988820;107.739838,185.908860;129.750895,290.791052;156.981271,420.543183;182.090637,540.188751;184.126832,540.153616;159.003087,420.439533;131.199407,287.955624;109.766626,185.828899;111.793413,185.748938;132.647920,285.120195;161.024903,420.335883;186.163027,540.118481;188.199222,540.083346;163.046719,420.232232;134.096433,282.284767;113.820201,185.668977;115.846989,185.589016;135.544945,279.449338;165.068535,420.128582;190.235417,540.048211;192.271612,540.013076;167.090351,420.024931;136.993458,276.613909;117.873776,185.509055;119.900564,185.429094;138.441970,273.778481;169.112167,419.921281;194.307807,539.977941;196.344002,539.942806;171.133983,419.817630;139.890483,270.943052;121.927351,185.349134;123.954139,185.269173;141.338995,268.107624;173.155799,419.713980;198.380197,539.907671;200.416392,539.872536;175.177615,419.610330;142.787508,265.272195;125.980927,185.189212;128.007714,185.109251;144.236021,262.436766;177.199431,419.506679;202.452587,539.837401;204.488782,539.802266;179.221247,419.403029;145.684533,259.601338;130.034502,185.029290;132.061290,184.949329;147.133046,256.765909;181.243063,419.299378;206.524977,539.767131;208.561172,539.731996;183.264879,419.195728;148.581558,253.930481;134.088077,184.869368;136.114865,184.789408;150.030071,251.095052;185.286695,419.092077;210.597367,539.696861;212.633562,539.661726;187.308511,418.988427;151.478583,248.259624;138.141652,184.709447;140.168440,184.629486;152.927096,245.424195;189.330327,418.884777;214.669757,539.626591;216.705952,539.591456;191.352143,418.781126;154.375609,242.588766;142.195228,184.549525;144.222015,184.469564;155.824121,239.753338;193.373959,418.677476;218.742147,539.556321;220.778342,539.521186;195.395775,418.573825;157.272634,236.917909;146.248803,184.389603;148.275591,184.309642;158.721146,234.082481;197.417591,418.470175;222.814537,539.486051;224.850732,539.450916;199.439408,418.366524;160.169659,231.247052;150.302378,184.229681;152.329166,184.149721;161.618171,228.411623;201.461224,418.262874;226.886927,539.415781;228.923122,539.380646;203.483040,418.159224;163.066684,225.576195;154.355954,184.069760;156.382741,183.989799;164.515197,222.740766;205.504856,418.055573;230.959317,539.345511;232.995512,539.310376;207.526672,417.951923;165.963709,219.905338;158.409529,183.909838;160.436316,183.829877;167.412222,217.069909;209.548488,417.848272;234.836343,538.344336;234.999068,529.382159;211.570304,417.744622;168.860734,214.234481;162.463104,183.749916;164.489892,183.669955;170.309247,211.399052;213.592120,417.640971;235.161793,520.419982;235.324518,511.457805;215.613936,417.537321;171.757759,208.563623;166.516679,183.589995;168.543467,183.510034;173.206272,205.728195;217.635752,417.433671;235.487243,502.495628;235.649969,493.533451;219.657568,417.330020;174.654784,202.892766;170.570255,183.430073;172.597042,183.350112;176.103297,200.057338;221.679384,417.226370;235.812694,484.571274;235.975419,475.609098;223.701200,417.122719;177.551810,197.221909;174.623830,183.270151;176.650618,183.190190;179.000322,194.386481;225.723016,417.019069;236.138144,466.646921;236.300869,457.684744;227.744832,416.915418;180.448835,191.551052;178.677405,183.110229;180.704193,183.030269;181.897347,188.715623;229.766648,416.811768;236.463594,448.722567;236.626319,439.760390;231.788464,416.708118;183.345860,185.880195;182.730980,182.950308;184.757768,182.870347;184.794372,183.044766;233.810280,416.604467;236.789045,430.798213;236.951770,421.836036;235.832096,416.500817
LAND1.returnPathCoordinates=-1
LAND1.returnPathRawCoordinates=-1
LAND1.returnPointCoordinates=-1
LAND1.updateTime=2025-12-23 17\:37\:19
LAND1.updateTime=2025-12-23 18\:32\:39
LAND1.userId=-1
lib/jackson-annotations-2.15.2.jar
Binary files differ
lib/jackson-core-2.15.2.jar
Binary files differ
lib/jackson-databind-2.15.2.jar
Binary files differ
lib/lombok-1.18.36.jar
Binary files differ
lib/org.eclipse.paho.client.mqttv3-1.2.2.jar
Binary files differ
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Tue Dec 23 17:49:41 CST 2025
#Tue Dec 23 18:35:51 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
@@ -8,12 +8,12 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=0.53
mapScale=2.83
measurementModeEnabled=false
mowerId=860
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
viewCenterX=-76.97
viewCenterY=8.49
viewCenterX=-148.00
viewCenterY=278.47
src/Mqttmessage/Client.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package Mqttmessage;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
/**
 * MQTT客户端工具类
 * ç”¨äºŽè¿žæŽ¥MQTT服务器并订阅主题
 */
public class Client {
    private String host;
    private String topic;
    private String clientId;
    private MqttClient client;
    private MqttConnectOptions options;
    /**
     * æž„造函数
     * @param host MQTT服务器地址,格式:tcp://ip:port
     * @param topic è®¢é˜…的主题
     * @param clientId å®¢æˆ·ç«¯ID,不能重复
     */
    public Client(String host, String topic, String clientId) {
        this.host = host;
        this.topic = topic;
        this.clientId = clientId;
        this.options = new MqttConnectOptions();
        this.options.setCleanSession(true);
    }
    /**
     * è¿žæŽ¥åˆ°MQTT服务器
     * @throws MqttException è¿žæŽ¥å¤±è´¥æ—¶æŠ›å‡ºå¼‚常
     */
    public void connect() throws MqttException {
        if (client != null && client.isConnected()) {
            return;
        }
        client = new MqttClient(host, clientId);
        client.connect(options);
        client.setCallback(new PushCallback());
    }
    /**
     * è®¢é˜…主题
     * @param qos æœåŠ¡è´¨é‡ç­‰çº§ï¼Œ0-2
     * @throws MqttException è®¢é˜…失败时抛出异常
     */
    public void subscribe(int qos) throws MqttException {
        if (client == null || !client.isConnected()) {
            connect();
        }
        client.subscribe(topic, qos);
    }
    /**
     * è®¢é˜…主题(默认QoS为2)
     * @throws MqttException è®¢é˜…失败时抛出异常
     */
    public void subscribe() throws MqttException {
        subscribe(2);
    }
    /**
     * æ–­å¼€è¿žæŽ¥
     * @throws MqttException æ–­å¼€è¿žæŽ¥å¤±è´¥æ—¶æŠ›å‡ºå¼‚常
     */
    public void disconnect() throws MqttException {
        if (client != null && client.isConnected()) {
            client.disconnect();
        }
    }
    /**
     * æ£€æŸ¥æ˜¯å¦å·²è¿žæŽ¥
     * @return true表示已连接,false表示未连接
     */
    public boolean isConnected() {
        return client != null && client.isConnected();
    }
    /**
     * èŽ·å–MQTT客户端实例
     * @return MqttClient实例
     */
    public MqttClient getClient() {
        return client;
    }
    /**
     * ç¤ºä¾‹ç”¨æ³•
     */
    public static void test()  {
        try {
            String host = "tcp://39.99.43.227:1883";
            String deiveID="6258";
            String clientId = "hxzkMQTT";
            String clientId2 = "hxzkMQTT2";
            String topic = "mower/"+deiveID+"/gps";
            String topic2 = "mower/"+deiveID+"/response";
            Client mqttClient = new Client(host, topic, clientId);
            Client mqttClient1 = new Client(host, topic2, clientId2);
            mqttClient.connect();
            mqttClient.subscribe();
            mqttClient1.connect();
            mqttClient1.subscribe();
            // ä¿æŒç¨‹åºè¿è¡Œ
           //Thread.sleep(Long.MAX_VALUE);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }
    }
}
src/Mqttmessage/Entity/BasestationData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package Mqttmessage.Entity;
import lombok.Data;
@Data
public class BasestationData {
    private String latitude_dm;
    private String latitude_hemisphere;
    private String longitude_dm;
    private String longitude_hemisphere;
    private double altitude;
    public BasestationData(String latitude_dm, String latitude_hemisphere,
                           String longitude_dm, String longitude_hemisphere,
                           double altitude) {
        this.latitude_dm = latitude_dm;
        this.latitude_hemisphere = latitude_hemisphere;
        this.longitude_dm = longitude_dm;
        this.longitude_hemisphere = longitude_hemisphere;
        this.altitude = altitude;
    }
}
src/Mqttmessage/Entity/CoordinatePoint.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package Mqttmessage.Entity;
import lombok.Data;
@Data
public class CoordinatePoint {
    private String x;  // X坐标
    private String y;  // Y坐标
    public CoordinatePoint() {
    }
    public CoordinatePoint(String x, String y) {
        this.x = x;
        this.y = y;
    }
}
src/Mqttmessage/Entity/GPSData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
package Mqttmessage.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GPSData{
    // JSON中的原始字段
    private String msg_id;          // æ¶ˆæ¯å”¯ä¸€æ ‡è¯†
    private Long timestamp;         // æ—¶é—´æˆ³ï¼ˆæ¯«ç§’)
    private String device_id;       // è®¾å¤‡ç¼–号
    private String data_type;       // æ•°æ®ç±»åž‹
    private String gps_raw;         // åŽŸå§‹GPS数据
    // IMU数据内部类
    private IMUData imu_data;
    // çŠ¶æ€æ•°æ®ï¼ˆæ–°åè®®æ ¼å¼ï¼‰
    private StatusInfo status;
    // NMEA GGA解析结果(可选,根据需要添加)
    private GGAData ggaData;
    /**
     * èŽ·å–åŽŸå§‹GPS数据
     * @return åŽŸå§‹GPS数据字符串
     */
    public String getGps_raw() {
        return gps_raw;
    }
    /**
     * è®¾ç½®åŽŸå§‹GPS数据
     * @param gps_raw åŽŸå§‹GPS数据字符串
     */
    public void setGps_raw(String gps_raw) {
        this.gps_raw = gps_raw;
    }
    /**
     * IMU数据内部类(简化版,只包含角度信息)
     */
    @Data
    @NoArgsConstructor
    public static class IMUData {
        private Double roll;       // æ¨ªæ»šè§’ è§’度
        private Double pitch;      // ä¿¯ä»°è§’ è§’度
        private Double yaw;        // åèˆªè§’ è§’度
        /**
         * å¸¦å‚数的构造函数
         * @param roll æ¨ªæ»šè§’ è§’度
         * @param pitch ä¿¯ä»°è§’ è§’度
         * @param yaw åèˆªè§’ è§’度
         */
        public IMUData(Double roll, Double pitch, Double yaw) {
            this.roll = roll;
            this.pitch = pitch;
            this.yaw = yaw;
        }
    }
    /**
     * çŠ¶æ€ä¿¡æ¯å†…éƒ¨ç±»ï¼ˆä¸ŽStatusData中的StatusInfo保持一致)
     */
    @Data
    @NoArgsConstructor
    public static class StatusInfo {
        private Integer battery_level;      // ç”µæ± ç”µé‡ç™¾åˆ†æ¯”
        private Double battery_voltage;     // ç”µæ± ç”µåŽ‹
        private String operation_mode;      // æ“ä½œæ¨¡å¼ï¼šmanual, auto, emergency_stop
        private String motor_status;        // ç”µæœºçŠ¶æ€ï¼šstopped, running, error
        private String blade_status;        // åˆ€ç‰‡çŠ¶æ€ï¼šstopped, rotating
        private Integer blade_height;       // åˆ€ç›˜é«˜åº¦ åŽ˜ç±³
        private Integer self_check_status;  // è‡ªæ£€çŠ¶æ€ï¼š1-完成,0-未完成
        private Integer error_code;         // é”™è¯¯ä»£ç 
        private String error_message;       // é”™è¯¯ä¿¡æ¯
        /**
         * å¸¦å‚数的构造函数
         * @param battery_level ç”µæ± ç”µé‡ç™¾åˆ†æ¯”
         * @param battery_voltage ç”µæ± ç”µåŽ‹
         * @param operation_mode æ“ä½œæ¨¡å¼ï¼šmanual, auto, emergency_stop
         * @param motor_status ç”µæœºçŠ¶æ€ï¼šstopped, running, error
         * @param blade_status åˆ€ç‰‡çŠ¶æ€ï¼šstopped, rotating
         * @param blade_height åˆ€ç›˜é«˜åº¦ åŽ˜ç±³
         * @param self_check_status è‡ªæ£€çŠ¶æ€ï¼š1-完成,0-未完成
         * @param error_code é”™è¯¯ä»£ç 
         * @param error_message é”™è¯¯ä¿¡æ¯
         */
        public StatusInfo(Integer battery_level, Double battery_voltage, String operation_mode,
                         String motor_status, String blade_status, Integer blade_height,
                         Integer self_check_status, Integer error_code, String error_message) {
            this.battery_level = battery_level;
            this.battery_voltage = battery_voltage;
            this.operation_mode = operation_mode;
            this.motor_status = motor_status;
            this.blade_status = blade_status;
            this.blade_height = blade_height;
            this.self_check_status = self_check_status;
            this.error_code = error_code;
            this.error_message = error_message;
        }
    }
    /**
     * GGA数据解析类(可选,用于存储解析后的GGA数据)
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class GGAData {
        private String utcTime;     // UTC时间
        private String latitude;    // çº¬åº¦ï¼ˆåŽŸå§‹åº¦åˆ†æ ¼å¼ï¼‰
        private String latitudeDir; // çº¬åº¦æ–¹å‘
        private String longitude;   // ç»åº¦ï¼ˆåŽŸå§‹åº¦åˆ†æ ¼å¼ï¼‰
        private String longitudeDir;// ç»åº¦æ–¹å‘
        private Integer gpsQuality; // GPS质量指示
        private Integer satellites; // ä½¿ç”¨çš„卫星数量
        private Double hdop;        // æ°´å¹³ç²¾åº¦å› å­
        private Double altitude;    // æµ·æ‹”高度
        private String altitudeUnit;// æµ·æ‹”高度单位
        private Double geoidSep;    // å¤§åœ°æ°´å‡†é¢åˆ†ç¦»
        private String geoidSepUnit;// å¤§åœ°æ°´å‡†é¢åˆ†ç¦»å•位
        private String age;         // å·®åˆ†GPS数据期限
        private String stationId;   // å·®åˆ†å‚考基站标号
    }
}
src/Mqttmessage/Entity/HxzkPathMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package Mqttmessage.Entity;
import lombok.Data;
@Data
public class HxzkPathMessage {
    private String msg_id;
    private long timestamp;
    private String user_id;
    private String device_id;
    private String command;
    private BasestationData basestation_data;
    private PathData path_data;
    public HxzkPathMessage(String msg_id, long timestamp, String user_id,
                           String device_id, String command,
                           BasestationData basestation_data, PathData path_data) {
        this.msg_id = msg_id;
        this.timestamp = timestamp;
        this.user_id = user_id;
        this.device_id = device_id;
        this.command = command;
        this.basestation_data = basestation_data;
        this.path_data = path_data;
    }
}
src/Mqttmessage/Entity/PathData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package Mqttmessage.Entity;
import lombok.Data;
import java.util.List;
@Data
public class PathData {
    private String path_id;
    private String coordinate_system;
    private List<CoordinatePoint> boundary_points;
    private List<CoordinatePoint> navigation_points;
    private String mowing_pattern;
    private Integer boundary_point_count;  // è¾¹ç•Œç‚¹æ•°é‡ ä¸Šé™æ•°é‡1000
    private Integer navigation_point_count; // å¯¼èˆªç‚¹æ•°é‡ ä¸Šé™æ•°é‡4000
    public PathData(String path_id, String coordinate_system,
                    List<CoordinatePoint> boundary_points,
                    List<CoordinatePoint> navigation_points,
                    String mowing_pattern,
                    Integer boundary_point_count, Integer navigation_point_count) {
        this.path_id = path_id;
        this.coordinate_system = coordinate_system;
        this.boundary_points = boundary_points;
        this.navigation_points = navigation_points;
        this.mowing_pattern = mowing_pattern;
        this.boundary_point_count = boundary_point_count;
        this.navigation_point_count = navigation_point_count;
    }
}
src/Mqttmessage/Entity/ResponseData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package Mqttmessage.Entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Map;
/**
 * è®¾å¤‡å“åº”消息实体类
 */
@Data
public class ResponseData {
    @JsonProperty("msg_id")
    private String msgId;
    private long timestamp;
    @JsonProperty("device_id")
    private String deviceId;
    @JsonProperty("original_msg_id")
    private String originalMsgId;
    private ResponseInfo response;
    // æž„造函数
    public ResponseData() {
    }
    public ResponseData(String msgId, long timestamp, String deviceId, String originalMsgId, ResponseInfo response) {
        this.msgId = msgId;
        this.timestamp = timestamp;
        this.deviceId = deviceId;
        this.originalMsgId = originalMsgId;
        this.response = response;
    }
    /**
     * å“åº”信息内部类
     */
    @Data
    public static class ResponseInfo {
        private String status;
        private String command;
        @JsonProperty("error_code")
        private int errorCode;
        @JsonProperty("error_message")
        private String errorMessage;
        @JsonProperty("additional_info")
        private Map<String, Object> additionalInfo;
        public ResponseInfo() {
        }
        public ResponseInfo(String status, String command, int errorCode, String errorMessage,
                            Map<String, Object> additionalInfo) {
            this.status = status;
            this.command = command;
            this.errorCode = errorCode;
            this.errorMessage = errorMessage;
            this.additionalInfo = additionalInfo;
        }
    }
}
src/Mqttmessage/Entity/StatusData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package Mqttmessage.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StatusData {
    // æ¶ˆæ¯åŸºæœ¬ä¿¡æ¯
    private String msg_id;          // æ¶ˆæ¯å”¯ä¸€æ ‡è¯†
    private Long timestamp;         // æ—¶é—´æˆ³
    private String device_id;       // è®¾å¤‡ç¼–号
    private String data_type;       // æ•°æ®ç±»åž‹
    // çŠ¶æ€æ•°æ®å†…éƒ¨ç±»
    private StatusInfo status;
    /**
     * çŠ¶æ€ä¿¡æ¯å†…éƒ¨ç±»
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class StatusInfo {
        private Integer battery_level;      // ç”µæ± ç”µé‡ç™¾åˆ†æ¯”
        private Double battery_voltage;     // ç”µæ± ç”µåŽ‹
        private String operation_mode;      // æ“ä½œæ¨¡å¼ï¼šmanual, auto, emergency_stop
        private String motor_status;        // ç”µæœºçŠ¶æ€ï¼šstopped, running, error
        private String blade_status;        // åˆ€ç‰‡çŠ¶æ€ï¼šstopped, rotating
        private Integer blade_height;       // åˆ€ç›˜é«˜åº¦ åŽ˜ç±³
        private Integer self_check_status;  // è‡ªæ£€çŠ¶æ€ï¼š1-完成,0-未完成
        private Integer error_code;         // é”™è¯¯ä»£ç 
        private String error_message;       // é”™è¯¯ä¿¡æ¯
        private Integer signal_strength;    // ä¿¡å·å¼ºåº¦ï¼ˆä¿ç•™å‘后兼容)
        private String gps_fix_status;      // GPS定位状态:no_fix, float, fixed(保留向后兼容)
        private Integer satellites_tracked; // è·Ÿè¸ªå«æ˜Ÿæ•°é‡ï¼ˆä¿ç•™å‘后兼容)
    }
}
src/Mqttmessage/PushCallback.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package Mqttmessage;
import Mqttmessage.Util.DeviceMessageParser;
import Mqttmessage.Entity.GPSData;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import udpdell.UDPServer;
public class PushCallback implements MqttCallback {
    public void connectionLost(Throwable cause) {
        // è¿žæŽ¥ä¸¢å¤±åŽï¼Œä¸€èˆ¬åœ¨è¿™é‡Œé¢è¿›è¡Œé‡è¿ž
        System.out.println("连接断开,可以做重连");
    }
    public void deliveryComplete(IMqttDeliveryToken token) {
        System.out.println("deliveryComplete---------" + token.isComplete());
    }
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // subscribe后得到的消息会执行到这里面
        // System.out.println(message);
        GPSData gpsData = DeviceMessageParser.parseGPSData(new String(message.getPayload()));//解析GNSS数据
        //ResponseData responseData = DeviceMessageParser.parseResponseData(new String(message.getPayload()));//解析响应数据
        String gpsRaw = gpsData.getGps_raw();
        UDPServer.processSerialData(gpsRaw);
    }
}
src/Mqttmessage/Util/DeviceMessageParser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,161 @@
package Mqttmessage.Util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import Mqttmessage.Entity.GPSData;
import Mqttmessage.Entity.ResponseData;
/**
 * è®¾å¤‡æ¶ˆæ¯è§£æžå·¥å…·ç±» - æ”¯æŒGPS数据、状态数据和响应数据的解析
 */
public class DeviceMessageParser {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    static {
        // é…ç½®ObjectMapper
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
    /**
     * è§£æžä¸ºGPS数据对象(支持新协议格式,包含GPS、IMU和状态数据)
     * @param jsonStr JSON字符串
     * @return GPSData对象
     * @throws JsonProcessingException è§£æžå¼‚常
     */
    public static GPSData parseGPSData(String jsonStr) throws JsonProcessingException {
        GPSData gpsData = objectMapper.readValue(jsonStr, GPSData.class);
        // å¯é€‰ï¼šè§£æžGGA数据
       /* if (gpsData.getGps_raw() != null && gpsData.getGps_raw().startsWith("$GNGGA")) {
            GPSData.GGAData ggaData = parseGGA(gpsData.getGps_raw());
            gpsData.setGgaData(ggaData);
        }*/
        return gpsData;
    }
    /**
     * è§£æžä¸ºå“åº”数据对象
     * @param jsonStr JSON字符串
     * @return ResponseData对象
     * @throws JsonProcessingException è§£æžå¼‚常
     */
    public static ResponseData parseResponseData(String jsonStr) throws JsonProcessingException {
        return objectMapper.readValue(jsonStr, ResponseData.class);
    }
    /**
     * è§£æžGGA格式的GPS数据(可选功能)
     * @param ggaString GGA格式字符串
     * @return GGAData对象
     */
    /*public static GPSData.GGAData parseGGA(String ggaString) {
        // ç§»é™¤å¯èƒ½çš„æ ¡éªŒå’Œéƒ¨åˆ†
        String cleanString = ggaString.split("\\*")[0];
        String[] fields = cleanString.split(",");
        if (fields.length < 15) {
            return null;
        }
        GPSData.GGAData ggaData = new GPSData.GGAData();
        try {
            ggaData.setUtcTime(fields[1]);
            ggaData.setLatitude(fields[2]);
            ggaData.setLatitudeDir(fields[3]);
            ggaData.setLongitude(fields[4]);
            ggaData.setLongitudeDir(fields[5]);
            if (!fields[6].isEmpty()) {
                ggaData.setGpsQuality(Integer.parseInt(fields[6]));
            }
            if (!fields[7].isEmpty()) {
                ggaData.setSatellites(Integer.parseInt(fields[7]));
            }
            if (!fields[8].isEmpty()) {
                ggaData.setHdop(Double.parseDouble(fields[8]));
            }
            if (!fields[9].isEmpty()) {
                ggaData.setAltitude(Double.parseDouble(fields[9]));
            }
            ggaData.setAltitudeUnit(fields[10]);
            if (!fields[11].isEmpty()) {
                ggaData.setGeoidSep(Double.parseDouble(fields[11]));
            }
            ggaData.setGeoidSepUnit(fields[12]);
            if (fields.length > 13) {
                ggaData.setAge(fields[13]);
            }
            if (fields.length > 14) {
                ggaData.setStationId(fields[14]);
            }
        } catch (Exception e) {
            // è§£æžå¼‚常,返回部分解析的数据或null
            System.err.println("解析GGA数据时出错: " + e.getMessage());
        }
        return ggaData;
    }*/
    /**
     * å°†æ¶ˆæ¯å¯¹è±¡è½¬æ¢ä¸ºJSON字符串
     * @param message æ¶ˆæ¯å¯¹è±¡ï¼ˆGPSData、StatusData或ResponseData)
     * @return JSON字符串
     * @throws JsonProcessingException è½¬æ¢å¼‚常
     */
    public static String toJson(Object message) throws JsonProcessingException {
        return objectMapper.writeValueAsString(message);
    }
    /**
     * èŽ·å–ObjectMapper实例(用于高级操作)
     * @return ObjectMapper实例
     */
    public static ObjectMapper getObjectMapper() {
        return objectMapper;
    }
    /**
     * æ¶ˆæ¯ç±»åž‹æžšä¸¾
     */
    public enum MessageType {
        GPS("gps"),
        STATUS("status"),
        RESPONSE("response");
        private final String type;
        MessageType(String type) {
            this.type = type;
        }
        public String getType() {
            return type;
        }
        public static MessageType fromString(String type) {
            for (MessageType mt : MessageType.values()) {
                if (mt.type.equalsIgnoreCase(type)) {
                    return mt;
                }
            }
            throw new IllegalArgumentException("未知的消息类型: " + type);
        }
    }
}
src/lujing/AoxinglujingNoObstacle.java
@@ -5,381 +5,236 @@
import java.util.List;
/**
 * æ— éšœç¢ç‰©å‡¸å½¢è‰åœ°è·¯å¾„规划类 (终极优化版)
 * ç‰¹æ€§ï¼š
 * 1. æœ€å°æŠ•影宽度方向选择 (效率最高,转弯最少)
 * 2. è¾¹ç¼˜è½®å»“优先切割 (无死角覆盖)
 * 3. æ”¯æŒå¤–部传入已包含重叠率的宽度参数
 * å‡¸å½¢è‰åœ°è·¯å¾„规划 (围边优化版)
 * ä¼˜åŒ–重点:围边坐标对齐扫描起点、全路径连贯性
 */
public class AoxinglujingNoObstacle {
    // å¼•入极小值用于浮点数比较,处理几何精度误差
    private static final double EPSILON = 1e-6;
    /**
     * è·¯å¾„段类
     */
    public static class PathSegment {
        public Point start;
        public Point end;
        public boolean isMowing; // true为割草工作段,false为过渡段
        public PathSegment(Point start, Point end, boolean isMowing) {
            this.start = start;
            this.end = end;
            this.isMowing = isMowing;
        }
        @Override
        public String toString() {
            return String.format("[%s -> %s, isMowing=%b]", start, end, isMowing);
        }
    }
    /**
     * åæ ‡ç‚¹ç±»
     */
    public static class Point {
        public double x, y;
        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }
        public Point(double x, double y) { this.x = x; this.y = y; }
        @Override
        public String toString() {
            return String.format("(%.4f, %.4f)", x, y);
        public String toString() { return String.format("%.6f,%.6f", x, y); }
    }
    public static class PathSegment {
        public Point start, end;
        public boolean isMowing;
        public PathSegment(Point start, Point end, boolean isMowing) {
            this.start = start; this.end = end; this.isMowing = isMowing;
        }
    }
    /**
     * å¯¹å¤–公开的静态调用方法
     *
     * @param boundaryCoordsStr åœ°å—边界坐标字符串 "x1,y1;x2,y2;..."
     * @param mowingWidthStr    æœ‰æ•ˆå‰²è‰å®½åº¦å­—符串 (已包含重叠率),如 "0.30"
     * @param safetyMarginStr   å®‰å…¨è¾¹è·å­—符串,如 "0.2"
     * @return è·¯å¾„段列表
     * å¯¹å¤–主接口
     */
    public static List<PathSegment> planPath(String boundaryCoordsStr, String mowingWidthStr, String safetyMarginStr) {
        // 1. è§£æžå‚æ•°
        List<Point> originalPolygon = parseCoords(boundaryCoordsStr);
        double mowingWidth;
        double safetyMargin;
        double width = Double.parseDouble(mowingWidthStr);
        double margin = Double.parseDouble(safetyMarginStr);
        try {
            mowingWidth = Double.parseDouble(mowingWidthStr);
            safetyMargin = Double.parseDouble(safetyMarginStr);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("割草宽度或安全边距格式错误");
        }
        // 2. è°ƒç”¨æ ¸å¿ƒç®—法
        return planPathCore(originalPolygon, mowingWidth, safetyMargin);
        return planPathCore(originalPolygon, width, margin);
    }
    /**
     * æ ¸å¿ƒç®—法逻辑
     */
    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double width, double safetyMargin) {
        if (originalPolygon == null || originalPolygon.size() < 3) {
            return new ArrayList<>(); // æˆ–抛出异常,视业务需求而定
        }
    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double width, double margin) {
        if (originalPolygon.size() < 3) return new ArrayList<>();
        // ç¡®ä¿å¤šè¾¹å½¢æ˜¯é€†æ—¶é’ˆæ–¹å‘
        // 1. ç¡®ä¿é€†æ—¶é’ˆå¹¶è¿›è¡Œå®‰å…¨å†…缩
        ensureCCW(originalPolygon);
        List<Point> workArea = shrinkPolygon(originalPolygon, margin);
        if (workArea.size() < 3) return new ArrayList<>();
        // 1. æ ¹æ®å®‰å…¨è¾¹è·å†…缩,得到实际作业区域
        List<Point> workAreaPolygon = shrinkPolygon(originalPolygon, safetyMargin);
        // 2. é¢„计算最优角度和填充路径的第一个点
        double bestAngle = findOptimalScanAngle(workArea);
        Point firstScanStart = getFirstScanStartPoint(workArea, bestAngle, width);
        // å¦‚果内缩后区域失效(如地块太小),返回空路径
        if (workAreaPolygon.size() < 3) {
            return new ArrayList<>();
        }
        // 3. å¯¹é½å›´è¾¹èµ·ç‚¹ï¼šè®©å›´è¾¹çš„æœ€åŽä¸€ä¸ªç‚¹åˆšå¥½è¿žæŽ¥æ‰«æå¡«å……的起点
        List<Point> alignedWorkArea = alignBoundaryToStart(workArea, firstScanStart);
        List<PathSegment> finalPath = new ArrayList<>();
        // 2. [优化] ä¼˜å…ˆç”Ÿæˆè½®å»“路径 (Contour Pass)
        // æ²¿ä½œä¸šè¾¹ç•Œèµ°ä¸€åœˆï¼Œç¡®ä¿è¾¹ç¼˜æ•´é½ä¸”无遗漏
        addContourPath(workAreaPolygon, finalPath);
        // 3. [优化] è®¡ç®—最佳扫描角度
        // å¯»æ‰¾è®©å¤šè¾¹å½¢æŠ•影高度最小的角度,从而最小化转弯次数
        double bestAngle = findOptimalScanAngle(workAreaPolygon);
        // 4. ç”Ÿæˆå†…部弓字形路径
        // ç›´æŽ¥ä½¿ç”¨ä¼ å…¥çš„ width (已包含重叠率)
        List<PathSegment> zigZagPaths = generateClippedMowingLines(workAreaPolygon, bestAngle, width);
        // 5. è¿žæŽ¥è½®å»“路径和弓字形路径
        if (!finalPath.isEmpty() && !zigZagPaths.isEmpty()) {
            Point contourEnd = finalPath.get(finalPath.size() - 1).end;
            Point zigzagStart = zigZagPaths.get(0).start;
            // å¦‚果轮廓终点与弓字形起点不重合,添加过渡段
            if (distanceSq(contourEnd, zigzagStart) > EPSILON) {
                finalPath.add(new PathSegment(contourEnd, zigzagStart, false));
            }
        // 4. ã€ç¬¬ä¸€é˜¶æ®µã€‘添加围边坐标路径
        for (int i = 0; i < alignedWorkArea.size(); i++) {
            Point p1 = alignedWorkArea.get(i);
            Point p2 = alignedWorkArea.get((i + 1) % alignedWorkArea.size());
            finalPath.add(new PathSegment(p1, p2, true));
        }
        // 6. åˆå¹¶å¼“字形路径
        finalPath.addAll(connectPathSegments(zigZagPaths));
        // 5. ã€ç¬¬äºŒé˜¶æ®µã€‘生成内部弓字形路径
        // ä»Žå›´è¾¹é—­åˆç‚¹ï¼ˆalignedWorkArea.get(0))开始连接
        Point currentPos = alignedWorkArea.get(0);
        List<PathSegment> zigZagLines = generateZigZagPath(workArea, bestAngle, width, currentPos);
        finalPath.addAll(zigZagLines);
        return finalPath;
    }
    // ================= æ ¸å¿ƒé€»è¾‘辅助方法 =================
    /**
     * æ·»åŠ è½®å»“è·¯å¾„ (围着多边形走一圈)
     * å¯»æ‰¾å¼“字形的第一条线的起点
     */
    private static void addContourPath(List<Point> polygon, List<PathSegment> path) {
        int n = polygon.size();
        for (int i = 0; i < n; i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % n);
            path.add(new PathSegment(p1, p2, true));
        }
    }
    /**
     * å¯»æ‰¾æœ€ä¼˜æ‰«æè§’度 (最小投影高度法)
     */
    private static double findOptimalScanAngle(List<Point> polygon) {
        double minHeight = Double.MAX_VALUE;
        double bestAngle = 0;
        int n = polygon.size();
        // éåŽ†æ¯ä¸€æ¡è¾¹ï¼Œè®¡ç®—ä»¥è¯¥è¾¹ä¸ºâ€œåº•â€æ—¶ï¼Œå¤šè¾¹å½¢çš„é«˜åº¦
        for (int i = 0; i < n; i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % n);
            // å½“前边的角度
            double currentAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
            // è®¡ç®—在这个角度下的投影高度
            double height = calculatePolygonHeightAtAngle(polygon, currentAngle);
            if (height < minHeight) {
                minHeight = height;
                bestAngle = currentAngle;
            }
        }
        return bestAngle;
    }
    /**
     * è®¡ç®—多边形在特定旋转角度下的Y轴投影高度
     */
    private static double calculatePolygonHeightAtAngle(List<Point> poly, double angle) {
    private static Point getFirstScanStartPoint(List<Point> polygon, double angle, double width) {
        List<Point> rotated = rotatePolygon(polygon, -angle);
        double minY = Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;
        for (Point p : rotated) minY = Math.min(minY, p.y);
        double cos = Math.cos(-angle);
        double sin = Math.sin(-angle);
        for (Point p : poly) {
            // åªéœ€è®¡ç®—旋转后的Y坐标
            double rotatedY = p.x * sin + p.y * cos;
            if (rotatedY < minY) minY = rotatedY;
            if (rotatedY > maxY) maxY = rotatedY;
        }
        return maxY - minY;
        double startY = minY + width + EPSILON;
        List<Double> xIntersections = getXIntersections(rotated, startY);
        if (xIntersections.isEmpty()) return polygon.get(0);
        Collections.sort(xIntersections);
        return rotatePoint(new Point(xIntersections.get(0), startY), angle);
    }
    private static List<PathSegment> generateClippedMowingLines(List<Point> polygon, double angle, double width) {
        List<PathSegment> segments = new ArrayList<>();
        // æ—‹è½¬å¤šè¾¹å½¢è‡³æ°´å¹³
        List<Point> rotatedPoly = rotatePolygon(polygon, -angle);
        double minY = Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;
        for (Point p : rotatedPoly) {
            if (p.y < minY) minY = p.y;
            if (p.y > maxY) maxY = p.y;
        }
        // èµ·å§‹æ‰«æçº¿ä½ç½®ï¼š
        // ä»Ž minY + width/2 å¼€å§‹ï¼Œå› ä¸ºä¹‹å‰å·²ç»èµ°äº†è½®å»“线(Contour Pass)。
        // è½®å»“线负责清理边缘区域,内部填充线保持 width çš„间距即可。
        // åŠ ä¸Š EPSILON é˜²æ­¢æµ®ç‚¹æ•°åˆšå¥½è½åœ¨è¾¹ç•Œä¸Šå¯¼è‡´çš„判断误差
        double currentY = minY + width / 2.0 + EPSILON;
        while (currentY < maxY) {
            List<Double> xIntersections = new ArrayList<>();
            int n = rotatedPoly.size();
            for (int i = 0; i < n; i++) {
                Point p1 = rotatedPoly.get(i);
                Point p2 = rotatedPoly.get((i + 1) % n);
                // å¿½ç•¥æ°´å¹³çº¿æ®µ
                if (Math.abs(p1.y - p2.y) < EPSILON) continue;
                double minP = Math.min(p1.y, p2.y);
                double maxP = Math.max(p1.y, p2.y);
                if (currentY >= minP && currentY < maxP) {
                    double x = p1.x + (currentY - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
                    xIntersections.add(x);
                }
    /**
     * é‡ç»„多边形顶点,使得索引0的点最靠近填充起点
     */
    private static List<Point> alignBoundaryToStart(List<Point> polygon, Point target) {
        int bestIdx = 0;
        double minDist = Double.MAX_VALUE;
        for (int i = 0; i < polygon.size(); i++) {
            double d = Math.hypot(polygon.get(i).x - target.x, polygon.get(i).y - target.y);
            if (d < minDist) {
                minDist = d;
                bestIdx = i;
            }
            Collections.sort(xIntersections);
            if (xIntersections.size() >= 2) {
                // å–最左和最右交点
                double xStart = xIntersections.get(0);
                double xEnd = xIntersections.get(xIntersections.size() - 1);
                if (xEnd - xStart > EPSILON) {
                    // åå‘旋转回原坐标系
                    Point rStart = rotatePoint(new Point(xStart, currentY), angle);
                    Point rEnd = rotatePoint(new Point(xEnd, currentY), angle);
                    segments.add(new PathSegment(rStart, rEnd, true));
                }
            }
            // æ­¥è¿›
            currentY += width;
        }
        return segments;
        List<Point> aligned = new ArrayList<>();
        for (int i = 0; i < polygon.size(); i++) {
            aligned.add(polygon.get((bestIdx + i) % polygon.size()));
        }
        return aligned;
    }
    private static List<PathSegment> connectPathSegments(List<PathSegment> lines) {
    private static List<PathSegment> generateZigZagPath(List<Point> polygon, double angle, double width, Point startPoint) {
        List<PathSegment> result = new ArrayList<>();
        if (lines.isEmpty()) return result;
        List<Point> rotated = rotatePolygon(polygon, -angle);
        for (int i = 0; i < lines.size(); i++) {
            PathSegment currentLine = lines.get(i);
            Point actualStart, actualEnd;
        double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
        for (Point p : rotated) {
            minY = Math.min(minY, p.y);
            maxY = Math.max(maxY, p.y);
        }
            // å¼“字形规划:偶数行正向,奇数行反向
            if (i % 2 == 0) {
                actualStart = currentLine.start;
                actualEnd = currentLine.end;
            } else {
                actualStart = currentLine.end;
                actualEnd = currentLine.start;
        Point currentPos = startPoint;
        boolean leftToRight = true;
        // èµ·ç‚¹ä»Ž minY + width å¼€å§‹ï¼Œå› ä¸ºè¾¹ç¼˜å·²ç»å›´è¾¹å‰²è¿‡
        for (double y = minY + width; y < maxY - width / 2; y += width) {
            List<Double> xInt = getXIntersections(rotated, y);
            if (xInt.size() < 2) continue;
            Collections.sort(xInt);
            double xStart = leftToRight ? xInt.get(0) : xInt.get(xInt.size() - 1);
            double xEnd = leftToRight ? xInt.get(xInt.size() - 1) : xInt.get(0);
            Point pS = rotatePoint(new Point(xStart, y), angle);
            Point pE = rotatePoint(new Point(xEnd, y), angle);
            // æ·»åŠ è¿‡æ¸¡æ®µ (如果是从围边切换过来或者换行)
            if (Math.hypot(currentPos.x - pS.x, currentPos.y - pS.y) > 0.05) {
                result.add(new PathSegment(currentPos, pS, false));
            }
            // æ·»åŠ è¿‡æ¸¡æ®µ
            if (i > 0) {
                Point prevEnd = result.get(result.size() - 1).end;
                if (distanceSq(prevEnd, actualStart) > EPSILON) {
                    result.add(new PathSegment(prevEnd, actualStart, false));
                }
            }
            result.add(new PathSegment(actualStart, actualEnd, true));
            result.add(new PathSegment(pS, pE, true));
            currentPos = pE;
            leftToRight = !leftToRight;
        }
        return result;
    }
    // ================= åŸºç¡€å‡ ä½•工具 =================
    private static List<Point> parseCoords(String s) {
        List<Point> list = new ArrayList<>();
        if (s == null || s.trim().isEmpty()) return list;
        String[] parts = s.split(";");
        for (String part : parts) {
            String[] xy = part.split(",");
            if (xy.length >= 2) {
                try {
                    double x = Double.parseDouble(xy[0].trim());
                    double y = Double.parseDouble(xy[1].trim());
                    list.add(new Point(x, y));
                } catch (NumberFormatException e) {
                    // å¿½ç•¥æ ¼å¼é”™è¯¯
                }
    private static List<Double> getXIntersections(List<Point> rotatedPoly, double y) {
        List<Double> xIntersections = new ArrayList<>();
        int n = rotatedPoly.size();
        for (int i = 0; i < n; i++) {
            Point p1 = rotatedPoly.get(i);
            Point p2 = rotatedPoly.get((i + 1) % n);
            if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) {
                double x = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
                xIntersections.add(x);
            }
        }
        return list;
        return xIntersections;
    }
    private static void ensureCCW(List<Point> polygon) {
        double sum = 0;
        for (int i = 0; i < polygon.size(); i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % polygon.size());
            sum += (p2.x - p1.x) * (p2.y + p1.y);
        }
        if (sum > 0) {
            Collections.reverse(polygon);
        }
    }
    // --- å‡ ä½•基础工具 ---
    private static List<Point> shrinkPolygon(List<Point> polygon, double margin) {
        List<Point> newPoints = new ArrayList<>();
        List<Point> result = new ArrayList<>();
        int n = polygon.size();
        for (int i = 0; i < n; i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % n);
            Point p0 = polygon.get((i - 1 + n) % n);
            Point pPrev = polygon.get((i - 1 + n) % n);
            Point pCurr = polygon.get(i);
            Point pNext = polygon.get((i + 1) % n);
            Line line1 = offsetLine(p1, p2, margin);
            Line line0 = offsetLine(p0, p1, margin);
            double d1x = pCurr.x - pPrev.x, d1y = pCurr.y - pPrev.y;
            double l1 = Math.hypot(d1x, d1y);
            double d2x = pNext.x - pCurr.x, d2y = pNext.y - pCurr.y;
            double l2 = Math.hypot(d2x, d2y);
            Point intersection = getIntersection(line0, line1);
            if (intersection != null) {
                newPoints.add(intersection);
            }
            double n1x = -d1y / l1, n1y = d1x / l1;
            double n2x = -d2y / l2, n2y = d2x / l2;
            double bx = n1x + n2x, by = n1y + n2y;
            double bLen = Math.hypot(bx, by);
            if (bLen < EPSILON) { bx = n1x; by = n1y; }
            else { bx /= bLen; by /= bLen; }
            double cosHalf = n1x * bx + n1y * by;
            double d = margin / Math.max(cosHalf, 0.1);
            result.add(new Point(pCurr.x + bx * d, pCurr.y + by * d));
        }
        return newPoints;
        return result;
    }
    private static class Line {
        double a, b, c;
        public Line(double a, double b, double c) { this.a = a; this.b = b; this.c = c; }
    private static double findOptimalScanAngle(List<Point> polygon) {
        double minH = Double.MAX_VALUE;
        double bestA = 0;
        for (int i = 0; i < polygon.size(); i++) {
            Point p1 = polygon.get(i), p2 = polygon.get((i + 1) % polygon.size());
            double angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
            double h = calculatePolygonHeightAtAngle(polygon, angle);
            if (h < minH) { minH = h; bestA = angle; }
        }
        return bestA;
    }
    private static Line offsetLine(Point p1, Point p2, double dist) {
        double dx = p2.x - p1.x;
        double dy = p2.y - p1.y;
        double len = Math.sqrt(dx * dx + dy * dy);
        if (len < EPSILON) return new Line(0, 0, 0);
        double nx = -dy / len;
        double ny = dx / len;
        // å‘左侧平移(假设逆时针)
        double newX = p1.x + nx * dist;
        double newY = p1.y + ny * dist;
        double a = -dy;
        double b = dx;
        double c = -a * newX - b * newY;
        return new Line(a, b, c);
    }
    private static Point getIntersection(Line l1, Line l2) {
        double det = l1.a * l2.b - l2.a * l1.b;
        if (Math.abs(det) < EPSILON) return null;
        double x = (l1.b * l2.c - l2.b * l1.c) / det;
        double y = (l2.a * l1.c - l1.a * l2.c) / det;
        return new Point(x, y);
    private static double calculatePolygonHeightAtAngle(List<Point> poly, double angle) {
        double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
        double sin = Math.sin(-angle), cos = Math.cos(-angle);
        for (Point p : poly) {
            double ry = p.x * sin + p.y * cos;
            minY = Math.min(minY, ry); maxY = Math.max(maxY, ry);
        }
        return maxY - minY;
    }
    private static Point rotatePoint(Point p, double angle) {
        double cos = Math.cos(angle);
        double sin = Math.sin(angle);
        return new Point(p.x * cos - p.y * sin, p.x * sin + p.y * cos);
        double c = Math.cos(angle), s = Math.sin(angle);
        return new Point(p.x * c - p.y * s, p.x * s + p.y * c);
    }
    private static List<Point> rotatePolygon(List<Point> poly, double angle) {
        List<Point> res = new ArrayList<>();
        for (Point p : poly) {
            res.add(rotatePoint(p, angle));
        }
        for (Point p : poly) res.add(rotatePoint(p, angle));
        return res;
    }
    private static double distanceSq(Point p1, Point p2) {
        return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
    private static void ensureCCW(List<Point> poly) {
        double s = 0;
        for (int i = 0; i < poly.size(); i++) {
            Point p1 = poly.get(i), p2 = poly.get((i + 1) % poly.size());
            s += (p2.x - p1.x) * (p2.y + p1.y);
        }
        if (s > 0) Collections.reverse(poly);
    }
    private static List<Point> parseCoords(String s) {
        List<Point> list = new ArrayList<>();
        for (String p : s.split(";")) {
            String[] xy = p.split(",");
            if (xy.length >= 2) list.add(new Point(Double.parseDouble(xy[0]), Double.parseDouble(xy[1])));
        }
        return list;
    }
}
src/lujing/MowingPathGenerationPage.java
@@ -125,11 +125,31 @@
        
        // åœ°å—边界
        boundaryArea = createInfoTextArea(boundaryValue != null ? boundaryValue : "", true, 6);
        contentPanel.add(createTextAreaSection("地块边界", boundaryArea));
        String boundaryTitle = "地块边界";
        if (boundaryValue != null && !boundaryValue.trim().isEmpty() && !"-1".equals(boundaryValue.trim())) {
            int boundaryCount = boundaryValue.split(";").length;
            boundaryTitle = "地块边界 (" + boundaryCount + "点)";
        }
        contentPanel.add(createTextAreaSection(boundaryTitle, boundaryArea));
        
        // éšœç¢ç‰©åæ ‡
        obstacleArea = createInfoTextArea(obstacleValue != null ? obstacleValue : "", true, 6);
        contentPanel.add(createTextAreaSection("障碍物坐标", obstacleArea));
        String obstacleTitle = "障碍物坐标";
        if (obstacleValue != null && !obstacleValue.trim().isEmpty() && !"-1".equals(obstacleValue.trim())) {
            // éšœç¢ç‰©åæ ‡æ ¼å¼å¯èƒ½æ˜¯ç©ºæ ¼åˆ†éš”的多个障碍物,每个障碍物用分号分隔坐标点
            // è®¡ç®—所有障碍物的总坐标点数
            String[] obstacles = obstacleValue.trim().split("\\s+");
            int totalObstaclePoints = 0;
            for (String obstacle : obstacles) {
                if (obstacle != null && !obstacle.trim().isEmpty()) {
                    totalObstaclePoints += obstacle.split(";").length;
                }
            }
            if (totalObstaclePoints > 0) {
                obstacleTitle = "障碍物坐标 (" + totalObstaclePoints + "点)";
            }
        }
        contentPanel.add(createTextAreaSection(obstacleTitle, obstacleArea));
        
        // å‰²è‰å®½åº¦
        widthField = createInfoTextField(widthValue != null ? widthValue : "", true);
@@ -175,7 +195,12 @@
        String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath());
        String pathSeed = initialGeneratedPath != null ? initialGeneratedPath : existingPath;
        pathArea = createInfoTextArea(pathSeed != null ? pathSeed : "", true, 10);
        contentPanel.add(createTextAreaSection("割草路径坐标", pathArea));
        String pathTitle = "割草路径坐标";
        if (pathSeed != null && !pathSeed.trim().isEmpty() && !"-1".equals(pathSeed.trim())) {
            int pathCount = pathSeed.split(";").length;
            pathTitle = "割草路径坐标 (" + pathCount + "点)";
        }
        contentPanel.add(createTextAreaSection(pathTitle, pathArea));
        
        JScrollPane dialogScrollPane = new JScrollPane(contentPanel);
        dialogScrollPane.setBorder(BorderFactory.createEmptyBorder());
src/udpdell/UDPServer.java
@@ -1,4 +1,5 @@
package udpdell;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -7,6 +8,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import Mqttmessage.Client;
import gecaoji.Device;
import zhuye.Coordinate;
@@ -35,6 +37,7 @@
        thread.setDaemon(false); // ä¿æŒ JVM æŒç»­å­˜æ´»
        thread.start();
        serverThread = thread;
//      Client.test();
        return thread;
    }
@@ -83,28 +86,28 @@
        }
        // æ£€æŸ¥åŒ…头是否正确
        if (!fields[0].equals("$GNGGA")) {
        if (!fields[0].equals("$GNGGA") && !fields[0].equals("$GPGGA") && !fields[0].equals("$GBGGA")) {
            System.err.println("Invalid message header: " + fields[0]);
            return;
        }
        int sequence = incrementReceivedPacketCounter();
        System.out.println("收到了差分数据(" + sequence + "):" + message);
        // ä½¿ç”¨Gpstoxuzuobiao处理并获取XY坐标
        double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message);
        if (xy != null) {
            // è¿™é‡Œå¯ä»¥å°†XY坐标传递给其他方法使用
            // System.out.println("UDP GNGGA -> XY: " + xy[0] + ", " + xy[1]);
        }
        Coordinate.parseGNGGAToCoordinateList(message);
        int count = Coordinate.coordinates.size();
        System.out.println("savenum:" + count);
        Device.updateFromGNGGA(message, fields[15]);
    }
    /**处理串口接收到的数据*/
    /** å¤„理串口接收到的数据 */
    public static void processSerialData(String message) {
        String[] fields = message.split(",");
        // æ£€æŸ¥å­—段数量是否完整
@@ -114,20 +117,20 @@
        }
        // æ£€æŸ¥åŒ…头是否正确
        if (!fields[0].equals("$GNGGA")) {
        if (!fields[0].equals("$GNGGA")&&!fields[0].equals("$GPGGA")&&!fields[0].equals("$GBGGA")) {
            System.err.println("Invalid message header: " + fields[0]);
            return;
        }
        int sequence = incrementReceivedPacketCounter();
        System.out.println("收到了串口数据(" + sequence + "):" + message);
        // ä½¿ç”¨Gpstoxuzuobiao处理并获取XY坐标
        double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message);
        if (xy != null) {
            // è¿™é‡Œå¯ä»¥å°†XY坐标传递给其他方法使用
            // System.out.println("Serial GNGGA -> XY: " + xy[0] + ", " + xy[1]);
        }
        Coordinate.dellchuankougngga(message);
        int count = Coordinate.coordinates.size();
        System.out.println("savenum:" + count);