Explorar el Código

add schedule hours to run

Tuan Nguyen hace 1 año
padre
commit
72829dba82
Se han modificado 3 ficheros con 133 adiciones y 7 borrados
  1. 79 5
      app.py
  2. 40 1
      static/main.js
  3. 14 1
      templates/index.html

+ 79 - 5
app.py

@@ -7,6 +7,7 @@ import threading
 import serial.tools.list_ports
 import serial.tools.list_ports
 import math
 import math
 import json
 import json
+from datetime import datetime
 
 
 app = Flask(__name__)
 app = Flask(__name__)
 
 
@@ -141,8 +142,56 @@ def send_command(command):
                 if response == "R":
                 if response == "R":
                     print("Command execution completed.")
                     print("Command execution completed.")
                     break
                     break
+                
+def wait_for_start_time(schedule_hours):
+    """
+    Keep checking every 30 seconds if the time is within the schedule to resume execution.
+    """
+    global pause_requested
+    start_time, end_time = schedule_hours
+
+    while pause_requested:
+        now = datetime.now().time()
+        if start_time <= now < end_time:
+            print("Resuming execution: Within schedule.")
+            pause_requested = False
+            with pause_condition:
+                pause_condition.notify_all()
+            break  # Exit the loop once resumed
+        else:
+            time.sleep(30)  # Wait for 30 seconds before checking again
+                
+# Function to check schedule based on start and end time
+def schedule_checker(schedule_hours):
+    """
+    Pauses/resumes execution based on a given time range.
+
+    Parameters:
+    - schedule_hours (tuple): (start_time, end_time) as `datetime.time` objects.
+    """
+    global pause_requested
+    if not schedule_hours:
+        return  # No scheduling restriction
 
 
-def run_theta_rho_file(file_path):
+    start_time, end_time = schedule_hours
+    now = datetime.now().time()  # Get the current time as `datetime.time`
+
+    # Check if we are currently within the scheduled time
+    if start_time <= now < end_time:
+        if pause_requested:
+            print("Starting execution: Within schedule.")
+        pause_requested = False  # Resume execution
+        with pause_condition:
+            pause_condition.notify_all()
+    else:
+        if not pause_requested:
+            print("Pausing execution: Outside schedule.")
+        pause_requested = True  # Pause execution
+        
+        # Start a background thread to periodically check for start time
+        threading.Thread(target=wait_for_start_time, args=(schedule_hours,), daemon=True).start()
+
+def run_theta_rho_file(file_path, schedule_hours=None):
     """Run a theta-rho file by sending data in optimized batches."""
     """Run a theta-rho file by sending data in optimized batches."""
     global stop_requested
     global stop_requested
     stop_requested = False
     stop_requested = False
@@ -171,6 +220,7 @@ def run_theta_rho_file(file_path):
             continue
             continue
         # Wait until Arduino is READY before sending the batch
         # Wait until Arduino is READY before sending the batch
         while True:
         while True:
+            schedule_checker(schedule_hours)
             with serial_lock:
             with serial_lock:
                 if ser.in_waiting > 0:
                 if ser.in_waiting > 0:
                     response = ser.readline().decode().strip()
                     response = ser.readline().decode().strip()
@@ -197,7 +247,8 @@ def run_theta_rho_files(
     pause_time=0,
     pause_time=0,
     clear_pattern=None,
     clear_pattern=None,
     run_mode="single",
     run_mode="single",
-    shuffle=False
+    shuffle=False,
+    schedule_hours=None
 ):
 ):
     """
     """
     Runs multiple .thr files in sequence with options for pausing, clearing, shuffling, and looping.
     Runs multiple .thr files in sequence with options for pausing, clearing, shuffling, and looping.
