TCP: Implemented ACK return stream, as a feedback channel (to be read properly!)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user