| | |
| | | <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> |
| | |
| | | # å²èæºå°åéç¢ç©é
ç½®æä»¶ |
| | | # çææ¶é´ï¼2025-12-23T17:37:19.839482400 |
| | | # çææ¶é´ï¼2025-12-23T18:32:39.445241900 |
| | | # åæ ç³»ï¼WGS84ï¼åº¦åæ ¼å¼ï¼ |
| | | |
| | | # ============ å°ååºåç«é
ç½® ============ |
| | |
| | | #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 |
| | |
| | | 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 |
| | |
| | | #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 |
| | |
| | | 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 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; // å·®ååèåºç«æ å· |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; // è·è¸ªå«ææ°éï¼ä¿çååå
¼å®¹ï¼ |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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; |
| | | } |
| | | } |
| | |
| | | |
| | | // å°åè¾¹ç |
| | | 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); |
| | |
| | | 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()); |
| | |
| | | package udpdell; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.DatagramPacket; |
| | | import java.net.DatagramSocket; |
| | |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import Mqttmessage.Client; |
| | | import gecaoji.Device; |
| | | import zhuye.Coordinate; |
| | | |
| | |
| | | thread.setDaemon(false); // ä¿æ JVM æç»åæ´» |
| | | thread.start(); |
| | | serverThread = thread; |
| | | // Client.test(); |
| | | return thread; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // æ£æ¥å
头æ¯å¦æ£ç¡® |
| | | 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(","); |
| | | // æ£æ¥å段æ°éæ¯å¦å®æ´ |
| | |
| | | } |
| | | |
| | | // æ£æ¥å
头æ¯å¦æ£ç¡® |
| | | 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); |