@@ -218,6 +269,7 @@ def run_theta_rho_files(
 
 
     while True:
     while True:
         for idx, path in enumerate(file_paths):
         for idx, path in enumerate(file_paths):
+            schedule_checker(schedule_hours)
             if stop_requested:
             if stop_requested:
                 print("Execution stopped before starting next pattern.")
                 print("Execution stopped before starting next pattern.")
                 return
                 return
@@ -230,12 +282,12 @@ def run_theta_rho_files(
                 # Determine the clear pattern to run
                 # Determine the clear pattern to run
                 clear_file_path = get_clear_pattern_file(clear_pattern)
                 clear_file_path = get_clear_pattern_file(clear_pattern)
                 print(f"Running clear pattern: {clear_file_path}")
                 print(f"Running clear pattern: {clear_file_path}")
-                run_theta_rho_file(clear_file_path)
+                run_theta_rho_file(clear_file_path, schedule_hours)
 
 
             if not stop_requested:
             if not stop_requested:
                 # Run the main pattern
                 # Run the main pattern
                 print(f"Running pattern {idx + 1} of {len(file_paths)}: {path}")
                 print(f"Running pattern {idx + 1} of {len(file_paths)}: {path}")
-                run_theta_rho_file(path)
+                run_theta_rho_file(path, schedule_hours)
 
 
             if idx < len(file_paths) -1:
             if idx < len(file_paths) -1:
                 if stop_requested:
                 if stop_requested:
@@ -687,6 +739,8 @@ def run_playlist():
         "clear_pattern": "random",         # Optional: "clear_in", "clear_out", "clear_sideway", or "random"
         "clear_pattern": "random",         # Optional: "clear_in", "clear_out", "clear_sideway", or "random"
         "run_mode": "single",              # 'single' or 'indefinite'
         "run_mode": "single",              # 'single' or 'indefinite'
         "shuffle": True                    # true or false
         "shuffle": True                    # true or false
+        "start_time": ""
+        "end_time": ""
     }
     }
     """
     """
     data = request.get_json()
     data = request.get_json()
@@ -700,6 +754,8 @@ def run_playlist():
     clear_pattern = data.get("clear_pattern", None)
     clear_pattern = data.get("clear_pattern", None)
     run_mode = data.get("run_mode", "single")  # Default to 'single' run
     run_mode = data.get("run_mode", "single")  # Default to 'single' run
     shuffle = data.get("shuffle", False)       # Default to no shuffle
     shuffle = data.get("shuffle", False)       # Default to no shuffle
+    start_time = data.get("start_time", None)
+    end_time = data.get("end_time", None)
 
 
     # Validate pause_time
     # Validate pause_time
     if not isinstance(pause_time, (int, float)) or pause_time < 0:
     if not isinstance(pause_time, (int, float)) or pause_time < 0:
@@ -717,6 +773,23 @@ def run_playlist():
     # Validate shuffle
     # Validate shuffle
     if not isinstance(shuffle, bool):
     if not isinstance(shuffle, bool):
         return jsonify({"success": False, "error": "'shuffle' must be a boolean value"}), 400
         return jsonify({"success": False, "error": "'shuffle' must be a boolean value"}), 400
+    
+    schedule_hours = None
+    if start_time and end_time:
+        try:
+            # Convert HH:MM to datetime.time objects
+            start_time_obj = datetime.strptime(start_time, "%H:%M").time()
+            end_time_obj = datetime.strptime(end_time, "%H:%M").time()
+
+            # Ensure start_time is before end_time
+            if start_time_obj >= end_time_obj:
+                return jsonify({"success": False, "error": "'start_time' must be earlier than 'end_time'"}), 400
+
+            # Create schedule tuple with full time
+            schedule_hours = (start_time_obj, end_time_obj)
+        except ValueError:
+            return jsonify({"success": False, "error": "Invalid time format. Use HH:MM (e.g., '09:30')"}), 400
+
 
 
     # Load playlists
     # Load playlists
     playlists = load_playlists()
     playlists = load_playlists()
@@ -739,7 +812,8 @@ def run_playlist():
                 'pause_time': pause_time,
                 'pause_time': pause_time,
                 'clear_pattern': clear_pattern,
                 'clear_pattern': clear_pattern,
                 'run_mode': run_mode,
                 'run_mode': run_mode,
-                'shuffle': shuffle
+                'shuffle': shuffle,
+                'schedule_hours': schedule_hours
             },
             },
             daemon=True  # Daemonize thread to exit with the main program
             daemon=True  # Daemonize thread to exit with the main program
         ).start()
         ).start()

+ 40 - 1
static/main.js

@@ -654,6 +654,10 @@ function openPlaylistEditor(playlistName) {
     loadPlaylist(playlistName);
     loadPlaylist(playlistName);
 }
 }
 
 
