Commit d8b91190 authored by Roque's avatar Roque

Implement drone API and physics improvements

See merge request nexedi/erp5!1711
parents 70d11122 73f73897
/*global console*/
/*jslint nomen: true, indent: 2, maxlen: 80, white: true */
/************************** DRONE A AILE FIXE API ****************************/
var DroneAaileFixeAPI = /** @class */ (function () {
"use strict";
// var TAKEOFF_RADIUS = 60,
var LOITER_LIMIT = 30,
LOITER_RADIUS_FACTOR = 0.60,
LOITER_SPEED_FACTOR = 1.5;
//** CONSTRUCTOR
function DroneAaileFixeAPI(gameManager, drone_info, flight_parameters, id) {
this._gameManager = gameManager;
this._mapManager = this._gameManager._mapManager;
this._map_dict = this._mapManager.getMapInfo();
this._flight_parameters = flight_parameters;
this._id = id;
this._drone_info = drone_info;
this._loiter_radius = 0;
this._last_loiter_point_reached = -1;
//this._start_altitude = 0;
//this._last_altitude_point_reached = -1;
this._loiter_mode = false;
this._drone_dict_list = [];
}
/*
** Function called on start phase of the drone, just before onStart AI script
*/
DroneAaileFixeAPI.prototype.internal_start = function () {
return;
};
/*
** Function called on every drone update, right after onUpdate AI script
*/
DroneAaileFixeAPI.prototype.internal_update = function (drone) {
if (this._loiter_mode) {
this.loiter(drone);
}
/*if (this._start_altitude > 0) { //TODO move start_altitude here
this.reachAltitude(drone);
}*/
var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
if (drone_position) {
drone_info = {
'altitudeRel' : drone_position.z,
'altitudeAbs' : this._mapManager.getMapInfo().start_AMSL +
drone_position.z,
'latitude' : drone_position.x,
'longitude' : drone_position.y
};
this._drone_dict_list[this._id] = drone_info;
//broadcast drone info using internal msg
this._gameManager._droneList.forEach(function (drone) {
if (drone.id !== _this._id) {
drone.internal_getMsg(drone_info, _this._id);
}
});
}
};
DroneAaileFixeAPI.prototype.internal_getMsg = function (msg, id) {
this._drone_dict_list[id] = msg;
};
DroneAaileFixeAPI.prototype.set_loiter_mode = function (radius) {
this._loiter_mode = true;
if (radius && radius > LOITER_LIMIT) {
this._loiter_radius = radius * LOITER_RADIUS_FACTOR;
this._loiter_center = this._last_target;
this._loiter_coordinates = [];
this._last_loiter_point_reached = -1;
var x1, y1, angle;
//for (var angle = 0; angle <360; angle+=8){ //counter-clockwise
for (angle = 360; angle > 0; angle-=8){ //clockwise
x1 = this._loiter_radius *
Math.cos(angle * (Math.PI / 180)) + this._loiter_center.x;
y1 = this._loiter_radius *
Math.sin(angle * (Math.PI / 180)) + this._loiter_center.y;
this._loiter_coordinates.push(
this.getCurrentPosition(x1, y1, this._loiter_center.z));
}
}
};
DroneAaileFixeAPI.prototype.internal_setTargetCoordinates =
function (drone, x, y, z, loiter) {
//this._start_altitude = 0;
//convert real geo-coordinates to virtual x-y coordinates
var coordinates = this.processCoordinates(x, y, z);
if (!loiter) {
this._loiter_mode = false;
drone._maxSpeed = this.getMaxSpeed();
//save last target point to use as next loiter center
this._last_target = coordinates;
}
this.internal_setVirtualPlaneTargetCoordinates(drone,
coordinates.x,
coordinates.y,
coordinates.z);
};
/*
** This expects x,y plane coordinates (not geo latitude-longitud)
*/
DroneAaileFixeAPI.prototype.internal_setVirtualPlaneTargetCoordinates =
function (drone, x, y, z) {
x -= drone._controlMesh.position.x;
y -= drone._controlMesh.position.z;
z -= drone._controlMesh.position.y;
drone.setDirection(x, y, z);
drone.setAcceleration(drone._maxAcceleration);
return;
};
DroneAaileFixeAPI.prototype.sendMsg = function (msg, to) {
var _this = this,
droneList = _this._gameManager._droneList;
_this._gameManager.delay(function () {
if (to < 0) {
// Send to all drones
droneList.forEach(function (drone) {
if (drone.infosMesh) {
try {
drone.onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
drone._internal_crash();
}
}
});
}
else {
// Send to specific drone
if (droneList[to].infosMesh) {
try {
droneList[to].onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
droneList[to]._internal_crash();
}
}
}
}, _this._flight_parameters.latency.communication);
};
DroneAaileFixeAPI.prototype.log = function (msg) {
console.log("API say : " + msg);
};
DroneAaileFixeAPI.prototype.getGameParameter = function (name) {
if (["gameTime", "map"].includes(name)) {
return this._gameManager.gameParameter[name];
}
};
/*
** Converts geo latitude-longitud coordinates (º) to x,y plane coordinates (m)
*/
DroneAaileFixeAPI.prototype.processCoordinates = function (lat, lon, z) {
if(isNaN(lat) || isNaN(lon) || isNaN(z)){
throw new Error('Target coordinates must be numbers');
}
var x = this._mapManager.longitudToX(lon, this._map_dict.width),
y = this._mapManager.latitudeToY(lat, this._map_dict.depth),
position = this._mapManager.normalize(x, y, this._map_dict),
processed_coordinates;
if (z > this._map_dict.start_AMSL) {
z -= this._map_dict.start_AMSL;
}
processed_coordinates = {
x: position[0],
y: position[1],
z: z
};
//this._last_altitude_point_reached = -1;
//this.takeoff_path = [];
return processed_coordinates;
};
DroneAaileFixeAPI.prototype.getCurrentPosition = function (x, y, z) {
return this._mapManager.convertToGeoCoordinates(x, y, z, this._map_dict);
};
DroneAaileFixeAPI.prototype.loiter = function (drone) {
if (this._loiter_radius > LOITER_LIMIT) {
var drone_pos = drone.getCurrentPosition(),
min = 9999, min_i, i, d, next_point;
//shift loiter circle to nearest point
if (this._last_loiter_point_reached === -1) {
if (!this.shifted) {
drone._maxSpeed = drone._maxSpeed * LOITER_SPEED_FACTOR;
for (i = 0; i < this._loiter_coordinates.length; i+=1){
d = this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[this._loiter_coordinates[i].x,
this._loiter_coordinates[i].y]);
if (d < min) {
min = d;
min_i = i;
}
}
this._loiter_coordinates = this._loiter_coordinates.concat(
this._loiter_coordinates.splice(0,min_i));
this.shifted = true;
}
} else {
this.shifted = false;
}
//stop
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
if (drone._maxSpeed !== this.getMaxSpeed()) {
drone._maxSpeed = this.getMaxSpeed();
}
drone.setDirection(0, 0, 0);
return;
}
//loiter
next_point =
this._loiter_coordinates[this._last_loiter_point_reached + 1];
this.internal_setTargetCoordinates(
drone, next_point.x, next_point.y, next_point.z, true);
if (this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[next_point.x, next_point.y]) < 1) {
this._last_loiter_point_reached += 1;
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
return;
}
next_point = this._loiter_coordinates[
this._last_loiter_point_reached + 1];
this.internal_setTargetCoordinates(
drone, next_point.x, next_point.y, next_point.z, true);
}
}
};
DroneAaileFixeAPI.prototype.getDroneAI = function () {
return null;
};
DroneAaileFixeAPI.prototype.setAltitude = function (altitude, drone) {
/*if (this._start_altitude === 0) {
this._start_altitude = 1;
}
this.takeoff_path = [];
if (skip_loiter) {*/
var drone_pos = drone.getCurrentPosition();
this.internal_setVirtualPlaneTargetCoordinates(drone,
drone_pos.x,
drone_pos.y,
altitude);
return;
/*}
var x1, y1,
LOOPS = 1,
CIRCLE_ANGLE = 8,
current_point = 0,
total_points = 360/CIRCLE_ANGLE*LOOPS,
initial_altitude = drone.getAltitudeAbs(),
center = {
x: drone.position.x,
y: drone.position.y,
z: drone.position.z
};
for (var l = 0; l <= LOOPS; l+=1){
for (var angle = 360; angle > 0; angle-=CIRCLE_ANGLE){ //clockwise sense
current_point++;
x1 = TAKEOFF_RADIUS * Math.cos(angle * (Math.PI / 180)) + center.x;
y1 = TAKEOFF_RADIUS * Math.sin(angle * (Math.PI / 180)) + center.y;
if (current_point < total_points/3) {
var FACTOR = 0.5;
x1 = center.x*FACTOR + x1*(1-FACTOR);
y1 = center.y*FACTOR + y1*(1-FACTOR);
}
this.takeoff_path.push({x: x1, y: y1, z: initial_altitude +
current_point * (altitude-initial_altitude)/total_points});
}
}*/
};
/*DroneAaileFixeAPI.prototype.reachAltitude = function (drone) {
function distance(p1, p2) {
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) +
Math.pow(p1[1] - p2[1], 2));
}
//stop
if (this._last_altitude_point_reached === this.takeoff_path.length - 1) {
this._last_altitude_point_reached = -1;
this.takeoff_path = [];
this._start_altitude = 0;
drone.setDirection(0, 0, 0);
return;
}
//loiter
var drone_pos = {
x: drone.position.x,
y: drone.position.y,
z: drone.position.z
};
var next_point = this.takeoff_path[this._last_altitude_point_reached + 1];
this.internal_setVirtualPlaneTargetCoordinates(
next_point.x, next_point.y, next_point.z);
if (distance([drone_pos.x, drone_pos.y],
[next_point.x, next_point.y]) < 1) {
this._last_altitude_point_reached += 1;
if (this._last_altitude_point_reached === this.takeoff_path.length - 1) {
return;
}
next_point = this.takeoff_path[this._last_altitude_point_reached + 1];
this.internal_setVirtualPlaneTargetCoordinates(
next_point.x, next_point.y, next_point.z);
}
};*/
DroneAaileFixeAPI.prototype.getMaxSpeed = function () {
return this._flight_parameters.drone.maxSpeed;
};
DroneAaileFixeAPI.prototype.triggerParachute = function (drone) {
var drone_pos = drone.getCurrentPosition();
this.internal_setTargetCoordinates(drone, drone_pos.x, drone_pos.y, 5);
};
DroneAaileFixeAPI.prototype.landed = function (drone) {
var drone_pos = drone.getCurrentPosition();
return Math.floor(drone_pos.z) < 10;
};
DroneAaileFixeAPI.prototype.exit = function () {
return;
};
DroneAaileFixeAPI.prototype.getInitialAltitude = function () {
return 0;
};
DroneAaileFixeAPI.prototype.getAltitudeAbs = function (altitude) {
return altitude;
};
DroneAaileFixeAPI.prototype.getMinHeight = function () {
return 0;
};
DroneAaileFixeAPI.prototype.getMaxHeight = function () {
return 800;
};
DroneAaileFixeAPI.prototype.getFlightParameters = function () {
return this._flight_parameters;
};
return DroneAaileFixeAPI;
}());
\ No newline at end of file
/*global console*/
/*global BABYLON, console*/
/*jslint nomen: true, indent: 2, maxlen: 80, white: true */
/**************************** DRONE LOG FOLLOWER ******************************/
var DroneLogAPI = /** @class */ (function () {
"use strict";
var TOP_SPEED = 250; //so fast that it virtually "teleports" to target
//** CONSTRUCTOR
function DroneLogAPI(gameManager, drone_info, flight_parameters, id) {
this._gameManager = gameManager;
......@@ -15,7 +16,13 @@ var DroneLogAPI = /** @class */ (function () {
/*
** Function called at start phase of the drone, just before onStart AI script
*/
DroneLogAPI.prototype.internal_start = function () {
DroneLogAPI.prototype.internal_start = function (drone) {
drone._minAcceleration = -1;
drone._maxAcceleration = 1;
drone._minSpeed = TOP_SPEED;
drone._maxSpeed = TOP_SPEED;
drone._acceleration = 10;
drone._speed = TOP_SPEED;
function getLogEntries(log) {
var i, line_list = log.split('\n'), log_entry_list = [], log_entry,
log_header_found;
......@@ -69,22 +76,43 @@ var DroneLogAPI = /** @class */ (function () {
this._flight_parameters.converted_log_point_list = converted_log_point_list;
};
/*
** Function called on every drone update, right before onUpdate AI script
*/
DroneLogAPI.prototype.internal_update = function (context, delta_time) {
var updateSpeed;
context._speed += context._acceleration * delta_time / 1000;
if (context._speed > context._maxSpeed) {
context._speed = context._maxSpeed;
}
if (context._speed < -context._maxSpeed) {
context._speed = -context._maxSpeed;
}
updateSpeed = context._speed * delta_time / 1000;
if (context._direction.x !== 0 ||
context._direction.y !== 0 ||
context._direction.z !== 0) {
context._controlMesh.position.addInPlace(new BABYLON.Vector3(
context._direction.x * updateSpeed,
context._direction.y * updateSpeed,
context._direction.z * updateSpeed));
}
context._controlMesh.computeWorldMatrix(true);
context._mesh.computeWorldMatrix(true);
};
/*
** Function called on every drone update, right after onUpdate AI script
*/
DroneLogAPI.prototype.internal_update = function () {
DroneLogAPI.prototype.internal_post_update = function (drone) {
return;
};
DroneLogAPI.prototype.internal_setTargetCoordinates =
function (drone, x, y, z) {
var coordinates = this.processCoordinates(x, y, z);
function (drone, coordinates) {
coordinates.x -= drone._controlMesh.position.x;
coordinates.y -= drone._controlMesh.position.z;
coordinates.z -= drone._controlMesh.position.y;
drone.setDirection(coordinates.x, coordinates.y, coordinates.z);
drone.setAcceleration(drone._maxAcceleration);
return;
};
DroneLogAPI.prototype.sendMsg = function (msg, to) {
return;
};
......@@ -96,6 +124,16 @@ var DroneLogAPI = /** @class */ (function () {
return this._gameManager.gameParameter[name];
}
};
DroneLogAPI.prototype.setStartingPosition = function (drone, x, y, z) {
if (!drone._canPlay) {
if (z <= 0.05) {
z = 0.05;
}
drone._controlMesh.position = new BABYLON.Vector3(x, z, y);
}
drone._controlMesh.computeWorldMatrix(true);
drone._mesh.computeWorldMatrix(true);
};
DroneLogAPI.prototype.processCoordinates = function (x, y, z) {
if(isNaN(x) || isNaN(y) || isNaN(z)){
throw new Error('Target coordinates must be numbers');
......@@ -126,12 +164,11 @@ var DroneLogAPI = /** @class */ (function () {
'me.setTargetCoordinates(me.checkpoint_list[0][0], ' +
'me.checkpoint_list[0][1], me.checkpoint_list[0][2]);' +
'me.last_checkpoint_reached = -1;' +
'me.setAcceleration(10);' +
'};' +
'me.onUpdate = function(timestamp) {' +
'var next_checkpoint = me.checkpoint_list' +
'[me.last_checkpoint_reached+1];' +
'if (distance([me.position.x, me.position.y], next_checkpoint) < 12) {' +
'if (distance([me.position.x, me.position.y], next_checkpoint) < 10) {' +
'me.going = false;' +
'var log_elapsed = next_checkpoint[3] - me.initTimestamp,' +
'time_elapsed = new Date() - me.startTime;' +
......@@ -165,30 +202,6 @@ var DroneLogAPI = /** @class */ (function () {
z: z
};
};
DroneLogAPI.prototype.set_loiter_mode = function (radius, drone) {
return;
};
DroneLogAPI.prototype.setAltitude = function (altitude, drone) {
return;
};
DroneLogAPI.prototype.getMaxSpeed = function () {
return 3000;
};
DroneLogAPI.prototype.getInitialAltitude = function () {
return 0;
};
DroneLogAPI.prototype.getAltitudeAbs = function () {
return 0;
};
DroneLogAPI.prototype.getMinHeight = function () {
return 0;
};
DroneLogAPI.prototype.getMaxHeight = function () {
return 220;
};
DroneLogAPI.prototype.triggerParachute = function (drone) {
return;
};
DroneLogAPI.prototype.getFlightParameters = function () {
return this._flight_parameters;
};
......
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1004.33468.24270.48913</string> </value>
<value> <string>1006.4961.7659.53043</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1669196851.18</float>
<float>1675263473.08</float>
<string>UTC</string>
</tuple>
</state>
......
/*global BABYLON, console*/
/*jslint nomen: true, indent: 2, maxlen: 80, todo: true */
/************************** FIXED WING DRONE API ****************************/
var FixedWingDroneAPI = /** @class */ (function () {
"use strict";
// var TAKEOFF_RADIUS = 60,
var DEFAULT_SPEED = 16,
EARTH_GRAVITY = 9.81,
LOITER_LIMIT = 30,
LOITER_RADIUS_FACTOR = 0.60,
LOITER_SPEED_FACTOR = 1.5,
MAX_ACCELERATION = 6,
MAX_DECELERATION = 1,
MIN_SPEED = 12,
MAX_SPEED = 26,
MAX_ROLL = 35,
MIN_PITCH = -20,
MAX_PITCH = 25,
MAX_CLIMB_RATE = 8,
MAX_SINK_RATE = 3;
//** CONSTRUCTOR
function FixedWingDroneAPI(gameManager, drone_info, flight_parameters, id) {
this._gameManager = gameManager;
this._mapManager = this._gameManager._mapManager;
this._map_dict = this._mapManager.getMapInfo();
this._flight_parameters = flight_parameters;
this._id = id;
this._drone_info = drone_info;
this._loiter_radius = 0;
this._last_loiter_point_reached = -1;
//this._start_altitude = 0;
//this._last_altitude_point_reached = -1;
this._loiter_mode = false;
this._drone_dict_list = [];
}
/*
** Function called on start phase of the drone, just before onStart AI script
*/
FixedWingDroneAPI.prototype.internal_start = function (drone) {
drone._maxDeceleration = this.getMaxDeceleration();
drone._maxAcceleration = this.getMaxAcceleration();
drone._minSpeed = this.getMinSpeed();
drone._maxSpeed = this.getMaxSpeed();
if (drone._minSpeed > drone._maxSpeed) {
throw new Error('min speed cannot be superior to max speed');
}
drone._speed = drone._targetSpeed = this.getInitialSpeed();
if (drone._speed < drone._minSpeed || drone._speed > drone._maxSpeed) {
throw new Error('Drone speed must be between min speed and max speed');
}
drone._minPitchAngle = this.getMinPitchAngle();
drone._maxPitchAngle = this.getMaxPitchAngle();
if (drone._minPitchAngle > drone._maxPitchAngle) {
throw new Error('min pitch angle cannot be superior to max pitch angle');
}
drone._maxRollAngle = this.getMaxRollAngle();
drone._maxSinkRate = this.getMaxSinkRate();
if (drone._maxSinkRate > drone._maxSpeed) {
throw new Error('max sink rate cannot be superior to max speed');
}
drone._maxClimbRate = this.getMaxClimbRate();
if (drone._maxClimbRate > drone._maxSpeed) {
throw new Error('max climb rate cannot be superior to max speed');
}
drone._maxOrientation = this.getMaxOrientation();
return;
};
/*
** Function called on every drone update, right before onUpdate AI script
*/
FixedWingDroneAPI.prototype.internal_update = function (context, delta_time) {
var diff, newrot, orientationValue, rotStep, updateSpeed;
//TODO rotation
if (context._rotationTarget) {
rotStep = BABYLON.Vector3.Zero();
diff = context._rotationTarget.subtract(context._controlMesh.rotation);
rotStep.x = (diff.x >= 1) ? 1 : diff.x;
rotStep.y = (diff.y >= 1) ? 1 : diff.y;
rotStep.z = (diff.z >= 1) ? 1 : diff.z;
if (rotStep === BABYLON.Vector3.Zero()) {
context._rotationTarget = null;
return;
}
newrot = new BABYLON.Vector3(context._controlMesh.rotation.x +
(rotStep.x * context._rotationSpeed),
context._controlMesh.rotation.y +
(rotStep.y * context._rotationSpeed),
context._controlMesh.rotation.z +
(rotStep.z * context._rotationSpeed)
);
context._controlMesh.rotation = newrot;
}
this._updateSpeed(context, delta_time);
this._updateDirection(context, delta_time);
updateSpeed = context._speed * delta_time / 1000;
if (context._direction.x !== 0 ||
context._direction.y !== 0 ||
context._direction.z !== 0) {
context._controlMesh.position.addInPlace(new BABYLON.Vector3(
context._direction.x * updateSpeed,
context._direction.y * updateSpeed,
context._direction.z * updateSpeed
));
}
//TODO rotation
orientationValue = context._maxOrientation *
(context._speed / context._maxSpeed);
context._mesh.rotation =
new BABYLON.Vector3(orientationValue * context._direction.z, 0,
-orientationValue * context._direction.x);
context._controlMesh.computeWorldMatrix(true);
context._mesh.computeWorldMatrix(true);
};
/*
** Function called on every drone update, right after onUpdate AI script
*/
FixedWingDroneAPI.prototype.internal_post_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
if (_this._loiter_mode) {
_this.loiter(drone);
}
/*if (_this._start_altitude > 0) { //TODO move start_altitude here
_this.reachAltitude(drone);
}*/
if (drone_position) {
drone_info = {
'altitudeRel' : drone_position.z,
'altitudeAbs' : _this._mapManager.getMapInfo().start_AMSL +
drone_position.z,
'latitude' : drone_position.x,
'longitude' : drone_position.y
};
_this._drone_dict_list[_this._id] = drone_info;
//broadcast drone info using internal msg
_this._gameManager._droneList.forEach(function (drone) {
if (drone.id !== _this._id) {
drone.internal_getMsg(drone_info, _this._id);
}
});
}
};
FixedWingDroneAPI.prototype._updateSpeed = function (drone, delta_time) {
var speed = drone.getSpeed(), speedDiff, speedUpdate;
if (speed !== this._targetSpeed) {
speedDiff = this._targetSpeed - speed;
speedUpdate = drone._acceleration * delta_time / 1000;
if (Math.abs(speedDiff) < Math.abs(speedUpdate)) {
drone._speed = this._targetSpeed;
drone._acceleration = 0;
} else {
drone._speed += speedUpdate;
}
}
};
FixedWingDroneAPI.prototype._updateDirection = function (drone, delta_time) {
var horizontalCoeff, newX, newY, newZ;
[newX, newZ] = this._getNewYaw(drone, delta_time);
newY = this._getNewAltitude(drone);
horizontalCoeff = Math.sqrt(
(
Math.pow(drone.getSpeed(), 2) - Math.pow(newY, 2)
) / (
Math.pow(newX, 2) + Math.pow(newZ, 2)
)
);
newX *= horizontalCoeff;
newZ *= horizontalCoeff;
// swap y and z axis so z axis represents altitude
drone.setDirection(newX, newZ, newY);
};
FixedWingDroneAPI.prototype._getNewYaw = function (drone, delta_time) {
// swap y and z axis so z axis represents altitude
var bearing = this._computeBearing(
drone.position.x,
drone.position.y,
drone._targetCoordinates.x,
drone._targetCoordinates.y
),
yaw = drone.getYaw(),
yawDiff = this._computeYawDiff(yaw, bearing),
yawUpdate = this.getYawVelocity(drone) * delta_time / 1000;
if (yawUpdate >= Math.abs(yawDiff)) {
yawUpdate = yawDiff;
} else if (yawDiff < 0) {
yawUpdate *= -1;
}
yaw += yawUpdate;
// trigonometric circle is east oriented, yaw angle is clockwise
yaw = this._toRad(-yaw + 90);
return [Math.cos(yaw), Math.sin(yaw)];
};
FixedWingDroneAPI.prototype._getNewAltitude = function (drone) {
// swap y and z axis so z axis represents altitude
var altitudeDiff = drone._targetCoordinates.z - drone.position.z,
verticalSpeed;
if (altitudeDiff >= 0) {
verticalSpeed = this._computeVerticalSpeed(
altitudeDiff,
this.getMaxClimbRate(),
drone.getSpeed(),
this.getMaxPitchAngle()
);
} else {
verticalSpeed = -this._computeVerticalSpeed(
Math.abs(altitudeDiff),
this.getMaxSinkRate(),
drone.getSpeed(),
-this.getMinPitchAngle()
);
}
return verticalSpeed;
};
FixedWingDroneAPI.prototype.setRotation = function (drone, x, y, z) {
//TODO rotation
drone._rotationTarget = new BABYLON.Vector3(x, z, y);
};
FixedWingDroneAPI.prototype.setRotationBy = function (drone, x, y, z) {
//TODO rotation
drone._rotationTarget = new BABYLON.Vector3(drone.rotation.x + x,
drone.rotation.y + z,
drone.rotation.z + y);
};
FixedWingDroneAPI.prototype.setAltitude = function (drone, altitude) {
drone._targetCoordinates.z = altitude;
};
FixedWingDroneAPI.prototype.setSpeed = function (drone, speed) {
this._targetSpeed = Math.max(
Math.min(speed, this.getMaxSpeed()),
this.getMinSpeed()
);
drone._acceleration = (this._targetSpeed > drone.getSpeed())
? this.getMaxAcceleration() : -this.getMaxDeceleration();
};
FixedWingDroneAPI.prototype.setStartingPosition = function (drone, x, y, z) {
if (!drone._canPlay) {
if (z <= 0.05) {
z = 0.05;
}
drone._controlMesh.position = new BABYLON.Vector3(x, z, y);
}
drone._controlMesh.computeWorldMatrix(true);
drone._mesh.computeWorldMatrix(true);
};
FixedWingDroneAPI.prototype.internal_getMsg = function (msg, id) {
this._drone_dict_list[id] = msg;
};
FixedWingDroneAPI.prototype.set_loiter_mode = function (radius) {
this._loiter_mode = true;
if (radius && radius > LOITER_LIMIT) {
this._loiter_radius = radius * LOITER_RADIUS_FACTOR;
this._loiter_center = this._last_target;
this._loiter_coordinates = [];
this._last_loiter_point_reached = -1;
var x1, y1, angle;
//for (var angle = 0; angle <360; angle+=8){ //counter-clockwise
for (angle = 360; angle > 0; angle -= 8) { //clockwise
x1 = this._loiter_radius *
Math.cos(this._toRad(angle)) + this._loiter_center.x;
y1 = this._loiter_radius *
Math.sin(this._toRad(angle)) + this._loiter_center.y;
this._loiter_coordinates.push(
this.getCurrentPosition(x1, y1, this._loiter_center.z)
);
}
}
};
FixedWingDroneAPI.prototype.internal_setTargetCoordinates =
function (drone, coordinates, loiter) {
if (!loiter) {
this._loiter_mode = false;
//save last target point to use as next loiter center
this._last_target = coordinates;
}
};
FixedWingDroneAPI.prototype.sendMsg = function (msg, to) {
var _this = this,
droneList = _this._gameManager._droneList;
_this._gameManager.delay(function () {
if (to < 0) {
// Send to all drones
droneList.forEach(function (drone) {
if (drone.infosMesh) {
try {
drone.onGetMsg(msg);
} catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
drone._internal_crash();
}
}
});
} else {
// Send to specific drone
if (droneList[to].infosMesh) {
try {
droneList[to].onGetMsg(msg);
} catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
droneList[to]._internal_crash();
}
}
}
}, _this._flight_parameters.latency.communication);
};
FixedWingDroneAPI.prototype.log = function (msg) {
console.log("API say : " + msg);
};
FixedWingDroneAPI.prototype.getGameParameter = function (name) {
if (["gameTime", "map"].includes(name)) {
return this._gameManager.gameParameter[name];
}
};
/*
** Converts geo latitude-longitud coordinates (º) to x,y plane coordinates (m)
*/
FixedWingDroneAPI.prototype.processCoordinates = function (lat, lon, z) {
if (isNaN(lat) || isNaN(lon) || isNaN(z)) {
throw new Error('Target coordinates must be numbers');
}
var x = this._mapManager.longitudToX(lon, this._map_dict.width),
y = this._mapManager.latitudeToY(lat, this._map_dict.depth),
position = this._mapManager.normalize(x, y, this._map_dict),
processed_coordinates;
if (z > this._map_dict.start_AMSL) {
z -= this._map_dict.start_AMSL;
}
processed_coordinates = {
x: position[0],
y: position[1],
z: z
};
//this._last_altitude_point_reached = -1;
//this.takeoff_path = [];
return processed_coordinates;
};
FixedWingDroneAPI.prototype.getCurrentPosition = function (x, y, z) {
return this._mapManager.convertToGeoCoordinates(x, y, z, this._map_dict);
};
FixedWingDroneAPI.prototype.loiter = function (drone) {
if (this._loiter_radius > LOITER_LIMIT) {
var drone_pos = drone.getCurrentPosition(),
min = 9999,
min_i,
i,
d,
next_point;
//shift loiter circle to nearest point
if (this._last_loiter_point_reached === -1) {
if (!this.shifted) {
drone._maxSpeed = drone._maxSpeed * LOITER_SPEED_FACTOR;
for (i = 0; i < this._loiter_coordinates.length; i += 1) {
d = this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[this._loiter_coordinates[i].x,
this._loiter_coordinates[i].y]);
if (d < min) {
min = d;
min_i = i;
}
}
this._loiter_coordinates = this._loiter_coordinates.concat(
this._loiter_coordinates.splice(0, min_i)
);
this.shifted = true;
}
} else {
this.shifted = false;
}
//stop
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
if (drone._maxSpeed !== this.getMaxSpeed()) {
drone._maxSpeed = this.getMaxSpeed();
}
drone.setDirection(0, 0, 0);
return;
}
//loiter
next_point =
this._loiter_coordinates[this._last_loiter_point_reached + 1];
this.internal_setTargetCoordinates(drone, next_point, true);
if (this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[next_point.x, next_point.y]) < 1) {
this._last_loiter_point_reached += 1;
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
return;
}
next_point = this._loiter_coordinates[
this._last_loiter_point_reached + 1
];
this.internal_setTargetCoordinates(drone, next_point, true);
}
}
};
FixedWingDroneAPI.prototype.getDroneAI = function () {
return null;
};
FixedWingDroneAPI.prototype.getMinSpeed = function () {
return this._flight_parameters.drone.minSpeed || MIN_SPEED;
};
FixedWingDroneAPI.prototype.getMaxSpeed = function () {
return this._flight_parameters.drone.maxSpeed || MAX_SPEED;
};
FixedWingDroneAPI.prototype.getInitialSpeed = function () {
return this._flight_parameters.drone.speed || DEFAULT_SPEED;
};
FixedWingDroneAPI.prototype.getMaxDeceleration = function () {
return this._flight_parameters.drone.maxDeceleration || MAX_DECELERATION;
};
FixedWingDroneAPI.prototype.getMaxAcceleration = function () {
return this._flight_parameters.drone.maxAcceleration || MAX_ACCELERATION;
};
FixedWingDroneAPI.prototype.getMinPitchAngle = function () {
return this._flight_parameters.drone.minPitchAngle || MIN_PITCH;
};
FixedWingDroneAPI.prototype.getMaxPitchAngle = function () {
return this._flight_parameters.drone.maxPitchAngle || MAX_PITCH;
};
FixedWingDroneAPI.prototype.getMaxRollAngle = function () {
return this._flight_parameters.drone.maxRoll || MAX_ROLL;
};
FixedWingDroneAPI.prototype.getMaxSinkRate = function () {
return this._flight_parameters.drone.maxSinkRate || MAX_SINK_RATE;
};
FixedWingDroneAPI.prototype.getMaxClimbRate = function () {
return this._flight_parameters.drone.maxClimbRate || MAX_CLIMB_RATE;
};
FixedWingDroneAPI.prototype.getMaxOrientation = function () {
//TODO should be a game parameter (but how to force value to PI quarters?)
return Math.PI / 4;
};
FixedWingDroneAPI.prototype.getYawVelocity = function (drone) {
return 360 * EARTH_GRAVITY
* Math.tan(this._toRad(this.getMaxRollAngle()))
/ (2 * Math.PI * drone.getSpeed());
};
FixedWingDroneAPI.prototype.getYaw = function (drone) {
var direction = drone.worldDirection;
return this._computeBearing(0, 0, direction.x, direction.z);
};
FixedWingDroneAPI.prototype._computeBearing = function (x1, z1, x2, z2) {
return this._toDeg(Math.atan2(x2 - x1, z2 - z1));
};
FixedWingDroneAPI.prototype._computeYawDiff = function (yaw1, yaw2) {
var diff = yaw2 - yaw1;
diff += (diff > 180) ? -360 : (diff < -180) ? 360 : 0;
return diff;
};
FixedWingDroneAPI.prototype._computeVerticalSpeed =
function (altitude_diff, max_climb_rate, speed, max_pitch) {
var maxVerticalSpeed = Math.min(altitude_diff, Math.min(max_climb_rate, speed));
return (this._toDeg(Math.asin(maxVerticalSpeed / speed)) > max_pitch)
? speed * Math.sin(this._toRad(max_pitch))
: maxVerticalSpeed;
};
FixedWingDroneAPI.prototype._toRad = function (angle) {
return angle * Math.PI / 180;
};
FixedWingDroneAPI.prototype._toDeg = function (angle) {
return angle * 180 / Math.PI;
};
FixedWingDroneAPI.prototype.getClimbRate = function (drone) {
return drone.worldDirection.y * drone.getSpeed();
};
FixedWingDroneAPI.prototype.triggerParachute = function (drone) {
var drone_pos = drone.getCurrentPosition();
drone.setTargetCoordinates(drone_pos.x, drone_pos.y, 5);
};
FixedWingDroneAPI.prototype.landed = function (drone) {
var drone_pos = drone.getCurrentPosition();
return Math.floor(drone_pos.z) < 10;
};
FixedWingDroneAPI.prototype.exit = function () {
return;
};
FixedWingDroneAPI.prototype.getInitialAltitude = function () {
return 0;
};
FixedWingDroneAPI.prototype.getAltitudeAbs = function (altitude) {
return altitude;
};
FixedWingDroneAPI.prototype.getMinHeight = function () {
return 0;
};
FixedWingDroneAPI.prototype.getMaxHeight = function () {
return 800;
};
FixedWingDroneAPI.prototype.getFlightParameters = function () {
return this._flight_parameters;
};
return FixedWingDroneAPI;
}());
......@@ -75,7 +75,7 @@
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_erp5_page_drone_simulator_droneaaailefixe.js</string> </value>
<value> <string>gadget_erp5_page_drone_simulator_fixedwingdrone.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -85,7 +85,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>drone_simulator_droneaaailefixe_js</string> </value>
<value> <string>drone_simulator_fixedwingdrone_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
......@@ -105,7 +105,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Drone A Aile Fixe (API)</string> </value>
<value> <string>Fixed Wing Drone (API)</string> </value>
</item>
<item>
<key> <string>version</string> </key>
......@@ -144,12 +144,6 @@
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
......@@ -195,7 +189,7 @@
</tuple>
<state>
<tuple>
<float>1664807682.76</float>
<float>1674834555.79</float>
<string>UTC</string>
</tuple>
</state>
......@@ -244,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1004.62583.63891.22220</string> </value>
<value> <string>1006.17822.27350.61713</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,70 +256,7 @@
</tuple>
<state>
<tuple>
<float>1670856495.99</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1664807031.69</float>
<float>1676035583.95</float>
<string>UTC</string>
</tuple>
</state>
......
/*global BABYLON, RSVP, console, DroneAaileFixeAPI, DroneLogAPI, document*/
/*jslint nomen: true, indent: 2, maxlen: 80, white: true, todo: true,
/*global BABYLON, RSVP, console, FixedWingDroneAPI, DroneLogAPI, document*/
/*jslint nomen: true, indent: 2, maxlen: 80, todo: true,
unparam: true */
var GAMEPARAMETERS = {};
......@@ -14,12 +14,20 @@ var DroneManager = /** @class */ (function () {
this._controlMesh = null;
this._canPlay = false;
this._canCommunicate = false;
this._maxDeceleration = 0;
this._maxAcceleration = 0;
this._minSpeed = 0;
this._maxSpeed = 0;
this._minPitchAngle = 0;
this._maxPitchAngle = 0;
this._maxRollAngle = 0;
this._maxSinkRate = 0;
this._maxClimbRate = 0;
this._maxOrientation = 0;
this._speed = 0;
this._acceleration = 0;
this._direction = BABYLON.Vector3.Zero();
this._maxOrientation = Math.PI / 4;
this._direction = new BABYLON.Vector3(0, 0, 1); // North
this._rotationSpeed = 0.4;
this._scene = scene;
this._canUpdate = true;
this._id = id;
......@@ -27,7 +35,10 @@ var DroneManager = /** @class */ (function () {
this._API = API; // var API created on AI evel
// Create the control mesh
this._controlMesh = BABYLON.Mesh.CreateBox(
"droneControl_" + id, 0.01, this._scene);
"droneControl_" + id,
0.01,
this._scene
);
this._controlMesh.isVisible = false;
this._controlMesh.computeWorldMatrix(true);
// Create the mesh from the drone prefab
......@@ -47,6 +58,7 @@ var DroneManager = /** @class */ (function () {
}
}
DroneManager.prototype._swapAxe = function (vector) {
// swap y and z axis so z axis represents altitude
return new BABYLON.Vector3(vector.x, vector.z, vector.y);
};
Object.defineProperty(DroneManager.prototype, "leader_id", {
......@@ -79,6 +91,7 @@ var DroneManager = /** @class */ (function () {
enumerable: true,
configurable: true
});
// swap y and z axis so z axis represents altitude
Object.defineProperty(DroneManager.prototype, "position", {
get: function () {
if (this._controlMesh !== null) {
......@@ -94,6 +107,7 @@ var DroneManager = /** @class */ (function () {
enumerable: true,
configurable: true
});
// swap y and z axis so z axis represents altitude
Object.defineProperty(DroneManager.prototype, "direction", {
get: function () { return this._swapAxe(this._direction); },
enumerable: true,
......@@ -102,17 +116,19 @@ var DroneManager = /** @class */ (function () {
Object.defineProperty(DroneManager.prototype, "worldDirection", {
get: function () {
return new BABYLON.Vector3(
this._direction.x, this._direction.y, this._direction.z);
this._direction.x,
this._direction.y,
this._direction.z
);
},
enumerable: true,
configurable: true
});
DroneManager.prototype.internal_start = function () {
this._maxAcceleration = GAMEPARAMETERS.drone.maxAcceleration;
this._maxSpeed = this._API.getMaxSpeed();
this._API.internal_start();
DroneManager.prototype.internal_start = function (initial_position) {
this._API.internal_start(this);
this._canPlay = true;
this._canCommunicate = true;
this._targetCoordinates = initial_position;
try {
return this.onStart();
} catch (error) {
......@@ -127,29 +143,17 @@ var DroneManager = /** @class */ (function () {
if (!this._canPlay) {
return;
}
return this._API.internal_setTargetCoordinates(this, x, y, z);
//convert real geo-coordinates to virtual x-y coordinates
this._targetCoordinates = this._API.processCoordinates(x, y, z);
return this._API.internal_setTargetCoordinates(
this,
this._targetCoordinates
);
};
DroneManager.prototype.internal_update = function (delta_time) {
var context = this, updateSpeed;
var context = this;
if (this._controlMesh) {
context._speed += context._acceleration * delta_time / 1000;
if (context._speed > context._maxSpeed) {
context._speed = context._maxSpeed;
}
if (context._speed < -context._maxSpeed) {
context._speed = -context._maxSpeed;
}
updateSpeed = context._speed * delta_time / 1000;
if (context._direction.x !== 0 ||
context._direction.y !== 0 ||
context._direction.z !== 0) {
context._controlMesh.position.addInPlace(new BABYLON.Vector3(
context._direction.x * updateSpeed,
context._direction.y * updateSpeed,
context._direction.z * updateSpeed));
}
context._controlMesh.computeWorldMatrix(true);
context._mesh.computeWorldMatrix(true);
context._API.internal_update(context, delta_time);
if (context._canUpdate) {
context._canUpdate = false;
return new RSVP.Queue()
......@@ -163,7 +167,11 @@ var DroneManager = /** @class */ (function () {
context._internal_crash(error);
})
.push(function () {
context._API.internal_update(context);
context._API.internal_post_update(context);
})
.push(undefined, function (error) {
console.warn('Drone crashed on update due to error:', error);
context._internal_crash(error);
});
}
return;
......@@ -181,29 +189,19 @@ var DroneManager = /** @class */ (function () {
this.onTouched();
};
DroneManager.prototype.setStartingPosition = function (x, y, z) {
if(isNaN(x) || isNaN(y) || isNaN(z)){
if (isNaN(x) || isNaN(y) || isNaN(z)) {
throw new Error('Position coordinates must be numbers');
}
if (!this._canPlay) {
if (z <= 0.05) {
z = 0.05;
}
this._controlMesh.position = new BABYLON.Vector3(x, z, y);
}
this._controlMesh.computeWorldMatrix(true);
this._mesh.computeWorldMatrix(true);
return this._API.setStartingPosition(this, x, y, z);
};
DroneManager.prototype.setAcceleration = function (factor) {
DroneManager.prototype.setSpeed = function (speed) {
if (!this._canPlay) {
return;
}
if (isNaN(factor)){
throw new Error('Acceleration must be a number');
}
if (factor > this._maxAcceleration) {
factor = this._maxAcceleration;
if (isNaN(speed)) {
throw new Error('Speed must be a number');
}
this._acceleration = factor;
return this._API.setSpeed(this, speed);
};
DroneManager.prototype.setDirection = function (x, y, z) {
if (!this._canPlay) {
......@@ -212,8 +210,24 @@ var DroneManager = /** @class */ (function () {
if (isNaN(x) || isNaN(y) || isNaN(z)) {
throw new Error('Direction coordinates must be numbers');
}
// swap y and z axis so z axis represents altitude
this._direction = new BABYLON.Vector3(x, z, y).normalize();
};
//TODO rotation
DroneManager.prototype.setRotation = function (x, y, z) {
if (!this._canPlay) {
return;
}
return this._API.setRotation(this, x, y, z);
};
DroneManager.prototype.setRotationBy = function (x, y, z) {
if (!this._canPlay) {
return;
}
return this._API.setRotation(this, x, y, z);
};
/**
* Send a message to drones
* @param msg The message to send
......@@ -269,6 +283,7 @@ var DroneManager = /** @class */ (function () {
};
DroneManager.prototype.getCurrentPosition = function () {
if (this._controlMesh) {
// swap y and z axis so z axis represents altitude
return this._API.getCurrentPosition(
this._controlMesh.position.x,
this._controlMesh.position.z,
......@@ -281,7 +296,7 @@ var DroneManager = /** @class */ (function () {
if (!this._canPlay) {
return;
}
return this._API.setAltitude(altitude, this);
return this._API.setAltitude(this, altitude);
};
/**
* Make the drone loiter (circle with a set radius)
......@@ -299,7 +314,16 @@ var DroneManager = /** @class */ (function () {
return null;
};
DroneManager.prototype.getYaw = function () {
return 0;
return this._API.getYaw(this);
};
DroneManager.prototype.getSpeed = function () {
return this._speed;
};
DroneManager.prototype.getClimbRate = function () {
return this._API.getClimbRate(this);
};
DroneManager.prototype.getSinkRate = function () {
return this._API.getSinkRate();
};
DroneManager.prototype.triggerParachute = function () {
return this._API.triggerParachute(this);
......@@ -316,26 +340,27 @@ var DroneManager = /** @class */ (function () {
* @param checkpoint to be set
*/
DroneManager.prototype.setCheckpoint = function (checkpoint) {
//TODO
return checkpoint;
};
/**
* Function called on game start
*/
DroneManager.prototype.onStart = function () { return;};
DroneManager.prototype.onStart = function () { return; };
/**
* Function called on game update
* @param timestamp The tic value
*/
DroneManager.prototype.onUpdate = function () { return;};
DroneManager.prototype.onUpdate = function () { return; };
/**
* Function called when drone crashes
*/
DroneManager.prototype.onTouched = function () { return;};
DroneManager.prototype.onTouched = function () { return; };
/**
* Function called when a message is received
* @param msg The message
*/
DroneManager.prototype.onGetMsg = function () { return;};
DroneManager.prototype.onGetMsg = function () { return; };
return DroneManager;
}());
......@@ -367,7 +392,8 @@ var MapManager = /** @class */ (function () {
position = map.normalize(
map.longitudToX(initial_position.longitude, map_size),
map.latitudeToY(initial_position.latitude, map_size),
map_info);
map_info
);
map_info.initial_position = {
"x": position[0],
"y": position[1],
......@@ -498,11 +524,10 @@ var GameManager = /** @class */ (function () {
if (GAMEPARAMETERS.draw_flight_path) {
this._last_position_drawn = [];
this._trace_objects_per_drone = [];
header_list = ["timestamp;", "latitude;", "longitude;", "AMSL (m);",
"rel altitude (m);", "pitch (°);", "roll(°);",
"yaw(°);", "air speed (m/s);", "throttle(%);",
header_list = ["timestamp", "latitude", "longitude", "AMSL (m)",
"rel altitude (m)", "yaw(°)", "air speed (m/s)",
"climb rate(m/s)"];
for (drone = 0; drone < GAMEPARAMETERS.droneList.length; drone+=1) {
for (drone = 0; drone < GAMEPARAMETERS.droneList.length; drone += 1) {
this._flight_log[drone] = [];
this._flight_log[drone].push(header_list);
this._log_count[drone] = 0;
......@@ -523,7 +548,7 @@ var GameManager = /** @class */ (function () {
];
}
this.APIs_dict = {
DroneAaileFixeAPI: DroneAaileFixeAPI,
FixedWingDroneAPI: FixedWingDroneAPI,
DroneLogAPI: DroneLogAPI
};
}
......@@ -557,20 +582,25 @@ var GameManager = /** @class */ (function () {
if ((_this._canUpdate) && (_this.ongoing_update_promise === null) &&
(0 < _this.waiting_update_count)) {
_this.ongoing_update_promise = _this._update(
TIME_DELTA, (_this.waiting_update_count === 1))
.push(function () {
TIME_DELTA,
(_this.waiting_update_count === 1)
).push(function () {
_this.waiting_update_count -= 1;
_this.ongoing_update_promise = null;
triggerUpdateIfPossible();
})
.push(undefined, function(error) {
console.log("ERROR on update:", error);
console.log("rejecting finish_deferred promise...");
}).push(undefined, function (error) {
console.log("ERROR on Game Manager update:", error);
_this.finish_deferred.reject.bind(_this.finish_deferred);
});
}
}
try {
triggerUpdateIfPossible();
} catch (error) {
console.log("ERROR on Game Manager update:", error);
_this.finish_deferred.reject.bind(_this.finish_deferred);
throw error;
}
};
GameManager.prototype.delay = function (callback, millisecond) {
......@@ -582,7 +612,8 @@ var GameManager = /** @class */ (function () {
};
GameManager.prototype._checkDroneRules = function (drone) {
//TODO move this to API methods
//TODO move this to API methods.
//each type of drone should define its rules
if (drone.getCurrentPosition()) {
return drone.getCurrentPosition().z > 1;
}
......@@ -632,54 +663,69 @@ var GameManager = /** @class */ (function () {
GameManager.prototype._updateTimeAndLog =
function (delta_time) {
this._game_duration += delta_time;
var seconds = Math.floor(this._game_duration / 1000), drone,
drone_position, map_info, geo_coordinates, position_obj, material, color;
var color, drone_position, game_manager = this, geo_coordinates,
log_count, map_info, map_manager, material, position_obj,
seconds = Math.floor(this._game_duration / 1000), trace_objects;
if (GAMEPARAMETERS.log_drone_flight || GAMEPARAMETERS.draw_flight_path) {
for (drone = 0; drone < GAMEPARAMETERS.droneList.length; drone+=1) {
if (this._droneList[drone].can_play) {
drone_position = this._droneList[drone].position;
this._droneList.forEach(function (drone, index) {
if (drone.can_play) {
drone_position = drone.position;
if (GAMEPARAMETERS.log_drone_flight) {
map_info = this._mapManager.getMapInfo();
if (this._log_count[drone] === 0 ||
this._game_duration / this._log_count[drone] > 1) {
this._log_count[drone] += GAMEPARAMETERS.log_interval_time;
geo_coordinates = this._mapManager.convertToGeoCoordinates(
drone_position.x, drone_position.y, drone_position.z, map_info);
this._flight_log[drone].push(
[this._game_duration, geo_coordinates.x, geo_coordinates.y,
map_info.start_AMSL + drone_position.z, drone_position.z]);
map_manager = game_manager._mapManager;
map_info = map_manager.getMapInfo();
log_count = game_manager._log_count[index];
if (log_count === 0 ||
game_manager._game_duration / log_count > 1) {
log_count += GAMEPARAMETERS.log_interval_time;
geo_coordinates = map_manager.convertToGeoCoordinates(
drone_position.x,
drone_position.y,
drone_position.z,
map_info
);
game_manager._flight_log[index].push([
game_manager._game_duration, geo_coordinates.x,
geo_coordinates.y, map_info.start_AMSL + drone_position.z,
drone_position.z, drone.getYaw(), drone.getSpeed(),
drone.getClimbRate()
]);
}
}
if (GAMEPARAMETERS.draw_flight_path) {
//draw drone position every some seconds
if (seconds - this._last_position_drawn[drone] > 0.2) {
this._last_position_drawn[drone] = seconds;
position_obj = BABYLON.MeshBuilder.CreateBox("obs_" + seconds,
if (seconds - game_manager._last_position_drawn[index] > 0.2) {
game_manager._last_position_drawn[index] = seconds;
position_obj = BABYLON.MeshBuilder.CreateBox(
"obs_" + seconds,
{ size: 1 },
this._scene);
game_manager._scene
);
// swap y and z axis so z axis represents altitude
position_obj.position = new BABYLON.Vector3(drone_position.x,
drone_position.z,
drone_position.y);
position_obj.scaling = new BABYLON.Vector3(4, 4, 4);
material = new BABYLON.StandardMaterial(this._scene);
material = new BABYLON.StandardMaterial(game_manager._scene);
material.alpha = 1;
color = new BABYLON.Color3(255, 0, 0);
if (this._colors[drone]) {
color = this._colors[drone];
if (game_manager._colors[index]) {
color = game_manager._colors[index];
}
material.diffuseColor = color;
position_obj.material = material;
if (GAMEPARAMETERS.temp_flight_path) {
if (this._trace_objects_per_drone[drone].length === 10) {
this._trace_objects_per_drone[drone][0].dispose();
this._trace_objects_per_drone[drone].splice(0, 1);
}
this._trace_objects_per_drone[drone].push(position_obj);
trace_objects = game_manager._trace_objects_per_drone[index];
if (trace_objects.length === 10) {
trace_objects[0].dispose();
trace_objects.splice(0, 1);
}
trace_objects.push(position_obj);
}
}
}
}
});
}
};
......@@ -723,16 +769,27 @@ var GameManager = /** @class */ (function () {
audioEngine: false
});
this._scene = new BABYLON.Scene(this._engine);
this._scene.clearColor = new BABYLON.Color4(88/255,171/255,217/255,255/255);
this._scene.clearColor = new BABYLON.Color4(
88 / 255,
171 / 255,
217 / 255,
255 / 255
);
//removed for event handling
//this._engine.enableOfflineSupport = false;
//this._scene.collisionsEnabled = true;
// Lights
hemi_north = new BABYLON.HemisphericLight(
"hemiN", new BABYLON.Vector3(1, -1, 1), this._scene);
"hemiN",
new BABYLON.Vector3(1, -1, 1),
this._scene
);
hemi_north.intensity = 0.75;
hemi_south = new BABYLON.HemisphericLight(
"hemiS", new BABYLON.Vector3(-1, 1, -1), this._scene);
"hemiS",
new BABYLON.Vector3(-1, 1, -1),
this._scene
);
hemi_south.intensity = 0.75;
camera = new BABYLON.ArcRotateCamera("camera", 0, 1.25, 800,
BABYLON.Vector3.Zero(), this._scene);
......@@ -764,10 +821,13 @@ var GameManager = /** @class */ (function () {
var documentTmp = document, advancedTexture, count,
controlMesh, rect, label;
document = undefined;
advancedTexture = BABYLON.GUI.AdvancedDynamicTexture
.CreateFullscreenUI("UI", true, ctx._scene);
advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI(
"UI",
true,
ctx._scene
);
document = documentTmp;
for (count = 0; count < GAMEPARAMETERS.droneList.length; count+=1) {
for (count = 0; count < GAMEPARAMETERS.droneList.length; count += 1) {
controlMesh = ctx._droneList[count].infosMesh;
rect = new BABYLON.GUI.Rectangle();
rect.width = "10px";
......@@ -818,7 +878,9 @@ var GameManager = /** @class */ (function () {
promise_list = [];
_this._droneList.forEach(function (drone) {
drone._tick = 0;
promise_list.push(drone.internal_start());
promise_list.push(drone.internal_start(
_this._mapManager.getMapInfo().initial_position
));
});
return RSVP.all(promise_list);
})
......@@ -895,7 +957,7 @@ var GameManager = /** @class */ (function () {
code_eval = "let drone = new DroneManager(ctx._scene, " +
index + ', api);' +
"let droneMe = function(NativeDate, me, Math, window, DroneManager," +
" GameManager, DroneLogAPI, DroneAaileFixeAPI, BABYLON, " +
" GameManager, DroneLogAPI, FixedWingDroneAPI, BABYLON, " +
"GAMEPARAMETERS) {" +
"var start_time = (new Date(2070, 0, 0, 0, 0, 0, 0)).getTime();" +
"Date.now = function () {" +
......@@ -917,8 +979,7 @@ var GameManager = /** @class */ (function () {
/*jslint evil: true*/
try {
eval(code_eval);
}
catch (error) {
} catch (error) {
eval(base);
}
/*jslint evil: false*/
......@@ -941,11 +1002,14 @@ var GameManager = /** @class */ (function () {
if (collision_nb < max_collision) {
i -= 1;
}
}
else {
} else {
position_list.push(position);
api = new this.APIs_dict[drone_list[i].type](
this, drone_list[i], GAMEPARAMETERS, i);
this,
drone_list[i],
GAMEPARAMETERS,
i
);
spawnDrone(position.x, position.y, position.z, i,
drone_list[i], api);
}
......
......@@ -144,12 +144,6 @@
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
......@@ -195,7 +189,7 @@
</tuple>
<state>
<tuple>
<float>1662477480.35</float>
<float>1674834569.27</float>
<string>UTC</string>
</tuple>
</state>
......@@ -244,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1004.32349.9544.44100</string> </value>
<value> <string>1006.2016.60568.40294</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,70 +256,7 @@
</tuple>
<state>
<tuple>
<float>1670852474.69</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1662477378.07</float>
<float>1675086932.85</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -13,7 +13,7 @@
HEIGHT = 340,
LOGIC_FILE_LIST = [
'gadget_erp5_page_drone_simulator_logic.js',
'gadget_erp5_page_drone_simulator_droneaaailefixe.js',
'gadget_erp5_page_drone_simulator_fixedwingdrone.js',
'gadget_erp5_page_drone_simulator_dronelogfollower.js'
];
......
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1005.1310.34623.11861</string> </value>
<value> <string>1005.28725.31731.34065</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1671112620.77</float>
<float>1672757496.65</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -3,17 +3,25 @@
(function (window, rJS, domsugar, document, Blob) {
"use strict";
//Default values
//Default values - TODO: get them from the drone API
var SIMULATION_SPEED = 200,
SIMULATION_TIME = 1500,
MAX_SPEED = 7.5, //16.666667,
MAX_ACCELERATION = 1,
min_lat = 45.6364,
max_lat = 45.65,
min_lon = 14.2521,
max_lon = 14.2766,
map_height = 100,
start_AMSL = 595,
DEFAULT_SPEED = 16,
MAX_ACCELERATION = 6,
MAX_DECELERATION = 1,
MIN_SPEED = 12,
MAX_SPEED = 26,
MAX_ROLL = 35,
MIN_PITCH = -20,
MAX_PITCH = 25,
MAX_CLIMB_RATE = 8,
MAX_SINK_RATE = 3,
INITIAL_POSITION = {
"latitude": 45.6412,
"longitude": 14.2658,
......@@ -122,7 +130,7 @@
HEIGHT = 340,
LOGIC_FILE_LIST = [
'gadget_erp5_page_drone_simulator_logic.js',
'gadget_erp5_page_drone_simulator_droneaaailefixe.js',
'gadget_erp5_page_drone_simulator_fixedwingdrone.js',
'gadget_erp5_page_drone_simulator_dronelogfollower.js'
];
......@@ -131,6 +139,7 @@
// Acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("notifySubmitted", "notifySubmitted")
.allowPublicAcquisition('notifySubmit', function () {
return this.triggerSubmit();
......@@ -167,7 +176,7 @@
"editable": 1,
"key": "simulation_speed",
"hidden": 0,
"type": "StringField"
"type": "IntegerField"
},
"my_simulation_time": {
"description": "Duration of the simulation (in seconds)",
......@@ -178,29 +187,117 @@
"editable": 1,
"key": "simulation_time",
"hidden": 0,
"type": "StringField"
"type": "IntegerField"
},
"my_drone_min_speed": {
"description": "",
"title": "Drone min speed",
"default": MIN_SPEED,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_min_speed",
"hidden": 0,
"type": "IntegerField"
},
"my_drone_speed": {
"description": "",
"title": "Drone speed",
"default": MAX_SPEED,
"default": DEFAULT_SPEED,
"css_class": "",
"required": 1,
"required": 0,
"editable": 1,
"key": "drone_speed",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_drone_acceleration": {
"my_drone_max_speed": {
"description": "",
"title": "Drone Acceleration",
"title": "Drone max speed",
"default": MAX_SPEED,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_max_speed",
"hidden": 0,
"type": "IntegerField"
},
"my_drone_max_acceleration": {
"description": "",
"title": "Drone max Acceleration",
"default": MAX_ACCELERATION,
"css_class": "",
"required": 1,
"required": 0,
"editable": 1,
"key": "drone_max_acceleration",
"hidden": 0,
"type": "IntegerField"
},
"my_drone_max_deceleration": {
"description": "",
"title": "Drone max Deceleration",
"default": MAX_DECELERATION,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_acceleration",
"key": "drone_max_deceleration",
"hidden": 0,
"type": "StringField"
"type": "IntegerField"
},
"my_drone_max_roll": {
"description": "",
"title": "Drone max roll",
"default": MAX_ROLL,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_max_roll",
"hidden": 0,
"type": "FloatField"
},
"my_drone_min_pitch": {
"description": "",
"title": "Drone min pitch",
"default": MIN_PITCH,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_min_pitch",
"hidden": 0,
"type": "FloatField"
},
"my_drone_max_pitch": {
"description": "",
"title": "Drone max pitch",
"default": MAX_PITCH,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_max_pitch",
"hidden": 0,
"type": "FloatField"
},
"my_drone_max_sink_rate": {
"description": "",
"title": "Drone max sink rate",
"default": MAX_SINK_RATE,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_max_sink_rate",
"hidden": 0,
"type": "FloatField"
},
"my_drone_max_climb_rate": {
"description": "",
"title": "Drone max climb rate",
"default": MAX_CLIMB_RATE,
"css_class": "",
"required": 0,
"editable": 1,
"key": "drone_max_climb_rate",
"hidden": 0,
"type": "FloatField"
},
"my_minimum_latitud": {
"description": "",
......@@ -211,7 +308,7 @@
"editable": 1,
"key": "min_lat",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_maximum_latitud": {
"description": "",
......@@ -222,7 +319,7 @@
"editable": 1,
"key": "max_lat",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_minimum_longitud": {
"description": "",
......@@ -233,7 +330,7 @@
"editable": 1,
"key": "min_lon",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_maximum_longitud": {
"description": "",
......@@ -244,7 +341,7 @@
"editable": 1,
"key": "max_lon",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_start_AMSL": {
"description": "",
......@@ -255,7 +352,7 @@
"editable": 1,
"key": "start_AMSL",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_map_height": {
"description": "",
......@@ -266,7 +363,7 @@
"editable": 1,
"key": "map_height",
"hidden": 0,
"type": "StringField"
"type": "IntegerField"
},
"my_init_pos_lon": {
"description": "",
......@@ -277,7 +374,7 @@
"editable": 1,
"key": "init_pos_lon",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_init_pos_lat": {
"description": "",
......@@ -288,7 +385,7 @@
"editable": 1,
"key": "init_pos_lat",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_init_pos_z": {
"description": "",
......@@ -299,7 +396,7 @@
"editable": 1,
"key": "init_pos_z",
"hidden": 0,
"type": "StringField"
"type": "FloatField"
},
"my_number_of_drones": {
"description": "",
......@@ -310,7 +407,7 @@
"editable": 1,
"key": "number_of_drones",
"hidden": 0,
"type": "StringField"
"type": "IntegerField"
},
"my_script": {
"default": DEFAULT_SCRIPT_CONTENT,
......@@ -334,14 +431,17 @@
form_definition: {
group_list: [[
"left",
[["my_simulation_speed"], ["my_simulation_time"],
["my_drone_speed"], ["my_drone_acceleration"],
["my_number_of_drones"], ["my_map_height"], ["my_start_AMSL"]]
[["my_simulation_speed"], ["my_simulation_time"], ["my_number_of_drones"],
["my_minimum_latitud"], ["my_maximum_latitud"],
["my_minimum_longitud"], ["my_maximum_longitud"],
["my_init_pos_lat"], ["my_init_pos_lon"], ["my_init_pos_z"],
["my_map_height"]]
], [
"right",
[["my_minimum_latitud"], ["my_maximum_latitud"],
["my_minimum_longitud"], ["my_maximum_longitud"],
["my_init_pos_lat"], ["my_init_pos_lon"], ["my_init_pos_z"]]
[["my_start_AMSL"], ["my_drone_min_speed"], ["my_drone_speed"], ["my_drone_max_speed"],
["my_drone_max_acceleration"], ["my_drone_max_deceleration"],
["my_drone_max_roll"], ["my_drone_min_pitch"], ["my_drone_max_pitch"],
["my_drone_max_sink_rate"], ["my_drone_max_climb_rate"]]
], [
"bottom",
[["my_script"]]
......@@ -358,23 +458,31 @@
})
.declareJob('runGame', function runGame(options) {
var gadget = this, simulator, i,
var gadget = this, i,
fragment = gadget.element.querySelector('.simulator_div'),
game_parameters_json;
fragment = domsugar(gadget.element.querySelector('.simulator_div'),
[domsugar('div')]).firstElementChild;
DRONE_LIST = [];
for (i = 0; i < options.number_of_drones; i += 1) {
DRONE_LIST[i] = {"id": i, "type": "DroneAaileFixeAPI",
DRONE_LIST[i] = {"id": i, "type": "FixedWingDroneAPI",
"script_content": options.script};
}
game_parameters_json = {
"drone": {
"maxAcceleration": parseFloat(options.drone_acceleration),
"maxSpeed": parseFloat(options.drone_speed)
"maxAcceleration": parseInt(options.drone_max_acceleration, 10),
"maxDeceleration": parseInt(options.drone_max_deceleration, 10),
"minSpeed": parseInt(options.drone_min_speed, 10),
"speed": parseFloat(options.drone_speed),
"maxSpeed": parseInt(options.drone_max_speed, 10),
"maxRoll": parseFloat(options.drone_max_roll),
"minPitchAngle": parseFloat(options.drone_min_pitch),
"maxPitchAngle": parseFloat(options.drone_max_pitch),
"maxSinkRate": parseFloat(options.drone_max_sink_rate),
"maxClimbRate": parseFloat(options.drone_max_climb_rate)
},
"gameTime": parseFloat(options.simulation_time),
"simulation_speed": parseFloat(options.simulation_speed),
"gameTime": parseInt(options.simulation_time, 10),
"simulation_speed": parseInt(options.simulation_speed, 10),
"latency": {
"information": 0,
"communication": 0
......@@ -384,7 +492,7 @@
"max_lat": parseFloat(options.max_lat),
"min_lon": parseFloat(options.min_lon),
"max_lon": parseFloat(options.max_lon),
"height": parseFloat(options.map_height),
"height": parseInt(options.map_height, 10),
"start_AMSL": parseFloat(options.start_AMSL)
},
"initialPosition": {
......@@ -445,18 +553,20 @@
return form_gadget.getContent();
})
.push(function (result) {
var i = 0,
log_content,
blob,
a,
log,
div;
for (var key in result) {
var a, blob, div, key, log, log_content;
i = 0;
for (key in result) {
if (result.hasOwnProperty(key)) {
log_content = result[key].join('\n').replaceAll(",", ";");
blob = new Blob([log_content], {type: 'text/plain'});
a = domsugar('a', {
text: 'Download Simulation LOG ' + i,
download: 'simulation_log_' + i + '.txt',
download: 'simulation_log_' + i
+ '_speed_' + game_parameters_json.drone.speed
+ '_max-roll_' + game_parameters_json.drone.maxRoll
+ '_min-pitch_' + game_parameters_json.drone.minPitchAngle
+ '_max-pitch_' + game_parameters_json.drone.maxPitchAngle
+ '.txt',
href: window.URL.createObjectURL(blob)
});
log = domsugar('textarea', { value: log_content });
......@@ -465,8 +575,12 @@
a.href].join(':');
document.querySelector('.container').appendChild(div);
document.querySelector('.container').appendChild(log);
i++;
i += 1;
}
}
}, function (error) {
return gadget.notifySubmitted({message: "Error: " + error.message,
status: 'error'});
});
});
......
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1005.37533.1915.8379</string> </value>
<value> <string>1006.17791.59880.25668</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1673286304.13</float>
<float>1676033282.59</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -6,7 +6,7 @@ url_list = [
"gadget_erp5_page_drone_simulator_log_page.js",
"gadget_erp5_panel_drone_simulator.html",
"gadget_erp5_panel_drone_simulator.js",
"gadget_erp5_page_drone_simulator_droneaaailefixe.js",
"gadget_erp5_page_drone_simulator_fixedwingdrone.js",
"gadget_erp5_page_drone_simulator_dronelogfollower.js",
"drone.png",
"gadget_officejs_drone_simulator.json",
......
......@@ -52,10 +52,16 @@ function mainToWorker(evt) {
})
.push(function () {
return postMessage({'type': 'updated'});
}, function (error) {
console.log("ERROR:", error);
return postMessage({'type': 'error', 'error': error});
});
case 'event':
handleEvent(evt.data);
break;
return new RSVP.Queue(handleEvent(evt.data))
.push(undefined, function (error) {
console.log("ERROR:", error);
return postMessage({'type': 'error', 'error': error});
});
default:
throw new Error('Unsupported message ' + JSON.stringify(evt.data));
}
......
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1004.22590.57904.22954</string> </value>
<value> <string>1005.41422.8371.11861</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1671113075.12</float>
<float>1674224630.4</float>
<string>UTC</string>
</tuple>
</state>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment