feat(tcp): TCPStreamer DataSources, in-process TCPStreamService, stream multiplexing and TCPStreamJoin
Add TCPStreamer/TCPStreamerClient (TCP transport behind the UDPSServerI/UDPSClientI seam), an optional single in-process TCPStreamService serving many streams over worker threads, multiplexed SUBSCRIBE (comma list, stream index in fragmentIdx), and TCPStreamJoin, which merges several streams into one sample-and-hold stream paced by a master. StreamHub takes a comma list of streams over one connection. Includes GTests, E2E scenarios s58-s73 and docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
5562877c99
commit
50d85363a3
79 files changed
+12348
-138
No files matched your search
@@ -84,6 +84,7 @@ UDPStreamer::UDPStreamer()
|
||||
: MemoryDataSourceI(), EmbeddedServiceMethodBinderI(), executor(*this) {
|
||||
port = UDPS_DEFAULT_PORT;
|
||||
maxPayloadSize = UDPS_DEFAULT_MAX_PAYLOAD;
|
||||
server = new UDPSServer();
|
||||
cpuMask = 0xFFFFFFFFu;
|
||||
stackSize = THREADS_DEFAULT_STACKSIZE;
|
||||
publishMode = UDPStreamerPublishStrict;
|
||||
@@ -135,7 +136,11 @@ UDPStreamer::~UDPStreamer() {
|
||||
}
|
||||
}
|
||||
|
||||
(void)server.Stop();
|
||||
if (server != NULL_PTR(UDPSServerI *)) {
|
||||
(void)server->Stop();
|
||||
delete server;
|
||||
server = NULL_PTR(UDPSServerI *);
|
||||
}
|
||||
|
||||
HeapI *heapAccum = GlobalObjectsDatabase::Instance()->GetStandardHeap();
|
||||
if (accumBuffer != NULL_PTR(uint8 *)) {
|
||||
@@ -192,7 +197,7 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
|
||||
|
||||
if (ok) {
|
||||
if (!data.Read("MaxPayloadSize", maxPayloadSize)) {
|
||||
maxPayloadSize = UDPS_DEFAULT_MAX_PAYLOAD;
|
||||
maxPayloadSize = DefaultMaxPayloadSize();
|
||||
REPORT_ERROR(ErrorManagement::Information,
|
||||
"MaxPayloadSize not specified; using default %u.",
|
||||
maxPayloadSize);
|
||||
@@ -286,38 +291,58 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
// Build server config with already-resolved Port and MaxPayloadSize so
|
||||
// UDPSServer::Initialise() always sees them, even when defaults were used.
|
||||
ConfigurationDatabase serverCfg;
|
||||
(void)serverCfg.Write("Port", static_cast<uint32>(port));
|
||||
(void)serverCfg.Write("MaxPayloadSize", maxPayloadSize);
|
||||
// Forward optional multicast / timeout params if present in caller's data.
|
||||
StreamString mcGroup;
|
||||
if (data.Read("MulticastGroup", mcGroup) && (mcGroup.Size() > 0u)) {
|
||||
(void)serverCfg.Write("MulticastGroup", mcGroup.Buffer());
|
||||
uint32 dp = 0u;
|
||||
if (data.Read("DataPort", dp)) {
|
||||
(void)serverCfg.Write("DataPort", dp);
|
||||
}
|
||||
StreamString iface;
|
||||
if (data.Read("Interface", iface)) {
|
||||
(void)serverCfg.Write("Interface", iface);
|
||||
} else {
|
||||
ok = false;
|
||||
REPORT_ERROR(ErrorManagement::InitialisationError,
|
||||
"Missing mandatory interface for multicasting");
|
||||
}
|
||||
}
|
||||
uint32 clientTimeout = 0u;
|
||||
if (data.Read("ClientTimeout", clientTimeout)) {
|
||||
(void)serverCfg.Write("ClientTimeout", clientTimeout);
|
||||
}
|
||||
ok = server.Initialise(serverCfg);
|
||||
ok = InitialiseTransport(data);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint32 UDPStreamer::DefaultMaxPayloadSize() const {
|
||||
return UDPS_DEFAULT_MAX_PAYLOAD;
|
||||
}
|
||||
|
||||
bool UDPStreamer::InitialiseTransport(StructuredDataI &data) {
|
||||
bool ok = true;
|
||||
// Build server config with already-resolved Port and MaxPayloadSize so
|
||||
// UDPSServer::Initialise() always sees them, even when defaults were used.
|
||||
ConfigurationDatabase serverCfg;
|
||||
(void)serverCfg.Write("Port", static_cast<uint32>(port));
|
||||
(void)serverCfg.Write("MaxPayloadSize", maxPayloadSize);
|
||||
// Forward optional multicast / timeout params if present in caller's data.
|
||||
StreamString mcGroup;
|
||||
if (data.Read("MulticastGroup", mcGroup) && (mcGroup.Size() > 0u)) {
|
||||
(void)serverCfg.Write("MulticastGroup", mcGroup.Buffer());
|
||||
uint32 dp = 0u;
|
||||
if (data.Read("DataPort", dp)) {
|
||||
(void)serverCfg.Write("DataPort", dp);
|
||||
}
|
||||
StreamString iface;
|
||||
if (data.Read("Interface", iface)) {
|
||||
(void)serverCfg.Write("Interface", iface);
|
||||
} else {
|
||||
ok = false;
|
||||
REPORT_ERROR(ErrorManagement::InitialisationError,
|
||||
"Missing mandatory interface for multicasting");
|
||||
}
|
||||
}
|
||||
uint32 clientTimeout = 0u;
|
||||
if (data.Read("ClientTimeout", clientTimeout)) {
|
||||
(void)serverCfg.Write("ClientTimeout", clientTimeout);
|
||||
}
|
||||
ok = server->Initialise(serverCfg);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void UDPStreamer::SetServer(UDPSServerI *newServer) {
|
||||
if (server != NULL_PTR(UDPSServerI *)) {
|
||||
delete server;
|
||||
}
|
||||
server = newServer;
|
||||
}
|
||||
|
||||
UDPSServerI *UDPStreamer::Transport() const { return server; }
|
||||
|
||||
bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
|
||||
bool ok = MemoryDataSourceI::SetConfiguredDatabase(data);
|
||||
if (!ok) {
|
||||
@@ -804,7 +829,7 @@ bool UDPStreamer::PrepareNextState(const char8 *const currentStateName,
|
||||
const char8 *const nextStateName) {
|
||||
bool ok = true;
|
||||
|
||||
ok = server.Start();
|
||||
ok = server->Start();
|
||||
|
||||
/* Build the CONFIG payload and cache it in the server so any CONNECT client
|
||||
* receives it immediately. The config is static for the lifetime of this
|
||||
@@ -818,7 +843,7 @@ bool UDPStreamer::PrepareNextState(const char8 *const currentStateName,
|
||||
if (cfgBuf != NULL_PTR(uint8 *)) {
|
||||
uint32 cfgPayloadSize = 0u;
|
||||
if (BuildConfigPayload(cfgBuf, configBufSize, cfgPayloadSize)) {
|
||||
(void)server.SendConfig(cfgBuf, cfgPayloadSize);
|
||||
(void)server->SendConfig(cfgBuf, cfgPayloadSize);
|
||||
} else {
|
||||
REPORT_ERROR(ErrorManagement::Warning,
|
||||
"Could not build initial CONFIG payload.");
|
||||
@@ -992,7 +1017,7 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
|
||||
|
||||
/* --- Poll for incoming control commands (CONNECT / DISCONNECT / ACK) ---
|
||||
*/
|
||||
server.ServiceClients();
|
||||
server->ServiceClients();
|
||||
|
||||
/* Synchronise() already gates publication to the correct rate (size/time
|
||||
* for Accumulate, every-Nth for Decimate, every call for Strict). The
|
||||
@@ -1015,12 +1040,12 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
|
||||
}
|
||||
bufMutex.FastUnLock();
|
||||
|
||||
if ((fill > 0u) && server.HasClients()) {
|
||||
if ((fill > 0u) && server->HasClients()) {
|
||||
SerializeAccumulated(scratchBuffer, scratchTimestamps, fill);
|
||||
uint32 sendBytes =
|
||||
UDPS_TIMESTAMP_BYTES + 4u + fill * singleCycleWireBytes;
|
||||
packetCounter++;
|
||||
if (!server.SendData(packetCounter, wireBuffer, sendBytes)) {
|
||||
if (!server->SendData(packetCounter, wireBuffer, sendBytes)) {
|
||||
REPORT_ERROR(ErrorManagement::Warning,
|
||||
"Failed to send Accumulate DATA packet (counter=%u).",
|
||||
packetCounter);
|
||||
@@ -1040,11 +1065,11 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
|
||||
}
|
||||
bufMutex.FastUnLock();
|
||||
|
||||
if (pending && server.HasClients()) {
|
||||
if (pending && server->HasClients()) {
|
||||
QuantizeAndSerialize(scratchBuffer, ts);
|
||||
|
||||
packetCounter++;
|
||||
if (!server.SendData(packetCounter, wireBuffer, totalWireBytes)) {
|
||||
if (!server->SendData(packetCounter, wireBuffer, totalWireBytes)) {
|
||||
REPORT_ERROR(ErrorManagement::Warning,
|
||||
"Failed to send DATA packet (counter=%u).",
|
||||
packetCounter);
|
||||
@@ -1056,7 +1081,7 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
|
||||
}
|
||||
|
||||
if (info.GetStage() == ExecutionInfo::TerminationStage) {
|
||||
(void)server.Stop();
|
||||
(void)server->Stop();
|
||||
REPORT_ERROR(ErrorManagement::Information,
|
||||
"UDPStreamer background thread terminated.");
|
||||
}
|
||||
@@ -1367,9 +1392,9 @@ uint16 UDPStreamer::GetPort() const { return port; }
|
||||
|
||||
uint32 UDPStreamer::GetMaxPayloadSize() const { return maxPayloadSize; }
|
||||
|
||||
bool UDPStreamer::IsClientConnected() const { return server.HasClients(); }
|
||||
bool UDPStreamer::IsClientConnected() const { return server->HasClients(); }
|
||||
|
||||
bool UDPStreamer::IsMulticast() const { return server.IsMulticast(); }
|
||||
bool UDPStreamer::IsMulticast() const { return server->IsMulticast(); }
|
||||
|
||||
uint32 UDPStreamer::GetDroppedPublications() const { return droppedPublications; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user