Skip to content

Commit

Permalink
fix: fixed stackedbar when time reversal occurs (#517)
Browse files Browse the repository at this point in the history
* Fixed StackedBar when time reversal occurs

* empty dataframe and reflect review results

* time_reversed not used

* reflected review results

* test code added

* reflected review results

* reflected review results
  • Loading branch information
miyakoshi-dev authored Sep 2, 2024
1 parent 2ca8d9b commit 2597e77
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 25 deletions.
18 changes: 13 additions & 5 deletions src/caret_analyze/plot/stacked_bar/latency_stacked_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from ..util import get_clock_converter
from ...common import ClockConverter
from ...record import RecordsInterface, ResponseTime, StackedBar
from ...record import ColumnValue, RecordsInterface, ResponseTime, StackedBar
from ...runtime import Path


Expand Down Expand Up @@ -113,17 +113,25 @@ def _get_response_time_record(
columns=target_object.column_names)
# include timestamp of response time (best, worst)
if self._case == 'all':
return response_time.to_all_stacked_bar()
record_if = response_time.to_all_stacked_bar()
elif self._case == 'best':
return response_time.to_best_case_stacked_bar()
record_if = response_time.to_best_case_stacked_bar()
elif self._case == 'worst':
return response_time.to_worst_case_stacked_bar()
record_if = response_time.to_worst_case_stacked_bar()
elif self._case == 'worst-with-external-latency':
return response_time.to_worst_with_external_latency_case_stacked_bar()
record_if = response_time.to_worst_with_external_latency_case_stacked_bar()
else:
raise ValueError('optional argument "case" must be following: \
"all", "best", "worst", "worst-with-external-latency".')

if response_time._has_invalid_timestamps() is True:
columns = []
columns += [ColumnValue(column) for column in response_time._records._columns]
columns += [ColumnValue('invalid_timestamps')]
record_if = response_time._records._create_empty_records(columns)

return record_if

@property
def target_objects(self) -> Path:
return self._target_objects
31 changes: 18 additions & 13 deletions src/caret_analyze/plot/visualize_lib/bokeh/stacked_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ def create_figure(self) -> Figure:
title: str = f'Stacked bar of response_time of {path_name} --- {self._case} case ---'

fig = init_figure(title, self._ywheel_zoom, self._xaxis_type, y_axis_label)
frame_min = data['start time'][0]
frame_max = data['start time'][-1]
if len(data['start time']) == 0:
frame_min: float = 0
frame_max: float = 0
else:
frame_min = data['start time'][0]
frame_max = data['start time'][-1]
x_label = 'start time'
if self._xaxis_type == 'system_time' or self._xaxis_type == 'sim_time':
apply_x_axis_offset(fig, frame_min, frame_max)
Expand Down Expand Up @@ -122,19 +126,20 @@ def __init__(
for prev_, next_ in zip(reversed(y_labels[:-1]), reversed(y_labels[1:])):
data[prev_] = [data[prev_][i] + data[next_][i] for i in range(len(data[next_]))]

if xaxis_type == 'system_time' or xaxis_type == 'sim_time':
# Update the timestamps from absolutely time to offset time
data[x_label] = self._updated_timestamps_to_offset_time(
data[x_label])
if len(data['start time']) != 0:
if xaxis_type == 'system_time' or xaxis_type == 'sim_time':
# Update the timestamps from absolutely time to offset time
data[x_label] = self._updated_timestamps_to_offset_time(
data[x_label])

x_width_list = self._get_x_width_list(data[x_label])
half_width_list = [x / 2 for x in x_width_list]
x_width_list = self._get_x_width_list(data[x_label])
half_width_list = [x / 2 for x in x_width_list]

# Slide x axis values so that the bottom left of bars are the start time.
data[x_label] = self._add_shift_value(data[x_label], half_width_list)
else: # index
data[x_label] = list(range(0, len(data[y_labels[0]])))
x_width_list = self._get_x_width_list(data[x_label])
# Slide x axis values so that the bottom left of bars are the start time.
data[x_label] = self._add_shift_value(data[x_label], half_width_list)
else: # index
data[x_label] = list(range(0, len(data[y_labels[0]])))
x_width_list = self._get_x_width_list(data[x_label])

self._data: dict[str, list[int | float]] = data
self._x_width_list: list[float] = x_width_list
Expand Down
17 changes: 17 additions & 0 deletions src/caret_analyze/record/records_service/response_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def __init__(
"""
self._columns = columns
self._time_reversed_count = 0
new_data = {}

input_min_time = None
Expand All @@ -128,6 +129,7 @@ def __init__(
input_time, output_time = data.get(self.input_column), data.get(self.output_column)

if self._has_reversed_timestamp(data):
self._time_reversed_count += 1
continue

if input_min_time is None:
Expand Down Expand Up @@ -762,6 +764,21 @@ def to_worst_with_external_latency_case_stacked_bar(self) -> RecordsInterface:
"""
return self._records.to_range_records('worst-with-external-latency')

def _has_invalid_timestamps(self) -> bool:
"""
Invalid timestamp judgement.
Returns
-------
bool
Invalid timestamp judgement result.
"""
if self._records._response_map._time_reversed_count != 0:
return True
else:
return False


class ResponseRecords:

Expand Down
20 changes: 13 additions & 7 deletions src/caret_analyze/record/records_service/stacked_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,17 @@ def __init__(
if converter:
series_seq = [round(converter.convert(float(t))) for t in series_seq if t is not None]
series_list: list[int] = self._convert_sequence_to_list(series_seq)
stacked_bar_records = \
self._merge_column_series(
stacked_bar_records,
series_list,
xlabel,
)
if 'invalid_timestamps' in records.columns:
# if there is an invalid timestamp, only create a column
dummy_list: list[int] = []
stacked_bar_records.append_column(ColumnValue(xlabel), dummy_list)
else:
stacked_bar_records = \
self._merge_column_series(
stacked_bar_records,
series_list,
xlabel,
)
self._stacked_bar_records = stacked_bar_records
self._columns = columns[:-1]

Expand Down Expand Up @@ -267,9 +272,10 @@ def _to_dict(self, records: RecordsInterface) -> dict[str, list[int]]:
"""
columns = records.columns
series_seq: Sequence[int | None]
output_dict: dict[str, list[int]] = {}
for column in columns:
series_seq: Sequence[int | None] = records.get_column_series(column)
series_seq = records.get_column_series(column)
series_list: list[int] = self._convert_sequence_to_list(series_seq)
output_dict[column] = series_list
return output_dict
Expand Down
51 changes: 51 additions & 0 deletions src/test/plot/stacked_bar/test_latency_stacked_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from caret_analyze.plot.stacked_bar import LatencyStackedBar
from caret_analyze.record import ColumnValue, RecordsFactory
from caret_analyze.record import RecordsInterface
from caret_analyze.runtime import Path

import pandas as pd
Expand All @@ -35,6 +36,16 @@ def _create_mock(data, columns):
return _create_mock


def create_records(records_raw, columns):
records = RecordsFactory.create_instance()
for column in columns:
records.append_column(column, [])

for record_raw in records_raw:
records.append(record_raw)
return records


def get_data_set():
columns = [
'/columns_0/rclcpp_publish_timestamp/0_min',
Expand Down Expand Up @@ -115,3 +126,43 @@ def test_to_dataframe(self, create_mock):
# create stacked bar data
output_df = stacked_bar_plot.to_dataframe()
assert output_df.equals(expect_df)

@pytest.mark.parametrize(
'case',
['all', 'best', 'worst', 'worst-with-external-latency']
)
def test_invalid_timestamps(self, mocker, case):
data = []
columns = []
records_columns = [ColumnValue(column) for column in columns]
records = RecordsFactory.create_instance(data, columns=records_columns)
target_objects = mocker.Mock(spec=Path)
records_Interface_mock = mocker.Mock(spec=RecordsInterface)

records_raw = [
{'start': 0, 'end': 1},
{'start': 5, 'end': 4},
{'start': 7, 'end': 8},
]
columns = [ColumnValue('start'), ColumnValue('end')]
records = create_records(records_raw, columns)

mocker.patch.object(target_objects, 'column_names', ['start', 'end'])
mocker.patch.object(target_objects, 'to_records', return_value=records)

mock_path = 'caret_analyze.record.records_service.response_time.ResponseTime.'
mocker.patch(mock_path+'to_all_stacked_bar',
return_value=records_Interface_mock)
mocker.patch(mock_path+'to_best_case_stacked_bar',
return_value=records_Interface_mock)
mocker.patch(mock_path+'to_worst_case_stacked_bar',
return_value=records_Interface_mock)
mocker.patch(mock_path+'to_worst_with_external_latency_case_stacked_bar',
return_value=records_Interface_mock)

stacked_bar_plot = LatencyStackedBar(target_objects)
stacked_bar_plot._case = case
response_records: RecordsInterface = \
stacked_bar_plot._get_response_time_record(stacked_bar_plot._target_objects)
assert response_records.columns[2] == 'invalid_timestamps'
assert len(response_records.data) == 0
25 changes: 25 additions & 0 deletions src/test/record/records_service/test_response_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,31 @@ def test_drop_case(self):
result = to_dict(response.to_worst_with_external_latency_case_stacked_bar())
assert result == expect_raw

def test_reversed_timestamp_case(self):
records_raw = [
{'start': 0, 'end': 1},
{'start': 5, 'end': 4},
{'start': 7, 'end': 8},
]
columns = [ColumnValue('start'), ColumnValue('end')]

records = create_records(records_raw, columns)
response = ResponseTime(records)

assert response._has_invalid_timestamps() is True

records_raw = [
{'start': 0, 'end': 1},
{'start': 4, 'end': 5},
{'start': 7, 'end': 8},
]
columns = [ColumnValue('start'), ColumnValue('end')]

records = create_records(records_raw, columns)
response = ResponseTime(records)

assert response._has_invalid_timestamps() is False

class TestMultiColumnCase:
records_raw = [
{'column0': 5, 'column1': 10, 'column2': 15}, # flow 1 [used as first data]
Expand Down
24 changes: 24 additions & 0 deletions src/test/record/records_service/test_stacked_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ def get_data_set():
return columns, data, expect_columns, expect_dict


def get_data_set_no_data():
columns = [
'/columns_8/callback_0/callback_start_timestamp/0',
'/columns_9/callback_0/callback_end_timestamp/0',
'invalid_timestamps',
]
expect_dict = {
}
expect_columns = [
'/columns_8/callback_0',
]
data = []

return columns, data, expect_columns, expect_dict


class TestStackedBar:

def test_empty_case(self):
Expand Down Expand Up @@ -113,3 +129,11 @@ def test_records(self):
stacked_bar = StackedBar(records)
result = to_dict(stacked_bar.records)
assert result == expect_dict

def test_invalid_timestamps(self):
columns, data, expect_columns, _ = get_data_set_no_data()
records: RecordsInterface = create_records(data, columns)

stacked_bar = StackedBar(records)
assert stacked_bar.columns == expect_columns
assert len(stacked_bar.records.data) == 0

0 comments on commit 2597e77

Please sign in to comment.