TCP: Implemented ACK return stream, as a feedback channel (to be read properly!)

This commit is contained in:
2026-03-04 12:24:05 +01:00
parent 939bb02ce2
commit a3a986830b
13 changed files with 611 additions and 80 deletions
+52 -34
View File
@@ -19,7 +19,6 @@ TCPStreamPusher::TCPStreamPusher(const std::vector<std::string> &addr,
}
}
void TCPStreamPusher::StartDataCollection(StartMessage &message) {
if (message.images_per_file < 1)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
@@ -35,6 +34,25 @@ void TCPStreamPusher::StartDataCollection(StartMessage &message) {
"TCP accept timeout/failure on socket " + socket[i]->GetEndpointName());
}
for (auto &s : socket)
s->StartWriterThread();
std::vector<bool> started(socket.size(), false);
auto rollback_cancel = [&]() {
for (size_t i = 0; i < socket.size(); i++) {
if (!started[i] || socket[i]->IsBroken())
continue;
(void)socket[i]->Send(nullptr, 0, TCPFrameType::CANCEL);
std::string cancel_ack_err;
(void)socket[i]->WaitForAck(TCPFrameType::CANCEL, std::chrono::milliseconds(500), &cancel_ack_err);
}
for (auto &s : socket)
s->StopWriterThread();
};
for (size_t i = 0; i < socket.size(); i++) {
message.socket_number = static_cast<int64_t>(i);
if (i > 0)
@@ -44,17 +62,20 @@ void TCPStreamPusher::StartDataCollection(StartMessage &message) {
socket[i]->SetRunNumber(run_number);
if (!socket[i]->Send(serialization_buffer.data(), serializer.GetBufferSize(), TCPFrameType::START)) {
// one-shot recovery: reconnect and retry START once
if (!socket[i]->AcceptConnection(std::chrono::seconds(5)) ||
!socket[i]->Send(serialization_buffer.data(), serializer.GetBufferSize(), TCPFrameType::START)) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Timeout/failure sending START");
}
rollback_cancel();
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Timeout/failure sending START on " + socket[i]->GetEndpointName());
}
}
for (auto &s : socket)
s->StartWriterThread();
std::string ack_err;
if (!socket[i]->WaitForAck(TCPFrameType::START, std::chrono::seconds(5), &ack_err)) {
rollback_cancel();
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"START ACK failed on " + socket[i]->GetEndpointName() + ": " + ack_err);
}
started[i] = true;
}
}
bool TCPStreamPusher::SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) {
@@ -88,12 +109,25 @@ bool TCPStreamPusher::EndDataCollection(const EndMessage &message) {
bool ret = true;
for (auto &s : socket) {
s->StopWriterThread();
if (s->IsBroken())
if (s->IsBroken()) {
ret = false;
else if (!s->Send(serialization_buffer.data(), serializer.GetBufferSize(), TCPFrameType::END))
continue;
}
if (!s->Send(serialization_buffer.data(), serializer.GetBufferSize(), TCPFrameType::END)) {
ret = false;
continue;
}
std::string ack_err;
if (!s->WaitForAck(TCPFrameType::END, std::chrono::seconds(10), &ack_err)) {
ret = false;
}
}
for (auto &s : socket)
s->StopWriterThread();
transmission_error = !ret;
return ret;
}
@@ -102,32 +136,16 @@ std::string TCPStreamPusher::Finalize() {
std::string ret;
if (transmission_error)
ret += "Timeout sending images (e.g., writer disabled during data collection);";
if (writer_notification_socket) {
for (size_t i = 0; i < socket.size(); i++) {
auto n = writer_notification_socket->Receive(run_number, run_name);
if (!n)
ret += "Writer " + std::to_string(i) + ": no end notification received within 1 minute from collection end";
else if (static_cast<size_t>(n->socket_number) >= socket.size())
ret += "Writer " + std::to_string(i) + ": mismatch in socket number";
else if (!n->ok)
ret += "Writer " + std::to_string(i) + ": " + n->error;
for (size_t i = 0; i < socket.size(); i++) {
if (socket[i]->IsBroken()) {
const auto reason = socket[i]->GetLastAckError();
ret += "Writer " + std::to_string(i) + ": " + (reason.empty() ? "stream broken" : reason) + ";";
}
}
return ret;
}
std::string TCPStreamPusher::GetWriterNotificationSocketAddress() const {
if (writer_notification_socket)
return writer_notification_socket->GetEndpointName();
else
return "";
}
TCPStreamPusher &TCPStreamPusher::WriterNotificationSocket(const std::string &addr) {
writer_notification_socket = std::make_unique<ZMQWriterNotificationPuller>(addr, std::chrono::minutes(1));
return *this;
}
std::string TCPStreamPusher::PrintSetup() const {
std::string output = "TCPStream2Pusher: Sending images to sockets: ";
for (const auto &s : socket)