+ raise Exception(
+ f"Traffic loss {loss} above loss acceptance: {loss_acceptance}"
+ )
+
+ def _parse_traffic_results(self, stdout):
+ """Parse stdout of scripts into fields of self.
+
+ Block of code to reuse, by sync start, or stop after async.
+
+ :param stdout: Text containing the standard output.
+ :type stdout: str
+ """
+ subtype = check_subtype(self._node)
+ if subtype == NodeSubTypeTG.TREX:
+ # Last line from console output
+ line = stdout.splitlines()[-1]
+ results = line.split(u";")
+ if results[-1] in (u" ", u""):
+ results.pop(-1)
+ self._result = dict()
+ for result in results:
+ key, value = result.split(u"=", maxsplit=1)
+ self._result[key.strip()] = value
+ logger.info(f"TrafficGen results:\n{self._result}")
+ self._received = int(self._result.get(u"total_received"), 0)
+ self._sent = int(self._result.get(u"total_sent", 0))
+ self._loss = int(self._result.get(u"frame_loss", 0))
+ self._approximated_duration = \
+ self._result.get(u"approximated_duration", 0.0)
+ if u"manual" not in str(self._approximated_duration):
+ self._approximated_duration = float(self._approximated_duration)
+ self._latency = list()
+ self._latency.append(self._result.get(u"latency_stream_0(usec)"))
+ self._latency.append(self._result.get(u"latency_stream_1(usec)"))
+ if self._mode == TrexMode.ASTF:
+ self._l7_data = dict()
+ self._l7_data[u"client"] = dict()
+ self._l7_data[u"client"][u"sent"] = \
+ int(self._result.get(u"client_sent", 0))
+ self._l7_data[u"client"][u"received"] = \
+ int(self._result.get(u"client_received", 0))
+ self._l7_data[u"client"][u"active_flows"] = \
+ int(self._result.get(u"client_active_flows", 0))
+ self._l7_data[u"client"][u"established_flows"] = \
+ int(self._result.get(u"client_established_flows", 0))
+ self._l7_data[u"client"][u"traffic_duration"] = \
+ float(self._result.get(u"client_traffic_duration", 0.0))
+ self._l7_data[u"client"][u"err_rx_throttled"] = \
+ int(self._result.get(u"client_err_rx_throttled", 0))
+ self._l7_data[u"client"][u"err_c_nf_throttled"] = \
+ int(self._result.get(u"client_err_nf_throttled", 0))
+ self._l7_data[u"client"][u"err_flow_overflow"] = \
+ int(self._result.get(u"client_err_flow_overflow", 0))
+ self._l7_data[u"server"] = dict()
+ self._l7_data[u"server"][u"active_flows"] = \
+ int(self._result.get(u"server_active_flows", 0))
+ self._l7_data[u"server"][u"established_flows"] = \
+ int(self._result.get(u"server_established_flows", 0))
+ self._l7_data[u"server"][u"traffic_duration"] = \
+ float(self._result.get(u"server_traffic_duration", 0.0))
+ self._l7_data[u"server"][u"err_rx_throttled"] = \
+ int(self._result.get(u"client_err_rx_throttled", 0))
+ if u"udp" in self.traffic_profile:
+ self._l7_data[u"client"][u"udp"] = dict()
+ self._l7_data[u"client"][u"udp"][u"connects"] = \
+ int(self._result.get(u"client_udp_connects", 0))
+ self._l7_data[u"client"][u"udp"][u"closed_flows"] = \
+ int(self._result.get(u"client_udp_closed", 0))
+ self._l7_data[u"client"][u"udp"][u"tx_bytes"] = \
+ int(self._result.get(u"client_udp_tx_bytes", 0))
+ self._l7_data[u"client"][u"udp"][u"rx_bytes"] = \
+ int(self._result.get(u"client_udp_rx_bytes", 0))
+ self._l7_data[u"client"][u"udp"][u"tx_packets"] = \
+ int(self._result.get(u"client_udp_tx_packets", 0))
+ self._l7_data[u"client"][u"udp"][u"rx_packets"] = \
+ int(self._result.get(u"client_udp_rx_packets", 0))
+ self._l7_data[u"client"][u"udp"][u"keep_drops"] = \
+ int(self._result.get(u"client_udp_keep_drops", 0))
+ self._l7_data[u"client"][u"udp"][u"err_cwf"] = \
+ int(self._result.get(u"client_err_cwf", 0))
+ self._l7_data[u"server"][u"udp"] = dict()
+ self._l7_data[u"server"][u"udp"][u"accepted_flows"] = \
+ int(self._result.get(u"server_udp_accepts", 0))
+ self._l7_data[u"server"][u"udp"][u"closed_flows"] = \
+ int(self._result.get(u"server_udp_closed", 0))
+ self._l7_data[u"server"][u"udp"][u"tx_bytes"] = \
+ int(self._result.get(u"server_udp_tx_bytes", 0))
+ self._l7_data[u"server"][u"udp"][u"rx_bytes"] = \
+ int(self._result.get(u"server_udp_rx_bytes", 0))
+ self._l7_data[u"server"][u"udp"][u"tx_packets"] = \
+ int(self._result.get(u"server_udp_tx_packets", 0))
+ self._l7_data[u"server"][u"udp"][u"rx_packets"] = \
+ int(self._result.get(u"server_udp_rx_packets", 0))
+ elif u"tcp" in self.traffic_profile:
+ self._l7_data[u"client"][u"tcp"] = dict()
+ self._l7_data[u"client"][u"tcp"][u"initiated_flows"] = \
+ int(self._result.get(u"client_tcp_connect_inits", 0))
+ self._l7_data[u"client"][u"tcp"][u"connects"] = \
+ int(self._result.get(u"client_tcp_connects", 0))
+ self._l7_data[u"client"][u"tcp"][u"closed_flows"] = \
+ int(self._result.get(u"client_tcp_closed", 0))
+ self._l7_data[u"client"][u"tcp"][u"connattempt"] = \
+ int(self._result.get(u"client_tcp_connattempt", 0))
+ self._l7_data[u"client"][u"tcp"][u"tx_bytes"] = \
+ int(self._result.get(u"client_tcp_tx_bytes", 0))
+ self._l7_data[u"client"][u"tcp"][u"rx_bytes"] = \
+ int(self._result.get(u"client_tcp_rx_bytes", 0))
+ self._l7_data[u"server"][u"tcp"] = dict()
+ self._l7_data[u"server"][u"tcp"][u"accepted_flows"] = \
+ int(self._result.get(u"server_tcp_accepts", 0))
+ self._l7_data[u"server"][u"tcp"][u"connects"] = \
+ int(self._result.get(u"server_tcp_connects", 0))
+ self._l7_data[u"server"][u"tcp"][u"closed_flows"] = \
+ int(self._result.get(u"server_tcp_closed", 0))
+ self._l7_data[u"server"][u"tcp"][u"tx_bytes"] = \
+ int(self._result.get(u"server_tcp_tx_bytes", 0))
+ self._l7_data[u"server"][u"tcp"][u"rx_bytes"] = \
+ int(self._result.get(u"server_tcp_rx_bytes", 0))
+
+ def _get_measurement_result(self):
+ """Return the result of last measurement as ReceiveRateMeasurement.
+
+ Separate function, as measurements can end either by time
+ or by explicit call, this is the common block at the end.
+
+ The target_tr field of ReceiveRateMeasurement is in
+ transactions per second. Transmit count and loss count units
+ depend on the transaction type. Usually they are in transactions
+ per second, or aggregated packets per second.
+
+ TODO: Fail on running or already reported measurement.
+
+ :returns: Structure containing the result of the measurement.
+ :rtype: ReceiveRateMeasurement
+ """
+ try:
+ # Client duration seems to include a setup period
+ # where TRex does not send any packets yet.
+ # Server duration does not include it.
+ server_data = self._l7_data[u"server"]
+ approximated_duration = float(server_data[u"traffic_duration"])
+ except (KeyError, AttributeError, ValueError, TypeError):
+ approximated_duration = None
+ try:
+ if not approximated_duration:
+ approximated_duration = float(self._approximated_duration)
+ except ValueError: # "manual"
+ approximated_duration = None
+ if not approximated_duration:
+ if self._duration and self._duration > 0:
+ # Known recomputed or target duration.
+ approximated_duration = self._duration
+ else:
+ # It was an explicit stop.
+ if not self._stop_time:
+ raise RuntimeError(u"Unable to determine duration.")
+ approximated_duration = self._stop_time - self._start_time
+ target_duration = self._target_duration
+ if not target_duration:
+ target_duration = approximated_duration
+ transmit_rate = self._rate
+ unsent = 0
+ if self.transaction_type == u"packet":
+ partial_attempt_count = self._sent
+ packet_rate = transmit_rate * self.ppta
+ # We have a float. TRex way of rounding it is not obvious.
+ # The biggest source of mismatch is Inter Stream Gap.
+ # So the code tolerates 10 usec of missing packets.
+ expected_attempt_count = (target_duration - 1e-5) * packet_rate
+ expected_attempt_count = math.ceil(expected_attempt_count)
+ # TRex can send more.
+ expected_attempt_count = max(expected_attempt_count, self._sent)
+ unsent = expected_attempt_count - self._sent
+ pass_count = self._received
+ fail_count = expected_attempt_count - pass_count
+ elif self.transaction_type == u"udp_cps":
+ if not self.transaction_scale:
+ raise RuntimeError(u"Add support for no-limit udp_cps.")
+ partial_attempt_count = self._l7_data[u"client"][u"sent"]
+ # We do not care whether TG is slow, it should have attempted all.
+ expected_attempt_count = self.transaction_scale
+ unsent = expected_attempt_count - partial_attempt_count
+ pass_count = self._l7_data[u"client"][u"received"]
+ fail_count = expected_attempt_count - pass_count
+ elif self.transaction_type == u"tcp_cps":
+ if not self.transaction_scale:
+ raise RuntimeError(u"Add support for no-limit tcp_cps.")
+ ctca = self._l7_data[u"client"][u"tcp"][u"connattempt"]
+ partial_attempt_count = ctca
+ # We do not care whether TG is slow, it should have attempted all.
+ expected_attempt_count = self.transaction_scale
+ unsent = expected_attempt_count - partial_attempt_count
+ # From TCP point of view, server/connects counts full connections,
+ # but we are testing NAT session so client/connects counts that
+ # (half connections from TCP point of view).
+ pass_count = self._l7_data[u"client"][u"tcp"][u"connects"]
+ fail_count = expected_attempt_count - pass_count
+ elif self.transaction_type == u"udp_pps":
+ if not self.transaction_scale:
+ raise RuntimeError(u"Add support for no-limit udp_pps.")
+ partial_attempt_count = self._sent
+ expected_attempt_count = self.transaction_scale * self.ppta
+ unsent = expected_attempt_count - self._sent
+ fail_count = self._loss + unsent
+ elif self.transaction_type == u"tcp_pps":
+ if not self.transaction_scale:
+ raise RuntimeError(u"Add support for no-limit tcp_pps.")
+ partial_attempt_count = self._sent
+ expected_attempt_count = self.transaction_scale * self.ppta
+ # One loss-like scenario happens when TRex receives all packets
+ # on L2 level, but is not fast enough to process them all
+ # at L7 level, which leads to retransmissions.
+ # Those manifest as opackets larger than expected.
+ # A simple workaround is to add absolute difference.
+ # Probability of retransmissions exactly cancelling
+ # packets unsent due to duration stretching is quite low.
+ unsent = abs(expected_attempt_count - self._sent)
+ fail_count = self._loss + unsent
+ else:
+ raise RuntimeError(f"Unknown parsing {self.transaction_type!r}")
+ if unsent and isinstance(self._approximated_duration, float):
+ # Do not report unsent for "manual".
+ logger.debug(f"Unsent packets/transactions: {unsent}")
+ if fail_count < 0 and not self.negative_loss:
+ fail_count = 0
+ measurement = ReceiveRateMeasurement(
+ duration=target_duration,
+ target_tr=transmit_rate,
+ transmit_count=expected_attempt_count,
+ loss_count=fail_count,
+ approximated_duration=approximated_duration,
+ partial_transmit_count=partial_attempt_count,
+ )
+ measurement.latency = self.get_latency_int()
+ return measurement
+
+ def measure(self, duration, transmit_rate):
+ """Run trial measurement, parse and return results.
+
+ The input rate is for transactions. Stateles bidirectional traffic
+ is understood as sequence of (asynchronous) transactions,
+ two packets each.
+
+ The result units depend on test type, generally
+ the count either transactions or packets (aggregated over directions).
+
+ Optionally, this method sleeps if measurement finished before
+ the time specified as duration.
+
+ :param duration: Trial duration [s].
+ :param transmit_rate: Target rate in transactions per second.
+ :type duration: float
+ :type transmit_rate: float
+ :returns: Structure containing the result of the measurement.
+ :rtype: ReceiveRateMeasurement
+ :raises RuntimeError: If TG is not set or if node is not TG
+ or if subtype is not specified.
+ :raises NotImplementedError: If TG is not supported.
+ """
+ duration = float(duration)
+ time_start = time.monotonic()
+ time_stop = time_start + duration
+ if self.resetter:
+ self.resetter()
+ result = self._send_traffic_on_tg_with_ramp_up(
+ duration=duration,
+ rate=transmit_rate,
+ async_call=False,
+ )
+ logger.debug(f"trial measurement result: {result!r}")
+ # In PLRsearch, computation needs the specified time to complete.
+ if self.sleep_till_duration:
+ sleeptime = time_stop - time.monotonic()
+ if sleeptime > 0.0:
+ # TODO: Sometimes we have time to do additional trials here,
+ # adapt PLRsearch to accept all the results.
+ time.sleep(sleeptime)
+ return result
+
+ def set_rate_provider_defaults(
+ self,
+ frame_size,
+ traffic_profile,
+ ppta=1,
+ resetter=None,
+ traffic_directions=2,
+ transaction_duration=0.0,
+ transaction_scale=0,
+ transaction_type=u"packet",
+ duration_limit=0.0,
+ negative_loss=True,
+ sleep_till_duration=False,
+ use_latency=False,
+ ramp_up_rate=None,
+ ramp_up_duration=None,
+ state_timeout=240.0,
+ ):
+ """Store values accessed by measure().
+
+ :param frame_size: Frame size identifier or value [B].
+ :param traffic_profile: Module name as a traffic profile identifier.
+ See GPL/traffic_profiles/trex for implemented modules.
+ :param ppta: Packets per transaction, aggregated over directions.
+ Needed for udp_pps which does not have a good transaction counter,
+ so we need to compute expected number of packets.
+ Default: 1.
+ :param resetter: Callable to reset DUT state for repeated trials.
+ :param traffic_directions: Traffic from packet counting point of view
+ is bi- (2) or uni- (1) directional.
+ Default: 2
+ :param transaction_duration: Total expected time to close transaction.
+ :param transaction_scale: Number of transactions to perform.
+ 0 (default) means unlimited.
+ :param transaction_type: An identifier specifying which counters
+ and formulas to use when computing attempted and failed
+ transactions. Default: "packet".
+ TODO: Does this also specify parsing for the measured duration?
+ :param duration_limit: Zero or maximum limit for computed (or given)
+ duration.
+ :param negative_loss: If false, negative loss is reported as zero loss.
+ :param sleep_till_duration: If true and measurement returned faster,
+ sleep until it matches duration. Needed for PLRsearch.
+ :param use_latency: Whether to measure latency during the trial.
+ Default: False.
+ :param ramp_up_rate: Rate to use in ramp-up trials [pps].
+ :param ramp_up_duration: Duration of ramp-up trials [s].
+ :param state_timeout: Time of life of DUT state [s].
+ :type frame_size: str or int
+ :type traffic_profile: str
+ :type ppta: int
+ :type resetter: Optional[Callable[[], None]]
+ :type traffic_directions: int
+ :type transaction_duration: float
+ :type transaction_scale: int
+ :type transaction_type: str
+ :type duration_limit: float
+ :type negative_loss: bool
+ :type sleep_till_duration: bool
+ :type use_latency: bool
+ :type ramp_up_rate: float
+ :type ramp_up_duration: float
+ :type state_timeout: float
+ """
+ self.frame_size = frame_size
+ self.traffic_profile = str(traffic_profile)
+ self.resetter = resetter
+ self.ppta = ppta
+ self.traffic_directions = int(traffic_directions)
+ self.transaction_duration = float(transaction_duration)
+ self.transaction_scale = int(transaction_scale)
+ self.transaction_type = str(transaction_type)
+ self.duration_limit = float(duration_limit)
+ self.negative_loss = bool(negative_loss)
+ self.sleep_till_duration = bool(sleep_till_duration)
+ self.use_latency = bool(use_latency)
+ self.ramp_up_rate = float(ramp_up_rate)
+ self.ramp_up_duration = float(ramp_up_duration)
+ self.state_timeout = float(state_timeout)
+
+
+class OptimizedSearch:
+ """Class to be imported as Robot Library, containing search keywords.
+
+ Aside of setting up measurer and forwarding arguments,
+ the main business is to translate min/max rate from unidir to aggregated.
+ """
+
+ @staticmethod
+ def perform_optimized_ndrpdr_search(
+ frame_size,
+ traffic_profile,
+ minimum_transmit_rate,
+ maximum_transmit_rate,
+ packet_loss_ratio=0.005,
+ final_relative_width=0.005,
+ final_trial_duration=30.0,
+ initial_trial_duration=1.0,
+ number_of_intermediate_phases=2,
+ timeout=1200.0,
+ ppta=1,
+ resetter=None,
+ traffic_directions=2,
+ transaction_duration=0.0,
+ transaction_scale=0,
+ transaction_type=u"packet",
+ use_latency=False,
+ ramp_up_rate=None,
+ ramp_up_duration=None,
+ state_timeout=240.0,
+ expansion_coefficient=4.0,
+ ):
+ """Setup initialized TG, perform optimized search, return intervals.
+
+ If transaction_scale is nonzero, all init and non-init trial durations
+ are set to 1.0 (as they do not affect the real trial duration)
+ and zero intermediate phases are used.
+ This way no re-measurement happens.
+ Warmup has to be handled via resetter or ramp-up mechanisms.
+
+ :param frame_size: Frame size identifier or value [B].
+ :param traffic_profile: Module name as a traffic profile identifier.
+ See GPL/traffic_profiles/trex for implemented modules.
+ :param minimum_transmit_rate: Minimal load in transactions per second.
+ :param maximum_transmit_rate: Maximal load in transactions per second.
+ :param packet_loss_ratio: Ratio of packets lost, for PDR [1].
+ :param final_relative_width: Final lower bound transmit rate
+ cannot be more distant that this multiple of upper bound [1].
+ :param final_trial_duration: Trial duration for the final phase [s].
+ :param initial_trial_duration: Trial duration for the initial phase
+ and also for the first intermediate phase [s].
+ :param number_of_intermediate_phases: Number of intermediate phases
+ to perform before the final phase [1].
+ :param timeout: The search will fail itself when not finished
+ before this overall time [s].
+ :param ppta: Packets per transaction, aggregated over directions.
+ Needed for udp_pps which does not have a good transaction counter,
+ so we need to compute expected number of packets.
+ Default: 1.
+ :param resetter: Callable to reset DUT state for repeated trials.
+ :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+ Default: 2
+ :param transaction_duration: Total expected time to close transaction.
+ :param transaction_scale: Number of transactions to perform.
+ 0 (default) means unlimited.
+ :param transaction_type: An identifier specifying which counters
+ and formulas to use when computing attempted and failed
+ transactions. Default: "packet".
+ :param use_latency: Whether to measure latency during the trial.
+ Default: False.
+ :param ramp_up_rate: Rate to use in ramp-up trials [pps].
+ :param ramp_up_duration: Duration of ramp-up trials [s].
+ :param state_timeout: Time of life of DUT state [s].
+ :param expansion_coefficient: In external search multiply width by this.
+ :type frame_size: str or int
+ :type traffic_profile: str
+ :type minimum_transmit_rate: float
+ :type maximum_transmit_rate: float
+ :type packet_loss_ratio: float
+ :type final_relative_width: float
+ :type final_trial_duration: float
+ :type initial_trial_duration: float
+ :type number_of_intermediate_phases: int
+ :type timeout: float
+ :type ppta: int
+ :type resetter: Optional[Callable[[], None]]
+ :type traffic_directions: int
+ :type transaction_duration: float
+ :type transaction_scale: int
+ :type transaction_type: str
+ :type use_latency: bool
+ :type ramp_up_rate: float
+ :type ramp_up_duration: float
+ :type state_timeout: float
+ :type expansion_coefficient: float
+ :returns: Structure containing narrowed down NDR and PDR intervals
+ and their measurements.
+ :rtype: List[Receiverateinterval]
+ :raises RuntimeError: If total duration is larger than timeout.
+ """
+ # we need instance of TrafficGenerator instantiated by Robot Framework
+ # to be able to use trex_stl-*()
+ tg_instance = BuiltIn().get_library_instance(
+ u"resources.libraries.python.TrafficGenerator"
+ )
+ # Overrides for fixed transaction amount.
+ # TODO: Move to robot code? We have two call sites, so this saves space,
+ # even though this is surprising for log readers.
+ if transaction_scale:
+ initial_trial_duration = 1.0
+ final_trial_duration = 1.0
+ number_of_intermediate_phases = 0
+ timeout += transaction_scale * 3e-4
+ tg_instance.set_rate_provider_defaults(
+ frame_size=frame_size,
+ traffic_profile=traffic_profile,
+ sleep_till_duration=False,
+ ppta=ppta,
+ resetter=resetter,
+ traffic_directions=traffic_directions,
+ transaction_duration=transaction_duration,
+ transaction_scale=transaction_scale,
+ transaction_type=transaction_type,
+ use_latency=use_latency,
+ ramp_up_rate=ramp_up_rate,
+ ramp_up_duration=ramp_up_duration,
+ state_timeout=state_timeout,
+ )
+ algorithm = MultipleLossRatioSearch(
+ measurer=tg_instance,
+ final_trial_duration=final_trial_duration,
+ final_relative_width=final_relative_width,
+ number_of_intermediate_phases=number_of_intermediate_phases,
+ initial_trial_duration=initial_trial_duration,
+ timeout=timeout,
+ debug=logger.debug,
+ expansion_coefficient=expansion_coefficient,
+ )
+ if packet_loss_ratio:
+ packet_loss_ratios = [0.0, packet_loss_ratio]
+ else:
+ # Happens in reconf tests.
+ packet_loss_ratios = [packet_loss_ratio]
+ results = algorithm.narrow_down_intervals(
+ min_rate=minimum_transmit_rate,
+ max_rate=maximum_transmit_rate,
+ packet_loss_ratios=packet_loss_ratios,
+ )
+ return results
+
+ @staticmethod
+ def perform_soak_search(
+ frame_size,
+ traffic_profile,
+ minimum_transmit_rate,
+ maximum_transmit_rate,
+ plr_target=1e-7,
+ tdpt=0.1,
+ initial_count=50,
+ timeout=7200.0,
+ ppta=1,
+ resetter=None,
+ trace_enabled=False,
+ traffic_directions=2,
+ transaction_duration=0.0,
+ transaction_scale=0,
+ transaction_type=u"packet",
+ use_latency=False,
+ ramp_up_rate=None,
+ ramp_up_duration=None,
+ state_timeout=240.0,
+ ):
+ """Setup initialized TG, perform soak search, return avg and stdev.
+
+ :param frame_size: Frame size identifier or value [B].
+ :param traffic_profile: Module name as a traffic profile identifier.
+ See GPL/traffic_profiles/trex for implemented modules.
+ :param minimum_transmit_rate: Minimal load in transactions per second.
+ :param maximum_transmit_rate: Maximal load in transactions per second.
+ :param plr_target: Ratio of packets lost to achieve [1].
+ :param tdpt: Trial duration per trial.
+ The algorithm linearly increases trial duration with trial number,
+ this is the increment between succesive trials, in seconds.
+ :param initial_count: Offset to apply before the first trial.
+ For example initial_count=50 makes first trial to be 51*tdpt long.
+ This is needed because initial "search" phase of integrator
+ takes significant time even without any trial results.
+ :param timeout: The search will stop after this overall time [s].
+ :param ppta: Packets per transaction, aggregated over directions.
+ Needed for udp_pps which does not have a good transaction counter,
+ so we need to compute expected number of packets.
+ Default: 1.
+ :param resetter: Callable to reset DUT state for repeated trials.
+ :param trace_enabled: True if trace enabled else False.
+ This is very verbose tracing on numeric computations,
+ do not use in production.
+ Default: False
+ :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+ Default: 2
+ :param transaction_duration: Total expected time to close transaction.
+ :param transaction_scale: Number of transactions to perform.
+ 0 (default) means unlimited.
+ :param transaction_type: An identifier specifying which counters
+ and formulas to use when computing attempted and failed
+ transactions. Default: "packet".
+ :param use_latency: Whether to measure latency during the trial.
+ Default: False.
+ :param ramp_up_rate: Rate to use in ramp-up trials [pps].
+ :param ramp_up_duration: Duration of ramp-up trials [s].
+ :param state_timeout: Time of life of DUT state [s].
+ :type frame_size: str or int
+ :type traffic_profile: str
+ :type minimum_transmit_rate: float
+ :type maximum_transmit_rate: float
+ :type plr_target: float
+ :type initial_count: int
+ :type timeout: float
+ :type ppta: int
+ :type resetter: Optional[Callable[[], None]]
+ :type trace_enabled: bool
+ :type traffic_directions: int
+ :type transaction_duration: float
+ :type transaction_scale: int
+ :type transaction_type: str
+ :type use_latency: bool
+ :type ramp_up_rate: float
+ :type ramp_up_duration: float
+ :type state_timeout: float
+ :returns: Average and stdev of estimated aggregated rate giving PLR.
+ :rtype: 2-tuple of float
+ """
+ tg_instance = BuiltIn().get_library_instance(
+ u"resources.libraries.python.TrafficGenerator"
+ )
+ # Overrides for fixed transaction amount.
+ # TODO: Move to robot code? We have a single call site
+ # but MLRsearch has two and we want the two to be used similarly.
+ if transaction_scale:
+ # TODO: What is a good value for max scale?
+ # TODO: Scale the timeout with transaction scale.
+ timeout = 7200.0
+ tg_instance.set_rate_provider_defaults(
+ frame_size=frame_size,
+ traffic_profile=traffic_profile,
+ negative_loss=False,
+ sleep_till_duration=True,
+ ppta=ppta,
+ resetter=resetter,
+ traffic_directions=traffic_directions,
+ transaction_duration=transaction_duration,
+ transaction_scale=transaction_scale,
+ transaction_type=transaction_type,
+ use_latency=use_latency,
+ ramp_up_rate=ramp_up_rate,
+ ramp_up_duration=ramp_up_duration,
+ state_timeout=state_timeout,
+ )
+ algorithm = PLRsearch(
+ measurer=tg_instance,
+ trial_duration_per_trial=tdpt,
+ packet_loss_ratio_target=plr_target,
+ trial_number_offset=initial_count,
+ timeout=timeout,
+ trace_enabled=trace_enabled,
+ )
+ result = algorithm.search(
+ min_rate=minimum_transmit_rate,
+ max_rate=maximum_transmit_rate,
+ )
+ return result