-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathec_sim_tester.py
More file actions
298 lines (267 loc) · 14.7 KB
/
Copy pathec_sim_tester.py
File metadata and controls
298 lines (267 loc) · 14.7 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
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import zmq
import json
import time
from datetime import datetime
import pandas as pd
import multiprocessing
import sys
import traceback
# Import all DSs, CC, SC, AEC server to subprocess later
from ds_sim_chan_stats import sim_chan_stats
from ds_sim_hwq_stats import sim_hwq_stats
from ds_sim_eth_stats import sim_eth_stats
from ds_load_avg_stats import load_avg_stats
from ds_sim_sniffer_event_gen import sim_sniffer
from cc_collector_collator import collector
from sc_file_writer import csv_writer
# Define Experiment Definitions (EDs) to loop through
list_of_expt_defs = [
{
'bk': False, # Send BacKground (BK) access category packets
'be': False, # Send Best Effort (BE) access category packets
'vi': False, # Send VIdeo (VI) access category packets
'vo': False, # Send VOice (VO) access category packets
'udb': 'U', # 'U' for Uplink traffic only, 'D' for Downlink only, 'B' for Both
't_ch_util': 0.7, # Desired total channel utilization from all traffic on network
'packet_size': 100, # Packet sizes to generate
'ud_ratio': 0.0, # Uplink/downlink ratio: 0.0 for all downlink, 1.0 for all uplink, 0.5 for even mix of both
'lehist_filename': f"./data/aecs_log1_{datetime.now().strftime('%Y-%m-%d %H%M%S')}.lehist", # AEC server debug log filename
'update': True # Take effect immediately
},
{
'bk': True,
'be': True,
'vi': True,
'vo': True,
'udb': 'U',
't_ch_util': 0.1,
'packet_size': 1400,
'ud_ratio': 0.5,
'lehist_filename': f"./data/aecs_log2_{datetime.now().strftime('%Y-%m-%d %H%M%S')}.lehist",
'update': True
}
]
# Global config for all subsystems
gc_config = {
# Define the EC instance
'ec': {
'process_name': 'ExptController', # User-defined name of this Experiment Controller
'z_ec_pub_bind_addr': 'tcp://localhost:5550', # ZMQ socket address to publish system control messages to
},
# Define DSs
'dss': {
'DS_SimChanStats': { # User-defined name for this Data Source (DS)
'target': sim_chan_stats, # Function imported at the top of the file to subprocess for this DS
'process': None, # Process object used by the EC to track clean exits with .join() later
'process_name': 'DS_SimChanStats', # User-defined name for this DS used by the DS itself
'pub_bind_addr': 'ipc:///tmp/gigacol/5650', # ZMQ socket address to publish data points
'nic_dev': 'wlp6s0', # Wi-Fi network interface device name
'standalone': False, # Run in standalone mode and print debug messages?
'max_data_freq': 100, # How many updates per second
'window_size': 100, # How many data points to keep in memory for averaging
'ec_sub_addr': 'tcp://localhost:5550', # ZMQ socket to subscribe to for control messages
},
'DS_SimHWQStats': { # The next few DSs are essentially the same
'target': sim_hwq_stats,
'process': None,
'process_name': 'DS_SimHWQStats',
'pub_bind_addr': 'tcp://localhost:5651',
'nic_dev': 'wlp6s0',
'standalone': False,
'max_data_freq': 400,
'window_size': 400,
'ec_sub_addr': 'tcp://localhost:5550',
},
'DS_SimEthStats': {
'target': sim_eth_stats,
'process': None,
'process_name': 'DS_SimEthStats',
'pub_bind_addr': 'tcp://localhost:5652',
'nic_dev': 'enp4s0',
'standalone': False,
'max_data_freq': 200,
'window_size': 200,
'ec_sub_addr': 'tcp://localhost:5550',
},
'DS_LoadAvgs': {
'target': load_avg_stats,
'process': None,
'process_name': 'DS_LoadAvgs',
'pub_bind_addr': 'tcp://localhost:5653',
'standalone': False,
'max_data_freq': 100,
'window_size': 100,
'ec_sub_addr': 'tcp://localhost:5550',
},
'DS_SimSniffer': { # The simulated sniffer has new options
'target': sim_sniffer,
'process': None,
'process_name': 'DS_SimSniffer',
'pub_bind_addr': 'tcp://localhost:5554',
'nic_dev': 'wlp7s0', # Wi-Fi network interface for sniffing packets
'target_mac': 'aa:bb:cc:dd:ee:ff', # MAC address of target device to filter traffic
'ap_mac': '00:11:22:33:44:55', # MAC address of AP network interface
'encryption_type': 'wpa-pwd', # Wi-Fi network encryption type
'decryption_key': 'putapasswordhere!:gc_network', # Wi-Fi password:network name
'ec_sub_addr': 'tcp://localhost:5550',
}
},
# Define CCs
'ccs': {
'CC0': {
'target': collector,
'process': None,
'process_name': 'CC0',
# Attached_DSs are DSs that this CC instance should subscribe to for data collection
'attached_dss': ['DS_SimChanStats', 'DS_SimEthStats', 'DS_LoadAvgs', 'DS_SimHWQStats'],
# event_sources are DSs or other applications that trigger COLLATE or COLLATE_WITH calls.
# DS_SimSniffer goes here because it doesn't generate any data for the CC to save, but tells the CC
# to create snapshots and append some data to it at collation time using COLLATE_WITH.
'event_sources': ['DS_SimSniffer'],
'max_data_history_size': 16384,
'collator_pub_bind_addr': 'tcp://localhost:6551',
'cc_write_debug_log': True, # Write a debug log?
'cc_debug_log_path': f"./sim_data/CC0_{datetime.now().strftime('%Y-%m-%d-%H.%M.%S')}.csv", # File path for the debug log
'ec_sub_addr': 'tcp://localhost:5550',
}
},
# Define SCs
'scs': {
'SC_CSVWriter': {
'target': csv_writer,
'process': None,
'process_name': 'SC_CSVWriter',
'out_fname': f"./sim_data/collated_data_{datetime.now().strftime('%Y-%m-%d-%H.%M.%S')}.csv",
# This list can be used to set a filter to use snapshots from certain event generators only,
# but needs to be implemented in the Snapshot Consumer to take effect.
'requesters': ['DS_SimSniffer'],
'ccs': ['CC0'], # The CC instances to subscribe to for snapshots. Can be multiple.
'ec_sub_addr': 'tcp://localhost:5550',
}
},
# Define AEC server
# 'aec_s': { #
# 'target': aec_server, # Function to call
# 'process': None, # Process object for AEC to manage clean exits with .join() (doesn't work with AEC for now)
# 'process_name': 'AECServer', # Define process name
# 'ec_sub_addr': 'tcp://localhost:5550', # ZMQ subscriber address for EC control messages
# 'chan_stats_sub_addr': 'ipc:///tmp/gigacol/5650', # ZMQ subscriber address for Wi-Fi channel statistics (ds_chan_stats.py)
# 'aec_s_write_debug_log': True, # Write a debug log for the AEC server?
# 'aec_s_debug_log_path': f"./sim_data/AECS_{datetime.now().strftime('%Y-%m-%d-%H.%M.%S')}.csv", # Filename for debug log
# 'dc_pub_bind_addr': 'tcp://localhost:5600', # ZMQ publisher address for overall stats (unused)
# 'aec_c_control_pub_bind_addr': 'tcp://172.16.0.1:9001', # ZMQ publisher address for publishing control messages to clients
# 'aec_sp_control_pub_bind_addr': 'ipc:///tmp/gigacol/5601', # ZMQ publisher address for server-side internal control messages
# 'aec_s_recvr_stats_pub_bind_addr': 'ipc:///tmp/gigacol/5602', # ZMQ publisher address for receiver statistics (unused now)
# 'bt_ports': { # Ports to send background traffic on
# 'bk': 9002, # BacKground (BK)
# 'be': 9003, # Best Effort (BE)
# 'vi': 9004, # VIdeo (VI)
# 'vo': 9005, # VOice (VO)
# },
# 'btc_sub_base_addrs': { # Addresses of client stations to transfer traffic with
# # 'btc_s1': 'tcp://192.168.0.11', # name:address pair
# # 'btc_s2': 'tcp://192.168.0.12',
# # 'btc_s3': 'tcp://192.168.0.13',
# 'btc_s5': 'tcp://192.168.0.15',
# },
# 'bts_pub_base_bind_addr': 'tcp://192.168.0.4', # Address on the server/AP for the AEC server to bind to
# 'max_data_freq': 5000, # Frequency at which the PID controllers will run
# }
}
# Initialize this EC
process_name = gc_config['ec']['process_name']
z_context = zmq.Context()
z_ec_pub = z_context.socket(zmq.PUB)
z_ec_pub_bind_addr = gc_config['ec']['z_ec_pub_bind_addr']
z_ec_pub.bind(z_ec_pub_bind_addr)
print(f"[{process_name}] ZMQ EC pub socket bound: {z_ec_pub_bind_addr}")
# Note 1: When used in full AP setup (not this demo setup), would also listen for hostapd ACS and STA (dis)assoc/auth messages
# Note 2: Or, as this implementation is meant to more clearly demonstrate subsystems, full AP data capture setup
# ------- could integrate EC + sniffer + SC instead.
try:
# Start subprocesses for DSs
for ds_process_name, params in gc_config['dss'].items():
# Can switch to multithreading mode by commenting the "multiprocessing" line and uncommenting the "threading" line for testing
process_obj = multiprocessing.Process(target=params['target'], name=ds_process_name, args=(params.copy(),))
# process_obj = threading.Thread(target=params['target'], name=process_name, args=(arg for arg in params['args'].values()))
gc_config['dss'][ds_process_name]['process'] = process_obj
process_obj.start()
# Start subprocesses for CCs
for cc_process_name, params in gc_config['ccs'].items():
old_a_dss = params['attached_dss']
new_a_dss = {k: gc_config['dss'][k]['pub_bind_addr'] for k in old_a_dss}
old_e_s = params['event_sources']
new_e_s = {k: gc_config['dss'][k]['pub_bind_addr'] for k in old_e_s}
params['attached_dss'] = new_a_dss
params['event_sources'] = new_e_s
process_obj = multiprocessing.Process(target=params['target'], name=cc_process_name, args=(params.copy(),))
gc_config['ccs'][cc_process_name]['process'] = process_obj
process_obj.start()
# Start subprocess for AEC Server
# Note: AEC server current does not exit cleanly, so it is not included here.
# ----- It is recommended to run the AEC server in a separate terminal window first, then run this EC after.
# EC experiment swapping loop
for expt_d in list_of_expt_defs:
# Start subprocesses for SCs. Inside the experiment loop because the SC_File_Writer implementation
# is intended to close and restart for every experiment.
for sc_process_name, params in gc_config['scs'].items():
old_ccs = params['ccs']
new_ccs = {k: gc_config['ccs'][k]['collator_pub_bind_addr'] for k in old_ccs}
params['ccs'] = new_ccs
# Update the output filename to most closely match the start of this next experiment
params['out_fname'] = f"./sim_data/collated_data_{datetime.now().strftime('%Y-%m-%d-%H.%M.%S')}.csv"
process_obj = multiprocessing.Process(target=params['target'], name=sc_process_name, args=(params.copy(),))
gc_config['scs'][sc_process_name]['process'] = process_obj
process_obj.start()
# Send new Experiment Definition to AEC server and SimSniffer event generator
time.sleep(2)
z_ec_pub.send_string('!!!synchronizer!!!')
print(f"[{process_name}] Sent synchronizer message...")
header = f'{process_name};{time.time_ns()};'
z_ec_pub.send_string(header + 'EXPT_DEF;' + json.dumps(list_of_expt_defs[0]))
print(f"[{process_name}] Sent EXPT_DEF: {expt_d}")
time.sleep(1)
# Start AEC traffic generation
z_ec_pub.send_string(header + 'BTS_START_TRAFFIC')
print(f"[{process_name}] Sent BTS_START_TRAFFIC")
# Wait a bit for AEC to spin up
time.sleep(3)
# Send EXPT_START signal so event gen starts
z_ec_pub.send_string(header + 'EXPT_START')
print(f"[{process_name}] Sent EXPT_START")
# Send EXPT_START_RECORD so file writer starts taking CC output
z_ec_pub.send_string(header + 'EXPT_START_RECORD_CSV')
print(f"[{process_name}] Sent EXPT_START_RECORD_CSV")
# Run experiment for 5 seconds
# Note: better metrics for stopping like number of captured packets may be used instead;
# this is kept simple for demonstration purposes
time.sleep(5)
# Send EXPT_STOP signal so event gen stops
z_ec_pub.send_string(header + 'EXPT_STOP')
print(f"[{process_name}] Sent EXPT_STOP")
# Stop CSV writer so it'll write to file with EXPT_STOP_RECORD
z_ec_pub.send_string(header + 'EXPT_STOP_RECORD_CSV')
print(f"[{process_name}] Sent EXPT_STOP_RECORD_CSV")
# Stop AEC traffic by sending message
z_ec_pub.send_string(header + 'BTS_STOP_TRAFFIC')
print(f"[{process_name}] Sent BTS_STOP_TRAFFIC")
time.sleep(1)
# Stop all systems after completion of experiments
z_ec_pub.send_string(header + 'QUIT')
# Wait for all child processes to join
for ds_process_name, params in gc_config['dss'].items():
params['process'].join()
print(f"[{process_name}] <- {ds_process_name} joined!")
for cc_process_name, params in gc_config['ccs'].items():
params['process'].join()
print(f"[{process_name}] <- {cc_process_name} joined!")
for sc_process_name, params in gc_config['scs'].items():
params['process'].join()
print(f"[{process_name}] <- {sc_process_name} joined!")
except Exception as e:
traceback.print_exc()
z_ec_pub.close()
z_context.term()
finally:
z_ec_pub.close()
z_context.term()