-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun.py
More file actions
140 lines (126 loc) · 5.57 KB
/
run.py
File metadata and controls
140 lines (126 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import os
import sys
import argparse
import multiprocessing
# Set the Qt platform plugin for Wayland if not already configured.
# This is a workaround for a common issue where PyQt5 fails to initialize
# on Wayland-based desktop environments. This must be done before any
# Qt-related library (like PyQt5 or matplotlib with a Qt backend) is imported.
if 'QT_QPA_PLATFORM' not in os.environ and 'wayland' in os.environ.get('XDG_SESSION_TYPE', '').lower():
os.environ['QT_QPA_PLATFORM'] = 'wayland'
# Set up the Python path to ensure that both 'src' and its parent directory
# are accessible. This allows for consistent absolute imports across the project,
# especially for finding the sibling 'ml' library.
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)
sys.path.insert(0, os.path.abspath(os.path.join(script_dir, '../..'))) # For `from ml...` and `from diagrams...`
if __name__ == "__main__":
import multiprocessing
# Set the multiprocessing start method to 'spawn'. This must be done
# inside the `if __name__ == '__main__'` block and before any
# multiprocessing-related objects are created. 'spawn' creates a clean
# new process, which is crucial for compatibility with GUI libraries and
# complex C-extensions like PyBullet.
try:
multiprocessing.set_start_method('spawn', force=True)
except RuntimeError:
# The start method can only be set once.
pass
parser = argparse.ArgumentParser(
description="Run the multirotor simulation or related tools.",
formatter_class=argparse.RawTextHelpFormatter
)
# Set the default command if none is provided
parser.set_defaults(command='simulate')
subparsers = parser.add_subparsers(
dest='command',
help='Available commands'
)
# --- Simulate Command ---
parser_simulate = subparsers.add_parser(
'simulate',
help='Run the simulation and generate trace files (default command).'
)
parser_simulate.add_argument(
'--trace-file', default='logs/trace.log',
help="Path for the simulation trace log file (default: logs/trace.log)."
)
parser_simulate.add_argument(
'--error-file', default='logs/errors.log',
help="Path for the simulation error log file (default: logs/errors.log)."
)
# --- Analyze Command ---
parser_analyze = subparsers.add_parser(
'analyze',
help='Analyze a simulation trace log.'
)
parser_analyze.add_argument(
'--trace-file', default='logs/trace.log',
help="Path of the simulation trace log file to analyze (default: logs/trace.log)."
)
parser_analyze.add_argument("--format", choices=['text', 'json', 'json:perfetto'], default='text', help="Output format (default: text).")
parser_analyze.add_argument("-o", "--output", help="Path to the output file. If not provided, prints to stdout.")
# --- Export Command ---
parser_export = subparsers.add_parser(
'export',
help='Export the model structure to a JSON file.'
)
parser_export.add_argument(
'--structure-file', default='logs/structure.json',
help="Path to save the model structure JSON file (default: logs/structure.json)."
)
parser_export.add_argument(
'part_id', nargs='?', default='multirotor',
help="The ID of the part to export (e.g., 'multirotor', 'multirotor.controller'). Default: 'multirotor'."
)
# --- Import Command ---
parser_import = subparsers.add_parser(
'import',
help='Import and display the model structure from a JSON file.'
)
parser_import.add_argument(
'--structure-file', default='logs/structure.json',
help="Path of the model structure JSON file to import (default: logs/structure.json)."
)
# --- Merge Command ---
parser_merge = subparsers.add_parser(
'merge',
help='Merge two Perfetto JSON trace files into one.'
)
parser_merge.add_argument(
'trace_file_1',
help="Path to the first trace file."
)
parser_merge.add_argument(
'trace_file_2',
help="Path to the second trace file."
)
parser_merge.add_argument(
'output_file',
help="Path to the output merged trace file."
)
# Parse arguments. If no command is given, default to 'simulate'.
args, unknown = parser.parse_known_args()
if args.command is None:
# No command was specified, so we run the default 'simulate' command.
# We need to re-parse the arguments for the 'simulate' subparser
# to correctly load its defaults (e.g., file paths).
args = parser_simulate.parse_args(unknown)
args.command = 'simulate'
if args.command == 'simulate':
# The simulate function is called directly. Lazy loading within runs.py
# prevents child processes from re-executing heavy initializations.
from runs import simulate
simulate(trace_filename=args.trace_file, error_filename=args.error_file)
elif args.command == 'analyze':
from runs import analyze_trace
analyze_trace(trace_file=args.trace_file, output_format=args.format, output_file=args.output)
elif args.command == 'export':
from runs import export_diagram
export_diagram(structure_filename=args.structure_file, part_id=args.part_id)
elif args.command == 'import':
from runs import import_diagram
import_diagram(structure_filename=args.structure_file)
elif args.command == 'merge':
from runs import merge_traces
merge_traces(args.trace_file_1, args.trace_file_2, args.output_file)