Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • ptzanis/ScaSoftware
  • tuna/ScaSoftware
  • rowang/ScaSoftware
  • dirk/ScaSoftware
  • atlas-dcs-common-software/ScaSoftware
5 results
Select Git revision
Show changes
Commits on Source (15)
Showing
with 1037 additions and 31 deletions
......@@ -3,3 +3,4 @@ CMakeCache.txt
cmake_install.cmake
Makefile
*~
/build/
[submodule "VeryHipEvents"]
path = VeryHipEvents
url = https://gitlab.cern.ch/atlas-dcs-common-software/VeryHipEvents.git
url = https://github.com/pnikiel/VeryHipEvents.git
[submodule "netio"]
path = netio
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/netio.git
[submodule "hdlc_coder"]
path = hdlc_coder
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/hdlc_coder.git
[submodule "felixbase"]
path = felixbase
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/felixbase.git
[submodule "netio-next"]
path = netio-next
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/netio-next.git
[submodule "felix-client"]
path = felix-client
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/felix-client.git
[submodule "felixbus"]
path = felixbus
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/felixbus.git
[submodule "felix-client-thread"]
path = felix-client-thread
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/felix-client-thread.git
[submodule "felix-interface"]
path = felix-interface
url = ssh://git@gitlab.cern.ch:7999/atlas-tdaq-felix/felix-interface.git
......@@ -3,33 +3,47 @@ cmake_minimum_required(VERSION 2.8)
option(STANDALONE_SCA_SOFTWARE "Whether to build demonstrator executable" OFF)
option(HAVE_NETIO "Whether to build backends which require netio" OFF)
option(HAVE_NETIO_NEXT "Whether to build backends which require netio-next" OFF)
option(STANDALONE_WITH_EVENT_RECORDER "Whether to include Event Recording in stand-alone builds" OFF)
add_definitions(-Wall)
include_directories( $ENV{BOOST_HEADERS} )
if (HAVE_NETIO)
include_directories( $ENV{FELIX_ROOT}/include )
link_directories( $ENV{FELIX_ROOT}/lib )
add_definitions( -DHAVE_NETIO )
if (HAVE_NETIO_NEXT)
add_definitions( -DHAVE_NETIO_NEXT )
include_directories( felix-interface )
include_directories( felix-client )
link_directories( ../../../felix-client/x86_64-centos7-gcc8-opt/ )
include_directories( netio-next )
include_directories( netio-next/external/libfabric/1.10.0.2/x86_64-centos7-gcc8-opt/include )
link_directories( ../../../netio-next/x86_64-centos7-gcc8-opt/ )
include_directories( felixbus )
link_directories( ../../../felixbus/x86_64-centos7-gcc8-opt/ )
include_directories( felixbus/external/czmq/3.0.2.4/x86_64-centos7-gcc8-opt/include )
link_directories( ../../../felixbus/external/czmq/3.0.2.4/x86_64-centos7-gcc8-opt/lib )
include_directories( felixbus/external/zyre/1.1.0.2/x86_64-centos7-gcc8-opt/include )
include_directories( felixbus/external/json/3.2.0/include )
include_directories( $ENV{ZMQ_HEADERS} )
include_directories( hdlc_coder )
link_directories( ../../../hdlc_coder/x86_64-centos7-gcc8-opt/ )
endif()
if( STANDALONE_SCA_SOFTWARE )
include_directories(include)
include_directories(include/LogIt)
include_directories(include/LogIt)
set (LOGIT_HAS_BOOSTLOG FALSE)
set (LOGIT_HAS_UATRACE FALSE)
set (LOGIT_HAS_STDOUTLOG TRUE)
add_subdirectory(LogIt)
if( STANDALONE_WITH_EVENT_RECORDER )
include_directories( ${PROJECT_SOURCE_DIR}/VeryHipEvents/include )
add_subdirectory( VeryHipEvents )
add_definitions( -DWITH_EVENT_RECORDER )
endif( STANDALONE_WITH_EVENT_RECORDER )
add_definitions( -std=gnu++17 )
add_subdirectory( Demonstrators )
......@@ -42,6 +56,12 @@ if (HAVE_NETIO)
)
endif()
if (HAVE_NETIO_NEXT)
file(GLOB NETIO_NEXT_BACKEND_SRCS
NetIoNextBackend/*.cpp
)
endif()
file(GLOB SRCS
Hdlc/*.cpp
Sca/*.cpp
......@@ -51,5 +71,4 @@ file(GLOB SRCS
)
add_library( ScaSoftware OBJECT ${SRCS} ${NETIO_SIMPLE_BACKEND_SRCS} )
add_library( ScaSoftware OBJECT ${SRCS} ${NETIO_SIMPLE_BACKEND_SRCS} ${NETIO_NEXT_BACKEND_SRCS} )
......@@ -6,7 +6,7 @@ link_directories( $ENV{BOOST_LIB_DIRECTORIES} )
set( DEMONSTRATORS_COMMON_LIBDIRS $ENV{BOOST_LIB_DIRECTORIES} )
set( DEMONSTRATORS_COMMON_LIBS
$ENV{BOOST_LIBS}
-lrt
-lrt
-lpthread )
set( DEMONSTRATORS_COMMON_OBJS
......@@ -14,19 +14,21 @@ set( DEMONSTRATORS_COMMON_OBJS
$<TARGET_OBJECTS:LogIt>
$<TARGET_OBJECTS:DemonstratorCommons>
)
if( STANDALONE_WITH_EVENT_RECORDER)
set( DEMONSTRATORS_COMMON_OBJS ${DEMONSTRATORS_COMMON_OBJS} $<TARGET_OBJECTS:VeryHipEvents>
set( DEMONSTRATORS_COMMON_OBJS ${DEMONSTRATORS_COMMON_OBJS} $<TARGET_OBJECTS:VeryHipEvents>
)
endif( STANDALONE_WITH_EVENT_RECORDER )
if (HAVE_NETIO)
if (HAVE_NETIO_NEXT)
set(DEMONSTRATORS_COMMON_LIBS ${DEMONSTRATORS_COMMON_LIBS}
$ENV{FELIX_ROOT}/lib/libnetio.so
$ENV{FELIX_ROOT}/lib/libhdlc_coder.so
$ENV{FELIX_ROOT}/lib/libtbb.so.2
felix-client-lib
netio-lib
felixbus
czmq
hdlc_coder
)
endif()
......@@ -41,6 +43,6 @@ add_subdirectory(I2cViaGpio)
add_subdirectory(GbtxConfiguration)
add_subdirectory(HenksFScaJtag)
if (HAVE_NETIO)
add_subdirectory(SimulatorOverNetIo)
if (HAVE_NETIO_NEXT)
add_subdirectory(SimulatorOverNetIoNext)
endif()
link_directories(/usr/lib64/boost148)
add_executable(simulator_netio
add_executable(simulator_netio_next
main.cpp
${DEMONSTRATORS_COMMON_OBJS}
)
target_link_libraries(simulator_netio ${DEMONSTRATORS_COMMON_LIBS} )
target_link_libraries(simulator_netio_next ${DEMONSTRATORS_COMMON_LIBS} )
......@@ -6,8 +6,12 @@
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include <felixbase/client.hpp>
#include <netio.hpp>
#include "felixbus/bus.hpp"
#include "felixbus/elinktable.hpp"
#include "felixbus/felixtable.hpp"
#include <felix/felix_client.hpp>
#include <netio/netio.h>
#include <hdlc_coder/hdlc.hpp>
#include <ScaSimulator/HdlcBackend.h>
......@@ -24,8 +28,11 @@ const unsigned int NETIO_FRAME_HDLC_START_OFFSET = 8; // HDLC frame shall be pl
const unsigned int HDLC_FRAME_PAYLOAD_OFFSET = 2; // HDLC payload offset in HDLC frame
const unsigned int HDLC_FRAME_TRAILER_SIZE = 2; // HDLC trailer size in HDLC frame
struct Config
struct
{
ScaSimulator::HdlcBackend* simulator;
std::string hostname;
uint16_t requestsPort;
uint16_t repliesPort;
ElinkId elinkId;
......@@ -33,17 +40,35 @@ struct Config
unsigned int noiseRepliesInterval;
bool doLoseReplies;
double requestsLossProbability;
};
Config parseProgramOptions(int argc, char* argv[])
std::string bus_interface;
bool verbose_bus;
bool verbose_zyre;
struct netio_context ctx;
struct netio_buffered_socket_attr publish_attr;
struct netio_publish_socket publish_socket;
struct netio_buffered_socket_attr receive_attr;
struct netio_buffered_listen_socket receive_socket;
struct netio_timer timer;
felix::bus::Bus bus;
felix::bus::FelixTable felix_table;
felix::bus::ElinkTable elink_table;
} config;
void parseProgramOptions(int argc, char* argv[])
{
using namespace boost::program_options;
using namespace ScaSwDemonstrators;
Config config;
options_description options;
std::string logLevelStr;
options.add_options()
("help,h", "show help")
("hostname", value<std::string>(&config.hostname)->default_value("192.168.100.4"), "Local hostname of network on which to publish and receive")
("trace_level,t", value<std::string>(&logLevelStr)->default_value("INF"), "Trace level, one of: ERR,WRN,INF,DBG,TRC")
("requests_port,x", value<uint16_t>(&config.requestsPort)->default_value(29000), "TCP/IP port on which netio will listen for requests")
("replies_port,y", value<uint16_t>(&config.repliesPort)->default_value(29001), "TCP/IP port to which netio will publish the replies")
......@@ -62,7 +87,6 @@ Config parseProgramOptions(int argc, char* argv[])
exit(1);
}
initializeLogging(logLevelStr);
return config;
}
bool simulateElinkDisruption = false;
......@@ -73,57 +97,195 @@ void signalEnableDisruption(int)
LOG(Log::INF) << "Simulating disruption=" << simulateElinkDisruption << "; Send SIGUSR1 to disable";
}
void noiseReplies(netio::publish_socket* socket, netio::tag* tag, unsigned int noiseIntervalUs)
void noiseReplies()
{
std::vector<unsigned char> data (256,0);
while(1)
// this_size will be the size of next random package, between 8-32 B
unsigned int this_size = 8 + rand() % 24;
std::generate_n(data.begin(), this_size, rand);
// netio::message msg (&data[0], this_size);
LOG(Log::TRC) << "Emitting noise frame, size=" << this_size;
netio_tag_t publish_tag = config.elinkId+1;
netio_buffered_publish( &config.publish_socket, publish_tag, &data[0], data.size(), 0, NULL );
netio_buffered_publish_flush( &config.publish_socket, publish_tag, NULL );
}
void on_subscribe(struct netio_publish_socket* socket, netio_tag_t tag, void* addr, size_t addrlen) {
printf("remote subscribed to tag %lu\n", tag);
}
void on_connection_established(struct netio_publish_socket* socket) {
printf("connection to subscriber established\n");
}
void on_connection_closed(struct netio_publish_socket* socket) {
printf("connection to subscriber closed\n");
}
void on_connection_established(struct netio_buffered_recv_socket* socket)
{
printf("connection from sender established\n");
}
void on_connection_closed(struct netio_buffered_recv_socket* socket) {
printf("connection from sender closed\n");
}
void on_msg_received(struct netio_buffered_recv_socket* socket, void* data, size_t len) {
printf("data received from sender\n");
LOG(Log::TRC) << "Received netio message, size=" << len << ", number of fragments: " << 1;
if (len < 24) // TODO?
{
// this_size will be the size of next random package, between 8-32 B
unsigned int this_size = 8 + rand() % 24;
std::generate_n(data.begin(), this_size, rand);
netio::message msg (&data[0], this_size);
LOG(Log::TRC) << "Emitting noise frame, size=" << this_size;
socket->publish(*tag, msg);
usleep(noiseIntervalUs);
LOG(Log::INF) << "Obtained message smaller than expected for SCA, ignoring it. (It might be a HDLC control frame)";
return;
}
if (simulateElinkDisruption)
{
LOG(Log::INF) << "Simulating disrupted elink connection - not passing the traffic to SCA";
return;
}
size_t pos = 0;
// size_t dma_pos = 0;
struct FelixClient::ToFlxHeader* hdr = NULL;
while (pos < len) {
if (len - pos < sizeof(struct FelixClient::ToFlxHeader)) {
LOG(Log::ERR) << "not enough data to decode a ToFlxHdr: pos= " << pos << " len= " << len;
return;
}
hdr = (struct FelixClient::ToFlxHeader*)((uint8_t*)data + pos);
pos += sizeof(struct FelixClient::ToFlxHeader);
if (hdr->length == 0) {
LOG(Log::ERR) << "invalid msg length: 0, pos= " << pos << " len= " << len;
return;
}
if (len - pos < hdr->length) {
LOG(Log::ERR) << "not enough data to decode message content: pos= " << pos << " len= " << len << " msg_len= " << hdr->length;
return;
}
LOG(Log::TRC) << "hdr.len=" << hdr->length;
if (hdr->fid == config.elinkId) {
// auto fullHdlcFramePtr = current + sizeof(felix::base::ToFELIXHeader);
bool allZeros = std::all_of(
(uint8_t*)data + pos,
(uint8_t*)data + pos + hdr->length,
[](uint8_t x){return x==0;});
if (allZeros)
{
LOG(Log::TRC) << "Skipping zero-block of size " << hdr->length;
usleep (hdr->length);
}
else
{
bool acceptRequest = true;
// if (config.doLoseReplies)
// {
// double randomNumber = distribution(generator);
// if (randomNumber < config.requestsLossProbability)
// {
// acceptRequest = false;
// LOG(Log::DBG) << "Losing a request! (simulated, selected by --request_loss)";
// }
// }
if (acceptRequest)
{
uint8_t* hdlcPayloadPtr = (uint8_t*)data + pos + HDLC_FRAME_PAYLOAD_OFFSET;
Hdlc::Payload payload(
hdlcPayloadPtr, hdr->length - HDLC_FRAME_PAYLOAD_OFFSET - HDLC_FRAME_TRAILER_SIZE);
LOG(Log::TRC) << "obtained frame: " << payload.toString();
config.simulator->send( payload );
}
}
} else {
LOG(Log::ERR) << "Received a message destined for elink " << hdr->fid << ", ignoring it, ours is " << (unsigned int)config.elinkId;
}
// message was processed, advance read pointer
pos += hdr->length;
}
}
void on_timer(void* ptr) {
// printf("timer event\n");
noiseReplies();
}
void on_init(void *ptr) {
if (config.doNoiseReplies) {
netio_timer_start_us(&config.timer, config.noiseRepliesInterval);
}
config.publish_attr.num_pages = 16;
config.publish_attr.pagesize = 64*1024;
config.publish_attr.watermark = 63*1024;
printf("Opening buffered publish socket on %s:%d\n", config.hostname.c_str(), config.repliesPort);
netio_publish_socket_init(&config.publish_socket, &config.ctx, config.hostname.c_str(), config.repliesPort, &config.publish_attr);
config.publish_socket.cb_subscribe = on_subscribe;
config.publish_socket.cb_connection_established = on_connection_established;
config.publish_socket.cb_connection_closed = on_connection_closed;
config.receive_attr.num_pages = 16;
config.receive_attr.pagesize = 64*1024;
config.receive_attr.watermark = 63*1024;
printf("Opening buffered receive socket on %s:%d\n", config.hostname.c_str(), config.requestsPort);
netio_buffered_listen_socket_init(&config.receive_socket, &config.ctx, &config.receive_attr);
netio_buffered_listen(&config.receive_socket, config.hostname.c_str(), config.requestsPort);
config.receive_socket.cb_connection_established = on_connection_established;
config.receive_socket.cb_connection_closed = on_connection_closed;
config.receive_socket.cb_msg_received = on_msg_received;
// bus
std::string address = "tcp://" + config.hostname + ":" + std::to_string(config.requestsPort);
printf("Using bus to publish %s\n", address.c_str());
bool unbuffered = false;
bool pubsub = true;
std::string uuid = config.felix_table.addFelix(address, unbuffered, pubsub);
config.elink_table.addElink(config.elinkId+1, uuid);
address = "tcp://" + config.hostname + ":" + std::to_string(config.repliesPort);
printf("Using bus to receive %s\n", address.c_str());
unbuffered = false;
pubsub = false;
uuid = config.felix_table.addFelix(address, unbuffered, pubsub);
config.elink_table.addElink(config.elinkId, uuid);
config.bus.setInterface(config.bus_interface);
config.bus.setVerbose(config.verbose_bus);
config.bus.setZyreVerbose(config.verbose_zyre);
config.bus.publish("FELIX", config.felix_table);
config.bus.publish("ELINKS", config.elink_table);
config.bus.connect();
}
int main(int argc, char* argv[] )
{
auto seed = std::chrono::system_clock::now().time_since_epoch().count() + getpid();
std::default_random_engine generator (seed);
std::default_random_engine generator;
std::uniform_real_distribution<double> distribution;
Config config = parseProgramOptions( argc, argv);
parseProgramOptions( argc, argv);
LOG(Log::INF) << "initialized logging";
signal(SIGUSR1, signalEnableDisruption );
netio::context context("posix");
boost::thread netio_thread( [&context](){
context.event_loop()->run_forever();
});
/* NOTE for the id. It doesn't matter as long as we keep one simulated SCA in this program.
/* NOTE for the id. It doesn't matter as long as we keep one simulated SCA in this program.
* It doesn't get exposed externally either.
*/
ScaSimulator::HdlcBackend simulator ("1");
netio::publish_socket reply_socket( &context, config.repliesPort );
netio::tag publish_tag ( config.elinkId );
std::unique_ptr<std::thread> replyNoiseThread;
if (config.doNoiseReplies)
{
replyNoiseThread = std::unique_ptr<std::thread>(new std::thread(noiseReplies, &reply_socket, &publish_tag, config.noiseRepliesInterval));
}
config.simulator = new ScaSimulator::HdlcBackend("1");
uint64_t seqnr = 0;
// This is a "lambda" type implementation which fits expected std::function handler
Hdlc::Backend::ReceiveCallBack receiveCallback ( [&reply_socket,&publish_tag,&seqnr,&config](const Hdlc::Payload& payload)
// removed &reply_socket,&publish_tag,
Hdlc::Backend::ReceiveCallBack receiveCallback ( [&seqnr](const Hdlc::Payload& payload)
{
// HDLC framing - Paris
......@@ -143,109 +305,27 @@ int main(int argc, char* argv[] )
LOG(Log::TRC) << "The size of the HDLC frame is: " << hdlcFrame.size();
felix::base::FromFELIXHeader header;
header.elinkid = config.elinkId;
header.gbtid = 0;
header.length = sizeof header + hdlcFrame.size();
header.status = 0;
uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
std::vector<uint8_t> netioFrame (sizeof header + hdlcFrame.size());
std::copy(headerPtr, headerPtr+sizeof header, netioFrame.begin());
std::copy(hdlcFrame.begin(), hdlcFrame.end(), netioFrame.begin()+sizeof header);
std::vector<uint8_t> netioFrame (hdlcFrame.size());
std::copy(hdlcFrame.begin(), hdlcFrame.end(), netioFrame.begin());
netio::message msg( &netioFrame[0], netioFrame.size() );
reply_socket.publish( publish_tag, msg );
netio_tag_t publish_tag = config.elinkId+1;
netio_buffered_publish( &config.publish_socket, publish_tag, &netioFrame[0], netioFrame.size(), 0, NULL );
netio_buffered_publish_flush( &config.publish_socket, publish_tag, NULL );
LOG(Log::TRC) << "Published reply( transaction id " << (int)payload[0] << ", sz=" << (int)netioFrame.size() << " )";
}
);
simulator.subscribeReceiver( receiveCallback );
config.simulator->subscribeReceiver( receiveCallback );
netio::low_latency_recv_socket socket(
&context,
config.requestsPort,
[&simulator,&config,&generator,&distribution](netio::endpoint &ep, netio::message& msg)
{
LOG(Log::TRC) << "Received netio message, size=" << msg.size() << ", number of fragments: " << msg.num_fragments();
if (msg.size() < 24) // TODO?
{
LOG(Log::INF) << "Obtained message smaller than expected for SCA, ignoring it. (It might be a HDLC control frame)";
return;
}
if (simulateElinkDisruption)
{
LOG(Log::INF) << "Simulating disrupted elink connection - not passing the traffic to SCA";
return;
}
std::vector<uint8_t> netioFrame (msg.data_copy());
netio_init(&config.ctx);
config.ctx.evloop.cb_init = on_init;
std::vector<uint8_t>::iterator current = netioFrame.begin();
/* Step 1 - extract the header */
size_t left = 0;
while ( (left = std::distance( current, netioFrame.end())) > 0 )
{
LOG(Log::TRC) << "left=" << left;
if (left < sizeof(felix::base::ToFELIXHeader))
{
LOG(Log::ERR) << "Malformed data? Remaining buffer is smaller than ToFLX header.";
return;
}
felix::base::ToFELIXHeader* header = reinterpret_cast<felix::base::ToFELIXHeader*> (&*current);
LOG(Log::TRC) << "header.len=" << header->length;
if (header->elinkid == config.elinkId)
{
auto fullHdlcFramePtr = current + sizeof(felix::base::ToFELIXHeader);
bool allZeros = std::all_of(
fullHdlcFramePtr,
fullHdlcFramePtr + header->length,
[](uint8_t x){return x==0;});
if (allZeros)
{
LOG(Log::TRC) << "Skipping zero-block of size " << header->length;
usleep (header->length);
}
else
{
bool acceptRequest = true;
if (config.doLoseReplies)
{
double randomNumber = distribution(generator);
// LOG(Log::DBG) << randomNumber;
if (randomNumber < config.requestsLossProbability)
{
acceptRequest = false;
LOG(Log::DBG) << "Losing a request! (simulated, selected by --request_loss)";
}
}
if (acceptRequest)
{
uint8_t* hdlcPayloadPtr = &*current + sizeof(felix::base::ToFELIXHeader) + HDLC_FRAME_PAYLOAD_OFFSET;
Hdlc::Payload payload(
hdlcPayloadPtr,
header->length - HDLC_FRAME_PAYLOAD_OFFSET - HDLC_FRAME_TRAILER_SIZE);
LOG(Log::TRC) << "obtained frame: " << payload.toString();
simulator.send( payload );
}
}
}
else
LOG(Log::ERR) << "Received a message destined for elink " << header->elinkid << ", ignoring it, ours is " << (unsigned int)config.elinkId;
current += sizeof (*header) + header->length;
}
}
);
netio_timer_init(&config.ctx.evloop, &config.timer);
config.timer.cb = on_timer;
LOG(Log::INF) << "I think that I'm ready to accept connections from SCA-SW through netio. "
LOG(Log::INF) << "I think that I'm ready to accept connections from SCA-SW through netio-next. "
"I listen on port " << config.requestsPort << " for SCA requests "
"and I publish replies on port " << config.repliesPort <<
" and my elink is: " << (unsigned int)config.elinkId;
while(1)
{
usleep(100000);
}
netio_run(&config.ctx.evloop);
}
......@@ -7,7 +7,7 @@ from __future__ import print_function
import os
import argparse
path_to_simulator = '../../build/Demonstrators/SimulatorOverNetIo/simulator_netio'
path_to_simulator = '../../build/Demonstrators/SimulatorOverNetIoNext/simulator_netio_next'
parser = argparse.ArgumentParser()
parser.add_argument('--num_scas', type=int, default=1, help='Number of SCAs to simulate')
......
......@@ -8,6 +8,9 @@
#ifdef HAVE_NETIO
#include <NetIoSimpleBackend/HdlcBackend.h>
#endif
#ifdef HAVE_NETIO_NEXT
#include <NetIoNextBackend/HdlcBackend.h>
#endif
#include <LogIt.h>
#include <ScaCommon/ScaSwLogComponents.h>
......@@ -59,6 +62,13 @@ namespace Hdlc
obtainedBackend = new NetIoSimpleBackend::HdlcBackend( specificAddress );
}
else
#endif // HAVE_NETIO
#ifdef HAVE_NETIO_NEXT
if (backendType == "netio-next")
{
obtainedBackend = new NetIoNextBackend::HdlcBackend( specificAddress );
}
else
#endif // HAVE_NETIO
scasw_throw_runtime_error_with_origin("backend type is unknown: '"+backendType+"'");
......
#include <LogIt.h>
#include <algorithm>
#include <functional>
#include <boost/bind.hpp>
#include <NetIoNextBackend/HdlcBackend.h>
#include <iostream>
#include <NetIoNextBackend/HdlcFrameTranslator.h>
#include <ScaCommon/ScaSwLogComponents.h>
#include <NetIoNextBackend/HdlcBackendCommon.h>
#include <felix/felix_client_thread_interface.hpp>
#include <felix/felix_client_thread_impl.hpp>
// http://atlas-felix.cern.ch/netio-next/
using Sca::LogComponentLevels;
namespace NetIoNextBackend
{
HdlcBackend::HdlcBackend (const std::string& specificAddress):
m_originalSpecificAddress(specificAddress),
m_specificAddress( std::make_shared<SpecificAddress>(specificAddress) ),
m_seqnr (7), // will be incremented before 1st use, and it is modulo 8,
m_replySocketSubscribed(false)
{
LOG(Log::INF, LogComponentLevels::netio()) << "Connecting to " << m_specificAddress->felixHostname()
<< ", port to SCA: " << m_specificAddress->portToSca()
<< ", SCA elink: TX: 0x" << std::hex << m_specificAddress->elinkIdTx() << " RX: 0x" << std::hex << m_specificAddress->elinkIdRx();
FelixClientThreadInterface::Config config;
config.on_data_callback = std::bind(&HdlcBackend::replyHandler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
client = new FelixClientThreadImpl(config);
try
{
LOG(Log::DBG, LogComponentLevels::netio()) << "Subscribing to elink: " << std::hex << (int)m_specificAddress->elinkIdRx();
client->subscribe(m_specificAddress->elinkIdRx());
// m_netioReplySocket.subscribe( m_specificAddress->elinkIdRx(), netio::endpoint( m_specificAddress->felixHostname(), m_specificAddress->portFromSca() ));
m_replySocketSubscribed = true;
usleep(20000);
sendHdlcControl( Hdlc::HdlcControlCodes::RESET );
}
catch (const std::exception &e )
{
LOG(Log::ERR, LogComponentLevels::netio()) << "Exception: " << e.what();
cleanUp();
throw e;
}
}
HdlcBackend::~HdlcBackend ()
{
LOG(Log::DBG, LogComponentLevels::netio()) << "Cleaning up from HdlcBackend dtr...";
cleanUp ();
// We want to release the send socket only in the destructor, so that the coherency of the object remains assured (the socket is kept as reference)
// FIXME
// NetioSendSocketsFactory::releaseSendSocket(this);
delete client;
}
void HdlcBackend::send (const Hdlc::Payload &request )
{
std::vector<Hdlc::Payload> requests ({ request });
std::vector<unsigned int> times ({0});
this->send( requests, times );
}
void HdlcBackend::send (
const std::vector<Hdlc::Payload> &requests,
const std::vector<unsigned int> times
)
{
if (requests.size() != times.size())
scasw_throw_runtime_error_with_origin("requests vector has different size than times vector!");
std::vector<unsigned char> netioFrame;
netioFrame.reserve( 8192 );
// comment: send() synchronization is very important to make sure that HDLC
// sequence numbers - stored as m_seqnr - are guaranteed sequential.
std::lock_guard<decltype(m_transmitSynchronizationMutex)> lock (m_transmitSynchronizationMutex);
for (unsigned int i=0; i<requests.size(); ++i)
{
const Hdlc::Payload& request = requests[i];
m_seqnr = (m_seqnr+1)%8;
std::vector<uint8_t> hdlc_encoded_frame( HDLC_FRAME_PAYLOAD_OFFSET + request.size() + HDLC_FRAME_TRAILER_SIZE, 0 );
felix::hdlc::encode_hdlc_msg_header( &hdlc_encoded_frame[0], /*HDLC address, always 0 for SCA*/0, m_seqnr);
std::copy( request.cbegin(), request.cend(), &hdlc_encoded_frame[ HDLC_FRAME_PAYLOAD_OFFSET] );
felix::hdlc::encode_hdlc_trailer(
&hdlc_encoded_frame[ HDLC_FRAME_PAYLOAD_OFFSET + request.size()], /* where to put the trailer */
&hdlc_encoded_frame[0], /* pointer to the beginning of HDLC frame*/
request.size() + HDLC_FRAME_PAYLOAD_OFFSET);
// ToFlxHeader included in send_data, but for the subsequent parts we can use more headers
// however it seems strange to me and we would probably want to send data one at a time as the fids can be different...
struct FelixClient::ToFlxHeader header;
header.length = hdlc_encoded_frame.size();
header.reserved = 0;
header.fid = m_specificAddress->elinkIdTx();
uint8_t * const headerPtr = reinterpret_cast<uint8_t*>(&header);
std::vector<uint8_t> thisChunk ( hdlc_encoded_frame.size() + (i == 0 ? 0 : sizeof(header)), 0 );
if (i > 0) {
std::copy(
headerPtr,
headerPtr + sizeof(header),
&thisChunk[0] );
}
int start = i == 0 ? 0 : sizeof header;
std::copy(
hdlc_encoded_frame.begin(),
hdlc_encoded_frame.end(),
&thisChunk[ start ] );
netioFrame.insert( netioFrame.end(), thisChunk.begin(), thisChunk.end() );
if (times[i] > 0)
{
/* We will glue a block of zeros after the contents for a delay in Henk B. method */
const unsigned int numBytes = FLX_ZEROS_PER_MICROSECOND * times[i];
header.length = numBytes;
netioFrame.insert( netioFrame.end(), headerPtr, headerPtr+sizeof header );
netioFrame.insert( netioFrame.end(), numBytes, 0);
}
m_statistician.onFrameSent();
}
LOG(Log::TRC, LogComponentLevels::netio()) << "sending netio request frame, group_size=" << requests.size() << ", frame_size=" << netioFrame.size();
LOG(Log::TRC, LogComponentLevels::netio()) << "contents: " << vectorAsHex(netioFrame);
try
{
client->send_data( m_specificAddress->elinkIdTx(), &netioFrame[0], netioFrame.size(), true );
} catch (const std::exception &e )
{
LOG(Log::ERR, LogComponentLevels::netio()) << "Exception: " << e.what();
cleanUp();
throw e;
}
}
void HdlcBackend::replyHandler(netio_tag_t fid, const uint8_t* data, size_t size, uint8_t status) {
printf("replyHanlder called for %lu with size %lu and status %u\n", fid, size, status);
LOG(Log::TRC, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' received reply";
if ( size > 20 )
{
LOG(Log::ERR, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' avoiding possibly extra large netIO frame. (size=" << size << " bytes)";
return;
}
// std::vector<uint8_t> netioFrame( size );
// m.serialize_to_usr_buffer( netioFrame.data() );
std::vector<uint8_t> hdlcFrame(data, data + size);
// Valid SCA frame sizes are 8, 10 and 12 bytes.
// Valid SCA HDLC frames can also be 4 (UA) and 6 (SREJ) bytes.
switch(size) {
case 8 :
case 10 :
case 12 :
break;
case 4 :
case 6 : {
HdlcFrameTranslator HdlcFrameTranslator(hdlcFrame, m_specificAddress);
return;
}
default: {
LOG(Log::ERR, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' "
<< "discarding incoming netIO frame containg an SCA reply of unexpected size. SCA reply size was "
<< size << " bytes (expected SCA replies sizes are 4, 6, 8, 10 or 12 bytes)";
LOG(Log::DBG, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' raw bytes of the netIO frame with unexpected size: " << size;
return;
}
}
// Validate HDLC FCS
if (!validateFcs(hdlcFrame.begin(), hdlcFrame.end()))
{
LOG(Log::ERR, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' HDLC checksum mismatch on incoming frame, discarding";
LOG(Log::DBG, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' raw bytes of the netIO frame with invalid FCS: " << size;
return;
}
// Cut the Address and the Control field from the HDLC frame | and the FCS at the end -- Paris
const uint8_t* hdlcPayloadPtr = &data[ NETIO_FRAME_HDLC_START_OFFSET + HDLC_FRAME_PAYLOAD_OFFSET ];
try // the following may throw if the reply is mutilated, too big, etc.
{
Hdlc::Payload payload( hdlcPayloadPtr, size - NETIO_FRAME_HDLC_START_OFFSET - HDLC_FRAME_PAYLOAD_OFFSET - HDLC_FRAME_TRAILER_SIZE);
// FIXME
// std::for_each( m_receivers.begin(), m_receivers.end(), [&payload]( ReceiveCallBack& cb){ cb(payload); });
}
catch (const std::exception& e)
{
LOG(Log::TRC, LogComponentLevels::netio()) << "At SCA with address: '" + getAddress() + "' ignoring reply: " << e.what();
}
m_statistician.onFrameReceived();
}
void HdlcBackend::sendHdlcControl (uint8_t hdlcControl)
{
// Both CONNECT and RESET HDLC OPs reset the sequence of HDLC
// ToFlxHeader included in send_data
std::vector<unsigned char> netioFrame ( HDLC_FRAME_PAYLOAD_OFFSET + HDLC_FRAME_TRAILER_SIZE );
felix::hdlc::encode_hdlc_ctrl_header( &netioFrame[ 0 ], 0, hdlcControl );
felix::hdlc::encode_hdlc_trailer(
&netioFrame[0] + HDLC_FRAME_PAYLOAD_OFFSET, /* where to put the trailer */
&netioFrame[0],
HDLC_FRAME_PAYLOAD_OFFSET);
LOG(Log::DBG, LogComponentLevels::netio()) << "Sending control: " << std::hex << (unsigned int)hdlcControl;
LOG(Log::TRC, LogComponentLevels::netio()) << "Size: " << netioFrame.size() << " contents: " << vectorAsHex(netioFrame);
client->send_data(m_specificAddress->elinkIdTx(), &netioFrame[0], netioFrame.size(), true );
}
void HdlcBackend::cleanUp ()
{
LOG(Log::INF, LogComponentLevels::netio()) << "Disconnecting from " << m_originalSpecificAddress;
if (m_replySocketSubscribed)
{
try
{
client->unsubscribe( m_specificAddress->elinkIdRx() );
}
catch (const std::exception& e)
{
LOG(Log::ERR, LogComponentLevels::netio()) <<
"While unsubscribing at address=" << m_originalSpecificAddress <<
"Caught: " << e.what();
}
m_replySocketSubscribed = false;
}
try
{
delete client;
}
catch (const std::exception& e)
{
LOG(Log::ERR, LogComponentLevels::netio()) <<
"While stopping event_loop for address=" << m_originalSpecificAddress <<
"Caught: " << e.what();
}
LOG(Log::TRC, LogComponentLevels::netio()) <<
"At address: " << m_originalSpecificAddress <<
"cleanUp() of subscribe socket finished";
}
}
/*
* HdlcBackendCommon.cpp
*
* Created on: Oct 25, 2019
* Author: Paris Moschovakos (paris.moschovakos@cern.ch)
* Piotr Nikiel (pnikiel@cern.ch)
*
* Description : Common utility functions used by netIO HDLC backend
*/
#include <NetIoNextBackend/HdlcBackendCommon.h>
#include <string>
#include <vector>
#include <iostream>
#include <ScaCommon/ScaSwLogComponents.h>
#include <hdlc_coder/hdlc.hpp>
using Sca::LogComponentLevels;
namespace NetIoNextBackend
{
std::string vectorAsHex (const std::vector<uint8_t> & v )
{
std::stringstream ss;
for (uint8_t octet : v)
{
ss.width(2);
ss.fill('0');
ss << std::hex << (unsigned int)octet << ' ';
}
return ss.str();
}
// Checks HDLC frame's FCS and returns true in case it is valid
bool validateFcs( const FrameIterator startOfHdlcFrame, const FrameIterator endOfHdlcFrame)
{
// How to check the HDLC checksum?
std::vector<uint8_t> correctTrailer (HDLC_FRAME_TRAILER_SIZE, 0);
felix::hdlc::encode_hdlc_trailer(
&correctTrailer.at(0),
&*startOfHdlcFrame,
std::distance(startOfHdlcFrame, endOfHdlcFrame) - HDLC_FRAME_TRAILER_SIZE);
auto wrongChecksumIndicator = std::mismatch(
std::begin(correctTrailer),
std::end(correctTrailer),
startOfHdlcFrame + std::distance(startOfHdlcFrame, endOfHdlcFrame) - HDLC_FRAME_TRAILER_SIZE);
if (wrongChecksumIndicator.first != std::end(correctTrailer)) return false;
return true;
}
// Checks HDLC frame's FCS and returns true in case it is valid
bool validateFcs( const std::vector<uint8_t>& hdlcFrame )
{
// How to check the HDLC checksum?
std::vector<uint8_t> correctTrailer (HDLC_FRAME_TRAILER_SIZE, 0);
felix::hdlc::encode_hdlc_trailer(
&correctTrailer.at(0),
&hdlcFrame.at(0),
hdlcFrame.size() - HDLC_FRAME_TRAILER_SIZE);
auto wrongChecksumIndicator = std::mismatch(
std::begin(correctTrailer),
std::end(correctTrailer),
std::begin(hdlcFrame) + hdlcFrame.size() - HDLC_FRAME_TRAILER_SIZE);
if (wrongChecksumIndicator.first != std::end(correctTrailer)) return false;
return true;
}
}
/*
* HdlcFrameTranslator.cpp
*
* Created on: Oct 22, 2019
* Author: Paris Moschovakos (paris.moschovakos@cern.ch)
*
* Description : Based on specifications of ePort HDLC IP core used in SCA
* https://espace.cern.ch/GBT-Project/GBT-SCA/Manuals/ePort_HDLC_specs.pdf
*/
#include <NetIoNextBackend/HdlcFrameTranslator.h>
#include <NetIoNextBackend/HdlcBackendCommon.h>
#include <ScaCommon/ScaSwLogComponents.h>
using Sca::LogComponentLevels;
namespace NetIoNextBackend
{
constexpr uint8_t UNNUMBERED_ACKNOWLEDGE = 0x63;
constexpr uint8_t TEST_REPLY = 0xE3;
HdlcFrameTranslator::HdlcFrameTranslator (
const std::vector<uint8_t>& hdlcFrame,
std::shared_ptr<const SpecificAddress>& specificAddress) :
m_address{hdlcFrame.at(0)},
m_control{hdlcFrame.at(1)},
m_range{},
m_fcs{},
m_isFcsValid{true},
m_hdlcFrameTypeSize{hdlcFrame.size()},
m_specificAddress(specificAddress)
{
switch(m_hdlcFrameTypeSize) {
case HdlcFrameTypeSize::HDLC_U_FRAME_SIZE : {
LOG(Log::DBG, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : Incoming 4-byte reply: 0x " << vectorAsHex(hdlcFrame);
m_fcs = ((uint16_t)hdlcFrame[3] << 8) | hdlcFrame[2];
break;
}
case HdlcFrameTypeSize::HDLC_SREJ_FRAME_SIZE : {
LOG(Log::DBG, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : Incoming 6-byte reply: 0x " << vectorAsHex(hdlcFrame);
m_range = ((uint16_t)hdlcFrame[3] << 8) | hdlcFrame[2];
m_fcs = ((uint16_t)hdlcFrame[5] << 8) | hdlcFrame[4];
break;
}
default: {
LOG(Log::ERR, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : Incoming reply of unknown size: 0x " << vectorAsHex(hdlcFrame);
break;
}
}
if (!validateFcs(hdlcFrame))
m_isFcsValid = false;
validateAddress();
validateControlRange();
}
bool HdlcFrameTranslator::validateAddress ()
{
if (getAddress() == 0xff)
{
LOG(Log::DBG, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : The address of the incoming frame is valid";
return true;
}
LOG(Log::ERR, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : The address in this HDLC frame is invalid";
return false;
}
bool HdlcFrameTranslator::validateControlRange ()
{
if (m_hdlcFrameTypeSize == HdlcFrameTypeSize::HDLC_U_FRAME_SIZE)
{
if (m_control == UNNUMBERED_ACKNOWLEDGE)
{
LOG(Log::WRN, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : reply to HDLC RESET/CONNECT frame";
return true;
}
else if (m_control == TEST_REPLY)
{
LOG(Log::WRN, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : reply to HDLC TEST frame";
return true;
}
THROW_WITH_ORIGIN(std::runtime_error, "The control field in this HDLC frame doesn't correspond to a known SCA reply");
return false;
}
else if (m_hdlcFrameTypeSize == HdlcFrameTypeSize::HDLC_SREJ_FRAME_SIZE)
{
constexpr unsigned int startNsMask = 0x000E;
constexpr unsigned int endNsMask = 0x00E0;
unsigned int startNs = (m_range & startNsMask) >> 1;
unsigned int endNs = (m_range & endNsMask) >> 5;
unsigned int missingRequests;
constexpr unsigned int includeZeroInSubtraction = 1;
constexpr unsigned int rollOverModulo8 = 8;
if ( startNs > endNs )
missingRequests = rollOverModulo8 + (endNs - startNs + includeZeroInSubtraction);
else
missingRequests = endNs - startNs + includeZeroInSubtraction;
LOG(Log::ERR, LogComponentLevels::netio()) << "SCA at " << m_specificAddress->toString() << " : Missing requests with sequence number from "
<< std::hex << startNs << " to " << std::hex << endNs
<< ". (total=" << missingRequests << ")";
return true;
}
else // forward any other packet
return true;
return false;
}
}
/*
* SpecificAddress.cpp
*
* Created on: Mar 15, 2017
* Author: pnikiel
*/
#include <sstream>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <NetIoNextBackend/SimpleNetioSpecificAddress.h>
#include <ScaCommon/except.h>
#include <ScaCommon/ScaSwLogComponents.h>
#include <LogIt.h>
using Sca::LogComponentLevels;
namespace NetIoNextBackend
{
SpecificAddress::SpecificAddress (const std::string& stringAddress):
m_originalAddress(stringAddress),
m_elinkIdTx(0), // is parsed in the body
m_elinkIdRx(0)
{
// Piotr: you can use regex101.com to help yourself designing the regex
boost::regex directAddressRegex( "^direct\\/"
"(?<address>[a-zA-Z0-9][a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)\\/"
"(?<port_to_sca>\\d+)\\/"
"(?<port_from_sca>\\d+)\\/"
"(?<elink_sca>[0-9a-fA-F]+)"
"(?:\\/(?<elink_sca_rx>[0-9a-fA-F]+))?" // this part is optional! In case there are different rx/tx elinks
"$"
);
boost::smatch matchResults;
bool matched = boost::regex_match( stringAddress, matchResults, directAddressRegex );
if (!matched)
{
THROW_WITH_ORIGIN(std::runtime_error,
"supplied specific address didn't match the regex for direct address. Given string was: '"+
stringAddress+"' Here is an example: 'direct/pcatlnswfelix01.cern.ch/12340/12345/BF'");
}
m_felixHostname = matchResults["address"];
m_portToSca = boost::lexical_cast<unsigned int>(matchResults["port_to_sca"]);
m_portFromSca = boost::lexical_cast<unsigned int>(matchResults["port_from_sca"]);
// elinks should be given in hex, that's why lexical_cast can't be used
std::stringstream ss;
ss.str(matchResults["elink_sca"]);
ss >> std::hex >> m_elinkIdTx;
if (ss.fail())
THROW_WITH_ORIGIN(std::runtime_error,"Didn't manage to read hex number from: "+ss.str());
if (matchResults["elink_sca_rx"].matched)
{
ss.clear();
ss.str(matchResults["elink_sca_rx"]);
ss >> std::hex >> m_elinkIdRx;
if (ss.fail())
THROW_WITH_ORIGIN(std::runtime_error,"Didn't manage to read hex number from: "+ss.str());
LOG(Log::INF, LogComponentLevels::netio()) << "Note: this SCA uses different elinks for TX and RX (this is a bit unusual!) TX: 0x" <<
std::hex << m_elinkIdTx <<
" RX: 0x" << std::hex << m_elinkIdRx;
}
else
m_elinkIdRx = m_elinkIdTx; // the usual situation: same elink ID for Tx and Rx
}
}
......@@ -9,16 +9,18 @@ For standalone SCA SW (which will let you run some demonstrators to talk to SCA'
build_standalone.sh
Note that:
1. By default, the software builds with netio and other parts of the TDAQ FELIX Software.
1. By default, the software builds with netio and other parts of the TDAQ FELIX Software.
You can remove netio dependency by changing, in build_standalone, HAVE_NETIO to 0.
2. The easiest way to get netio and TDAQ FELIX is to get the distro from:
https://atlas-project-felix.web.cern.ch/atlas-project-felix/user/dist/software/apps/4.x/
2. The felix-client, netio-next, felixbus and hdlc_coder software checkout as submodules.
felix-client, netio-next, felixbus and hdlc_coder need to be compiled and linked by
The current supported package is for CC7 in the link below:
https://atlas-project-felix.web.cern.ch/atlas-project-felix/user/dist/software/apps/4.x/felix-04-00-03-x86_64-centos7-gcc49-opt.tar.gz
```
cd felix-client; source cmake_tdaq/bin/setup.sh; cmake_config; cd x86_64-centos7-gcc8-opt; make;
cd netio-next; source cmake_tdaq/bin/setup.sh; cmake_config; cd x86_64-centos7-gcc8-opt; make;
cd felixbus; source cmake_tdaq/bin/setup.sh; cmake_config; cd x86_64-centos7-gcc8-opt; make;
cd hdlc_coder; source cmake_tdaq/bin/setup.sh; cmake_config; cd x86_64-centos7-gcc8-opt; make;
```
3. Please note that you should use exactly same compiler as the one used for TDAQ FELIX chain. For that source the setup_paths.sh to define your environment.
Don't forget to also source the setup.sh from your FELIX installation directory.
VeryHipEvents @ bbf4d7e1
Subproject commit bbf4d7e1683de4645260b562406d34006130c1c7
......@@ -4,7 +4,7 @@
rm -Rf build
mkdir build
cd build
cmake ../ -DSTANDALONE_SCA_SOFTWARE=1 -DHAVE_NETIO=1 -DSTANDALONE_WITH_EVENT_RECORDER=ON -DCMAKE_BUILD_TYPE=Debug
cmake ../ -DSTANDALONE_SCA_SOFTWARE=1 -DHAVE_NETIO=0 -DHAVE_NETIO_NEXT=1 -DSTANDALONE_WITH_EVENT_RECORDER=OFF -DCMAKE_BUILD_TYPE=Debug
make -j `nproc` || exit 1
cd ..
echo "Build was made in build/ directory"
Subproject commit 9b448bc452f21ff502d4641324d237f7be682ffd
Subproject commit 7332c383b3321c26e057452f76a92f2e0134a242
Subproject commit 99f634275b81efc27fb8fde3294ab4f57a59234c
Subproject commit a22e53a97fe786e399aa4b468c6e89d2802666ba
#ifndef NETIONEXTBACKEND_H
#define NETIONEXTBACKEND_H
/** @author Piotr Nikiel
@date 18-Sep-2016
*/
#include <memory> // for shared_ptr
#include <boost/thread.hpp>
#include <mutex>
#include <Hdlc/Backend.h>
#include <Hdlc/Payload.h>
#include <Hdlc/LocalStatistician.h>
#include <NetIoNextBackend/SimpleNetioSpecificAddress.h>
#include <netio/netio.h>
#include <hdlc_coder/hdlc.hpp>
#include <felix/felix_client_thread_interface.hpp>
namespace NetIoNextBackend
{
class HdlcBackend : public Hdlc::Backend
{
public:
HdlcBackend (const std::string& specificAddress);
virtual ~HdlcBackend ();
virtual void send (const Hdlc::Payload &request );
virtual void send (
const std::vector<Hdlc::Payload> &requests,
const std::vector<unsigned int> times
);
virtual void subscribeReceiver ( ReceiveCallBack f ) { m_receivers.push_back(f); }
virtual const std::string& getAddress ( ) const { return m_originalSpecificAddress; }
virtual Hdlc::BackendStatistics getStatistics () const { return m_statistician.toBackendStatistics(); }
virtual void sendHdlcControl (uint8_t hdlcControl);
void cleanUp ();
private:
std::string m_originalSpecificAddress;
std::shared_ptr<const SpecificAddress> m_specificAddress;
FelixClientThreadInterface *client;
std::list<ReceiveCallBack> m_receivers;
Hdlc::LocalStatistician m_statistician;
unsigned int m_seqnr; // HDLC sequence number
bool m_replySocketSubscribed;
void replyHandler(netio_tag_t fid, const uint8_t* data, size_t size, uint8_t status);
std::mutex m_transmitSynchronizationMutex;
};
}
#endif // NETIONEXTBACKEND