1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-16 21:05:28 +03:00
This commit is contained in:
Scavanger 2024-11-03 12:00:10 -03:00
parent 4dfd16dbd8
commit 6e7a04a817
15 changed files with 3063 additions and 694 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 141.7 141.7"
enable-background="new 0 0 141.7 141.7"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
id="metadata955"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs953" />&#10;&#10;<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.5;stroke-dasharray:none;stroke-opacity:1"
d="M 58.476193,18.095785 120.30346,54.454907 81.598584,125.66517 33.678266,100.19703 15.079821,42.558605 Z"
id="path1" /></svg>

After

Width:  |  Height:  |  Size: 934 B

View file

@ -10,7 +10,8 @@ const ProgrammingPidStatus = require('./programmingPidStatus');
const WaypointCollection = require('./waypointCollection'); const WaypointCollection = require('./waypointCollection');
const OutputMappingCollection = require('./outputMapping'); const OutputMappingCollection = require('./outputMapping');
const SafehomeCollection = require('./safehomeCollection'); const SafehomeCollection = require('./safehomeCollection');
const FwApproachCollection = require('./fwApproachCollection') const FwApproachCollection = require('./fwApproachCollection');
const GeozoneCollection = require('./geozoneCollection');
const { PLATFORM } = require('./model') const { PLATFORM } = require('./model')
const VTX = require('./vtx'); const VTX = require('./vtx');
const BitHelper = require('./bitHelper'); const BitHelper = require('./bitHelper');
@ -86,6 +87,7 @@ var FC = {
RATE_DYNAMICS: null, RATE_DYNAMICS: null,
EZ_TUNE: null, EZ_TUNE: null,
FLIGHT_MODES: null, FLIGHT_MODES: null,
GEOZONES: null,
restartRequired: false, restartRequired: false,
MAX_SERVO_RATE: 125, MAX_SERVO_RATE: 125,
@ -597,6 +599,7 @@ var FC = {
this.FW_APPROACH = new FwApproachCollection(); this.FW_APPROACH = new FwApproachCollection();
this.GEOZONES = new GeozoneCollection();
this.OSD_CUSTOM_ELEMENTS = { this.OSD_CUSTOM_ELEMENTS = {
settings: {customElementsCount: 0, customElementTextSize: 0}, settings: {customElementsCount: 0, customElementTextSize: 0},
@ -634,7 +637,8 @@ var FC = {
{bit: 2, group: 'other', name: 'TX_PROF_SEL', haveTip: false, showNameInTip: false}, {bit: 2, group: 'other', name: 'TX_PROF_SEL', haveTip: false, showNameInTip: false},
{bit: 0, group: 'other', name: 'THR_VBAT_COMP', haveTip: true, showNameInTip: true}, {bit: 0, group: 'other', name: 'THR_VBAT_COMP', haveTip: true, showNameInTip: true},
{bit: 3, group: 'other', name: 'BAT_PROFILE_AUTOSWITCH', haveTip: true, showNameInTip: true}, {bit: 3, group: 'other', name: 'BAT_PROFILE_AUTOSWITCH', haveTip: true, showNameInTip: true},
{bit: 31, group: 'other', name: "FW_AUTOTRIM", haveTip: true, showNameInTip: true} {bit: 31, group: 'other', name: "FW_AUTOTRIM", haveTip: true, showNameInTip: true},
{bit: 4, group: 'other', name: "GEOZONE", haveTip: true, showNameInTip: true}
]; ];
return features.reverse(); return features.reverse();

174
js/geozone.js Normal file
View file

@ -0,0 +1,174 @@
'use strict'
const GeozoneType = Object.freeze({
EXCULSIVE: 0,
INCLUSIVE: 1,
});
const GeozoneShapes = Object.freeze({
CIRCULAR: 0,
POLYGON: 1,
});
const GeozoneFenceAction = Object.freeze({
NONE: 0,
AVOID: 1,
POSHOLD: 2,
RTH: 3,
});
let GeozoneVertex = function (number, lat, lon) {
self = {};
self.setNumber = (data) => {
number = data;
}
self.getNumber = () => {
return number;
}
self.setLat = (data) => {
lat = data;
}
self.getLat = () => {
return lat;
}
self.setLon = (data) => {
lon = data;
}
self.getLon = () => {
return lon;
}
self.getLatMap = () => {
return lat / 1e7;
}
self.getLonMap = () => {
return lon / 1e7;
}
return self;
}
let Geozone = function (type, shape, minAltitude, maxAltitude, radius, fenceAction, vertices, number = 0) {
var self = {};
self.setNumber = (data) => {
number = data;
}
self.getNumber = () => {
return number;
}
self.setType = (data) => {
type = data;
}
self.getType = () => {
return type;
}
self.setShape = (data) => {
shape = data;
}
self.getShape = () => {
return shape;
}
self.setMinAltitude = (data) => {
minAltitude = data;
}
self.getMinAltitude = () => {
return minAltitude;
}
self.setMaxAltitude = (data) => {
maxAltitude = data;
}
self.getMaxAltitude = () => {
return maxAltitude;
}
self.setRadius = (data) => {
radius = data;
}
self.getRadius = () => {
return radius;
}
self.setFenceAction = (data) => {
fenceAction = data;
}
self.getFenceAction = () => {
return fenceAction;
}
self.getFirstVertex = () => {
return vertices[0];
}
self.getLastVertex = () => {
return vertices[vertices.length - 1];
}
self.getVerticesCount = () => {
return vertices.length;
}
self.getVertex = (idx) => {
return vertices[idx];
}
self.setVertex = (idx, vertex) => {
vertices[idx] = vertex;
}
self.getVertices = () => {
return vertices;
}
self.setVertices = (data) => {
vertices = data;
}
self.getVerticesCount = () => {
return vertices.length;
}
self.insertVertex = (idx, newVertex) => {
vertices.forEach(vertex => {
if (vertex.getNumber() >= idx) {
vertex.setNumber(vertex.getNumber() + 1);
}
});
vertices.splice(idx, 0, newVertex);
}
self.dropVertex = (idx) => {
vertices.forEach(vertex => {
if (vertex.getNumber() >= idx) {
vertex.setNumber(vertex.getNumber() - 1);
}
});
vertices.splice(idx, 1);
}
self.resetVertices = () => {
vertices = [];
}
return self;
}
module.exports = { Geozone, GeozoneVertex, GeozoneType, GeozoneShapes, GeozoneFenceAction };

151
js/geozoneCollection.js Normal file
View file

@ -0,0 +1,151 @@
'use strict';
const BitHelper = require('./bitHelper');
const { GeozoneShapes } = require('./geozone');
let GeozoneCollection = function() {
let self = {},
data = [],
maxVertices = 126,
maxZones = 62;
self.getMaxVertices = () => {
return maxVertices;
}
self.getMaxZones = () => {
return maxZones;
}
self.put = (element) => {
element.setNumber(data.length);
data.push(element);
};
self.get = () => {
return data;
};
self.at = (idx) => {
return data[idx];
}
self.isEmpty = () => {
return data.length == 0;
};
self.flush = () => {
data = [];
};
self.first = () => {
return !self.isEmpty() ? data[0] : null;
}
self.last = () => {
return !self.isEmpty() ? data[data.length - 1] : null;
}
self.updateGeozone = (newGeozone) => {
data[newGeozone.getNumber()] = newGeozone;
}
self.geozoneCount = () => {
return data.length;
}
self.getUsedVerticesCount = () => {
let count = 0;
data.forEach(zone => {
if (zone.getShape() == GeozoneShapes.POLYGON) {
count += zone.getVerticesCount();
} else {
count += 2;
}
});
return count;
}
self.drop = (idx) => {
data.forEach(zone => {
if (zone.getNumber() >= idx) {
zone.setNumber(zone.getNumber() - 1);
}
});
data.splice(idx, 1);
}
self.insert = (geoZone, idx) => {
data.forEach(zone => {
if (zone.getNumber() >= idx) {
zone.setNumber(zone.getNumber() + 1);
}
});
data.splice(idx, 0, geoZone);
}
self.extractBufferVertices = (zoneId, vertexId) => {
let buffer = [];
if (zoneId < self.geozoneCount()) {
let zone = data[zoneId];
if (vertexId < zone.getVerticesCount()) {
buffer.push(zoneId);
let vertex = zone.getVertex(vertexId);
buffer.push(vertex.getNumber());
buffer.push(BitHelper.specificByte(vertex.getLat(), 0));
buffer.push(BitHelper.specificByte(vertex.getLat(), 1));
buffer.push(BitHelper.specificByte(vertex.getLat(), 2));
buffer.push(BitHelper.specificByte(vertex.getLat(), 3));
buffer.push(BitHelper.specificByte(vertex.getLon(), 0));
buffer.push(BitHelper.specificByte(vertex.getLon(), 1));
buffer.push(BitHelper.specificByte(vertex.getLon(), 2));
buffer.push(BitHelper.specificByte(vertex.getLon(), 3));
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
buffer.push(BitHelper.specificByte(zone.getRadius(), 0));
buffer.push(BitHelper.specificByte(zone.getRadius(), 1));
buffer.push(BitHelper.specificByte(zone.getRadius(), 2));
buffer.push(BitHelper.specificByte(zone.getRadius(), 3));
}
}
}
if (buffer.length == 0) {
buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
return buffer;
}
self.extractBufferZone = (id) => {
let buffer = [];
if (id < self.geozoneCount()) {
let zone = data[id];
buffer.push(zone.getNumber());
buffer.push(zone.getType());
buffer.push(zone.getShape());
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 0));
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 1));
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 2));
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 3));
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 0));
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 1));
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 2));
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 3));
buffer.push(zone.getFenceAction());
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
buffer.push(2);
} else {
buffer.push(zone.getVerticesCount());
}
} else {
buffer = [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
}
return buffer;
}
return self;
};
module.exports = GeozoneCollection;

