166 lines
5.2 KiB
Python
Executable File
166 lines
5.2 KiB
Python
Executable File
'''
|
|
__ __ _
|
|
| \/ | __ _ ___| |_ ___ _ __
|
|
| |\/| |/ _` / __| __/ _ \ '__|
|
|
| | | | (_| \__ \ || __/ |
|
|
|_| |_|\__,_|___/\__\___|_|
|
|
|
|
Master Python script that will trigger other scripts at every chosen time pace
|
|
This script is triggered as a systemd service used as an alternative to cronjobs
|
|
|
|
Attention:
|
|
to do -> prevent SARA R4 Script to run again if it's taking more than 60 secs to finish (using a lock file ??)
|
|
|
|
First time: need to create the service file
|
|
|
|
--> sudo nano /etc/systemd/system/master_nebuleair.service
|
|
|
|
⬇️
|
|
[Unit]
|
|
Description=Master manager for the Python loop scripts
|
|
After=network.target
|
|
|
|
[Service]
|
|
ExecStart=/usr/bin/python3 /var/www/nebuleair_pro_4g/master.py
|
|
Restart=always
|
|
User=root
|
|
WorkingDirectory=/var/www/nebuleair_pro_4g
|
|
StandardOutput=append:/var/www/nebuleair_pro_4g/logs/master.log
|
|
StandardError=append:/var/www/nebuleair_pro_4g/logs/master_errors.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
⬆️
|
|
|
|
Reload systemd (first time after creating the service):
|
|
sudo systemctl daemon-reload
|
|
|
|
Enable (once), start (once and after stopping) and restart (after modification)systemd:
|
|
sudo systemctl enable master_nebuleair.service
|
|
sudo systemctl start master_nebuleair.service
|
|
sudo systemctl restart master_nebuleair.service
|
|
|
|
Check the service status:
|
|
sudo systemctl status master_nebuleair.service
|
|
|
|
|
|
Specific scripts can be disabled with config.json
|
|
Exemple: stop gathering data from NPM
|
|
Exemple: stop sending data with SARA R4
|
|
|
|
'''
|
|
import time
|
|
import threading
|
|
import subprocess
|
|
import os
|
|
import sqlite3
|
|
|
|
|
|
# Base directory where scripts are stored
|
|
SCRIPT_DIR = "/var/www/nebuleair_pro_4g/"
|
|
DB_PATH = "/var/www/nebuleair_pro_4g/sqlite/sensors.db"
|
|
|
|
# Lock file path for SARA script
|
|
SARA_LOCK_FILE = "/var/www/nebuleair_pro_4g/sara_script.lock"
|
|
|
|
|
|
def is_script_enabled(script_name):
|
|
"""Check if a script is enabled in the database."""
|
|
try:
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute(
|
|
"SELECT enabled FROM config_scripts_table WHERE script_path = ?",
|
|
(script_name,)
|
|
)
|
|
|
|
result = cursor.fetchone()
|
|
conn.close()
|
|
|
|
if result is None:
|
|
return True # Default to enabled if not found in database
|
|
|
|
return bool(result[0])
|
|
except Exception:
|
|
# If any database error occurs, default to enabled
|
|
return True
|
|
|
|
|
|
def create_lock_file():
|
|
"""Create a lock file for the SARA script."""
|
|
with open(SARA_LOCK_FILE, 'w') as f:
|
|
f.write(str(int(time.time())))
|
|
|
|
|
|
def remove_lock_file():
|
|
"""Remove the SARA script lock file."""
|
|
if os.path.exists(SARA_LOCK_FILE):
|
|
os.remove(SARA_LOCK_FILE)
|
|
|
|
|
|
def is_script_locked():
|
|
"""Check if the SARA script is currently locked."""
|
|
if not os.path.exists(SARA_LOCK_FILE):
|
|
return False
|
|
|
|
# Check if lock is older than 60 seconds (stale)
|
|
with open(SARA_LOCK_FILE, 'r') as f:
|
|
try:
|
|
lock_time = int(f.read().strip())
|
|
if time.time() - lock_time > 60:
|
|
# Lock is stale, remove it
|
|
remove_lock_file()
|
|
return False
|
|
except ValueError:
|
|
# Invalid lock file content
|
|
remove_lock_file()
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def run_script(script_name, interval, delay=0):
|
|
"""Run a script in a synchronized loop with an optional start delay."""
|
|
script_path = os.path.join(SCRIPT_DIR, script_name) # Build full path
|
|
next_run = time.monotonic() + delay # Apply the initial delay
|
|
|
|
while True:
|
|
if is_script_enabled(script_name):
|
|
# Special handling for SARA script to prevent concurrent runs
|
|
if script_name == "loop/SARA_send_data_v2.py":
|
|
if not is_script_locked():
|
|
create_lock_file()
|
|
try:
|
|
subprocess.run(["python3", script_path], timeout=200)
|
|
finally:
|
|
remove_lock_file()
|
|
else:
|
|
# Run other scripts normally
|
|
subprocess.run(["python3", script_path])
|
|
|
|
# Wait until the next exact interval
|
|
next_run += interval
|
|
sleep_time = max(0, next_run - time.monotonic()) # Prevent negative sleep times
|
|
time.sleep(sleep_time)
|
|
|
|
|
|
# Define scripts and their execution intervals (seconds)
|
|
SCRIPTS = [
|
|
# Format: (script_name, interval_in_seconds, start_delay_in_seconds)
|
|
("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s
|
|
("envea/read_value_v2.py", 10, 0), # Get Envea data every 10s
|
|
("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 1s delay
|
|
("BME280/get_data_v2.py", 120, 0), # Get BME280 data every 120 seconds
|
|
("MPPT/read.py", 120, 0), # Get MPPT data every 120 seconds
|
|
("sqlite/flush_old_data.py", 86400, 0) # Flush old data inside db every day
|
|
]
|
|
|
|
# Start threads for scripts
|
|
for script_name, interval, delay in SCRIPTS:
|
|
thread = threading.Thread(target=run_script, args=(script_name, interval, delay), daemon=True)
|
|
thread.start()
|
|
|
|
# Keep the main script running
|
|
while True:
|
|
time.sleep(1) |