"Refactor CJMS11 image grab functionality with timeout handling and error resilience

- Introduced a new `GrabImageListener` method with asynchronous timeout support for improved response handling.
- Enhanced error logging and implemented retry logic for capturing images to address NACK scenarios.
- Added overlay rendering and event-based image delivery for better UI integration.
- Refined `SendImageListener` logic to include command handling improvements and listener termination checks.
- Minor parameter adjustments in `OnImageReceived` for default values support."
This commit is contained in:
Michal Buzik 2025-07-30 10:34:56 +02:00
parent 3e643854bc
commit 50a38bc557

View File

@ -1088,6 +1088,139 @@ namespace TBF.Rig.Network.Camera.CJMS11
// Get network stream
using (cameraStream = this.cameraTcpClient.GetStream())
using (cameraWriter = new StreamWriter(cameraStream, Encoding.ASCII) { AutoFlush = true })
using (cameraReader = new StreamReader(cameraStream, Encoding.ASCII))
{
cameraStream.ReadTimeout = 1000;
try
{
SendCommand(CommandM.prepare_camera_);
}
catch (Exception exc)
{
log.Error($"Initialisation of connection readers failed: {exc.Message}");
return;
}
stopGrabImageListenerFlag = false;
int iCountOf_NACK = 0;
int iActualImageIndex = 0;
SendCommand(CommandM.grab_image_);
while (!stopGrabImageListenerFlag)
{
try
{
Task<string> responseAnswerTask = cameraReader.ReadLineAsync();
var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(1));
var completedAnswerTask = Task.WhenAny(responseAnswerTask, timeoutAnswerTask).Result;
if (completedAnswerTask == timeoutAnswerTask)
{
throw new TimeoutException("Answer response attempt timed out after 1 second.");
}
string responseAnswer = responseAnswerTask.Result;
if (responseAnswer.Length > 0)
{
/// Process the packet
JmsPacket newPacket = new JmsPacket(responseAnswer);
if (newPacket.JmsMessage.Status != MessageStatus.ACK)
{
if (iCountOf_NACK < 5)
{
iCountOf_NACK++;
continue;
}
log.ErrorFormat("Error occured more as 5 time in GrabImageListener: {0}",
newPacket.JmsMessage.Status);
stopGrabImageListenerFlag = true; // mam obrazok ukoncim a zavriem thread
break;
}
else
{
switch (newPacket.JmsMessage.Command)
{
case CommandM.grab_image_:
ParsedImage parsedImage = newPacket.JmsMessage.getImage();
if (parsedImage.Encoding == ImageType.BASE_64)
{
Image image = parsedImage.Image;
if (showOverlayRect && overlayRect != null && overlayThickness > 0)
{
int penWidth = 1;
Pen pen = new Pen(Color.Green, penWidth);
using (var grph = Graphics.FromImage(image))
{
if (penWidth != overlayThickness)
{
penWidth = overlayThickness;
pen = new Pen(Color.Green, penWidth);
}
grph.DrawRectangle(pen, overlayRect);
}
}
OnImageReceived(this, this.cameraIdx, image, RoiConfiguration.ImageRotation);
iActualImageIndex++;
if (iActualImageIndex >= RoiConfiguration.ImageOpacity.Count)
{
stopGrabImageListenerFlag =
true; // mam obrazok ukoncim a zavriem thread
}
}
break;
}
}
}
}
catch (Exception exc)
{
log.Error($"Grab Image - Hardware Addr: {CameraCfg.HardwareAddress} Camera IP: {CameraCfg.IPAddressCJMS}, Port: {iPort} Exception in MJpeg RTP listener thread: { exc.Message}");
}
}
stopGrabImageListenerFlag = false;
SendCommandAndWaitToAnswer(cameraWriter, cameraReader, CommandM.close_);
SendCommandAndWaitToAnswer(cameraWriter, cameraReader, CommandM.disconnect_);
CloseCameraResources();
return;
}
}catch(Exception exc)
{
log.Error($"Grab Image - Hardware Addr: {CameraCfg.HardwareAddress} Camera IP: {CameraCfg.IPAddressCJMS}, Port: {iPort} exception: {exc.Message}");
UiBridge.Bridge.OnImage(this.cameraIdx, null);
}
}
}
void SendImageListener()
{
using (this.cameraTcpClient = new TcpClient())
{
try
{
EstablishCameraConnection(ref this.cameraTcpClient, CameraCfg.IPAddressCJMS, iPort);
log.Info(string.Format("Camera connected to server {0}:{1}.", CameraCfg.IPAddressCJMS, iPort));
ipAddress = IPAddress.Parse(CameraCfg.IPAddressCJMS);
// Get network stream
using (cameraStream = this.cameraTcpClient.GetStream())
using (cameraWriter = new StreamWriter(cameraStream, Encoding.ASCII) { AutoFlush = true })
using (cameraReader = new StreamReader(cameraStream, Encoding.ASCII))
@ -1109,7 +1242,7 @@ namespace TBF.Rig.Network.Camera.CJMS11
int iActualImageIndex = 0;
//TODO BUMI send grab image or send - based on count of images - but must work similary
if (RoiConfiguration.ImageOpacity.Count > 1)
if (RoiConfiguration.ImageOpacity.Count <= 1)
{
SendCommand(CommandM.grab_image_);
}
@ -1156,6 +1289,7 @@ namespace TBF.Rig.Network.Camera.CJMS11
switch (newPacket.JmsMessage.Command)
{
case CommandM.send_:
case CommandM.grab_image_:
ParsedImage parsedImage = newPacket.JmsMessage.getImage();
if (parsedImage.Encoding == ImageType.BASE_64)
@ -1579,7 +1713,7 @@ namespace TBF.Rig.Network.Camera.CJMS11
/// This method is called from within this class when
/// a prompt string was received from the telnet server.
/// </summary>
public static void OnImageReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx, int imageCount)
public static void OnImageReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx = 0, int imageCount = 1)
{
log.Info("Image received");