View file

@ -243,6 +243,11 @@ var MSPCodes = {
MSP2_INAV_SERVO_CONFIG: 0x2200, MSP2_INAV_SERVO_CONFIG: 0x2200,
MSP2_INAV_SET_SERVO_CONFIG: 0x2201, MSP2_INAV_SET_SERVO_CONFIG: 0x2201,
MSP2_INAV_GEOZONE: 0x2210,
MSP2_INAV_SET_GEOZONE: 0x2211,
MSP2_INAV_GEOZONE_VERTEX: 0x2212,
MSP2_INAV_SET_GEOZONE_VERTICE: 0x2213
}; };
module.exports = MSPCodes; module.exports = MSPCodes;

View file

@ -21,6 +21,7 @@ const Waypoint = require('./../waypoint');
const mspDeduplicationQueue = require('./mspDeduplicationQueue'); const mspDeduplicationQueue = require('./mspDeduplicationQueue');
const mspStatistics = require('./mspStatistics'); const mspStatistics = require('./mspStatistics');
const settingsCache = require('./../settingsCache'); const settingsCache = require('./../settingsCache');
const {Geozone, GeozoneVertex, GeozoneShapes } = require('./../geozone');
var mspHelper = (function () { var mspHelper = (function () {
var self = {}; var self = {};
@ -1590,10 +1591,63 @@ var mspHelper = (function () {
FC.OSD_CUSTOM_ELEMENTS .items.push(customElement) FC.OSD_CUSTOM_ELEMENTS .items.push(customElement)
} }
break; break;
case MSPCodes.MSP2_INAV_GPS_UBLOX_COMMAND: case MSPCodes.MSP2_INAV_GPS_UBLOX_COMMAND:
// Just and ACK from the fc. // Just and ACK from the fc.
break; break;
case MSPCodes.MSP2_INAV_GEOZONE:
var geozone = new Geozone(
data.getUint8(0),
data.getUint8(1),
data.getInt32(2, true),
data.getInt32(6, true),
0,
data.getInt8(10, true),
null,
data.getUint8(12, true),
);
let verticesCount = data.getUint8(11, true);
if (verticesCount == 0) {
break;
}
if (geozone.getShape() == GeozoneShapes.POLYGON) {
geozone.setVertices(new Array(verticesCount));
} else {
geozone.setVertices(new Array(1));
}
FC.GEOZONES.put(geozone);
break;
case MSPCodes.MSP2_INAV_GEOZONE_VERTEX:
if (data.buffer.byteLength == 0) {
break;
}
var zoneID = data.getUint8(0);
var vertexId = data.getUint8(1);
var geozone = FC.GEOZONES.at(zoneID);
if (zoneID < FC.GEOZONES.geozoneCount()) {
geozone.setVertex(
vertexId,
new GeozoneVertex(
vertexId,
data.getUint32(2, true),
data.getUint32(6, true),
)
);
if (geozone.getShape() == GeozoneShapes.CIRCULAR) {
geozone.setRadius(data.getUint32(10, true));
}
}
break;
case MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE:
console.log("Geozone vertex saved")
break;
case MSPCodes.MSP2_INAV_SET_GEOZONE:
console.log("Geozone saved")
break;
default: default:
console.log('Unknown code detected: 0x' + dataHandler.code.toString(16)); console.log('Unknown code detected: 0x' + dataHandler.code.toString(16));
} else { } else {
@ -3071,6 +3125,68 @@ var mspHelper = (function () {
}; };
}; };
self.loadGeozones = function (callback) {
FC.GEOZONES.flush();
let geozoneID = -1;
let vertexID = -1;
nextGeozone();
function nextVertex() {
vertexID++;
let zone = FC.GEOZONES.at(geozoneID);
if (!zone || zone.getVerticesCount() == 0) {
nextGeozone();
return;
}
if (vertexID < FC.GEOZONES.at(geozoneID).getVerticesCount() - 1 && zone.getShape() == GeozoneShapes.POLYGON) {
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE_VERTEX, [geozoneID, vertexID], false, nextVertex);
} else {
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE_VERTEX, [geozoneID, vertexID], false, nextGeozone);
}
}
function nextGeozone() {
geozoneID++;
vertexID = -1;
if (geozoneID < FC.GEOZONES.getMaxZones() - 1) {
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE, [geozoneID], false, nextVertex);
} else {
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE, [geozoneID], false, callback);
}
}
};
self.saveGeozones = function (callback) {
let geozoneID = -1;
let vertexID = -1;
nextGeozone()
function nextVertex() {
vertexID++;
let zone = FC.GEOZONES.at(geozoneID);
if (!zone || zone.getVerticesCount() == 0) {
nextGeozone();
return;
}
if (vertexID < FC.GEOZONES.at(geozoneID).getVerticesCount() - 1) {
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE, FC.GEOZONES.extractBufferVertices(geozoneID, vertexID), false, nextVertex);
} else {
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE, FC.GEOZONES.extractBufferVertices(geozoneID, vertexID), false, nextGeozone);
}
}
function nextGeozone() {
geozoneID++;
vertexID = -1;
if (geozoneID < FC.GEOZONES.getMaxZones() - 1) {
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE, FC.GEOZONES.extractBufferZone(geozoneID), false, nextVertex);
} else {
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE, FC.GEOZONES.extractBufferZone(geozoneID), false, callback);
}
}
}
self._getSetting = function (name) { self._getSetting = function (name) {
const storedSetting = settingsCache.get(name); const storedSetting = settingsCache.get(name);

View file

@ -4288,6 +4288,15 @@
"osdElement_OSD_RANGEFINDER": { "osdElement_OSD_RANGEFINDER": {
"message": "Rangefinder distance" "message": "Rangefinder distance"
}, },
"osdElement_COURSE_NEXT_GEOZONE": {
"message": "Course to next Geozone"
},
"osdElement_HOR_DIST_TO_NEXT_GEOZONE": {
"message": "Horizontal distance to the next geozone"
},
"osdElement_VERT_DIST_TO_NEXT_GEOZONE": {
"message": "Vertical distance to the next geozone"
},
"osdSettingPLUS_CODE_DIGITS_HELP": { "osdSettingPLUS_CODE_DIGITS_HELP": {
"message": "Precision at the equator: 10=13.9x13.9m; 11=2.8x3.5m; 12=56x87cm; 13=11x22cm." "message": "Precision at the equator: 10=13.9x13.9m; 11=2.8x3.5m; 12=56x87cm; 13=11x22cm."
}, },
@ -4645,6 +4654,147 @@
"missionSafehomeMaxSafehomesReached": { "missionSafehomeMaxSafehomesReached": {
"message": "Maximum number of safehomes reached." "message": "Maximum number of safehomes reached."
}, },
"missionGeozoneHead": {
"message": "Geozones"
},
"missionGeozoneEdit": {
"message": "Edit Geozone $1"
},
"missionGeozoneSaveAndReboot": {
"message": "Save Eeprom Geozones and reboot"
},
"missionGeozoneLoad": {
"message": "Load Eeprom Geozones"
},
"missionGeozone": {
"message": "-Geozone "
},
"missionGezoneType": {
"message": "Type"
},
"missionGezoneShape": {
"message": "Shape"
},
"missionGeozoneTypePolygon": {
"message": "Polygon"
},
"missionGeozoneTypeCircular": {
"message": "Circular"
},
"missionGeozoneMaxZonesReached": {
"message": "Maximum number of geozones reached."
},
"missionGeozoneMaxVerticesReached": {
"message": "Maximum number of geozone vertices reached."
},
"missionGeozoneWarning": {
"message": "At least one mission and one geozone are configured, please ensure that the mission does not violate the geozone."
},
"geozoneEdit": {
"message": "Edit Geozone "
},
"geozoneShape": {
"message": "Shape"
},
"geozoneInclusive": {
"message": "Inclusive"
},
"geozoneExcusive": {
"message": "Exclusive"
},
"geozoneMinAlt": {
"message": "Min. Alt (cm):"
},
"geozoneMaxAlt": {
"message": "Max. Alt (cm):"
},
"geozoneInfiniteAlt": {
"message": "0 = Infinite altitude"
},
"geozoneAction": {
"message": "Action:"
},
"geozoneActionNone": {
"message": "None"
},
"geozoneActionAvoid": {
"message": "Avoid"
},
"geozoneActionPosHold": {
"message": "Pos. Hold"
},
"geozoneActionRTH": {
"message": "RTH"
},
"geozoneRadius": {
"message": "Radius (cm):"
},
"geozoneVerices": {
"message": "Vertices:"
},
"": {
"message": ""
},
"featureGEOZONE": {
"message": "Geozone"
},
"featureGEOZONETip": {
"message": "Virtual perimeters for geographical areas (also called geofence) with automatically triggered actions when the perimeters are violated."
},
"GeozoneSettings": {
"message": "Geozone Settings"
},
"geozoneDetectionDistance": {
"message": "Detection distance"
},
"geozoneDetectionDistanceHelp": {
"message": "Distance from which a geozone is detected"
},
"geozoneAvoidAltitudeRange": {
"message": "Avoid altitude range"
},
"geozoneAvoidAltitudeRangeHelp": {
"message": "Altitude range in which an attempt is made to avoid a geozone upwards"
},
"geozoneSafeAltitudeDistance": {
"message": "Safe altitude distance"
},
"geozoneSafeAltitudeDistanceHelp": {
"message": "Vertical distance that must be maintained to the upper and lower limits of the zone."
},
"geozoneSafehomeAsInclusive": {
"message": "Safehome as inclusive"
},
"geozoneSafehomeAsInclusiveHelp": {
"message": "Treat nearest safehome as inclusive geozone"
},
"geozoneSafehomeZoneAction": {
"message": "Safehome zone action"
},
"geozoneSafehomeZoneActionHelp": {
"message": "Fence action for safehome zone"
},
"geozoneMrStopDistance": {
"message" : "Multirotor stop distance"
},
"geozoneMrStopDistanceHelp": {
"message" : "Distance in which multirotors stops before the border"
},
"geozoneNoWayHomeAction": {
"message": "No way home action"
},
"geozoneNoWayHomeActionHelp": {
"message": "Action if RTH with active geozones is unable to calculate a course to home. RTH: Return to home and ignore any geozones."
},
"missionGeozoneReboot": {
"message": "Do you want to save and reboot?"
},
"missionGeozoneAvailableZones": {
"message": "Available Geozones:"
},
"missionGeozoneAvailableVertices": {
"message": "Available Vertices:"
},
"missionMultiMissionHead": { "missionMultiMissionHead": {
"message": "Multi Missions" "message": "Multi Missions"
}, },

2368
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -317,6 +317,58 @@
top: 140px; top: 140px;
} }
.geozone-settings {
top: 165px;
left: .5em;
}
.ol-touch .geozone-settings {
top: 165px;
}
.tab-mission-control .geozoneVerticesTable {
width: 100%;
text-align: center;
font-size: 12px;
}
.tab-mission-control .geozoneVerticesTable input[type="text"] {
font-size: 8px;
width: 100%;
}
.tab-mission-control .geozoneVerticesTable input[type="number"] {
font-size: 10px;
width: 100%;
-moz-appearance: textfield;
}
/* Chrome, Safari, Edge, Opera */
.tab-mission-control .geozoneVerticesTable input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.tab-mission-control .geozoneVerticesTable thead {
display: table-header-group !important;
}
.tab-mission-control .geozoneVerticesTable thead tr {
border-left: 1px solid #e4e4e4;
border-right: 1px solid #e4e4e4;
background-color: #828885;
color: #FFF;
}
.tab-mission-control .geozoneVerticesTable td,
.tab-mission-control .geozoneVerticesTable th {
padding: 2px;
/* height: 2.5em; */
}
.tab-mission-control .geozoneVerticesTable tr:nth-child(even) td,
.tab-mission-control .geozoneVerticesTable tr:nth-child(even) th {
background-color: #ebe7e7;
}
.mission-control-template { .mission-control-template {
top: 140px; top: 140px;
left: .5em; left: .5em;

View file

@ -513,6 +513,48 @@
--> -->
</div> </div>
</div> </div>
<div class="config-section gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="GeozoneSettings"></div>
</div>
<div class="spacer_box">
<div class="number">
<input type="number" id="geozoneDetectionDistance" data-unit="cm" data-setting="geozone_detection_distance" data-setting-multiplier="1" step="1" min="0" max="1000000" />
<label for="geozoneDetectionDistance"><span data-i18n="geozoneDetectionDistance"></span></label>
<div for="geozoneDetectionDistance" class="helpicon cf_tip" data-i18n_title="geozoneDetectionDistanceHelp"></div>
</div>
<div class="number">
<input type="number" id="geozoneAvoidAltitudeRange" data-unit="cm" data-setting="geozone_avoid_altitude_range" data-setting-multiplier="1" step="1" min="0" max="1000000" />
<label for="geozoneAvoidAltitudeRange"><span data-i18n="geozoneAvoidAltitudeRange"></span></label>
<div for="geozoneAvoidAltitudeRange" class="helpicon cf_tip" data-i18n_title="geozoneAvoidAltitudeRangeHelp"></div>
</div>
<div class="number">
<input type="number" id="geozoneSafeAltitudeDistance" data-unit="cm" data-setting="geozone_safe_altitude_distance" data-setting-multiplier="1" step="1" min="0" max="10000" />
<label for="geozoneSafeAltitudeDistance"><span data-i18n="geozoneSafeAltitudeDistance"></span></label>
<div for="geozoneSafeAltitudeDistance" class="helpicon cf_tip" data-i18n_title="geozoneSafeAltitudeDistanceHelp"></div>
</div>
<div class="checkbox">
<input type="checkbox" class="toggle update_preview" id="geozoneSafehomeAsInclusive" data-setting="geozone_safehome_as_inclusive" data-live="true" />
<label for="geozoneSafehomeAsInclusive"><span data-i18n="geozoneSafehomeAsInclusive"></span></label>
<div for="geozoneSafehomeAsInclusive" class="helpicon cf_tip" data-i18n_title="geozoneSafehomeAsInclusiveHelp"></div>
</div>
<div class="select">
<select id="geozoneSafehomeZoneAction" data-setting="geozone_safehome_zone_action"></select>
<label for="geozoneSafehomeZoneAction"><span data-i18n="geozoneSafehomeZoneAction"></span></label>
<div for="geozoneSafehomeZoneAction" class="helpicon cf_tip" data-i18n_title="geozoneSafehomeZoneActionHelp"></div>
</div>
<div class="number">
<input type="number" id="geozoneMrStopDistance" data-unit="cm" data-setting="geozone_mr_stop_distance" data-setting-multiplier="1" step="1" min="0" max="1000000" />
<label for="geozoneMrStopDistance"><span data-i18n="geozoneMrStopDistance"></span></label>
<div for="geozoneMrStopDistance" class="helpicon cf_tip" data-i18n_title="geozoneMrStopDistanceHelp"></div>
</div>
<div class="select">
<select id="geozoneNoWayHomeAction" data-setting="geozone_no_way_home_action"></select>
<label for="geozoneNoWayHomeAction"><span data-i18n="geozoneNoWayHomeAction"></span></label>
<div for="geozoneNoWayHomeAction" class="helpicon cf_tip" data-i18n_title="geozoneNoWayHomeActionHelp"></div>
</div>
</div>
</div>
</div> </div>
<!-- Left wrapper --> <!-- Left wrapper -->

View file

@ -62,6 +62,10 @@
<span data-i18n="missionTotalInfoMissionValid"></span> <span data-i18n="missionTotalInfoMissionValid"></span>
<div id="missionValid" style="display: inline-block"></div> <div id="missionValid" style="display: inline-block"></div>
</div> </div>
<div id="infoGeozoneMissionWarning" style="padding-bottom: 2px;">
<span style="color: red" i18n-replaced data-i18n="warning"></span>
<div id="geozoneMissionWarning" style="display: inline-block" data-i18n="missionGeozoneWarning"></div>
</div>
</div> </div>
</div> </div>
@ -281,6 +285,96 @@
</div> </div>
</div> </div>
<div id="missionPlannerGeozones" class="gui_box grey" style="display: none">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="missionGeozoneHead"></div>
<div class="btnMenu btnMenuIcon save_btn">
<div id="showHideGeozonesButton">
<a class="ic_hide" href="#" title="Hide"></a>
</div>
<a id="cancelGeozone" class="ic_cancel" href="#" title="Cancel"></a>
<a id="addGeozone" class="ic_add" href="#" title="Add Geozone"></a>
<a id="saveEepromGeozoneButton" class="ic_save2Eprom" href="#" i18n_title="missionGeozoneSaveAndReboot"></a>
<a id="loadEepromGeozoneButton" class="ic_loadFromEprom" href="#" i18n_title="missionGeozoneLoad"></a>
</div>
</div>
<div class="spacer" id="geozoneContent">
<div style="padding-bottom: 2px;">
<span data-i18n="missionGeozoneAvailableZones"></span>
<span id="availableGeozones"></span>
</div>
<div id="infoAvailableVeritces" style="padding-bottom: 2px;">
<span data-i18n="missionGeozoneAvailableVertices"></span>
<span id="availableVertices"></span>
</div>
<div id="geozoneContentBox" style="display: none;" >
<div class="gui_box grey missionPlannerGeozone">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="geozoneEdit"></div>
<div class="btnMenu btnMenuIcon">
<div class="btnMenu-danger">
<a id="deleteGeozone" class="ic_removeAll" href="#" title="Delete"></a>
</div>
</div>
</div>
<div class="spacer" id="geozoneSingelContent">
<div class="point">
<label class="point-label" for="zoneType" data-i18n="geozoneShape"></label>
<select name="zoneShape" id="geozoneShape">
<option value="1" data-i18n="missionGeozoneTypePolygon"></option>
<option value="0" data-i18n="missionGeozoneTypeCircular"></option>
</select>
</div>
<div class="point">
<label class="point-label" for="zoneType" data-i18n="missionGezoneType"></label>
<select name="zoneType" id="geozoneType">
<option value="1" data-i18n="geozoneInclusive"></option>
<option value="0" data-i18n="geozoneExcusive"></option>
</select>
</div>
<div class="point">
<label class="point-label" for="zoneMinAlt" data-i18n="geozoneMinAlt"></label>
<input type="number" id="geozoneMinAlt"></input>
</div>
<div class="point">
<label class="point-label" for="zoneMaxAlt" data-i18n="geozoneMaxAlt"></label>
<input type="number" id="geozoneMaxAlt"></input>
<span data-i18n="geozoneInfiniteAlt"></span>
</div>
<div class="point">
<label class="point-label" for="zoneAction" data-i18n="geozoneAction">Action:</label>
<select name="zoneAction" id="geozoneAction">
<option value="0" data-i18n="geozoneActionNone"></option>
<option value="1" data-i18n="geozoneActionAvoid"></option>
<option value="2" data-i18n="geozoneActionPosHold"></option>
<option value="3" data-i18n="geozoneActionRTH"></option>
</select>
</div>
<div class="point">
<label class="point-label" for="raduis" data-i18n="geozoneRadius"></label>
<input type="number" id="geozoneRadius"></input>
</div>
<div class="point">
<label class="point-label" for="verticesTable" data-i18n="geozoneVerices"></label>
<table class="geozoneVerticesTable" id="verticesTable">
<thead>
<tr>
<th style="width: 20px">&nbsp;</th>
<th style="width: 40px">#</th>
<th style="width: 120px" data-i18n="gpsLat"></th>
<th style="width: 120px" data-i18n="gpsLon"></th>
</tr>
</thead>
<tbody id="geozoneVerticesTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="gui_box grey" id="MPeditPoint" style="display: none"> <div class="gui_box grey" id="MPeditPoint" style="display: none">
<div class="gui_box_titlebar"> <div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="editPointHead" id="EditPointNumber"></div> <div class="spacer_box_title" data-i18n="editPointHead" id="EditPointNumber"></div>

View file

@ -29,6 +29,8 @@ const SerialBackend = require('./../js/serial_backend');
const { distanceOnLine, wrap_360, calculate_new_cooridatnes } = require('./../js/helpers'); const { distanceOnLine, wrap_360, calculate_new_cooridatnes } = require('./../js/helpers');
const Plotly = require('./../js/libraries/plotly-latest.min'); const Plotly = require('./../js/libraries/plotly-latest.min');
const interval = require('./../js/intervals'); const interval = require('./../js/intervals');
const { Geozone, GeozoneVertex, GeozoneType, GeozoneShapes, GeozoneFenceAction } = require('./../js/geozone');
const GeozoneCollection = require('./../js/geozoneCollection');
var MAX_NEG_FW_LAND_ALT = -2000; // cm var MAX_NEG_FW_LAND_ALT = -2000; // cm
@ -72,12 +74,19 @@ TABS.mission_control.initialize = function (callback) {
let selectedSafehome; let selectedSafehome;
let $safehomeContentBox; let $safehomeContentBox;
let $waypointOptionsTableBody; let $waypointOptionsTableBody;
let selectedGeozone;
let $geozoneContent;
let isGeozoneEnabeld = false;
let settings = {speed: 0, alt: 5000, safeRadiusSH: 50, fwApproachAlt: 60, fwLandAlt: 5, maxDistSH: 0, fwApproachLength: 0, fwLoiterRadius: 0, bingDemModel: false}; let settings = {speed: 0, alt: 5000, safeRadiusSH: 50, fwApproachAlt: 60, fwLandAlt: 5, maxDistSH: 0, fwApproachLength: 0, fwLoiterRadius: 0, bingDemModel: false};
if (GUI.active_tab != 'mission_control') { if (GUI.active_tab != 'mission_control') {
GUI.active_tab = 'mission_control'; GUI.active_tab = 'mission_control';
} }
if (FC.isFeatureEnabled('GEOZONE')) {
isGeozoneEnabeld = true;
}
if (CONFIGURATOR.connectionValid) { if (CONFIGURATOR.connectionValid) {
var loadChainer = new MSPChainerClass(); var loadChainer = new MSPChainerClass();
loadChainer.setChain([ loadChainer.setChain([
@ -85,6 +94,13 @@ TABS.mission_control.initialize = function (callback) {
//mspHelper.loadWaypoints, //mspHelper.loadWaypoints,
mspHelper.loadSafehomes, mspHelper.loadSafehomes,
mspHelper.loadFwApproach, mspHelper.loadFwApproach,
function (callback) {
if (isGeozoneEnabeld) {
mspHelper.loadGeozones(callback);
} else {
callback();
}
},
function (callback) { function (callback) {
mspHelper.getSetting("nav_fw_land_approach_length").then((data) => { mspHelper.getSetting("nav_fw_land_approach_length").then((data) => {
settings.fwApproachLength = parseInt(data.value); settings.fwApproachLength = parseInt(data.value);
@ -135,8 +151,10 @@ TABS.mission_control.initialize = function (callback) {
isOffline = true; isOffline = true;
} }
$('#infoGeozoneMissionWarning').hide();
$safehomeContentBox = $('#SafehomeContentBox'); $safehomeContentBox = $('#SafehomeContentBox');
$waypointOptionsTableBody = $('#waypointOptionsTableBody'); $waypointOptionsTableBody = $('#waypointOptionsTableBody');
$geozoneContent = $('#geozoneContent');
if (typeof require !== "undefined") { if (typeof require !== "undefined") {
loadSettings(); loadSettings();
@ -153,6 +171,11 @@ TABS.mission_control.initialize = function (callback) {
renderSafehomesOnMap(); renderSafehomesOnMap();
updateSafehomeInfo(); updateSafehomeInfo();
}, 500); }, 500);
setTimeout(() => {
selectedGeozone = FC.GEOZONES.last();
renderGeozonesOnMap();
updateGeozoneInfo();
}, 500);
} }
} else { } else {
$('#missionMap, #missionControls').hide(); $('#missionMap, #missionControls').hide();
@ -366,6 +389,8 @@ TABS.mission_control.initialize = function (callback) {
var approachLayers = [] // Layers for FW approach var approachLayers = [] // Layers for FW approach
var safehomeMarkers = []; // layer for Safehome points var safehomeMarkers = []; // layer for Safehome points
var approachLayers = [] // Layers for FW approach var approachLayers = [] // Layers for FW approach
var geozoneMarkers = []; // Layer for Geozonemarkers
var geozoneLines = []; // Layer for Lines between geozone vertices
var map; var map;
@ -741,6 +766,226 @@ TABS.mission_control.initialize = function (callback) {
return radiusProjected; return radiusProjected;
} }
/////////////////////////////////////////////
//
// Manage Geozones
//
/////////////////////////////////////////////
function getGeozoneIcon(geozone, number) {
return new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 1],
opacity: 1,
scale: 0.5,
src: geozone.getType() == GeozoneType.EXCULSIVE ? './images/icons/cf_icon_geozone_excl.png' : './images/icons/cf_icon_geozone_incl.png'
})),
text: new ol.style.Text(({
text: String(number + 1),
font: '12px sans-serif',
offsetY: -15,
offsetX: -2,
fill: new ol.style.Fill({
color: '#FFFFFF'
}),
stroke: new ol.style.Stroke({
color: '#FFFFFF'
}),
}))
});
}
function addZoneVertex(zone, vertex) {
let coord = ol.proj.fromLonLat([vertex.getLonMap(), vertex.getLatMap()]);
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(coord),
name: 'geozone'
});
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [iconFeature]
}),
style : function(iconFeature) {
return [getGeozoneIcon(zone, zone.getShape() == GeozoneShapes.POLYGON ? vertex.getNumber() : zone.getNumber())];
}
});
vectorLayer.kind = "geozone";
vectorLayer.number = vertex.getNumber();
vectorLayer.layerNumber = zone.getNumber();
vectorLayer.selection = true;
geozoneMarkers.push(vectorLayer);
return vectorLayer;
}
function paintGeozoneLine(pos1, pos2, color, number, zoneNum)
{
var line = new ol.geom.LineString([pos1, pos2]);
var feature = new ol.Feature({
geometry: line
});
feature.setStyle(
new ol.style.Style({
stroke: new ol.style.Stroke({
color: color,
width: 3,
}),
text: new ol.style.Text({
text: String(zoneNum + 1),
font: '14px sans-serif',
placement : 'line',
textBaseline: 'ideographic',
stroke: new ol.style.Stroke({
color: color
}),
}),
}),
);
var vectorSource = new ol.source.Vector({
features: [feature]
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
vectorLayer.kind = "geozoneline";
vectorLayer.selection = true;
vectorLayer.number = number;
vectorLayer.layerNumber = zoneNum;
geozoneLines.push(vectorLayer);
map.addLayer(vectorLayer);
}
function repaintGeozoneLines() {
cleanGeozoneLines();
FC.GEOZONES.get().forEach(zone => {
if (zone.getVerticesCount() != 0) {
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
var circleFeature = new ol.Feature({
geometry: new ol.geom.Circle(ol.proj.fromLonLat([zone.getFirstVertex().getLonMap(), zone.getFirstVertex().getLatMap()]), getProjectedRadius(zone.getRadius() / 100)),
name: "geozoneCircle",
});
var vectorSource = new ol.source.Vector();
vectorSource.addFeatures([circleFeature]);
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style : [
new ol.style.Style({
stroke: new ol.style.Stroke({
color: zone.getType() == GeozoneType.EXCULSIVE ? '#E62121' : '#1DBE0A',
width: 3,
})
})
]
});
vectorLayer.kind = "geozoneline";
vectorLayer.selection = false;
geozoneLines.push(vectorLayer);
map.addLayer(vectorLayer);
} else if (zone.getShape() == GeozoneShapes.POLYGON) {
var verticesCount = zone.getVerticesCount();
var prev = zone.getLastVertex();
var current;
for (let i = 0; i < verticesCount; i++) {
current = zone.getVertex(i);
let pos1 = ol.proj.fromLonLat([prev.getLonMap(), prev.getLatMap()]);
let pos2 = ol.proj.fromLonLat([current.getLonMap(), current.getLatMap()]);
paintGeozoneLine(pos1, pos2, zone.getType() == GeozoneType.EXCULSIVE ? '#E62121' : '#1DBE0A', prev.getNumber(), zone.getNumber());
prev = current;
}
}
}
});
}
function renderGeozonesOnMap()
{
cleanGeozoneLayers();
if (!selectedGeozone) {
cleanGeozoneLines();
geozoneMissionWarning();
return;
}
repaintGeozoneLines();
FC.GEOZONES.get().forEach(zone => {
if (zone.getVerticesCount() > 0) {
zone.getVertices().forEach(vertex => {
map.addLayer(addZoneVertex(zone, vertex));
});
}
});
geozoneMissionWarning();
}
function cleanGeozoneLines() {
geozoneLines.forEach(line => {
map.removeLayer(line);
});
geozoneLines = [];
}
function cleanGeozoneLayers() {
geozoneMarkers.forEach(marker => {
map.removeLayer(marker);
});
geozoneMarkers = [];
}
function geozoneMissionWarning() {
if (markers.length >= 1 && geozoneMarkers.length >= 1) {
$('#infoGeozoneMissionWarning').show();
} else {
$('#infoGeozoneMissionWarning').hide();
}
}
function updateGeozoneInfo() {
$('#availableGeozones').text((FC.GEOZONES.getMaxZones() - FC.GEOZONES.geozoneCount()) + '/' + FC.GEOZONES.getMaxZones());
$('#availableVertices').text((FC.GEOZONES.getMaxVertices() - FC.GEOZONES.getUsedVerticesCount()) + '/' + FC.GEOZONES.getMaxVertices());
}
function addGeozone() {
if (FC.GEOZONES.geozoneCount() + 1 > FC.GEOZONES.getMaxZones()) {
GUI.alert(i18n.getMessage('missionGeozoneMaxZonesReached'));
return;
}
if (FC.GEOZONES.getUsedVerticesCount() + 2 > FC.GEOZONES.getMaxVertices()) {FC.
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
return;
}
let mapCenter = map.getView().getCenter();
let midLon = Math.round(ol.proj.toLonLat(mapCenter)[0] * 1e7);
let midLat = Math.round(ol.proj.toLonLat(mapCenter)[1] * 1e7);
FC.GEOZONES.put(new Geozone(GeozoneType.INCLUSIVE, GeozoneShapes.CIRCULAR, 0, 10000, 20000, GeozoneFenceAction.NONE, [ new GeozoneVertex(0, midLat, midLon) ]));
selectedGeozone = FC.GEOZONES.last();
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
}
///////////////////////////////////////////// /////////////////////////////////////////////
// //
// Manage Take Off Home // Manage Take Off Home
@ -1376,6 +1621,7 @@ TABS.mission_control.initialize = function (callback) {
}); });
} }
geozoneMissionWarning();
} }
function redrawLayer() { function redrawLayer() {
@ -1429,6 +1675,68 @@ TABS.mission_control.initialize = function (callback) {
} }
} }
function renderGeozoneOptions() {
if (selectedGeozone) {
if (!$('#missionPlannerGeozones').is(':visible')) {
$('#missionPlannerGeozones').fadeIn(300);
}
$('#geozoneContentBox').show();
const $geozonContent = $geozoneContent.find('.missionPlannerGeozone:last-child');
$geozonContent.find('.spacer_box_title').text(i18n.getMessage('missionGeozoneEdit', selectedGeozone.getNumber() + 1));
$('#geozoneShape').val(selectedGeozone.getShape());
$('#geozoneType').val(selectedGeozone.getType());
$('#geozoneMinAlt').val(selectedGeozone.getMinAltitude());
$('#geozoneMaxAlt').val(selectedGeozone.getMaxAltitude());
$('#geozoneAction').val(selectedGeozone.getFenceAction());
$('#geozoneRadius').val(selectedGeozone.getRadius);
if (selectedGeozone.getShape() == GeozoneShapes.CIRCULAR) {
$('#geozoneRadius').prop('disabled', false);
} else {
$('#geozoneRadius').prop('disabled', true);
}
let $verticesTable = $('#geozoneVerticesTableBody');
$verticesTable.empty();
selectedGeozone.getVertices().forEach(vertex => {
$verticesTable.append('\
<tr> \
<td> \
<div class="btnTable btnTableIcon"> \
<a class="ic_removeAll" id="removeVertex" href="#" title="Remove"></a> \
</div>\
</td> \
<td> \
<span class="vertexNumber"></span> \
</td> \
<td> \
<input type="number" class="vertexLat" readonly/> \
</td> \
<td> \
<input type="number" class="vertexLon" readonly/> \
</td> \
</tr> \
');
const $row = $verticesTable.find('tr:last');
$row.find('.vertexNumber').text(vertex.getNumber() + 1);
$row.find('.vertexLat').val((vertex.getLatMap()).toLocaleString(['en-US'], {minimumFractionDigits: 7}));
$row.find('.vertexLon').val((vertex.getLonMap()).toLocaleString(['en-US'], {minimumFractionDigits: 7}));
$row.find('#removeVertex').on('click', event => {
if (selectedGeozone.getVerticesCount() > 3) {
selectedGeozone.dropVertex(vertex.getNumber());
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
}
});
});
} else {
$('#geozoneContentBox').hide();
}
}
function renderWaypointOptionsTable(waypoint) { function renderWaypointOptionsTable(waypoint) {
/* /*
* Process Waypoint Options table UI * Process Waypoint Options table UI
@ -1667,6 +1975,40 @@ TABS.mission_control.initialize = function (callback) {
}; };
ol.inherits(app.PlannerSafehomeControl, ol.control.Control); ol.inherits(app.PlannerSafehomeControl, ol.control.Control);
/**
* @constructor
* @extends {ol.control.Control}
* @param {Object=} opt_options Control options.
*/
app.GeozonesControl = function(opt_options) {
var options = opt_options || {};
var button = document.createElement('button');
button.innerHTML = ' ';
button.style = 'background: url(\'./images/icons/cf_icon_geozone_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);';
var handleShowGeozoneSettings = function () {
$('#missionPlannerGeozones').fadeIn(300);
renderGeozoneOptions();
renderGeozonesOnMap();
};
button.addEventListener('click', handleShowGeozoneSettings, false);
button.addEventListener('touchstart', handleShowGeozoneSettings, false);
var element = document.createElement('div');
element.className = 'geozone-settings ol-unselectable ol-control';
element.appendChild(button);
element.title = 'Geozone';
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(app.GeozonesControl, ol.control.Control);
/** /**
* @constructor * @constructor
* @extends {ol.control.Control} * @extends {ol.control.Control}
@ -1767,6 +2109,7 @@ TABS.mission_control.initialize = function (callback) {
* @param {ol.MapBrowserEvent} evt Map browser event. * @param {ol.MapBrowserEvent} evt Map browser event.
*/ */
app.Drag.prototype.handleDragEvent = function (evt) { app.Drag.prototype.handleDragEvent = function (evt) {
if (tempMarker.kind == "safehomecircle") { if (tempMarker.kind == "safehomecircle") {
return; return;
} }
@ -1783,7 +2126,7 @@ TABS.mission_control.initialize = function (callback) {
var geometry = /** @type {ol.geom.SimpleGeometry} */ var geometry = /** @type {ol.geom.SimpleGeometry} */
(this.feature_.getGeometry()); (this.feature_.getGeometry());
if (tempMarker.kind == "waypoint" || tempMarker.kind == "safehome" || tempMarker.kind == "home") { if (tempMarker.kind == "waypoint" || tempMarker.kind == "safehome" || tempMarker.kind == "home" || tempMarker.kind == "geozone") {
geometry.translate(deltaX, deltaY); geometry.translate(deltaX, deltaY);
this.coordinate_[0] = evt.coordinate[0]; this.coordinate_[0] = evt.coordinate[0];
this.coordinate_[1] = evt.coordinate[1]; this.coordinate_[1] = evt.coordinate[1];
@ -1818,6 +2161,18 @@ TABS.mission_control.initialize = function (callback) {
HOME.setLat(Math.round(coord[1] * 10000000)); HOME.setLat(Math.round(coord[1] * 10000000));
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000); $('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000); $('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
} else if (tempMarker.kind == "geozone") {
let tmpVertex = FC.GEOZONES.at(tempMarker.layerNumber).getVertex(tempMarker.number);
tmpVertex.setLon(Math.round(coord[0] * 1e7));
tmpVertex.setLat(Math.round(coord[1] * 1e7));
//GEOZONES.updateGeozone(tmpVertex);
let tableBody = $($geozoneContent.find('.missionPlannerGeozone').get(tempMarker.layerNumber)).find('#geozoneVerticesTableBody');
tableBody.find('tr:nth-child(' + String(tempMarker.number + 1) + ') > td > .vertexLon').val(Math.round(coord[0] * 1e7) / 1e7);
tableBody.find('tr:nth-child(' + String(tempMarker.number + 1) + ') > td > .vertexLat').val(Math.round(coord[1] * 1e7) / 1e7);
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
} }
}; };
@ -1933,6 +2288,10 @@ TABS.mission_control.initialize = function (callback) {
new app.PlannerSafehomeControl(), new app.PlannerSafehomeControl(),
new app.PlannerElevationControl(), new app.PlannerElevationControl(),
] ]
if (isGeozoneEnabeld) {
control_list.push(new app.GeozonesControl());
}
} }
else { else {
control_list = [ control_list = [
@ -2150,6 +2509,25 @@ TABS.mission_control.initialize = function (callback) {
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000); $('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000); $('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
} }
else if (selectedFeature && tempMarker.kind == "geozone" && tempMarker.selection) {
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
renderGeozoneOptions();
}
else if (selectedFeature && tempMarker.kind == "geozoneline" && tempMarker.selection) {
if (FC.GEOZONES.getUsedVerticesCount() + 1 >= FC.GEOZONES.getMaxVertices()) {
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
return;
}
let tempCoord = ol.proj.toLonLat(evt.coordinate);
let tmpVertex = new GeozoneVertex(tempMarker.number + 1, Math.round(tempCoord[1] * 1e7), Math.round(tempCoord[0] * 1e7));
FC.GEOZONES.at(tempMarker.layerNumber).insertVertex(tempMarker.number + 1, tmpVertex);
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
}
else if (!disableMarkerEdit) { else if (!disableMarkerEdit) {
let tempWpCoord = ol.proj.toLonLat(evt.coordinate); let tempWpCoord = ol.proj.toLonLat(evt.coordinate);
let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed)); let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed));
@ -2194,7 +2572,7 @@ TABS.mission_control.initialize = function (callback) {
} }
return true; return true;
}); });
if (hit && name != "safehomeDist" && name != "safehomeSafe") { if (hit && name != "safehomeDist" && name != "safehomeSafe" && name != "geozoneCircle") {
map.getTarget().style.cursor = 'pointer'; map.getTarget().style.cursor = 'pointer';
} else { } else {
map.getTarget().style.cursor = ''; map.getTarget().style.cursor = '';
@ -2299,6 +2677,19 @@ TABS.mission_control.initialize = function (callback) {
} }
}); });
$('#showHideGeozonesButton').on('click', function () {
var src = ($(this).children().attr('class') === 'ic_hide')
? 'ic_show'
: 'ic_hide';
$(this).children().attr('class', src);
if ($(this).children().attr('class') === 'ic_hide') {
$('#geozoneContent').fadeIn(300);
}
else {
$('#geozoneContent').fadeOut(300);
}
});
///////////////////////////////////////////// /////////////////////////////////////////////
// Callback for Waypoint edition // Callback for Waypoint edition
///////////////////////////////////////////// /////////////////////////////////////////////
@ -2913,6 +3304,141 @@ TABS.mission_control.initialize = function (callback) {
} }
}); });
/////////////////////////////////////////////
// Callback for Geozones
/////////////////////////////////////////////
function reboot() {
//noinspection JSUnresolvedVariable
GUI.log(i18n.getMessage('configurationEepromSaved'));
GUI.tab_switch_cleanup(function () {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
});
}
function reinitialize() {
//noinspection JSUnresolvedVariable
GUI.log(i18n.getMessage('deviceRebooting'));
GUI.handleReconnect($('.tab_mission_control a'));
}
$('#cancelGeozone').on('click', function() {
$('#missionPlannerGeozones').hide();
cleanGeozoneLayers();
cleanGeozoneLines();
selectedGeozone = null;
});
$('#addGeozone').on('click', function() {
addGeozone();
});
$('#deleteGeozone').on('click', event => {
FC.GEOZONES.drop(selectedGeozone.getNumber());
selectedGeozone = FC.GEOZONES.last();
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
});
$('#loadEepromGeozoneButton').on('click', event => {
$(event.currentTarget).addClass('disabled');
GUI.log('Start of getting Geozones');
mspHelper.loadGeozones();
setTimeout( () => {
if (FC.GEOZONES.geozoneCount() >= 1) {
selectedGeozone = FC.GEOZONES.first();
} else {
selectedGeozone = null;
}
renderGeozoneOptions();
renderGeozonesOnMap();
updateGeozoneInfo();
GUI.log('End of getting Geozones');
$(event.currentTarget).removeClass('disabled');
}, 1000);
});
$('#saveEepromGeozoneButton').on('click', event => {
if (confirm(i18n.getMessage("missionGeozoneReboot"))) {
$(event.currentTarget).addClass('disabled');
GUI.log('Start of sending Geozones');
mspHelper.saveGeozones();
setTimeout(() => {
mspHelper.saveToEeprom();
GUI.log('End of sending Geozones');
reboot();
}, 1000);
}
});
$('#geozoneShape').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setShape($(event.currentTarget).val());
if ($(event.currentTarget).val() == GeozoneShapes.CIRCULAR) {
$('#geozoneRadius').prop('disabled', false);
let tmpVertex = selectedGeozone.getFirstVertex();
selectedGeozone.resetVertices();
selectedGeozone.setVertices([tmpVertex]);
} else {
if (FC.GEOZONES.getUsedVerticesCount() + 3 > FC.GEOZONES.getMaxVertices()) {
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
return;
}
$('#geozoneRadius').prop('disabled', true);
if (selectedGeozone.getVerticesCount() < 3) {
let lat = selectedGeozone.getFirstVertex().getLat();
let lon = selectedGeozone.getFirstVertex().getLon();
let vertices = [
new GeozoneVertex(0, lat - 25000, lon - 25000),
new GeozoneVertex(1, lat - 25000, lon + 25000),
new GeozoneVertex(2, lat + 25000, lon + 25000),
new GeozoneVertex(3, lat + 25000, lon - 25000)
];
selectedGeozone.setVertices(vertices);
};
}
renderGeozonesOnMap();
}
});
$('#geozoneType').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setType($(event.currentTarget).val());
renderGeozonesOnMap();
}
});
$('#geozoneMinAlt').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setMinAltitude($(event.currentTarget).val());
renderGeozonesOnMap();
}
});
$('#geozoneMaxAlt').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setMaxAltitude($(event.currentTarget).val());
renderGeozonesOnMap();
}
});
$('#geozoneAction').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setFenceAction($(event.currentTarget).val());
renderGeozonesOnMap();
}
});
$('#geozoneRadius').on('change', event => {
if (selectedGeozone) {
selectedGeozone.setRadius($(event.currentTarget).val());
renderGeozonesOnMap();
}
});
///////////////////////////////////////////// /////////////////////////////////////////////
// Callback for HOME Table // Callback for HOME Table
///////////////////////////////////////////// /////////////////////////////////////////////

