From 6b1f6c7b89e4f817bf589d5e52aaf0a2d263b3b7 Mon Sep 17 00:00:00 2001 From: litincandela Date: Mon, 6 Apr 2026 12:06:51 +0000 Subject: [PATCH 1/3] lf_interop_video_streaming.py: Added bandsteering in video streaming test with robo --- py-scripts/lf_interop_video_streaming.py | 160 +++++++++++++++++++---- 1 file changed, 136 insertions(+), 24 deletions(-) diff --git a/py-scripts/lf_interop_video_streaming.py b/py-scripts/lf_interop_video_streaming.py index 13ba05795..d95bcb6ab 100755 --- a/py-scripts/lf_interop_video_streaming.py +++ b/py-scripts/lf_interop_video_streaming.py @@ -89,6 +89,11 @@ python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug --test_name video_streaming_test --robot_test --robot_ip 192.168.204.101 --coordinate 3,4 + Example-17: + Command Line Interface to run Video Streaming test using a DASH stream hosted on the local LANforge server with Robot at specified coordinates with bandsteering + python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug + --test_name video_streaming_test --robot_test --robot_ip 192.168.204.76 --coordinate 3,4 --total_cycles 1 --do_bandsteering + SCRIPT CLASSIFICATION: Test SCRIPT_CATEGORIES: Performance, Functional, Report Generation @@ -152,7 +157,6 @@ port_utils = importlib.import_module("py-json.port_utils") PortUtils = port_utils.PortUtils DeviceConfig = importlib.import_module("py-scripts.DeviceConfig") - iot_scripts_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../local/interop-webGUI/IoT/scripts/")) if os.path.exists(iot_scripts_path): sys.path.insert(0, iot_scripts_path) @@ -172,7 +176,10 @@ def __init__(self, host, ssid, passwd, encryp, media_source, media_quality, supo coordinate=None, rotation=None, rotation_enabled=None, - angle_list=None): + angle_list=None, + do_bandsteering=False, + total_cycles=1, + bssids=None): super().__init__(lfclient_host=host, lfclient_port=8080) self.adb_device_list = None self.host = host @@ -241,8 +248,12 @@ def __init__(self, host, ssid, passwd, encryp, media_source, media_quality, supo self.robot_test = robot_test self.vs_data = {} self.test_stopped = False + self.do_bandsteering = do_bandsteering if robot_test: self.robot_ip = robot_ip + if self.do_bandsteering: + self.total_cycles = total_cycles + self.bssids = bssids if bssids else [] self.coordinate = coordinate self.rotation = rotation self.rotation_enabled = False @@ -724,7 +735,6 @@ def my_monitor_runtime(self): rx_rate = [] frame_rate = [] video_quality = [] - if len(self.created_cx.keys()) > 1: data = data['endpoint'] for endpoint in data: @@ -755,7 +765,6 @@ def my_monitor_runtime(self): rx_rate.append(endpoint.get('rx rate', 0)) frame_rate.append(endpoint.get('frame-rate', 0)) video_quality.append(endpoint.get('video-quality', 0)) - self.data['status'] = statuses self.data["total_urls"] = total_urls self.data["urls_per_sec"] = urls_per_sec @@ -856,9 +865,11 @@ def get_signal_data(self): rssi = [] tx_rate = [] rx_rate = [] + bssid = [] + channel = [] try: - eid_data = self.json_get("ports?fields=alias,rx-rate,tx-rate,ssid,signal") + eid_data = self.json_get("ports?fields=alias,rx-rate,tx-rate,ssid,signal,ap,channel") except KeyError: logger.error("Error: 'interfaces' key not found in port data") exit(1) @@ -877,9 +888,11 @@ def get_signal_data(self): tx_rate.append(alias[i]['tx-rate']) rx_rate.append(alias[i]['rx-rate']) + bssid.append(alias[i]['ap']) + channel.append(alias[i]['channel']) rssi = [0 if i.strip() == "" else int(i) for i in rssi] - return rssi, tx_rate + return rssi, tx_rate, bssid, channel def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, actual_start_time, cx_list=None, curr_coordinate=None, curr_rotation=None, monitor_charge_time=None): try: @@ -907,9 +920,10 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, channel = [] tx_rate = [] rx_rate = [] + bssid = [] resource_ids = list(map(int, self.resource_ids.split(','))) - eid_data = self.json_get("ports?fields=alias,mac,mode,Parent Dev,rx-rate,tx-rate,ssid,signal,channel") + eid_data = self.json_get("ports?fields=alias,mac,mode,Parent Dev,rx-rate,tx-rate,ssid,signal,channel,ap") if "interfaces" not in eid_data.keys(): logger.error("Error: 'interfaces' key not found in port data") exit(1) @@ -929,6 +943,7 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, channel.append(alias[i]['channel']) tx_rate.append(alias[i]['tx-rate']) rx_rate.append(alias[i]['rx-rate']) + bssid.append(alias[i]['ap']) incremental_capacity_list = self.get_incremental_capacity_list() video_rate_dict = {i: [] for i in range(len(device_type))} @@ -965,7 +980,7 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, monitor_charge_time = datetime.now() # Get signal data for RSSI and link speed - rssi_data, link_speed_data = self.get_signal_data() + rssi_data, link_speed_data, bssid_data, channel_data = self.get_signal_data() individual_df_data = [] @@ -985,7 +1000,8 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, self.data['remaining_time_webGUI'] = ["< 1 min"] else: self.data['remaining_time_webGUI'] = [str(datetime.strptime(self.data['end_time_webGUI'][0], "%Y-%m-%d %H:%M:%S") - datetime.strptime(curr_time, "%Y-%m-%d %H:%M:%S"))] - + if self.do_bandsteering: + self.data['remaining_time_webGUI'] = [""] # Get the present time present_time = datetime.now().strftime("%H:%M:%S") self.my_monitor_runtime() @@ -1001,7 +1017,7 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, min_value_video_rate = self.process_list(video_rate_dict[i]) individual_df_data.extend([0, 0, self.data["total_urls"][i], rssi_data[i], link_speed_data[i], self.data["total_buffer"][i], self.data["total_err"][i], min_value_video_rate, max(video_rate_dict[i]), sum(video_rate_dict[i]) / len(video_rate_dict[i]), self.data["bytes_rd"][i], - self.data["rx_rate"][i], self.data['frame_rate'][i], self.data['video_quality'][i]]) + self.data["rx_rate"][i], self.data['frame_rate'][i], self.data['video_quality'][i], bssid_data[i], channel_data[i]]) # If the status is not 'Stopped', append the calculated video rate to the video rate dictionary and overall video rate else: @@ -1024,17 +1040,24 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, self.data["bytes_rd"][i], self.data["rx_rate"][i], self.data['frame_rate'][i], - self.data['video_quality'][i]]) + self.data['video_quality'][i], + bssid_data[i], channel_data[i]]) individual_df_data.extend([sum(overall_video_rate), present_time, iteration + 1, actual_start_time.strftime('%Y-%m-%d %H:%M:%S'), self.data['end_time_webGUI'][0], self.data['remaining_time_webGUI'][0], "Running"]) + # Append robot's current coordinate and orientation to the data if it's a robot test with bandsteering + if self.robot_test and self.do_bandsteering: + robot_x, robot_y, from_coordinate, to_coordinate = self.robot.get_robot_pose() + if from_coordinate == to_coordinate: + return test_stopped_by_user + individual_df_data.extend([robot_x, robot_y, from_coordinate, to_coordinate]) if self.robot_test and self.rotation_enabled: individual_df_data.append(self.current_angle) individual_df.loc[len(individual_df)] = individual_df_data new_row_df = individual_df.tail(1) # Use a separate CSV file per coordinate for robot test - if self.robot_test: + if self.robot_test and not self.do_bandsteering: csv_filename = f'video_streaming_realtime_data_{self.current_coordinate}.csv' else: csv_filename = 'video_streaming_realtime_data.csv' @@ -1050,11 +1073,13 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, logging.info('Test is stopped by the user') self.test_stopped = True test_stopped_by_user = True + if self.do_bandsteering: + return test_stopped_by_user break if self.dowebgui: # Use a separate CSV file per coordinate for robot test - if self.robot_test: + if self.robot_test and not self.do_bandsteering: webgui_csv = '{}/video_streaming_realtime_data_{}.csv'.format(self.result_dir, self.current_coordinate) else: webgui_csv = '{}/video_streaming_realtime_data.csv'.format(self.result_dir) @@ -1072,6 +1097,8 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, break if not self.background_run and self.background_run is not None: break + if self.do_bandsteering: + return test_stopped_by_user present_time = datetime.now().strftime("%H:%M:%S") individual_df_data = [] overall_video_rate = [] @@ -1084,7 +1111,7 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, min_value_video_rate = self.process_list(video_rate_dict[i]) individual_df_data.extend([0, 0, self.data["total_urls"][i], rssi_data[i], link_speed_data[i], self.data["total_buffer"][i], self.data["total_err"][i], min_value_video_rate, max(video_rate_dict[i]), sum(video_rate_dict[i]) / len(video_rate_dict[i]), self.data["bytes_rd"][i], self.data["rx_rate"][i], - self.data['frame_rate'][i], self.data['video_quality'][i]]) + self.data['frame_rate'][i], self.data['video_quality'][i], bssid_data[i], channel_data[i]]) else: overall_video_rate.append(round(self.data["video_format_bitrate"][i] / 1000000, 2)) video_rate_dict[i].append(round(self.data["video_format_bitrate"][i] / 1000000, 2)) @@ -1104,7 +1131,8 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, self.data["bytes_rd"][i], self.data["rx_rate"][i], self.data['frame_rate'][i], - self.data['video_quality'][i]]) + self.data['video_quality'][i], + bssid_data[i], channel_data[i]]) if iteration + 1 == len(incremental_capacity_list): individual_df_data.extend([sum(overall_video_rate), present_time, iteration + 1, actual_start_time.strftime('%Y-%m-%d %H:%M:%S'), self.data['end_time_webGUI'][0], 0, "Stopped"]) @@ -1592,7 +1620,8 @@ def generate_report(self, date, iterations_before_test_stopped_by_user, test_set report.set_graph_image(graph_png) report.move_graph_image() report.build_graph() - self.add_buffer_and_wait_time_images(report=report) + if self.dowebgui and self.get_live_view and not self.do_bandsteering: + self.add_buffer_and_wait_time_images(report=report) # Table 1 report.set_obj_html("Overall - Detailed Result Table", "The below tables provides detailed information for the Video Streaming test.") @@ -1909,6 +1938,11 @@ def create_test_setup_info(self, media_source, media_quality): "Media Source": media_source, "Media Quality": media_quality } + if self.robot_test: + test_setup_info["Robot Ip"] = self.robot_ip + test_setup_info["Coordinates"] = self.coordinate + if self.do_bandsteering: + test_setup_info["Total Cycles"] = self.total_cycles test_setup_info['Incremental Values'] = self.test_setup_info_incremental_values # test_setup_info['Total Duration (min)'] = str(self.test_setup_info_total_duration) return test_setup_info @@ -2054,7 +2088,11 @@ def generate_report_for_robo(self, test_setup_info, report_path='', passed_coord tx_rate.append(alias[i]['tx-rate']) self.add_buffer_and_wait_time_images(report=report) - for coordinate in range(len(passed_coordinates)): + for coordinate in range(len(self.coordinate_list)): + if (not self.rotation_enabled and self.coordinate_list[coordinate] not in self.vs_data): + continue + elif (self.rotation_enabled and int(self.coordinate_list[coordinate]) not in self.vs_data): + continue self.current_coordinate = self.coordinate_list[coordinate] csv_suffix = "_{}".format(self.current_coordinate) if self.rotation_enabled: @@ -2068,6 +2106,9 @@ def generate_report_for_robo(self, test_setup_info, report_path='', passed_coord self.data = self.vs_data[self.coordinate_list[coordinate]]["self_data"] shutil.move('video_streaming_realtime_data{}.csv'.format(csv_suffix), report_path_date_time) self.generate_individual_coordinate(report, device_type, username, ssid, mac, channel, mode, rssi, tx_rate, created_incremental_values, keys) + report.build_footer() + report.write_html() + report.write_pdf() def generate_individual_coordinate(self, report, device_type, username, ssid, mac, channel, mode, rssi, tx_rate, created_incremental_values, keys, report_path=""): """ @@ -2310,9 +2351,6 @@ def generate_individual_coordinate(self, report, device_type, username, ssid, ma dataframe3 = pd.DataFrame(dataframe2) report.set_table_dataframe(dataframe3) report.build_table() - report.build_footer() - report.write_html() - report.write_pdf() def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, actual_start_time, iterations_before_test_stopped_by_user): """ @@ -2337,6 +2375,62 @@ def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, act self.robot.testname = self.test_name passed_coord_list = [] abort = False + if self.do_bandsteering: + curr_cycle = 1 + pause_coord, test_stopped_by_user = self.robot.wait_for_battery() + self.robot.total_cycles = self.total_cycles + self.robot.coordinate_list = self.coordinate_list + # To get the coordinates list of the robot for number of cycles specified by user in bandsteering mode + coordinate_list_with_robo = self.robot.get_coordinates_list() + self.robot.do_bandsteering = True + self.data = {} + self.data["start_time_webGUI"] = [datetime.now().strftime('%Y-%m-%d %H:%M:%S')] + end_time_webGUI = (datetime.now() + timedelta(minutes=int(args.duration))).strftime('%Y-%m-%d %H:%M:%S') + self.data['end_time_webGUI'] = [end_time_webGUI] + file_path = "video_streaming_realtime_data.csv" + if cx_order_list[i]: + logging.info("Test started on Devices with resource Ids : {selected}".format(selected=cx_order_list[i])) + else: + logging.info("Test started on Devices with resource Ids : {selected}".format(selected=cx_order_list[i])) + self.start_specific(cx_order_list[i]) + individual_df = pd.DataFrame(columns=individual_dataframe_columns) + for coord in coordinate_list_with_robo: + # To check for battery level before moving to next coordinate and also monitor cx while moving to next coordinate in bandsteering mode + pause, stopped, all_data_frames = self.robot.wait_for_battery(monitor_function=lambda: + self.monitor_for_runtime_csv(args.duration, file_path, individual_df, i, actual_start_time, cx_order_list[i])) + if stopped: + break + # Moving to next coordinate and also monitor cx while moving to next coordinate in bandsteering mode + matched, abort, all_data_frames = self.robot.move_to_coordinate(coord, + monitor_function=lambda: self.monitor_for_runtime_csv(args.duration, file_path, + individual_df, i, actual_start_time, cx_order_list[i])) + if coord == self.coordinate_list[0]: + curr_cycle += 1 + if curr_cycle > int(self.total_cycles): + logger.info("Completed all {} cycles".format(self.total_cycles)) + else: + logger.info("current cycle {}".format(curr_cycle)) + if abort: + break + # To get add last entry in the csv + last_idx = individual_df.index[-1] + individual_df.loc[last_idx, "status"] = "Stopped" + last_row_df = individual_df.loc[[last_idx]] + if self.dowebgui: + last_row_df.to_csv(f"{args.result_dir}/video_streaming_realtime_data.csv", mode="a", header=False, index=False) + else: + last_row_df.to_csv("video_streaming_realtime_data.csv", mode="a", header=False, index=False) + # stop cx's after completing all cycles in bandsteering mode or if test is stopped by user in between the test + self.stop() + test_setup_info = self.create_test_setup_info(media_source=self.media_source, media_quality=self.media_quality) + date = str(datetime.now()).split(",")[0].replace(" ", "-").split(".")[0] + self.generate_report(date, [0], test_setup_info=test_setup_info, realtime_dataset=individual_df, iot_summary=None) + if self.postcleanup: + self.postcleanup() + + if args.dowebgui: + self.copy_reports_to_home_dir() + exit() for coordinate in coord_list: if self.test_stopped: break @@ -2352,6 +2446,9 @@ def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, act logger.info("Reached the coordinate {}".format(coordinate)) if abort: break + # If the robot fails to reach the coordinate, skip to the next coordinate instead of stopping the entire test + if not matched: + continue passed_coord_list.append(coordinate) coordinate_df = pd.DataFrame(columns=individual_dataframe_columns) if matched: @@ -2811,6 +2908,11 @@ def main(): python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug --test_name video_streaming_test --robot_test --robot_ip 192.168.204.101 --coordinate 3,4 + Example-15: + Command Line Interface to run Video Streaming test using a DASH stream hosted on the local LANforge server with Robot at specified coordinates with bandsteering + python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug + --test_name video_streaming_test --robot_test --robot_ip 192.168.204.76 --coordinate 3,4 --total_cycles 1 --do_bandsteering + SCRIPT CLASSIFICATION: Test SCRIPT_CATEGORIES: Performance, Functional, Report Generation @@ -2935,7 +3037,9 @@ def main(): type=str, default='', help='Comma-separated list of device counts to incrementally test (e.g., "1,3,5")') - + optional.add_argument('--do_bandsteering', help='Enable bandsteering', action='store_true') + optional.add_argument('--bssids', type=str, help='Comma separated list of BSSIDs to be used for the test', default="") + optional.add_argument('--total_cycles', help='Enable bandsteering', default="1") args = parser.parse_args() if args.help_summary: @@ -3035,7 +3139,10 @@ def main(): coordinate=args.coordinate, rotation=args.rotation, rotation_enabled=rotation_enabled, - angle_list=angle_list + angle_list=angle_list, + do_bandsteering=args.do_bandsteering, + total_cycles=args.total_cycles, + bssids=args.bssids.split(",") if args.bssids else [] ) args.upstream_port = obj.change_port_to_ip(args.upstream_port) obj.upstream_port = args.upstream_port @@ -3232,11 +3339,16 @@ def main(): f'bytes_rd_{keys[i]}', f'rx rate_{keys[i]} bps', f'frame_rate_{keys[i]}', - f'Video Quality_{keys[i]}' + f'Video Quality_{keys[i]}', + f'BSSID_{keys[i]}', + f'Channel_{keys[i]}', ]) individual_dataframe_columns.extend(['overall_video_format_bitrate', 'timestamp', 'iteration', 'start_time', 'end_time', 'remaining_Time', 'status']) - if args.robot_test and args.rotation: + # Add Robot related columns if robot testing is enabled + if args.robot_test and args.do_bandsteering: + individual_dataframe_columns.extend(['Robot X', 'Robot Y', 'From Coordinate', 'To Coordinate']) + elif args.robot_test and args.rotation: individual_dataframe_columns.append('angle') individual_df = pd.DataFrame(columns=individual_dataframe_columns) From 3e6485b12197739e968316724d2711fcdb04ed0b Mon Sep 17 00:00:00 2001 From: litincandela Date: Mon, 6 Apr 2026 12:14:31 +0000 Subject: [PATCH 2/3] lf_interop_video_streaming.py: Added bandsteering stats in report --- py-scripts/lf_interop_video_streaming.py | 130 ++++++++++++++++++++--- 1 file changed, 118 insertions(+), 12 deletions(-) diff --git a/py-scripts/lf_interop_video_streaming.py b/py-scripts/lf_interop_video_streaming.py index d95bcb6ab..fa28dc809 100755 --- a/py-scripts/lf_interop_video_streaming.py +++ b/py-scripts/lf_interop_video_streaming.py @@ -129,9 +129,9 @@ import csv from datetime import datetime, timedelta from lf_graph import lf_bar_graph_horizontal -from lf_graph import lf_line_graph +from lf_graph import lf_line_graph, lf_bar_graph import threading -from collections import OrderedDict +from collections import OrderedDict, Counter if sys.version_info[0] != 3: @@ -176,10 +176,7 @@ def __init__(self, host, ssid, passwd, encryp, media_source, media_quality, supo coordinate=None, rotation=None, rotation_enabled=None, - angle_list=None, - do_bandsteering=False, - total_cycles=1, - bssids=None): + angle_list=None, do_bandsteering=False, total_cycles=1, bssids=None): super().__init__(lfclient_host=host, lfclient_port=8080) self.adb_device_list = None self.host = host @@ -1045,7 +1042,6 @@ def monitor_for_runtime_csv(self, duration, file_path, individual_df, iteration, individual_df_data.extend([sum(overall_video_rate), present_time, iteration + 1, actual_start_time.strftime('%Y-%m-%d %H:%M:%S'), self.data['end_time_webGUI'][0], self.data['remaining_time_webGUI'][0], "Running"]) - # Append robot's current coordinate and orientation to the data if it's a robot test with bandsteering if self.robot_test and self.do_bandsteering: robot_x, robot_y, from_coordinate, to_coordinate = self.robot.get_robot_pose() if from_coordinate == to_coordinate: @@ -1664,6 +1660,14 @@ def generate_report(self, date, iterations_before_test_stopped_by_user, test_set dataframe3 = pd.DataFrame(dataframe2) report.set_table_dataframe(dataframe3) report.build_table() + if self.do_bandsteering: + devices_on_running_state = [] + device_names_on_running = [] + for j in range(created_incremental_values[iter]): + devices_on_running_state.append(keys[j]) + device_names_on_running.append(username[j]) + # Added bandsteering stats to the report if bandsteering is enabled + self.get_bandsteering_stats(report, realtime_dataset, devices_on_running_state, device_names_on_running) if iot_summary: self.build_iot_report_section(report, iot_summary) report.build_footer() @@ -1685,6 +1689,96 @@ def copy_reports_to_home_dir(self): os.makedirs(test_name_dir) shutil.copytree(curr_path, test_name_dir, dirs_exist_ok=True) + def get_bandsteering_stats(self, report=None, df=None, data1=None, data2=None): + """ + Retrieves and adds bandsteering statistics to the report. + + """ + bssid_cols = [c for c in df.columns if c.startswith("BSSID")] + channel_cols = [c for c in df.columns if c.startswith("Channel")] + + bssid_to_channel = { + bssid_col: next( + ch for ch in channel_cols + if ch.replace("Channel", "").strip() == + bssid_col.replace("BSSID", "").strip() + ) + for bssid_col in bssid_cols + } + for idx, col in enumerate(bssid_cols): + + channel_col = bssid_to_channel[col] + + # Detect BSSID changes + mask = df[col] != df[col].shift() + filtered_df = df.loc[mask] + if self.bssids: + filtered_df = df.loc[mask & df[col].isin(self.bssids)] + + bssid_list = filtered_df[col].tolist() + channel_list = filtered_df[channel_col].tolist() + timestamp_list = filtered_df['timestamp'].tolist() + from_coordinate_list = filtered_df['From Coordinate'].tolist() + to_coordinate_list = filtered_df['To Coordinate'].tolist() + bssid_counts = Counter(bssid_list) + + x_axis = list(bssid_counts.keys()) # BSSID values + y_axis = [[float(i)] for i in list(bssid_counts.values())] + if len(self.bssids) > 0: + x_axis = self.bssids + y_axis = [[float(bssid_counts.get(bssid, 0))] for bssid in self.bssids] + device_name = data2[idx] + report.set_obj_html( + _obj_title=f"BSSID change count of the {device_name}", + _obj=" ") + report.build_objective() + graph = lf_bar_graph(_data_set=y_axis, + _xaxis_name="BSSID", + _yaxis_name="Number of Changes", + # _xaxis_categories = [", ".join(x_axis)], + _xaxis_categories=[""], + _xaxis_label=x_axis, + _graph_image_name=f"bssid_change_count_{device_name}", + _label=x_axis, + _xaxis_step=1, + _graph_title=f"BSSID change count – {device_name}", + _title_size=16, + _color_edge='black', + _bar_width=0.15, + _figsize=(18, 6), + _legend_loc="best", + _legend_box=(1.0, 1.0), + _dpi=96, + _show_bar_value=True, + _enable_csv=True, + _color=['orange', 'lightcoral', 'steelblue', 'lightgrey'], + _color_name=['orange', 'lightcoral', 'steelblue', 'lightgrey'], + + ) + + graph_png = graph.build_bar_graph() + report.set_graph_image(graph_png) + # need to move the graph image to the results directory + report.move_graph_image() + report.set_csv_filename(graph_png) + report.move_csv_file() + report.build_graph() + + report.set_obj_html( + _obj_title=f"Band Steering Results for {device_name}", + _obj=" ") + report.build_objective() + table_df = { + "Timestamp": timestamp_list, + "BSSID": bssid_list, + "Channel": channel_list, + "From Coordinate": from_coordinate_list, + "To Coordinate": to_coordinate_list + } + table_df = pd.DataFrame(table_df) + report.set_table_dataframe(table_df) + report.build_table() + def filter_ios_devices(self, device_list): """ Filters out iOS devices from the given device list based on hardware and software identifiers. @@ -2396,14 +2490,15 @@ def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, act individual_df = pd.DataFrame(columns=individual_dataframe_columns) for coord in coordinate_list_with_robo: # To check for battery level before moving to next coordinate and also monitor cx while moving to next coordinate in bandsteering mode - pause, stopped, all_data_frames = self.robot.wait_for_battery(monitor_function=lambda: - self.monitor_for_runtime_csv(args.duration, file_path, individual_df, i, actual_start_time, cx_order_list[i])) + pause, stopped, all_data_frames = self.robot.wait_for_battery( + monitor_function=lambda: self.monitor_for_runtime_csv( + args.duration, file_path, individual_df, i, actual_start_time, cx_order_list[i])) if stopped: break # Moving to next coordinate and also monitor cx while moving to next coordinate in bandsteering mode - matched, abort, all_data_frames = self.robot.move_to_coordinate(coord, - monitor_function=lambda: self.monitor_for_runtime_csv(args.duration, file_path, - individual_df, i, actual_start_time, cx_order_list[i])) + matched, abort, all_data_frames = self.robot.move_to_coordinate( + coord, monitor_function=lambda: self.monitor_for_runtime_csv( + args.duration, file_path, individual_df, i, actual_start_time, cx_order_list[i])) if coord == self.coordinate_list[0]: curr_cycle += 1 if curr_cycle > int(self.total_cycles): @@ -2586,6 +2681,17 @@ def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, act self.vs_data[int(coordinate)] = {} self.vs_data[int(coordinate)][self.rotation_list[angle]] = params test_setup_info = self.create_test_setup_info(media_source=args.media_source, media_quality=args.media_quality) + if self.dowebgui: + self.copy_reports_to_home_dir() + # Update nav_data.json with test completion status for web GUI navigation + with open(nav_data, 'r') as x: + navdata = json.load(x) + navdata['status'] = '' + navdata['Canbee_location'] = '' + navdata['Canbee_angle'] = '' + navdata['Test_status'] = 'Completed' + with open(nav_data, 'w') as x: + json.dump(navdata, x, indent=4) self.generate_report_for_robo(test_setup_info, passed_coordinates=passed_coord_list) def build_iot_report_section(self, report, iot_summary): From 2db1b09b0596b88a81292cf1ea3c3c7917db5e88 Mon Sep 17 00:00:00 2001 From: litincandela Date: Mon, 6 Apr 2026 12:21:43 +0000 Subject: [PATCH 3/3] lf_interop_video_streaming.py: Added logic to skip coordinates whenever an obstacle is encountered while moving. VERIFIED CLI: python3 lf_interop_video_streaming.py --mgr 192.168.207.78 / --url "http://192.168.204.63/kalki/kalki.mpd" / --media_source dash / --media_quality 1080P / --duration 1m / --debug / --test_name video_streaming_test / --robot_test / --robot_ip 192.168.204.76 / --coordinate 3,4 / --total_cycles 1 / --do_bandsteering --- py-scripts/lf_interop_video_streaming.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/py-scripts/lf_interop_video_streaming.py b/py-scripts/lf_interop_video_streaming.py index fa28dc809..15ffd0556 100755 --- a/py-scripts/lf_interop_video_streaming.py +++ b/py-scripts/lf_interop_video_streaming.py @@ -91,7 +91,7 @@ Example-17: Command Line Interface to run Video Streaming test using a DASH stream hosted on the local LANforge server with Robot at specified coordinates with bandsteering - python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug + python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug --test_name video_streaming_test --robot_test --robot_ip 192.168.204.76 --coordinate 3,4 --total_cycles 1 --do_bandsteering SCRIPT CLASSIFICATION: Test @@ -176,7 +176,7 @@ def __init__(self, host, ssid, passwd, encryp, media_source, media_quality, supo coordinate=None, rotation=None, rotation_enabled=None, - angle_list=None, do_bandsteering=False, total_cycles=1, bssids=None): + angle_list=None, do_bandsteering=False, total_cycles=1, bssids=None, duration_to_skip=None): super().__init__(lfclient_host=host, lfclient_port=8080) self.adb_device_list = None self.host = host @@ -263,6 +263,7 @@ def __init__(self, host, ssid, passwd, encryp, media_source, media_quality, supo self.robot = RobotClass(robo_ip=self.robot_ip, angle_list=self.angle_list) self.last_rotated_angles = [] self.charge_point_name = None + self.robot.time_to_reach = int(duration_to_skip) * 60 @property def run(self): @@ -1666,7 +1667,6 @@ def generate_report(self, date, iterations_before_test_stopped_by_user, test_set for j in range(created_incremental_values[iter]): devices_on_running_state.append(keys[j]) device_names_on_running.append(username[j]) - # Added bandsteering stats to the report if bandsteering is enabled self.get_bandsteering_stats(report, realtime_dataset, devices_on_running_state, device_names_on_running) if iot_summary: self.build_iot_report_section(report, iot_summary) @@ -2683,7 +2683,6 @@ def perform_robo(self, args, individual_dataframe_columns, cx_order_list, i, act test_setup_info = self.create_test_setup_info(media_source=args.media_source, media_quality=args.media_quality) if self.dowebgui: self.copy_reports_to_home_dir() - # Update nav_data.json with test completion status for web GUI navigation with open(nav_data, 'r') as x: navdata = json.load(x) navdata['status'] = '' @@ -3016,9 +3015,9 @@ def main(): Example-15: Command Line Interface to run Video Streaming test using a DASH stream hosted on the local LANforge server with Robot at specified coordinates with bandsteering - python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug + python3 lf_interop_video_streaming.py --mgr 192.168.207.78 --url "http://192.168.204.63/kalki/kalki.mpd" --media_source dash --media_quality 1080P --duration 1m --debug --test_name video_streaming_test --robot_test --robot_ip 192.168.204.76 --coordinate 3,4 --total_cycles 1 --do_bandsteering - + SCRIPT CLASSIFICATION: Test SCRIPT_CATEGORIES: Performance, Functional, Report Generation @@ -3098,6 +3097,7 @@ def main(): parser.add_argument('--config', action='store_true', help='specify this flag whether to config devices or not') parser.add_argument("--device_csv_name", type=str, help="Specify the device csv name for pass/fail", default=None) # Args for robot testing + parser.add_argument('--duration_to_skip', help='Robot wait duration in seconds at obstacle', default="1") parser.add_argument("--robot_test", help='to trigger robot test', action='store_true') parser.add_argument('--robot_ip', type=str, default='localhost', help='hostname for where Robot server is running') parser.add_argument('--coordinate', type=str, default='', help="The coordinate contains list of coordinates to be ") @@ -3248,7 +3248,8 @@ def main(): angle_list=angle_list, do_bandsteering=args.do_bandsteering, total_cycles=args.total_cycles, - bssids=args.bssids.split(",") if args.bssids else [] + bssids=args.bssids.split(",") if args.bssids else [], + duration_to_skip=args.duration_to_skip ) args.upstream_port = obj.change_port_to_ip(args.upstream_port) obj.upstream_port = args.upstream_port