26
26
logger .setLevel (logging .DEBUG )
27
27
handler = logging .StreamHandler (sys .stdout )
28
28
handler .setLevel (logging .DEBUG )
29
- formatter = logging .Formatter (' %(asctime)s - %(levelname)s - %(message)s' )
29
+ formatter = logging .Formatter (" %(asctime)s - %(levelname)s - %(message)s" )
30
30
handler .setFormatter (formatter )
31
31
logger .addHandler (handler )
32
32
33
33
# Set up the paths and variables for the compiler
34
- compiler_path = ' /compiler'
35
- output_path = ' /compiler/out/flightplan_'
36
- platform_path = ' /compiler/python/project'
34
+ compiler_path = " /compiler"
35
+ output_path = " /compiler/out/flightplan_"
36
+ platform_path = " /compiler/python/project"
37
37
38
38
39
39
def download_script (script_url ):
40
40
try :
41
41
# Get the ZIP file name from the URL
42
- filename = script_url .rsplit (sep = '/' )[- 1 ]
43
- logger .info (f' Writing { filename } to disk...' )
44
-
42
+ filename = script_url .rsplit (sep = "/" )[- 1 ]
43
+ logger .info (f" Writing { filename } to disk..." )
44
+
45
45
# Download the ZIP file
46
46
r = requests .get (script_url , stream = True )
47
- with open (filename , mode = 'wb' ) as f :
47
+ with open (filename , mode = "wb" ) as f :
48
48
for chunk in r .iter_content (chunk_size = 8192 ):
49
49
f .write (chunk )
50
50
@@ -53,177 +53,209 @@ def download_script(script_url):
53
53
kml_file = None
54
54
55
55
# Extract all contents of the ZIP file and remember .dsl and .kml filenames
56
- with ZipFile (filename , 'r' ) as z :
56
+ with ZipFile (filename , "r" ) as z :
57
57
z .extractall (path = compiler_path )
58
58
for file_name in z .namelist ():
59
- if file_name .endswith (' .dsl' ):
59
+ if file_name .endswith (" .dsl" ):
60
60
dsl_file = file_name
61
- elif file_name .endswith (' .kml' ):
61
+ elif file_name .endswith (" .kml" ):
62
62
kml_file = file_name
63
63
64
64
# Log or return the results
65
65
logger .info (f"Extracted DSL files: { dsl_file } " )
66
66
logger .info (f"Extracted KML files: { kml_file } " )
67
-
67
+
68
68
return dsl_file , kml_file
69
69
70
70
except Exception as e :
71
71
logger .error (f"Error during download or extraction: { e } " )
72
-
72
+
73
+
73
74
def compile_mission (dsl_file , kml_file , drone_list , alt , compiler_file ):
74
75
# Construct the full paths for the DSL and KML files
75
76
dsl_file_path = os .path .join (compiler_path , dsl_file )
76
77
kml_file_path = os .path .join (compiler_path , kml_file )
77
78
jar_path = os .path .join (compiler_path , compiler_file )
78
79
altitude = str (alt )
79
-
80
+
80
81
# Define the command and arguments
81
82
command = [
82
83
"java" ,
83
- "-jar" , jar_path ,
84
- "-d" , drone_list ,
85
- "-s" , dsl_file_path ,
86
- "-k" , kml_file_path ,
87
- "-o" , output_path ,
88
- "-p" , platform_path ,
89
- "-a" , altitude
84
+ "-jar" ,
85
+ jar_path ,
86
+ "-d" ,
87
+ drone_list ,
88
+ "-s" ,
89
+ dsl_file_path ,
90
+ "-k" ,
91
+ kml_file_path ,
92
+ "-o" ,
93
+ output_path ,
94
+ "-p" ,
95
+ platform_path ,
96
+ "-a" ,
97
+ altitude ,
90
98
]
91
-
99
+
92
100
# Run the command
93
101
logger .info (f"Running command: { ' ' .join (command )} " )
94
102
result = subprocess .run (command , check = True , capture_output = True , text = True )
95
-
103
+
96
104
# Log the output
97
105
logger .info (f"Compilation output: { result .stdout } " )
98
-
106
+
99
107
# Output the results
100
108
logger .info ("Compilation successful." )
101
-
109
+
102
110
103
111
def send_to_drone (msg , base_url , drone_list , cmd_front_cmdr_sock , redis ):
104
112
try :
105
113
logger .info ("Sending request to drone..." )
106
- # Send the command to each drone
114
+ # Send the command to each drone
107
115
for drone_id in drone_list :
108
116
# check if the cmd is a mission
109
- if ( base_url ) :
117
+ if base_url :
110
118
# reconstruct the script url with the correct compiler output path
111
119
msg .cmd .script_url = f"{ base_url } { output_path } { drone_id } .ms"
112
120
logger .info (f"script url: { msg .cmd .script_url } " )
113
-
121
+
114
122
# send the command to the drone
115
- cmd_front_cmdr_sock .send_multipart ([drone_id .encode ('utf-8' ), msg .SerializeToString ()])
116
- logger .info (f'Delivered request to drone { drone_id } :\n { text_format .MessageToString (msg )} ' )
117
-
123
+ cmd_front_cmdr_sock .send_multipart ([drone_id .encode ("utf-8" ), msg .SerializeToString ()])
124
+ logger .info (
125
+ f"Delivered request to drone { drone_id } :\n { text_format .MessageToString (msg )} "
126
+ )
127
+
118
128
# store the record in redis
119
129
key = redis .xadd (
120
130
"commands" ,
121
- {"commander" : msg .commander_id , "drone" : drone_id , "value" : text_format .MessageToString (msg ),}
131
+ {
132
+ "commander" : msg .commander_id ,
133
+ "drone" : drone_id ,
134
+ "value" : text_format .MessageToString (msg ),
135
+ },
122
136
)
123
137
logger .debug (f"Updated redis under stream commands at key { key } " )
124
138
except Exception as e :
125
139
logger .error (f"Error sending request to drone: { e } " )
126
140
127
-
141
+
128
142
def listen_cmdrs (cmdr_sock , cmd_front_cmdr_sock , redis , alt , compiler_file ):
129
143
while True :
130
-
131
144
# Listen for incoming requests from cmdr
132
145
req = cmdr_sock .recv ()
133
146
try :
134
147
msg = cnc_pb2 .Extras ()
135
148
msg .ParseFromString (req )
136
- logger .info (f' Request received:\n { text_format .MessageToString (msg )} ' )
149
+ logger .info (f" Request received:\n { text_format .MessageToString (msg )} " )
137
150
except DecodeError :
138
- cmdr_sock .send (b' Error decoding protobuf. Did you send a cnc_pb2?' )
139
- logger .info (' Error decoding protobuf. Did you send a cnc_pb2?' )
151
+ cmdr_sock .send (b" Error decoding protobuf. Did you send a cnc_pb2?" )
152
+ logger .info (" Error decoding protobuf. Did you send a cnc_pb2?" )
140
153
continue
141
-
154
+
142
155
# get the drone list
143
156
try :
144
157
drone_list_json = msg .cmd .for_drone_id
145
158
drone_list = json .loads (drone_list_json )
146
159
logger .info (f"drone list: { drone_list } " )
147
160
except json .JSONDecodeError :
148
- cmdr_sock .send (b' Error decoding drone list. Did you send a JSON list?' )
149
- logger .info (' Error decoding drone list. Did you send a JSON list?' )
161
+ cmdr_sock .send (b" Error decoding drone list. Did you send a JSON list?" )
162
+ logger .info (" Error decoding drone list. Did you send a JSON list?" )
150
163
continue
151
-
164
+
152
165
# Check if the command contains a mission and compile it if true
153
166
base_url = None
154
- if ( msg .cmd .script_url ) :
167
+ if msg .cmd .script_url :
155
168
# download the script
156
169
script_url = msg .cmd .script_url
157
170
logger .info (f"script url: { script_url } " )
158
171
dsl , kml = download_script (script_url )
159
-
172
+
160
173
# compile the mission
161
174
drone_list_revised = "&" .join (drone_list )
162
175
logger .info (f"drone list revised: { drone_list_revised } " )
163
176
compile_mission (dsl , kml , drone_list_revised , alt , compiler_file )
164
-
177
+
165
178
# get the base url
166
179
parsed_url = urlparse (script_url )
167
180
base_url = f"{ parsed_url .scheme } ://{ parsed_url .netloc } "
168
181
169
-
170
182
# send the command to the drone
171
183
send_to_drone (msg , base_url , drone_list , cmd_front_cmdr_sock , redis )
172
-
173
184
174
-
175
- cmdr_sock . send ( b' ACK' )
176
- logger . info ( 'Sent ACK to commander' )
185
+ cmdr_sock . send ( b"ACK" )
186
+ logger . info ( "Sent ACK to commander" )
187
+
177
188
178
189
def main ():
179
190
parser = argparse .ArgumentParser ()
180
- parser .add_argument ('-d' , '--droneport' , type = int , default = 5003 , help = 'Specify port to listen for drone requests [default: 5003]' )
181
- parser .add_argument ('-c' , '--cmdrport' , type = int , default = 6001 , help = 'Specify port to listen for commander requests [default: 6001]' )
182
191
parser .add_argument (
183
- "-r" , "--redis" , type = int , default = 6379 , help = "Set port number for redis connection [default: 6379]"
192
+ "-d" ,
193
+ "--droneport" ,
194
+ type = int ,
195
+ default = 5003 ,
196
+ help = "Specify port to listen for drone requests [default: 5003]" ,
184
197
)
185
198
parser .add_argument (
186
- "-a" , "--auth" , default = "" , help = "Share key for redis user."
199
+ "-c" ,
200
+ "--cmdrport" ,
201
+ type = int ,
202
+ default = 6001 ,
203
+ help = "Specify port to listen for commander requests [default: 6001]" ,
187
204
)
205
+ parser .add_argument (
206
+ "-r" ,
207
+ "--redis" ,
208
+ type = int ,
209
+ default = 6379 ,
210
+ help = "Set port number for redis connection [default: 6379]" ,
211
+ )
212
+ parser .add_argument ("-a" , "--auth" , default = "" , help = "Share key for redis user." )
188
213
parser .add_argument (
189
214
"--altitude" , type = int , default = 15 , help = "base altitude for the drones mission"
190
215
)
191
216
parser .add_argument (
192
- "--compiler_file" , default = ' compile-1.5-full.jar' , help = "compiler file name"
193
- )
217
+ "--compiler_file" , default = " compile-1.5-full.jar" , help = "compiler file name"
218
+ )
194
219
args = parser .parse_args ()
195
-
220
+
196
221
# Set the altitude
197
222
alt = args .altitude
198
223
logger .info (f"Starting control plane with altitude { alt } ..." )
199
-
224
+
200
225
compiler_file = args .compiler_file
201
226
logger .info (f"Using compiler file: { compiler_file } " )
202
-
227
+
203
228
# Connect to redis
204
- r = redis .Redis (host = 'redis' , port = args .redis , username = 'steeleagle' , password = f'{ args .auth } ' ,decode_responses = True )
229
+ r = redis .Redis (
230
+ host = "redis" ,
231
+ port = args .redis ,
232
+ username = "steeleagle" ,
233
+ password = f"{ args .auth } " ,
234
+ decode_responses = True ,
235
+ )
205
236
logger .info (f"Connected to redis on port { args .redis } ..." )
206
237
207
238
# Set up the commander socket
208
239
ctx = zmq .Context ()
209
240
cmdr_sock = ctx .socket (zmq .REP )
210
- cmdr_sock .bind (f' tcp://*:{ args .cmdrport } ' )
211
- logger .info (f' Listening on tcp://*:{ args .cmdrport } for commander requests...' )
241
+ cmdr_sock .bind (f" tcp://*:{ args .cmdrport } " )
242
+ logger .info (f" Listening on tcp://*:{ args .cmdrport } for commander requests..." )
212
243
213
244
# Set up the drone socket
214
245
async_ctx = zmq .asyncio .Context ()
215
246
cmd_front_cmdr_sock = async_ctx .socket (zmq .ROUTER )
216
247
cmd_front_cmdr_sock .setsockopt (zmq .ROUTER_HANDOVER , 1 )
217
- cmd_front_cmdr_sock .bind (f' tcp://*:{ args .droneport } ' )
218
- logger .info (f' Listening on tcp://*:{ args .droneport } for drone requests...' )
219
-
248
+ cmd_front_cmdr_sock .bind (f" tcp://*:{ args .droneport } " )
249
+ logger .info (f" Listening on tcp://*:{ args .droneport } for drone requests..." )
250
+
220
251
# Listen for incoming requests from cmdr
221
252
try :
222
253
listen_cmdrs (cmdr_sock , cmd_front_cmdr_sock , r , alt , compiler_file )
223
254
except KeyboardInterrupt :
224
- logger .info (' Shutting down...' )
255
+ logger .info (" Shutting down..." )
225
256
cmdr_sock .close ()
226
257
cmd_front_cmdr_sock .close ()
227
258
228
- if __name__ == '__main__' :
229
- main ()
259
+
260
+ if __name__ == "__main__" :
261
+ main ()
0 commit comments