-
Notifications
You must be signed in to change notification settings - Fork 18
/
orbitx.py
171 lines (141 loc) · 6.18 KB
/
orbitx.py
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env python3
"""
main() for OrbitX. Can launch one of several OrbitX programs.
Look in the submodules of orbitx.programs for different programs.
"""
import argparse
import logging
import sys
import time
import warnings
from pathlib import Path
import vpython
from orbitx import logs
from orbitx import programs
from orbitx.graphics.flight import launcher
log = logging.getLogger('orbitx')
def log_git_info():
"""For ease in debugging, try to get some version information.
This should never throw a fatal error, it's just nice-to-know stuff.
But if I get a logfile from someone at spacesim, this will help to make
sure they're on the latest version."""
try:
git_dir = Path('.git')
head_file = git_dir / 'HEAD'
with head_file.open() as f:
head_contents = f.readline().strip()
log.info(f'Contents of .git/HEAD: {head_contents}')
if head_contents.split()[0] == 'ref:':
hash_file = git_dir / head_contents.split()[1]
with hash_file.open() as f:
log.info(f'Current reference hash: {f.readline().strip()}')
except FileNotFoundError:
return
def vpython_error_message():
"""Lets the user know that something bad happened.
Note, if there's no vpython canvas, this vpython code has no effect."""
error_message = (
"<p>⚠ Sorry, spacesimmer! OrbitX has crashed for "
"some reason.</p>"
"<p>Any information that OrbitX has on the crash has "
"been saved to a logfile. If you want to get this problem fixed,"
" send the contents of the log file "
"<blockquote>" +
logs.logfile_name.replace('\\', '\\\\') +
"</blockquote> "
"to Patrick Melanson along with a description of what was "
"happening in the program when it crashed.</p>"
"<p>Again, thank you for using OrbitX!</p>"
)
vpython.canvas.get_selected().append_to_caption(f"""<script>
if (document.querySelector('div.error') == null) {{
error_div = document.createElement('div');
error_div.className = 'error';
error_div.innerHTML = "{error_message}";
document.querySelector('body').prepend(error_div);
}}
</script>""")
vpython.canvas.get_selected().append_to_caption("""<style>
.error {
color: #D8000C !important;
background-color: #FFBABA;
margin: 10px 0;
padding: 10px;
border-radius: 5px 5px 5px 5px;
width: 700px;
}
span.code {
color: #D8000C !important;
font-family: monospace;
}
blockquote {
font-family: monospace;
}
</style>""")
time.sleep(0.1) # Let vpython send out this update
def main():
"""Delegate work to one of the OrbitX programs."""
log_git_info()
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='store_true', default=False,
help='Logs everything to both logfile and output.')
parser.add_argument('--flamegraph', action='store_true', default=False,
help='Generating profiling reports, for a flamegraph.')
parser.add_argument('--profile', action='store_true', default=False,
help='Profile and print simple stats at exit.')
# Use the argument parsers that each program defines
subparsers = parser.add_subparsers(help='Which OrbitX program to run',
dest='program')
for program in programs.LISTING:
subparser = subparsers.add_parser(
program.argparser.prog,
add_help=False, parents=[program.argparser])
subparser.set_defaults(main_loop=program.main)
args = parser.parse_args()
if args.verbose:
logs.enable_verbose_logging()
try:
if args.program is None:
# No CLI args were specified, get the needed information from the
# user using a graphical launcher interface.
launch = launcher.Launcher()
args = parser.parse_args(launch.get_args())
logs.make_program_logfile(args.program)
args.main_loop(args)
except KeyboardInterrupt:
# We're expecting ctrl-C will end the program, hide the exception from
# the user.
pass
except Exception as e:
log.exception('Unexpected exception, exiting...')
print(f'OrbitX got an unexpected exception! Check {logs.logfile_name} for logs if you\'re an orbitx developer.')
print('Or, send the logfile to Patrick.')
vpython_error_message()
# The usual errors that result from stale grpc/protobuf definitions are
# attribute errors (like entity.x throwing an AttributeError), or
# GRPC errors.
if isinstance(e, (AttributeError, ValueError)) or \
hasattr(e, '__module__') and (
'grpc' in e.__module__ or 'google' in e.__module__):
proto_file = Path('orbitx', 'orbitx.proto')
generated_file = Path('orbitx', 'orbitx_pb2.py')
if not generated_file.is_file():
log.warning('================================================')
log.warning(f'{proto_file} does not exist.')
elif proto_file.stat().st_mtime > generated_file.stat().st_mtime:
log.warning('================================================')
log.warning(f'{proto_file} is newer than {generated_file}.')
else:
# We thought that generated protobuf definitions were out of
# date, but it doesn't actually look like that's the case.
sys.exit(1)
log.warning('A likely fix for this fatal exception is to run the')
log.warning('generate-protobuf script for your platform!')
log.warning('You\'ll have to do this every time you change')
log.warning(str(proto_file))
log.warning('================================================')
# We don't exit by allowing the exception to be uncaught, because then
# it would be printed again unneccessarily.
sys.exit(1)
if __name__ == '__main__':
main()