csv logging added
This commit is contained in:
parent
87f0a7cc8c
commit
2e2792c913
@ -59,6 +59,8 @@ class RSICommandLineInterface:
|
|||||||
self.adjust_speed(parts[1], parts[2])
|
self.adjust_speed(parts[1], parts[2])
|
||||||
elif cmd == "override" and len(parts) == 2:
|
elif cmd == "override" and len(parts) == 2:
|
||||||
self.override_safety(parts[1])
|
self.override_safety(parts[1])
|
||||||
|
elif cmd == "log" and len(parts) >= 2:
|
||||||
|
self.handle_logging_command(parts)
|
||||||
elif cmd == "exit":
|
elif cmd == "exit":
|
||||||
self.stop_rsi()
|
self.stop_rsi()
|
||||||
self.running = False
|
self.running = False
|
||||||
@ -161,6 +163,22 @@ class RSICommandLineInterface:
|
|||||||
"""Override safety limits."""
|
"""Override safety limits."""
|
||||||
print(f"⚠️ Overriding safety limit: {limit}")
|
print(f"⚠️ Overriding safety limit: {limit}")
|
||||||
|
|
||||||
|
def handle_logging_command(self, parts):
|
||||||
|
"""Handles logging-related commands."""
|
||||||
|
subcmd = parts[1]
|
||||||
|
if subcmd == "start" and len(parts) == 3:
|
||||||
|
filename = parts[2]
|
||||||
|
self.client.start_logging(filename)
|
||||||
|
print(f"✅ Logging started: {filename}")
|
||||||
|
elif subcmd == "stop":
|
||||||
|
self.client.stop_logging()
|
||||||
|
print("🛑 Logging stopped.")
|
||||||
|
elif subcmd == "status":
|
||||||
|
status = "ON" if self.client.is_logging_active() else "OFF"
|
||||||
|
print(f"📊 Logging Status: {status}")
|
||||||
|
else:
|
||||||
|
print("❌ Invalid log command. Use 'log start <file>.csv', 'log stop', or 'log status'.")
|
||||||
|
|
||||||
def show_help(self):
|
def show_help(self):
|
||||||
"""Displays the list of available commands."""
|
"""Displays the list of available commands."""
|
||||||
print("""
|
print("""
|
||||||
@ -170,6 +188,7 @@ Available Commands:
|
|||||||
toggle <DiO/DiL> <0/1>, move_external <axis> <value>
|
toggle <DiO/DiL> <0/1>, move_external <axis> <value>
|
||||||
correct <RKorr/AKorr> <X/Y/Z/A/B/C> <value>, speed <Tech.TX> <value>
|
correct <RKorr/AKorr> <X/Y/Z/A/B/C> <value>, speed <Tech.TX> <value>
|
||||||
override <limit>
|
override <limit>
|
||||||
|
log start <file>.csv, log stop, log status
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,25 +1,15 @@
|
|||||||
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import csv
|
||||||
import logging
|
import logging
|
||||||
from config_parser import ConfigParser
|
import xml.etree.ElementTree as ET # ✅ FIX: Import ElementTree
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
import multiprocessing
|
|
||||||
from xml_handler import XMLGenerator # ✅ Import XML generator module
|
|
||||||
from config_parser import ConfigParser # ✅ Import the updated config parser
|
|
||||||
|
|
||||||
|
|
||||||
import multiprocessing
|
|
||||||
import socket
|
|
||||||
import logging
|
|
||||||
from config_parser import ConfigParser
|
from config_parser import ConfigParser
|
||||||
from xml_handler import XMLGenerator
|
from xml_handler import XMLGenerator
|
||||||
|
|
||||||
class NetworkProcess(multiprocessing.Process):
|
class NetworkProcess(multiprocessing.Process):
|
||||||
"""Handles UDP communication in a separate process."""
|
"""Handles UDP communication and optional CSV logging in a separate process."""
|
||||||
|
|
||||||
def __init__(self, ip, port, send_variables, receive_variables, stop_event, config_parser):
|
def __init__(self, ip, port, send_variables, receive_variables, stop_event, config_parser):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -29,14 +19,17 @@ class NetworkProcess(multiprocessing.Process):
|
|||||||
self.config_parser = config_parser
|
self.config_parser = config_parser
|
||||||
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
# ✅ Fallback IP handling
|
|
||||||
self.client_address = (ip, port)
|
self.client_address = (ip, port)
|
||||||
|
|
||||||
if not self.is_valid_ip(ip):
|
if not self.is_valid_ip(ip):
|
||||||
logging.warning(f"Invalid IP address '{ip}' detected. Falling back to '0.0.0.0'.")
|
logging.warning(f"Invalid IP address '{ip}' detected. Falling back to '0.0.0.0'.")
|
||||||
print(f"⚠️ Invalid IP '{ip}', falling back to '0.0.0.0'.")
|
print(f"⚠️ Invalid IP '{ip}', falling back to '0.0.0.0'.")
|
||||||
self.client_address = ('0.0.0.0', port)
|
self.client_address = ('0.0.0.0', port)
|
||||||
else:
|
else:
|
||||||
self.client_address = (ip, port)
|
self.client_address = (ip, port)
|
||||||
|
self.logging_active = multiprocessing.Value('b', False) # Shared flag for logging
|
||||||
|
self.log_filename = multiprocessing.Array('c', 256) # Shared memory for filename
|
||||||
|
self.csv_process = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.udp_socket.bind(self.client_address)
|
self.udp_socket.bind(self.client_address)
|
||||||
@ -63,44 +56,74 @@ class NetworkProcess(multiprocessing.Process):
|
|||||||
print("[DEBUG] Network process started.")
|
print("[DEBUG] Network process started.")
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
print("[DEBUG] Waiting for incoming message...")
|
|
||||||
self.udp_socket.settimeout(5)
|
self.udp_socket.settimeout(5)
|
||||||
data_received, self.controller_ip_and_port = self.udp_socket.recvfrom(1024)
|
data_received, self.controller_ip_and_port = self.udp_socket.recvfrom(1024)
|
||||||
message = data_received.decode()
|
message = data_received.decode()
|
||||||
|
|
||||||
print(f"[DEBUG] Received message: {message}")
|
|
||||||
self.process_received_data(message)
|
self.process_received_data(message)
|
||||||
|
|
||||||
# ✅ Generate the send XML using the updated XMLGenerator
|
|
||||||
send_xml = XMLGenerator.generate_send_xml(self.send_variables, self.config_parser.network_settings)
|
send_xml = XMLGenerator.generate_send_xml(self.send_variables, self.config_parser.network_settings)
|
||||||
print(f"[DEBUG] Sending response: {send_xml}")
|
|
||||||
|
|
||||||
self.udp_socket.sendto(send_xml.encode(), self.controller_ip_and_port)
|
self.udp_socket.sendto(send_xml.encode(), self.controller_ip_and_port)
|
||||||
|
|
||||||
|
# ✅ If logging is active, write data to CSV
|
||||||
|
if self.logging_active.value:
|
||||||
|
self.log_to_csv()
|
||||||
|
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
print("[WARNING] No message received within timeout period.")
|
print("[WARNING] No message received within timeout period.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Network process error: {e}")
|
print(f"[ERROR] Network process error: {e}")
|
||||||
|
|
||||||
|
|
||||||
def stop_network(self):
|
|
||||||
"""Safely stop the network process."""
|
|
||||||
if self.udp_socket:
|
|
||||||
self.udp_socket.close()
|
|
||||||
print("✅ Network socket closed.")
|
|
||||||
|
|
||||||
def process_received_data(self, xml_string):
|
def process_received_data(self, xml_string):
|
||||||
"""Parse incoming XML and update shared variables."""
|
"""Parse incoming XML and update shared variables."""
|
||||||
try:
|
try:
|
||||||
root = ET.fromstring(xml_string)
|
root = ET.fromstring(xml_string)
|
||||||
for element in root:
|
for element in root:
|
||||||
if element.tag in self.receive_variables:
|
if element.tag in self.receive_variables:
|
||||||
if len(element.attrib) > 0: # ✅ Handle structured data (dictionaries)
|
if len(element.attrib) > 0:
|
||||||
self.receive_variables[element.tag] = {k: float(v) for k, v in element.attrib.items()}
|
self.receive_variables[element.tag] = {k: float(v) for k, v in element.attrib.items()}
|
||||||
else:
|
else:
|
||||||
self.receive_variables[element.tag] = element.text
|
self.receive_variables[element.tag] = element.text
|
||||||
|
|
||||||
print(f"[DEBUG] Updated received variables: {self.receive_variables}")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Error parsing received message: {e}")
|
print(f"[ERROR] Error parsing received message: {e}")
|
||||||
|
|
||||||
|
def log_to_csv(self):
|
||||||
|
"""Write send/receive variables to the CSV log."""
|
||||||
|
filename = self.log_filename.value.decode().strip()
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(filename, mode="a", newline="") as file:
|
||||||
|
writer = csv.writer(file)
|
||||||
|
|
||||||
|
# Write header if the file is new
|
||||||
|
if file.tell() == 0:
|
||||||
|
headers = ["Timestamp", "IPOC"]
|
||||||
|
headers += [f"Send.{k}" for k in self.send_variables.keys()]
|
||||||
|
headers += [f"Receive.{k}" for k in self.receive_variables.keys()]
|
||||||
|
writer.writerow(headers)
|
||||||
|
|
||||||
|
# Write current data
|
||||||
|
timestamp = time.strftime("%d-%m-%Y %H:%M:%S")
|
||||||
|
ipoc = self.receive_variables.get("IPOC", 0)
|
||||||
|
send_data = [self.send_variables.get(k, "") for k in self.send_variables.keys()]
|
||||||
|
receive_data = [self.receive_variables.get(k, "") for k in self.receive_variables.keys()]
|
||||||
|
writer.writerow([timestamp, ipoc] + send_data + receive_data)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Failed to log data to CSV: {e}")
|
||||||
|
|
||||||
|
def start_logging(self, filename):
|
||||||
|
"""Start logging RSI data to CSV."""
|
||||||
|
self.logging_active.value = True
|
||||||
|
self.log_filename.value = filename.encode()
|
||||||
|
print(f"✅ CSV Logging started: {filename}")
|
||||||
|
|
||||||
|
def stop_logging(self):
|
||||||
|
"""Stop logging RSI data."""
|
||||||
|
self.logging_active.value = False
|
||||||
|
print("🛑 CSV Logging stopped.")
|
||||||
|
|
||||||
|
def is_logging_active(self):
|
||||||
|
"""Return logging status."""
|
||||||
|
return self.logging_active.value
|
||||||
|
|||||||
@ -77,3 +77,17 @@ class RSIAPI:
|
|||||||
"send_variables": dict(self.client.send_variables),
|
"send_variables": dict(self.client.send_variables),
|
||||||
"receive_variables": dict(self.client.receive_variables)
|
"receive_variables": dict(self.client.receive_variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def start_logging(self, filename):
|
||||||
|
"""Start logging RSI data to CSV."""
|
||||||
|
self.client.start_logging(filename)
|
||||||
|
return f"✅ CSV Logging started: {filename}"
|
||||||
|
|
||||||
|
def stop_logging(self):
|
||||||
|
"""Stop logging RSI data."""
|
||||||
|
self.client.stop_logging()
|
||||||
|
return "🛑 CSV Logging stopped."
|
||||||
|
|
||||||
|
def is_logging_active(self):
|
||||||
|
"""Return logging status."""
|
||||||
|
return self.client.is_logging_active()
|
||||||
Loading…
Reference in New Issue
Block a user