View file

@ -1731,7 +1731,51 @@ OSD.constants = {
return FONT.symbol(SYM.CROSS_TRACK_ERROR) + FONT.embed_dot('1.57') + FONT.symbol(SYM.DIST_KM); return FONT.symbol(SYM.CROSS_TRACK_ERROR) + FONT.embed_dot('1.57') + FONT.symbol(SYM.DIST_KM);
} }
} }
},{
name: 'COURSE_NEXT_GEOZONE',
id: 154,
min_version: '8.0.0',
enabled: function() {
return FC.isFeatureEnabled('GEOZONE');
}, },
preview: FONT.symbol(SYM.DIR_TO_HOME)
}, {
name: 'HOR_DIST_TO_NEXT_GEOZONE',
id: 155,
min_version: '8.0.0',
enabled: function() {
return FC.isFeatureEnabled('GEOZONE');
},
preview: function(osd_data) {
switch (OSD.data.preferences.units) {
case 0: // Imperial
case 3: // UK
return 'FD ' + FONT.embed_dot('0.88') + FONT.symbol(SYM.DIST_MI);
case 4: // GA
return 'FD ' + FONT.embed_dot('0.78') + FONT.symbol(SYM.DIST_NM);
default: // Metric
return 'FD ' + FONT.embed_dot('1.42') + FONT.symbol(SYM.DIST_KM);
}
}
},
{
name: 'VERT_DIST_TO_NEXT_GEOZONE',
id: 156,
min_version: '8.0.0',
enabled: function() {
return FC.isFeatureEnabled('GEOZONE');
},
preview: function(osd_data) {
switch (OSD.data.preferences.units) {
case 0: // Imperial
case 3: // UK
case 4: // GA
return 'FD 466' + FONT.symbol(SYM.ALT_FT) + FONT.symbol(SYM.DIR_TO_HOME);
default: // Metric
return 'FD 142' + FONT.symbol(SYM.ALT_M) + FONT.symbol(SYM.DIR_TO_HOME);
}
}
}
] ]
}, },
{ {