+function clearSchedule() {
+    document.getElementById("start_time").value = "";
+    document.getElementById("end_time").value = "";
+}
 
 
 // Function to run the selected playlist with specified parameters
 // Function to run the selected playlist with specified parameters
 async function runPlaylist() {
 async function runPlaylist() {
@@ -668,6 +672,8 @@ async function runPlaylist() {
     const clearPatternSelect = document.getElementById('clear_pattern').value;
     const clearPatternSelect = document.getElementById('clear_pattern').value;
     const runMode = document.querySelector('input[name="run_mode"]:checked').value;
     const runMode = document.querySelector('input[name="run_mode"]:checked').value;
     const shuffle = document.getElementById('shuffle_playlist').checked;
     const shuffle = document.getElementById('shuffle_playlist').checked;
+    const startTimeInput = document.getElementById('start_time').value.trim();
+    const endTimeInput = document.getElementById('end_time').value.trim();
 
 
     const pauseTime = parseFloat(pauseTimeInput);
     const pauseTime = parseFloat(pauseTimeInput);
     if (isNaN(pauseTime) || pauseTime < 0) {
     if (isNaN(pauseTime) || pauseTime < 0) {
@@ -675,6 +681,37 @@ async function runPlaylist() {
         return;
         return;
     }
     }
 
 
+    // Validate start and end time format and logic
+    let startTime = startTimeInput || null;
+    let endTime = endTimeInput || null;
+
+    // Ensure that if one time is filled, the other must be as well
+    if ((startTime && !endTime) || (!startTime && endTime)) {
+        logMessage("Both start and end times must be provided together or left blank.", LOG_TYPE.WARNING);
+        return;
+    }
+
+    // If both are provided, validate format and ensure start_time < end_time
+    if (startTime && endTime) {
+        try {
+            const startDateTime = new Date(`1970-01-01T${startTime}:00`);
+            const endDateTime = new Date(`1970-01-01T${endTime}:00`);
+
+            if (isNaN(startDateTime.getTime()) || isNaN(endDateTime.getTime())) {
+                logMessage("Invalid time format. Please use HH:MM format (e.g., 09:30).", LOG_TYPE.WARNING);
+                return;
+            }
+
+            if (startDateTime >= endDateTime) {
+                logMessage("Start time must be earlier than end time.", LOG_TYPE.WARNING);
+                return;
+            }
+        } catch (error) {
+            logMessage("Error parsing start or end time. Ensure correct HH:MM format.", LOG_TYPE.ERROR);
+            return;
+        }
+    }
+
     logMessage(`Running playlist: ${playlistName} with pause_time=${pauseTime}, clear_pattern=${clearPatternSelect}, run_mode=${runMode}, shuffle=${shuffle}.`);
     logMessage(`Running playlist: ${playlistName} with pause_time=${pauseTime}, clear_pattern=${clearPatternSelect}, run_mode=${runMode}, shuffle=${shuffle}.`);
 
 
     try {
     try {
@@ -686,7 +723,9 @@ async function runPlaylist() {
                 pause_time: pauseTime,
                 pause_time: pauseTime,
                 clear_pattern: clearPatternSelect,
                 clear_pattern: clearPatternSelect,
                 run_mode: runMode,
                 run_mode: runMode,
-                shuffle: shuffle
+                shuffle: shuffle,
+                start_time: startTimeInput,
+                end_time: endTimeInput
             })
             })
         });
         });
 
 

+ 14 - 1
templates/index.html

@@ -121,6 +121,19 @@
                         </select>
                         </select>
                     </div>
                     </div>
                 </div>
                 </div>
+                <h3>Schedule:</h3>
+                <div class="control-group">
+                    <div class="item column">
+                        <label for="start_time">Start time</label>
+                        <input type="time" id="start_time" min="00:00" max="24:00">
+                    </div>
+                    <div class="item column">
+                        <label for="end_time">End time</label>
+                        <input type="time" id="end_time" min="00:00" max="24:00">
+                    </div>
+                </div>
+                <button onclick="clearSchedule()" class="cancel">Clear</button>
+
             </div>
             </div>
         </section>
         </section>
 
 
@@ -177,7 +190,7 @@
             </div>
             </div>
 
 
             <div class="action-buttons">
             <div class="action-buttons">
-                <button onclick="stopExecution()" class="cancel">Stop Current Pattern</button>
+                <button onclick="stopExecution()" class="cancel">Stop Execution</button>
                 <button id="pausePlayButton" onclick="togglePausePlay()" class="cancel">⏸</button> <!-- Default: Pause Icon -->
                 <button id="pausePlayButton" onclick="togglePausePlay()" class="cancel">⏸</button> <!-- Default: Pause Icon -->
                 <button onclick="runClearIn()">Clear In</button>
                 <button onclick="runClearIn()">Clear In</button>
                 <button onclick="runClearOut()">Clear Out</button>
                 <button onclick="runClearOut()">Clear Out</button>