summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-09-04 15:25:42 +0300
committerimarom <imarom@cisco.com>2016-09-07 14:02:55 +0300
commit04eae221e7c0089ae974f86e3f6fe156d4cb56ce (patch)
tree53b378e86f8173a8cc1f4bf2107ce6f797a91760
parent873e398fe2a52655b4d683acbcd05ef726cd97fa (diff)
DUAL mode - phase #1
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py2
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py40
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py6
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py2
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py6
-rwxr-xr-xsrc/common/erf.cpp17
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp22
-rw-r--r--src/stateless/cp/trex_stateless_port.cpp30
-rw-r--r--src/stateless/cp/trex_stateless_port.h11
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.cpp17
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.h6
-rw-r--r--src/stateless/dp/trex_stream_node.h15
-rw-r--r--src/stateless/messaging/trex_stateless_messaging.cpp6
-rw-r--r--src/stateless/messaging/trex_stateless_messaging.h6
14 files changed, 133 insertions, 53 deletions
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
index e769b9b2..c020f532 100644
--- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
@@ -1000,7 +1000,7 @@ class AsyncKeysEngineConsole:
for x in os.listdir(d):
if os.path.isdir(os.path.join(d, x)):
files.append(x + '/')
- elif x.endswith('.py') or x.endswith('yaml') or x.endswith('pcap') or x.endswith('cap'):
+ elif x.endswith( ('.py', 'yaml', 'pcap', 'cap', 'erf') ):
files.append(x)
# dir might not have the files
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
index 7101b8a2..2c1fa5e9 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
@@ -743,13 +743,17 @@ class STLClient(object):
return rc
- def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration):
+ def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual):
port_id_list = self.__ports(port_id_list)
rc = RC()
for port_id in port_id_list:
- rc.add(self.ports[port_id].push_remote(pcap_filename, ipg_usec, speedup, count, duration))
+
+ # for dual, provide the slave handler as well
+ slave_handler = self.ports[port_id ^ 0x1].handler if is_dual else ""
+
+ rc.add(self.ports[port_id].push_remote(pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler))
return rc
@@ -2183,7 +2187,8 @@ class STLClient(object):
ipg_usec = None,
speedup = 1.0,
count = 1,
- duration = -1):
+ duration = -1,
+ is_dual = False):
"""
Push a remote server-reachable PCAP file
the path must be fullpath accessible to the server
@@ -2206,6 +2211,13 @@ class STLClient(object):
duration: float
Limit runtime by duration in seconds
+
+ is_dual: bool
+ Inject from both directions.
+ requires ERF file with meta data for direction.
+ also requires that all the ports will be in master mode
+ with their adjacent ports as slaves
+
:raises:
+ :exc:`STLError`
@@ -2218,9 +2230,23 @@ class STLClient(object):
validate_type('speedup', speedup, (float, int))
validate_type('count', count, int)
validate_type('duration', duration, (float, int))
+ validate_type('is_dual', is_dual, bool)
+
+ # for dual mode check that all are masters
+ if is_dual:
+ for port in ports:
+ master = port
+ slave = port ^ 0x1
+
+ if slave in ports:
+ raise STLError("dual mode: cannot provide adjacent ports ({0}, {1}) in a batch".format(master, slave))
+
+ if not slave in self.get_acquired_ports():
+ raise STLError("dual mode: port {0} must be owned as well".format(slave))
+
self.logger.pre_cmd("Pushing remote PCAP on port(s) {0}:".format(ports))
- rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration)
+ rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual)
self.logger.post_cmd(rc)
if not rc:
@@ -3023,7 +3049,8 @@ class STLClient(object):
parsing_opts.DURATION,
parsing_opts.IPG,
parsing_opts.SPEEDUP,
- parsing_opts.FORCE)
+ parsing_opts.FORCE,
+ parsing_opts.DUAL)
opts = parser.parse_args(line.split())
if not opts:
@@ -3046,7 +3073,8 @@ class STLClient(object):
ipg_usec = opts.ipg_usec,
speedup = opts.speedup,
count = opts.count,
- duration = opts.duration)
+ duration = opts.duration,
+ is_dual = opts.dual)
else:
self.push_pcap(opts.file[0],
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
index 890ce7de..f0e3b109 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
@@ -582,7 +582,7 @@ class Port(object):
return self.ok()
@writeable
- def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration):
+ def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler):
params = {"handler": self.handler,
"port_id": self.port_id,
@@ -590,7 +590,9 @@ class Port(object):
"ipg_usec": ipg_usec if ipg_usec is not None else -1,
"speedup": speedup,
"count": count,
- "duration": duration}
+ "duration": duration,
+ "is_dual": is_dual,
+ "slave_handler": slave_handler}
rc = self.transmit("push_remote", params)
if rc.bad():
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
index afb01791..4586f647 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
@@ -1008,7 +1008,7 @@ class CPortStats(CTRexStats):
bps_L1 = calc_bps_L1(bps, pps)
bps_rx_L1 = calc_bps_L1(rx_bps, rx_pps)
snapshot['m_total_tx_bps_L1'] = bps_L1
- snapshot['m_percentage'] = (bps_L1 / self._port_obj.get_speed_bps()) * 100
+ snapshot['m_percentage'] = (bps_L1 / self._port_obj.get_speed_bps()) * 100.0
# TX line util not smoothed
diff_tx_pkts = snapshot.get('opackets', 0) - self.latest_stats.get('opackets', 0)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
index 9ed6c0f8..4e57aae3 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -39,6 +39,7 @@ REMOTE_FILE = 23
LOCKED = 24
PIN_CORES = 25
CORE_MASK = 26
+DUAL = 28
GLOBAL_STATS = 50
PORT_STATS = 51
@@ -313,6 +314,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': False,
'help': "file path should be interpeted by the server (remote file)"}),
+ DUAL: ArgumentPack(['--dual'],
+ {"action": "store_true",
+ 'default': False,
+ 'help': "Transmit in a dual mode - requires a slave attached to the port"}),
+
FILE_PATH: ArgumentPack(['-f'],
{'metavar': 'FILE',
'dest': 'file',
diff --git a/src/common/erf.cpp b/src/common/erf.cpp
index 304f758b..c4c14998 100755
--- a/src/common/erf.cpp
+++ b/src/common/erf.cpp
@@ -108,7 +108,7 @@ int erf_open(wtap *wth, int *err)
memset(&prevts, 0, sizeof(prevts));
- int records_for_erf_check = 10;
+ long records_for_erf_check = 10;
/* ERF is a little hard because there's no magic number */
@@ -166,7 +166,7 @@ int erf_open(wtap *wth, int *err)
}
-int erf_read(wtap *wth,char *p,uint32_t *sec,uint32_t *nsec)
+int erf_read(wtap *wth,char *p,uint32_t *sec,uint32_t *nsec, uint8_t *interface)
{
erf_header_t header;
int common_type = 0;
@@ -214,6 +214,7 @@ int erf_read(wtap *wth,char *p,uint32_t *sec,uint32_t *nsec)
uint32_t frac =(ts &0xffffffff);
double usec_frac =(double)frac*(1000000000.0/(4294967296.0));
*nsec = (uint32_t) (usec_frac);
+ *interface = header.flags & 0x3;
return (g_ntohs(header.wlen));
}else{
return (-1);
@@ -438,15 +439,19 @@ void CErfFileReader::Delete(){
bool CErfFileReader::ReadPacket(CCapPktRaw * lpPacket){
+ uint8_t interface;
wtap wth;
+
wth.fh = m_handle;
- int length;
- length=erf_read(&wth,lpPacket->raw,&lpPacket->time_sec,
- &lpPacket->time_nsec
- );
+ int length = erf_read(&wth,
+ lpPacket->raw,
+ &lpPacket->time_sec,
+ &lpPacket->time_nsec,
+ &interface);
if ( length >0 ) {
lpPacket->pkt_len =(uint16_t)length;
lpPacket->pkt_cnt++;
+ lpPacket->setInterface(interface);
return (true);
}
return (false);
diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
index cd845fca..ba3c1658 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
@@ -509,16 +509,26 @@ trex_rpc_cmd_rc_e
TrexRpcCmdPushRemote::_run(const Json::Value &params, Json::Value &result) {
uint8_t port_id = parse_port(params, result);
- std::string pcap_filename = parse_string(params, "pcap_filename", result);
- double ipg_usec = parse_double(params, "ipg_usec", result);
- double speedup = parse_double(params, "speedup", result);
- uint32_t count = parse_uint32(params, "count", result);
- double duration = parse_double(params, "duration", result);
+ std::string pcap_filename = parse_string(params, "pcap_filename", result);
+ double ipg_usec = parse_double(params, "ipg_usec", result);
+ double speedup = parse_double(params, "speedup", result);
+ uint32_t count = parse_uint32(params, "count", result);
+ double duration = parse_double(params, "duration", result);
+ bool is_dual = parse_bool(params, "is_dual", result, false);
+ std::string slave_handler = parse_string(params, "slave_handler", result, "");
TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
+ if (is_dual) {
+ TrexStatelessPort *slave = get_stateless_obj()->get_port_by_id(port_id ^ 0x1);
+
+ if (!slave->get_owner().verify(slave_handler)) {
+ generate_execute_err(result, "incorrect or missing slave port handler");
+ }
+ }
+
try {
- port->push_remote(pcap_filename, ipg_usec, speedup, count, duration);
+ port->push_remote(pcap_filename, ipg_usec, speedup, count, duration, is_dual);
} catch (const TrexException &ex) {
generate_execute_err(result, ex.what());
}
diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp
index 2a545c5f..df50d3e2 100644
--- a/src/stateless/cp/trex_stateless_port.cpp
+++ b/src/stateless/cp/trex_stateless_port.cpp
@@ -244,7 +244,7 @@ void
TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, bool force, uint64_t core_mask) {
/* command allowed only on state stream */
- verify_state(PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_STREAMS, "start");
/* just making sure no leftovers... */
delete_streams_graph();
@@ -370,7 +370,7 @@ TrexStatelessPort::stop_traffic(void) {
void
TrexStatelessPort::remove_rx_filters(void) {
/* only valid when IDLE or with streams and not TXing */
- verify_state(PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_STREAMS, "remove_rx_filters");
for (auto entry : m_stream_table) {
get_stateless_obj()->m_rx_flow_stat.stop_stream(entry.second);
@@ -410,7 +410,7 @@ TrexStatelessPort::is_core_active(int core_id) {
void
TrexStatelessPort::pause_traffic(void) {
- verify_state(PORT_STATE_TX);
+ verify_state(PORT_STATE_TX, "pause");
if (m_last_all_streams_continues == false) {
throw TrexException(" pause is supported when all streams are in continues mode ");
@@ -441,7 +441,7 @@ TrexStatelessPort::pause_traffic(void) {
void
TrexStatelessPort::resume_traffic(void) {
- verify_state(PORT_STATE_PAUSE);
+ verify_state(PORT_STATE_PAUSE, "resume");
/* generate a message to all the relevant DP cores to start transmitting */
TrexStatelessCpToDpMsgBase *resume_msg = new TrexStatelessDpResume(m_port_id);
@@ -459,7 +459,7 @@ TrexStatelessPort::update_traffic(const TrexPortMultiplier &mul, bool force) {
double factor;
- verify_state(PORT_STATE_TX | PORT_STATE_PAUSE);
+ verify_state(PORT_STATE_TX | PORT_STATE_PAUSE, "update");
/* generate a message to all the relevant DP cores to start transmitting */
double new_factor = calculate_effective_factor(mul, force);
@@ -497,10 +497,11 @@ TrexStatelessPort::push_remote(const std::string &pcap_filename,
double ipg_usec,
double speedup,
uint32_t count,
- double duration) {
+ double duration,
+ bool is_dual) {
/* command allowed only on state stream */
- verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "push_remote");
/* check that file exists */
CCapReaderBase *reader;
@@ -532,7 +533,8 @@ TrexStatelessPort::push_remote(const std::string &pcap_filename,
ipg_usec,
speedup,
count,
- duration);
+ duration,
+ is_dual);
send_message_to_dp(tx_core, push_msg);
/* update subscribers */
@@ -580,10 +582,12 @@ TrexStatelessPort::get_properties(std::string &driver, uint32_t &speed) {
}
bool
-TrexStatelessPort::verify_state(int state, bool should_throw) const {
+TrexStatelessPort::verify_state(int state, const char *cmd_name, bool should_throw) const {
if ( (state & m_port_state) == 0 ) {
if (should_throw) {
- throw TrexException("command cannot be executed on current state: '" + get_state_as_string() + "'");
+ std::stringstream ss;
+ ss << "command '" << cmd_name << "' cannot be executed on current state: '" << get_state_as_string() << "'";
+ throw TrexException(ss.str());
} else {
return false;
}
@@ -893,7 +897,7 @@ TrexStatelessPort::get_pci_info(std::string &pci_addr, int &numa_node) {
void
TrexStatelessPort::add_stream(TrexStream *stream) {
- verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "add_stream");
get_stateless_obj()->m_rx_flow_stat.add_stream(stream);
@@ -906,7 +910,7 @@ TrexStatelessPort::add_stream(TrexStream *stream) {
void
TrexStatelessPort::remove_stream(TrexStream *stream) {
- verify_state(PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_STREAMS, "remove_stream");
get_stateless_obj()->m_rx_flow_stat.del_stream(stream);
@@ -920,7 +924,7 @@ TrexStatelessPort::remove_stream(TrexStream *stream) {
void
TrexStatelessPort::remove_and_delete_all_streams() {
- verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS);
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "remove_and_delete_all_streams");
vector<TrexStream *> streams;
get_object_list(streams);
diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h
index ba86a279..147efc70 100644
--- a/src/stateless/cp/trex_stateless_port.h
+++ b/src/stateless/cp/trex_stateless_port.h
@@ -217,10 +217,11 @@ public:
*
*/
void push_remote(const std::string &pcap_filename,
- double ipg_usec,
- double speedup,
- uint32_t count,
- double duration);
+ double ipg_usec,
+ double speedup,
+ uint32_t count,
+ double duration,
+ bool is_dual);
/**
* get the port state
@@ -385,7 +386,7 @@ private:
return m_cores_id_list;
}
- bool verify_state(int state, bool should_throw = true) const;
+ bool verify_state(int state, const char *cmd_name, bool should_throw = true) const;
void change_state(port_state_e new_state);
diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp
index 4d9137f1..e5679590 100644
--- a/src/stateless/dp/trex_stateless_dp_core.cpp
+++ b/src/stateless/dp/trex_stateless_dp_core.cpp
@@ -478,7 +478,8 @@ bool TrexStatelessDpPerPort::push_pcap(uint8_t port_id,
const std::string &pcap_filename,
double ipg_usec,
double speedup,
- uint32_t count) {
+ uint32_t count,
+ bool is_dual) {
/* push pcap can only happen on an idle port from the core prespective */
assert(m_state == TrexStatelessDpPerPort::ppSTATE_IDLE);
@@ -501,7 +502,8 @@ bool TrexStatelessDpPerPort::push_pcap(uint8_t port_id,
pcap_filename,
ipg_usec,
speedup,
- count);
+ count,
+ is_dual);
if (!rc) {
m_core->free_node((CGenNode *)pcap_node);
return (false);
@@ -1162,14 +1164,15 @@ TrexStatelessDpCore::push_pcap(uint8_t port_id,
double ipg_usec,
double speedup,
uint32_t count,
- double duration) {
+ double duration,
+ bool is_dual) {
TrexStatelessDpPerPort * lp_port = get_port_db(port_id);
lp_port->set_event_id(event_id);
/* delegate the command to the port */
- bool rc = lp_port->push_pcap(port_id, pcap_filename, ipg_usec, speedup, count);
+ bool rc = lp_port->push_pcap(port_id, pcap_filename, ipg_usec, speedup, count, is_dual);
if (!rc) {
/* report back that we stopped */
CNodeRing *ring = CMsgIns::Ins()->getCpDp()->getRingDpToCp(m_core->m_thread_id);
@@ -1253,7 +1256,8 @@ bool CGenNodePCAP::create(uint8_t port_id,
const std::string &pcap_filename,
double ipg_usec,
double speedup,
- uint32_t count) {
+ uint32_t count,
+ bool is_dual) {
std::stringstream ss;
m_type = CGenNode::PCAP_PKT;
@@ -1261,7 +1265,8 @@ bool CGenNodePCAP::create(uint8_t port_id,
m_src_port = 0;
m_port_id = port_id;
m_count = count;
-
+ m_is_dual = is_dual;
+
/* mark this node as slow path */
set_slow_path(true);
diff --git a/src/stateless/dp/trex_stateless_dp_core.h b/src/stateless/dp/trex_stateless_dp_core.h
index 9babb172..b386daf7 100644
--- a/src/stateless/dp/trex_stateless_dp_core.h
+++ b/src/stateless/dp/trex_stateless_dp_core.h
@@ -75,7 +75,8 @@ public:
const std::string &pcap_filename,
double ipg_usec,
double speedup,
- uint32_t count);
+ uint32_t count,
+ bool is_dual);
bool stop_traffic(uint8_t port_id,
bool stop_on_id,
@@ -184,7 +185,8 @@ public:
double ipg_usec,
double speedup,
uint32_t count,
- double duration);
+ double duration,
+ bool is_dual);
/**
diff --git a/src/stateless/dp/trex_stream_node.h b/src/stateless/dp/trex_stream_node.h
index 8a68625c..b4910fce 100644
--- a/src/stateless/dp/trex_stream_node.h
+++ b/src/stateless/dp/trex_stream_node.h
@@ -468,7 +468,8 @@ public:
const std::string &pcap_filename,
double ipg_usec,
double speedup,
- uint32_t count);
+ uint32_t count,
+ bool is_dual);
/**
* destroy the node cleaning up any data
@@ -476,6 +477,10 @@ public:
*/
void destroy();
+ bool is_dual() const {
+ return m_is_dual;
+ }
+
/**
* advance - will read the next packet
*
@@ -505,6 +510,10 @@ public:
}
}
+ if (is_dual()) {
+ uint8_t dir = m_raw_packet->getInterface() & 0x1;
+ set_mbuf_dir(dir);
+ }
}
/**
@@ -615,8 +624,10 @@ private:
uint8_t m_port_id;
+ bool m_is_dual;
+
/* pad to match the size of CGenNode */
- uint8_t m_pad_end[33];
+ uint8_t m_pad_end[32];
} __rte_cache_aligned;
diff --git a/src/stateless/messaging/trex_stateless_messaging.cpp b/src/stateless/messaging/trex_stateless_messaging.cpp
index 1cbacb6f..95613b41 100644
--- a/src/stateless/messaging/trex_stateless_messaging.cpp
+++ b/src/stateless/messaging/trex_stateless_messaging.cpp
@@ -193,7 +193,8 @@ TrexStatelessDpPushPCAP::handle(TrexStatelessDpCore *dp_core) {
m_ipg_usec,
m_speedup,
m_count,
- m_duration);
+ m_duration,
+ m_is_dual);
return true;
}
@@ -205,7 +206,8 @@ TrexStatelessDpPushPCAP::clone() {
m_ipg_usec,
m_speedup,
m_count,
- m_duration);
+ m_duration,
+ m_is_dual);
return new_msg;
}
diff --git a/src/stateless/messaging/trex_stateless_messaging.h b/src/stateless/messaging/trex_stateless_messaging.h
index 9b1f2e31..fb2c27ab 100644
--- a/src/stateless/messaging/trex_stateless_messaging.h
+++ b/src/stateless/messaging/trex_stateless_messaging.h
@@ -259,13 +259,16 @@ public:
double ipg_usec,
double speedup,
uint32_t count,
- double duration) : m_pcap_filename(pcap_filename) {
+ double duration,
+ bool is_dual) : m_pcap_filename(pcap_filename) {
+
m_port_id = port_id;
m_event_id = event_id;
m_ipg_usec = ipg_usec;
m_speedup = speedup;
m_count = count;
m_duration = duration;
+ m_is_dual = is_dual;
}
virtual bool handle(TrexStatelessDpCore *dp_core);
@@ -280,6 +283,7 @@ private:
double m_duration;
uint32_t m_count;
uint8_t m_port_id;
+ bool m_is_dual;
};