From 2f7689d1f20b348f446b2c1b357769788f44dd81 Mon Sep 17 00:00:00 2001
From: Leo-Paul Geneau <leo-paul.geneau@nexedi.com>
Date: Fri, 23 Feb 2024 16:24:38 +0000
Subject: [PATCH] erp5_officejs_drone_simulator: add max_command_frequency

See merge request !1894

* set _game_duration as an unix epoch
* provide timestamp to onStart function
* add max_command_frequency, if direction commands (setTargetCoordinates
  and loiter) are send more often than max frenquency the drone crashes
---
 .../drone_capture_flag_enemydrone_js.js       |  5 ++++-
 .../drone_capture_flag_enemydrone_js.xml      |  4 ++--
 .../drone_capture_flag_fixedwingdrone_js.js   |  7 ++++++
 .../drone_capture_flag_fixedwingdrone_js.xml  |  4 ++--
 .../drone_capture_flag_logic_js.js            | 18 ++++++++++-----
 .../drone_capture_flag_logic_js.xml           |  4 ++--
 .../ojs_drone_capture_flag_API_page_html.html | 18 +++++++++++----
 .../ojs_drone_capture_flag_API_page_html.xml  |  4 ++--
 .../ojs_drone_capture_flag_script_page_js.xml |  4 ++--
 .../test_capture_drone_flight_js.js           | 10 +++++----
 .../test_capture_drone_flight_js.xml          |  4 ++--
 .../drone_simulator_dronelogfollower_js.js    |  3 +++
 .../drone_simulator_dronelogfollower_js.xml   |  4 ++--
 .../drone_simulator_fixedwingdrone_js.js      |  7 ++++++
 .../drone_simulator_fixedwingdrone_js.xml     |  4 ++--
 .../drone_simulator_logic_js.js               | 18 ++++++++++-----
 .../drone_simulator_logic_js.xml              |  4 ++--
 .../ojs_drone_simulator_script_page_js.js     | 22 +++++++++++++++----
 .../ojs_drone_simulator_script_page_js.xml    |  4 ++--
 .../test_drone_simulator_flight_js.js         | 11 ++++++----
 .../test_drone_simulator_flight_js.xml        |  4 ++--
 21 files changed, 114 insertions(+), 49 deletions(-)

diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.js b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.js
index 33f2f81efd..7f6dc1c70c 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.js
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.js
@@ -241,7 +241,7 @@ var EnemyDroneAPI = /** @class */ (function () {
       '  return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2 + (a.z - b.z) ** 2);\n' +
       '}\n' +
       '\n' +
-      'me.onStart = function () {\n' +
+      'me.onStart = function (timestamp) {\n' +
       '  me.setDirection(0,0,0);\n' +
       '  return;\n' +
       '\n' +
@@ -280,6 +280,9 @@ var EnemyDroneAPI = /** @class */ (function () {
   EnemyDroneAPI.prototype.getMaxAcceleration = function () {
     return this._flight_parameters.drone.maxAcceleration;
   };
+  EnemyDroneAPI.prototype.getMaxCommandFrequency = function () {
+    return Infinity;
+  };
   EnemyDroneAPI.prototype.land = function (drone) {
     var drone_pos = drone.getCurrentPosition();
     drone.setTargetCoordinates(drone_pos.latitude, drone_pos.longitude, 0);
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.xml b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.xml
index 6480a27e3d..a55c62a0b8 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.xml
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_enemydrone_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.56714.1017.16076</string> </value>
+                      <value> <string>1014.60631.26636.59528</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1709221104.19</float>
+                              <float>1709288499.16</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.js b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.js
index 6936906d50..4dceb851bf 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.js
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.js
@@ -88,6 +88,10 @@ var FixedWingDroneAPI = /** @class */ (function () {
     if (drone._maxClimbRate > drone._maxSpeed) {
       throw new Error('max climb rate cannot be superior to max speed');
     }
+    drone._maxCommandFrequency = this.getMaxCommandFrequency();
+    if (drone._maxCommandFrequency <= 0) {
+      throw new Error('max command frequence must be superior to 0');
+    }
     return;
   };
   /*
@@ -450,6 +454,9 @@ var FixedWingDroneAPI = /** @class */ (function () {
   FixedWingDroneAPI.prototype.getMaxClimbRate = function () {
     return this._flight_parameters.drone.maxClimbRate;
   };
+  FixedWingDroneAPI.prototype.getMaxCommandFrequency = function () {
+    return this._flight_parameters.drone.maxCommandFrequency;
+  };
   FixedWingDroneAPI.prototype.getYawVelocity = function (drone) {
     return 360 * EARTH_GRAVITY *
       Math.tan(this._toRad(this.getMaxRollAngle())) /
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.xml b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.xml
index eb425b0fa3..0f0c5eef60 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.xml
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_fixedwingdrone_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.56746.48697.2713</string> </value>
+                      <value> <string>1014.60714.57448.36454</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1709223091.4</float>
+                              <float>1709288420.35</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.js b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.js
index 9979463984..8c8a09fbe8 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.js
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.js
@@ -25,6 +25,8 @@ var DroneManager = /** @class */ (function () {
     this._maxRollAngle = 0;
     this._maxSinkRate = 0;
     this._maxClimbRate = 0;
+    this._maxCommandFrequency = 0;
+    this._last_command_timestamp = 0;
     this._speed = 0;
     this._acceleration = 0;
     this._direction = new BABYLON.Vector3(0, 0, 1); // North
@@ -138,7 +140,7 @@ var DroneManager = /** @class */ (function () {
     this._canPlay = true;
     this._canCommunicate = true;
     try {
-      return this.onStart();
+      return this.onStart(this._API._gameManager._game_duration);
     } catch (error) {
       console.warn('Drone crashed on start due to error:', error);
       this._internal_crash(error);
@@ -156,16 +158,22 @@ var DroneManager = /** @class */ (function () {
       if (!this._canPlay || !this.isReadyToFly()) {
         return;
       }
+      if (this._API._gameManager._game_duration - this._last_command_timestamp
+            < 1000 / this._API.getMaxCommandFrequency()) {
+        this._internal_crash(new Error('Minimum interval between commands is ' +
+            1000 / this._API.getMaxCommandFrequency() + ' milliseconds'));
+      }
       //each drone API process coordinates on its needs
       //e.g. fixedwing drone converts real geo-coordinates to virtual x-y
       this._targetCoordinates =
         this._API.processCoordinates(latitude, longitude, altitude);
-      return this._API.internal_setTargetCoordinates(
+      this._API.internal_setTargetCoordinates(
         this,
         this._targetCoordinates,
         speed,
         radius
       );
+      this._last_command_timestamp = this._API._gameManager._game_duration;
     };
   /**
    * Returns the list of things a drone "sees"
@@ -373,7 +381,7 @@ var DroneManager = /** @class */ (function () {
   /**
    * Function called on game start
    */
-  DroneManager.prototype.onStart = function () { return; };
+  DroneManager.prototype.onStart = function (timestamp) { return; };
   /**
    * Function called on game update
    * @param timestamp The tic value
@@ -1219,8 +1227,8 @@ var GameManager = /** @class */ (function () {
     _this.ongoing_update_promise = null;
     _this.finish_deferred = RSVP.defer();
     console.log("Simulation started.");
-    this._game_duration = 0;
-    this._totalTime = GAMEPARAMETERS.gameTime;
+    this._game_duration = Date.now();
+    this._totalTime = GAMEPARAMETERS.gameTime + this._game_duration;
 
     return new RSVP.Queue()
       .push(function () {
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.xml b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.xml
index e3abd85ab8..321cfa6a56 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.xml
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/drone_capture_flag_logic_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.56745.33504.26197</string> </value>
+                      <value> <string>1014.60733.7318.44953</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1709223446.72</float>
+                              <float>1709546292.39</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.html b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.html
index a06476e0ab..766f2e9eed 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.html
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.html
@@ -153,14 +153,24 @@
       <h3>Functions called by game on event</h3>
 
       <!-- Start -->
-      <h4 class="item-name" id="start"><span>onStart</span><span>: void</span></h4>
+      <h4 class="item-name" id="start"><span>onStart</span><span>: timestamp</span></h4>
       <p class="item-descr">Function called on game start.</p>
 
+     <div>
+        <h5 class="item-param-1">Param</h5>
+        <h5 class="item-param-2">Description</h5>
+      </div>
+
+      <div>
+        <p class="item-param-1">timestamp: Integer</p>
+        <p class="item-param-2">Timestamp in milliseconds from UNIX epoch</p>
+      </div>
+
       <div>
         <h5 class="item-param-1">Example</h5>
       </div>
 
-      <p class="item-param-1">me.onStart = function() {<br>
+      <p class="item-param-1">me.onStart = function(timestamp) {<br>
         &nbsp;&nbsp;//one time execution code<br>
         }
       </p>
@@ -168,7 +178,7 @@
       <div class="line"></div>
 
       <!-- Update -->
-      <h4 class="item-name" id="update"><span>onUpdate</span><span>: void</span></h4>
+      <h4 class="item-name" id="update"><span>onUpdate</span><span>: timestamp</span></h4>
       <p class="item-descr">Function called on game update, 60 times / second. <br></p>
 
       <div>
@@ -183,7 +193,7 @@
 
       <h5 class="item-param-1">Example</h5>
 
-      <p class="item-example">me.onUpdate = function() {<br>
+      <p class="item-example">me.onUpdate = function(timestamp) {<br>
         &nbsp;&nbsp;//code executed 60 times per second<br>
         }
       </p>
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.xml b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.xml
index d91963e8e6..d73940a695 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.xml
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_API_page_html.xml
@@ -244,7 +244,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.46767.47211.3123</string> </value>
+                      <value> <string>1014.48209.18178.31351</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -264,7 +264,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1708688564.0</float>
+                              <float>1708710825.64</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_script_page_js.xml b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_script_page_js.xml
index bcb7dc77b0..bf210aaa63 100644
--- a/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_script_page_js.xml
+++ b/bt5/erp5_officejs_drone_capture_flag/PathTemplateItem/web_page_module/ojs_drone_capture_flag_script_page_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.46368.9372.48435</string> </value>
+                      <value> <string>1014.48194.32365.65399</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1708616910.25</float>
+                              <float>1708709921.07</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.js b/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.js
index 927fd5fd81..56d80666ba 100644
--- a/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.js
+++ b/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.js
@@ -52,10 +52,11 @@
       '  assert(coord1.altitude, coord2.altitude, "Altitude")\n' +
       '}\n' +
       '\n' +
-      'me.onStart = function () {\n' +
+      'me.onStart = function (timestamp) {\n' +
       '  assert(me.getSpeed(), ' + DEFAULT_SPEED + ', "Initial speed");\n' +
       '  assert(me.getYaw(), 0, "Yaw angle")\n' +
       '  me.initialPosition = me.getCurrentPosition();\n' +
+      '  me.start_time = timestamp;\n' +
       '  me.setTargetCoordinates(\n' +
       '    me.initialPosition.latitude + 0.01,\n' +
       '    me.initialPosition.longitude,\n' +
@@ -72,9 +73,10 @@
       '    me.getCurrentPosition().latitude,\n' +
       '    me.getCurrentPosition().longitude\n' +
       '  ).toFixed(8),\n' +
-      '    time_interval = 1000 / 60,\n' +
-      '    expectedDistance = (me.getSpeed() * time_interval / 1000).toFixed(8);\n' +
-      '    assert(timestamp, Math.floor(time_interval), "Timestamp");\n' +
+      '    time_interval = timestamp - me.start_time,\n' +
+      '    expected_interval = 1000 / 60,\n' +
+      '    expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' +
+      '    assert(time_interval, Math.floor(expected_interval), "Timestamp");\n' +
       '    assert(realDistance, expectedDistance, "Distance");\n' +
       '  current_position.latitude = current_position.latitude.toFixed(7);\n' +
       '  compare(current_position, {\n' +
diff --git a/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.xml b/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.xml
index 48441e156f..90c149d586 100644
--- a/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.xml
+++ b/bt5/erp5_officejs_drone_capture_flag_test/PathTemplateItem/web_page_module/test_capture_drone_flight_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.55287.25371.8430</string> </value>
+                      <value> <string>1014.60680.20078.34286</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1709136147.57</float>
+                              <float>1709286290.59</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.js b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.js
index 1b0f795d16..bbbf9ca57c 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.js
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.js
@@ -210,6 +210,9 @@ var DroneLogAPI = /** @class */ (function () {
   DroneLogAPI.prototype.isReadyToFly = function () {
     return true;
   };
+  DroneLogAPI.prototype.getMaxCommandFrequency = function () {
+    return Infinity;
+  };
 
   return DroneLogAPI;
 }());
\ No newline at end of file
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.xml b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.xml
index 4ce9debda0..064d676ca7 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.xml
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_dronelogfollower_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.12050.13065.34372</string> </value>
+                      <value> <string>1014.60631.26636.59528</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1706542267.28</float>
+                              <float>1709288518.39</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.js b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.js
index 9ccb14289d..d527034c19 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.js
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.js
@@ -92,6 +92,10 @@ var FixedWingDroneAPI = /** @class */ (function () {
     if (drone._maxClimbRate > drone._maxSpeed) {
       throw new Error('max climb rate cannot be superior to max speed');
     }
+    drone._maxCommandFrequency = this.getMaxCommandFrequency();
+    if (drone._maxCommandFrequency <= 0) {
+      throw new Error('max command frequence must be superior to 0');
+    }
     return;
   };
   /*
@@ -408,6 +412,9 @@ var FixedWingDroneAPI = /** @class */ (function () {
   FixedWingDroneAPI.prototype.getMaxClimbRate = function () {
     return this._flight_parameters.drone.maxClimbRate;
   };
+  FixedWingDroneAPI.prototype.getMaxCommandFrequency = function () {
+    return this._flight_parameters.drone.maxCommandFrequency;
+  };
   FixedWingDroneAPI.prototype.getYawVelocity = function (drone) {
     return 360 * EARTH_GRAVITY
       * Math.tan(this._toRad(this.getMaxRollAngle()))
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.xml b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.xml
index dd42f3e953..cce936288b 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.xml
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_fixedwingdrone_js.xml
@@ -240,7 +240,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.12067.27492.45397</string> </value>
+                      <value> <string>1014.60724.24572.64853</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -260,7 +260,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1706542739.07</float>
+                              <float>1709289024.77</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.js b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.js
index 0f8d9d078c..ad7c91b0b8 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.js
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.js
@@ -25,6 +25,8 @@ var DroneManager = /** @class */ (function () {
     this._maxRollAngle = 0;
     this._maxSinkRate = 0;
     this._maxClimbRate = 0;
+    this._maxCommandFrequency = 0;
+    this._last_command_timestamp = 0;
     this._speed = 0;
     this._acceleration = 0;
     this._direction = new BABYLON.Vector3(0, 0, 1); // North
@@ -124,7 +126,7 @@ var DroneManager = /** @class */ (function () {
     this._canCommunicate = true;
     this._targetCoordinates = initial_position;
     try {
-      return this.onStart();
+      return this.onStart(this._API._gameManager._game_duration);
     } catch (error) {
       console.warn('Drone crashed on start due to error:', error);
       this._internal_crash(error);
@@ -142,15 +144,21 @@ var DroneManager = /** @class */ (function () {
       if (!this._canPlay || !this.isReadyToFly()) {
         return;
       }
+      if (this._API._gameManager._game_duration - this._last_command_timestamp
+            < 1000 / this._API.getMaxCommandFrequency()) {
+        this._internal_crash(new Error('Minimum interval between commands is ' +
+            1000 / this._API.getMaxCommandFrequency() + ' milliseconds'));
+      }
       //convert real geo-coordinates to virtual x-y coordinates
       this._targetCoordinates =
         this._API.processCoordinates(latitude, longitude, altitude);
-      return this._API.internal_setTargetCoordinates(
+      this._API.internal_setTargetCoordinates(
         this,
         this._targetCoordinates,
         speed,
         radius
       );
+      this._last_command_timestamp = this._API._gameManager._game_duration;
     };
   DroneManager.prototype.internal_update = function (delta_time) {
     var context = this;
@@ -328,7 +336,7 @@ var DroneManager = /** @class */ (function () {
   /**
    * Function called on game start
    */
-  DroneManager.prototype.onStart = function () { return; };
+  DroneManager.prototype.onStart = function (timestamp) { return; };
   /**
    * Function called on game update
    * @param timestamp The tic value
@@ -895,8 +903,8 @@ var GameManager = /** @class */ (function () {
     _this.ongoing_update_promise = null;
     _this.finish_deferred = RSVP.defer();
     console.log("Simulation started.");
-    this._game_duration = 0;
-    this._totalTime = GAMEPARAMETERS.gameTime;
+    this._game_duration = Date.now();
+    this._totalTime = GAMEPARAMETERS.gameTime + this._game_duration;
 
     return new RSVP.Queue()
       .push(function () {
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.xml b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.xml
index 6346cd0627..c466c26a9c 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.xml
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/drone_simulator_logic_js.xml
@@ -240,7 +240,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.12072.47950.5358</string> </value>
+                      <value> <string>1014.60716.53618.32563</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -260,7 +260,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1706542965.82</float>
+                              <float>1709288704.17</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.js b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.js
index f7c0e864e8..b30fef6317 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.js
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.js
@@ -22,6 +22,7 @@
     MAX_PITCH = 25,
     MAX_CLIMB_RATE = 8,
     MAX_SINK_RATE = 3,
+    MAX_COMMAND_FREQUENCY = 2,
     INITIAL_POSITION = {
       "latitude": 45.6412,
       "longitude": 14.2658,
@@ -87,12 +88,12 @@
       '  return 2 * R * Math.asin(Math.sqrt(h));\n' +
       '}\n' +
       '\n' +
-      'me.onStart = function () {\n' +
+      'me.onStart = function (timestamp) {\n' +
       '  me.direction_set = false;\n' +
       '  me.next_checkpoint = 0;\n' +
       '};\n' +
       '\n' +
-      'me.onUpdate = function (timestamp) {' +
+      'me.onUpdate = function (timestamp) {\n' +
       '  if (!me.direction_set) {\n' +
       '    if (me.next_checkpoint < CHECKPOINT_LIST.length) {\n' +
       '      me.setTargetCoordinates(\n' +
@@ -300,6 +301,17 @@
                   "hidden": 0,
                   "type": "FloatField"
                 },
+                "my_drone_max_command_frequency": {
+                  "description": "",
+                  "title": "Drone max command frequency",
+                  "default": MAX_COMMAND_FREQUENCY,
+                  "css_class": "",
+                  "required": 1,
+                  "editable": 1,
+                  "key": "drone_max_command_frequency",
+                  "hidden": 0,
+                  "type": "FloatField"
+                },
                 "my_minimum_latitud": {
                   "description": "",
                   "title": "Minimum latitude",
@@ -442,7 +454,8 @@
                 [["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"]]
+                  ["my_drone_max_pitch"], ["my_drone_max_sink_rate"], ["my_drone_max_climb_rate"],
+                  ["my_drone_max_command_frequency"]]
               ], [
                 "bottom",
                 [["my_script"]]
@@ -480,7 +493,8 @@
           "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)
+          "maxClimbRate": parseFloat(options.drone_max_climb_rate),
+          "maxCommandFrequency": parseFloat(options.drone_max_command_frequency)
         },
         "gameTime": parseInt(options.simulation_time, 10),
         "simulation_speed": parseInt(options.simulation_speed, 10),
diff --git a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.xml b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.xml
index d163e02a8e..edb4840b61 100644
--- a/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.xml
+++ b/bt5/erp5_officejs_drone_simulator/PathTemplateItem/web_page_module/ojs_drone_simulator_script_page_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.11755.64575.41864</string> </value>
+                      <value> <string>1014.60725.60577.24917</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1706525997.75</float>
+                              <float>1709289017.41</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
diff --git a/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.js b/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.js
index f03c801511..0a6343df9c 100644
--- a/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.js
+++ b/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.js
@@ -56,15 +56,16 @@
       '  assert(coord1.altitude, coord2.altitude, "Altitude")\n' +
       '}\n' +
       '\n' +
-      'me.onStart = function () {\n' +
+      'me.onStart = function (timestamp) {\n' +
       '  assert(me.getSpeed(), ' + DEFAULT_SPEED + ', "Initial speed");\n' +
       '  assert(me.getYaw(), 0, "Yaw angle")\n' +
       '  me.initialPosition = me.getCurrentPosition();\n' +
+      '  me.start_time = timestamp;\n' +
       '  me.setTargetCoordinates(\n' +
       '    me.initialPosition.latitude + 0.01,\n' +
       '    me.initialPosition.longitude,\n' +
       '    me.getAltitudeAbs(),\n' +
-      '    '+ DEFAULT_SPEED + '\n' +
+      '    ' + DEFAULT_SPEED + '\n' +
       '  );\n' +
       '};\n' +
       '\n' +
@@ -76,8 +77,10 @@
       '    me.getCurrentPosition().latitude,\n' +
       '    me.getCurrentPosition().longitude\n' +
       '  ).toFixed(8),\n' +
-      '    expectedDistance = (me.getSpeed() * timestamp / 1000).toFixed(8);\n' +
-      '    assert(timestamp, 1000 / 60, "Timestamp");\n' +
+      '    time_interval = timestamp - me.start_time,\n' +
+      '    expected_interval = 1000 / 60,\n' +
+      '    expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' +
+      '    assert(time_interval.toFixed(4), expected_interval.toFixed(4), "Timestamp");\n' +
       '    assert(realDistance, expectedDistance, "Distance");\n' +
       '  current_position.latitude = current_position.latitude.toFixed(7);\n' +
       '  compare(current_position, {\n' +
diff --git a/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.xml b/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.xml
index 3fd5168ffd..d759a180f1 100644
--- a/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.xml
+++ b/bt5/erp5_officejs_drone_simulator_test/PathTemplateItem/web_page_module/test_drone_simulator_flight_js.xml
@@ -246,7 +246,7 @@
                   </item>
                   <item>
                       <key> <string>serial</string> </key>
-                      <value> <string>1014.55287.25371.8430</string> </value>
+                      <value> <string>1014.60681.20667.37171</string> </value>
                   </item>
                   <item>
                       <key> <string>state</string> </key>
@@ -266,7 +266,7 @@
                           </tuple>
                           <state>
                             <tuple>
-                              <float>1709136309.62</float>
+                              <float>1709286436.53</float>
                               <string>UTC</string>
                             </tuple>
                           </state>
-- 
2.30.9