diff --git a/TBF/Rig/Network/Camera/CJMS11/Camera.cs b/TBF/Rig/Network/Camera/CJMS11/Camera.cs index 0d6034503..5057f8bfb 100644 --- a/TBF/Rig/Network/Camera/CJMS11/Camera.cs +++ b/TBF/Rig/Network/Camera/CJMS11/Camera.cs @@ -3,6 +3,7 @@ /// using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -55,7 +56,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 private readonly SemaphoreSlim cameraReadLock = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim cameraWriteLock = new SemaphoreSlim(1, 1); + //private readonly SemaphoreSlim cameraWriteLock = new SemaphoreSlim(1, 1); /// /// Properties @@ -133,24 +134,24 @@ namespace TBF.Rig.Network.Camera.CJMS11 int cameraTcpLocalPort; TcpClient cameraTcpClient; private NetworkStream cameraStream = null; - private StreamWriter cameraWriter = null; + //private StreamWriter cameraWriter = null; private StreamReader cameraReader = null; private Image lastImage; private bool lockCameraInitialisation = false; Thread liveStreamListenerThread; - static bool stopLiveStreamListenerFlag; + private bool stopLiveStreamListenerFlag; public bool IsGrabImageListenerThreadAlive { get { return grabImageListenerThread != null && grabImageListenerThread.IsAlive; } } Thread grabImageListenerThread; - static bool stopGrabImageListenerFlag; + private bool stopGrabImageListenerFlag; public void SetStopGrabImageListenerFlag() { stopGrabImageListenerFlag = true; } Thread stopUIListenerThread; - static bool stopUIListenerFlag; + private bool stopUIListenerFlag; Thread saveImageListenerThread; - static bool stopSaveImageListenerFlag; + private bool stopSaveImageListenerFlag; public bool IsSaveImageListenerThreadAlive { get { return saveImageListenerThread != null && saveImageListenerThread.IsAlive; } } /// @@ -202,9 +203,22 @@ namespace TBF.Rig.Network.Camera.CJMS11 /// Detect a camera CameraDetectionResult cameraDetectionResult = null; - cameraDetectionResult = DetectJMSCamera() - .GetAwaiter() - .GetResult(); + try + { + cameraDetectionResult = Task + .Run(() => DetectJMSCamera()) + .GetAwaiter() + .GetResult(); + } + catch (Exception exc) + { + log.Error($"Camera detection failed: {exc.Message}"); + + cameraDetectionResult = new CameraDetectionResult + { + IsDetected = false + }; + } //test // var detectJmsCamera1 = DetectJMSCamera("192.168.0.170",32456); @@ -223,14 +237,30 @@ namespace TBF.Rig.Network.Camera.CJMS11 if (cameraDetectionResult.IsDetected) { - UiBridge.Bridge.OnCameraInfo(cameraIdx, string.Format("{0} ver.{1} {2} s/n {3}", Name, sdCardVer, ipAddress, CameraCfg.HardwareAddress)); + ipAddress = cameraDetectionResult.IpAddress; - // start live stream, grab image, ... listener thread - UiBridge.Bridge.CameraUICmdHandler += HandleCameraUiCmd; - - //PromptCameraReceivedHandler += OnPromptReceivedCamera; - - ImageCameraHandler += OnPromptImageReceived; + UiBridge.Bridge.OnCameraInfo( + cameraIdx, + string.Format( + "{0} ver.{1} {2} s/n {3}", + Name, + sdCardVer, + ipAddress, + CameraCfg.HardwareAddress)); + + // start live stream, grab image, ... listener thread + UiBridge.Bridge.CameraUICmdHandler -= HandleCameraUiCmd; + UiBridge.Bridge.CameraUICmdHandler += HandleCameraUiCmd; + + //PromptCameraReceivedHandler -= OnPromptReceivedCamera; + //PromptCameraReceivedHandler += OnPromptReceivedCamera; + + ImageCameraHandler -= OnPromptImageReceived; + ImageCameraHandler += OnPromptImageReceived; + + ImagesCameraHandler -= OnPromptImagesReceived; + ImagesCameraHandler += OnPromptImagesReceived; + } else { @@ -246,6 +276,19 @@ namespace TBF.Rig.Network.Camera.CJMS11 if (this.cameraIdx == e.idxCamera) { UiBridge.Bridge.OnImage(e.idxCamera, e.image); + log.Debug($"Image received from camera: {e.idxCamera}, pushed to bridge UI!"); + } + } + } + + private void OnPromptImagesReceived(object sender, PromptReceivedImagesEventArgs e) + { + if (e != null && e.image != null) + { + if (this.cameraIdx == e.idxCamera) + { + UiBridge.Bridge.OnImage(e.idxCamera, e.image); + log.Debug($"Image received from camera: {e.idxCamera}, pushed to bridge UI!"); } } } @@ -386,170 +429,397 @@ namespace TBF.Rig.Network.Camera.CJMS11 { return await DetectJMSCamera(CameraCfg.IPAddressCJMS, iPort); } + + async Task DetectJMSCamera( string ipAddressParam, int portParam) + { + var result = new CameraDetectionResult + { + IsDetected = false, + IpAddress = null, + Hardware = null, + Revision = null, + Serial = null, + SdCardVersion = null + }; + + if (CameraCfg.DebugLevel == DebugMode.Simulate) + { + result.IsDetected = true; + return result; + } + + if (string.IsNullOrWhiteSpace(ipAddressParam)) + { + log.Error( $"Camera {CameraCfg.Name}: IP address is missing."); + return result; + } + + var detectionStopwatch = Stopwatch.StartNew(); + + try + { + using (var client = new TcpClient()) + { + Task connectTask = + client.ConnectAsync( + ipAddressParam, + portParam); + + Task timeoutTask = + Task.Delay(TimeSpan.FromSeconds(1)); + + Task completedTask = + await Task.WhenAny( + connectTask, + timeoutTask) + .ConfigureAwait(false); + + if (completedTask == timeoutTask) + { + client.Close(); + + throw new TimeoutException( $"Camera connection timeout after 1 second: {ipAddressParam}:{portParam}"); + } + + // Vyhodí SocketException, ak ConnectAsync zlyhal. + await connectTask.ConfigureAwait(false); + + if (!client.Connected) + { + throw new IOException( $"Camera connection was not established: {ipAddressParam}:{portParam}"); + } + + log.Debug( $"Camera connected: name={CameraCfg.Name}, IP={ipAddressParam}, port={portParam}"); + + using (NetworkStream stream = client.GetStream()) + { + // Jeden reader pre celé spojenie. + var reader = new CameraLineReader( + stream, + 64 * 1024); + + this.ipAddress = + IPAddress.Parse(ipAddressParam); + + /* + * 1. Nastavenie typu kamery + */ + string configMessage = getConfigMessage(); + + JmsPacket typeCameraAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.type_camera_, + configMessage, + 5000, + CancellationToken.None) + .ConfigureAwait(false); + + if (typeCameraAnswer == null || typeCameraAnswer.JmsMessage.Status != MessageStatus.ACK) + { + log.Warn( $"type_camera failed: " + $"camera={CameraCfg.Name}, " + $"IP={ipAddressParam}, " + $"answer={FormatPacketForLog(typeCameraAnswer)}"); + } + + /* + * 2. Prepare camera + */ + JmsPacket prepareAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.prepare_camera_, + null, + 3000, + CancellationToken.None) + .ConfigureAwait(false); + + bool alreadyOpened = + prepareAnswer != null && + prepareAnswer.JmsMessage.Status == + MessageStatus.NACK && + !string.IsNullOrEmpty( + prepareAnswer.JmsMessage.Payload) && + prepareAnswer.JmsMessage.Payload.Contains( + "OPENED"); + + if (alreadyOpened) + { + log.Debug( $"Camera already opened: " + $"name={CameraCfg.Name}, " + $"IP={ipAddressParam}"); + } + else if (prepareAnswer == null || prepareAnswer.JmsMessage.Status != MessageStatus.ACK) + { + log.Warn( $"Prepare camera failed: " + $"camera={CameraCfg.Name}, " + $"IP={ipAddressParam}, " + $"answer={FormatPacketForLog(prepareAnswer)}"); + + /* + * Pokus o reset: + * close -> prepare + */ + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.close_, + null, + 2000, + CancellationToken.None) + .ConfigureAwait(false); + + prepareAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.prepare_camera_, + null, + 3000, + CancellationToken.None) + .ConfigureAwait(false); + + bool restartSucceeded = + prepareAnswer != null && + ( + prepareAnswer.JmsMessage.Status == + MessageStatus.ACK || + ( + prepareAnswer.JmsMessage.Status == + MessageStatus.NACK && + !string.IsNullOrEmpty( + prepareAnswer.JmsMessage.Payload) && + prepareAnswer.JmsMessage.Payload.Contains( + "OPENED") + ) + ); + + if (!restartSucceeded) + { + log.Error( + $"Camera prepare restart failed: " + + $"camera={CameraCfg.Name}, " + + $"IP={ipAddressParam}, " + + $"answer={FormatPacketForLog(prepareAnswer)}"); + } + } + + /* + * 3. Status + */ + JmsPacket statusAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.status_, + null, + 3000, + CancellationToken.None) + .ConfigureAwait(false); + + if (statusAnswer == null || + statusAnswer.JmsMessage.Status != + MessageStatus.ACK) + { + log.Warn( + $"Camera status failed: " + + $"camera={CameraCfg.Name}, " + + $"IP={ipAddressParam}, " + + $"answer={FormatPacketForLog(statusAnswer)}"); + } + + /* + * 4. Get configuration + */ + JmsPacket configAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.get_cfg_, + null, + 3000, + CancellationToken.None) + .ConfigureAwait(false); + + if (configAnswer == null || + configAnswer.JmsMessage.Status != + MessageStatus.ACK) + { + throw new IOException( + $"Camera get_cfg failed: " + + $"camera={CameraCfg.Name}, " + + $"IP={ipAddressParam}, " + + $"answer={FormatPacketForLog(configAnswer)}"); + } + + result.IpAddress = + IPAddress.Parse(ipAddressParam); + + result.Hardware = string.Empty; + result.Revision = string.Empty; + result.Serial = string.Empty; + result.SdCardVersion = string.Empty; + result.Response = configAnswer.ToString(); + result.IsDetected = true; + + string origin = + string.IsNullOrEmpty(configAnswer.MessageOrigin) + ? "unknown origin" + : configAnswer.MessageOrigin; + + detectionStopwatch.Stop(); + result.Duration_ms = + detectionStopwatch.Elapsed.TotalMilliseconds; + + log.Debug( + $"Camera detected: " + + $"name={CameraCfg.Name}, " + + $"duration={result.Duration_ms:F0} ms, " + + $"HWAddress={CameraCfg.HardwareAddress}, " + + $"IP={ipAddressParam}, " + + $"origin={origin}"); + + /* + * Voliteľný cleanup. + * + * Ak má kamera po detekcii zostať otvorená, + * túto časť odstráň. + */ + try + { + JmsPacket closeAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.close_, + null, + 2000, + CancellationToken.None) + .ConfigureAwait(false); + + bool closeAccepted = + closeAnswer != null && + ( + closeAnswer.JmsMessage.Status == MessageStatus.ACK || + ( + closeAnswer.JmsMessage.Status == MessageStatus.NACK && + !string.IsNullOrEmpty(closeAnswer.JmsMessage.Payload) && + closeAnswer.JmsMessage.Payload.Contains( + "Close command already in progress") + ) + ); + + if (!closeAccepted) + { + log.Warn( + $"Close after detection was not confirmed: " + + $"camera={CameraCfg.Name}, " + + $"answer={FormatPacketForLog(closeAnswer)}"); + } + } + catch (Exception closeException) + { + log.Warn( + $"Camera close after detection failed: " + + $"{closeException.Message}"); + } + /* + * Disconnect after detection. + */ + try + { + await SendCommandAsync( + stream, + CommandM.disconnect_, + CancellationToken.None, + null) + .ConfigureAwait(false); + + log.Debug( + $"Disconnect command sent after detection: " + + $"camera={CameraCfg.Name}, IP={ipAddressParam}"); + } + catch (Exception disconnectException) + { + log.Warn( + $"Camera disconnect after detection failed: " + + $"{disconnectException.Message}"); + } + + return result; + } + } + } + catch (TimeoutException exc) + { + detectionStopwatch.Stop(); + result.Duration_ms = + detectionStopwatch.Elapsed.TotalMilliseconds; + + log.Error( + $"Camera detection timeout: " + + $"name={CameraCfg.Name}, " + + $"HWAddress={CameraCfg.HardwareAddress}, " + + $"IP={ipAddressParam}, " + + $"port={portParam}, " + + $"duration={result.Duration_ms:F0} ms, " + + $"error={exc.Message}"); + } + catch (SocketException exc) + { + detectionStopwatch.Stop(); + result.Duration_ms = + detectionStopwatch.Elapsed.TotalMilliseconds; + + log.Error( + $"Camera socket connection failed: " + + $"name={CameraCfg.Name}, " + + $"IP={ipAddressParam}, " + + $"port={portParam}, " + + $"socketError={exc.SocketErrorCode}, " + + $"error={exc.Message}"); + } + catch (Exception exc) + { + detectionStopwatch.Stop(); + result.Duration_ms = + detectionStopwatch.Elapsed.TotalMilliseconds; + + log.Error( + $"Camera detection failed: " + + $"name={CameraCfg.Name}, " + + $"HWAddress={CameraCfg.HardwareAddress}, " + + $"IP={ipAddressParam}, " + + $"port={portParam}, " + + $"duration={result.Duration_ms:F0} ms, " + + $"error={exc.Message}", + exc); + } + + result.IsDetected = false; + result.IpAddress = null; + result.Hardware = null; + result.Revision = null; + result.Serial = null; + result.SdCardVersion = null; + + return result; + } - async Task DetectJMSCamera(string ipAddressParam, int portParam) - { - CameraDetectionResult result = new CameraDetectionResult(); - if (CameraCfg.IPAddressCJMS != null) - { - try - { + private static string FormatPacketForLog( JmsPacket packet) + { + if (packet == null) + return ""; - using (this.cameraTcpClient = new TcpClient()) - { - var connectTask = this.cameraTcpClient.ConnectAsync(ipAddressParam, portParam); - var timeoutTask = Task.Delay(TimeSpan.FromSeconds(1)); - - var completedTask = Task.WhenAny(connectTask, timeoutTask).Result; + if (packet.JmsMessage == null) + return ""; - if (completedTask == timeoutTask) - { - throw new TimeoutException("Connection attempt timed out after 1 second."); - } - //TcpClient tcpClient = await EstablishCameraConnection(CameraCfg.IPAddressCJMS, iPort); - log.Debug(string.Format("Camera connected to server {0}:{1}.", CameraCfg.IPAddressCJMS, iPort)); - this.ipAddress = IPAddress.Parse(CameraCfg.IPAddressCJMS); - // Get network stream + string payload = packet.JmsMessage.Payload; + if (!string.IsNullOrEmpty(payload) && payload.Length > 200) + { + payload = payload.Substring(0, 200) + "..."; + } - using (NetworkStream stream = cameraTcpClient.GetStream()) - using (StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true }) - //using (StreamReader reader = new StreamReader(stream, Encoding.ASCII)) - { - stream.ReadTimeout = 1000; + return $"command={packet.JmsMessage.Command}, " + $"status={packet.JmsMessage.Status}, " + $"payload={payload}"; + } - // Send command - DateTime detectionStartConnect = DateTime.Now; - - // create config and send - // Send command - DateTime detectionStart = DateTime.Now; - - JmsPacket answer = null; - try - { - - //await ClearPendingResponsesAsync(reader); - // create config and send - string message = getConfigMessage(); - answer = await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.type_camera_, message, 5000); - if (answer == null || answer.JmsMessage.Status != MessageStatus.ACK) - { - log.Error(answer); - } - } - catch (Exception exc) - { - log.Error(exc.Message); - } - - JmsPacket answerCfg = null; - try - { - //await ClearPendingResponsesAsync(reader); - //Prepare camera - JmsPacket answerPrepare = await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.prepare_camera_); - if (answerPrepare == null || answerPrepare.JmsMessage.Status != MessageStatus.ACK) - { - if (!(answerPrepare != null && - answerPrepare.JmsMessage.Status == MessageStatus.NACK && - answerPrepare.JmsMessage.Payload == "OPENED")) //correct if NACK & OPENED - { - log.Error(answerPrepare); - } - else - { - await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.close_); - var sendCommandAndWaitToAnswer = await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.prepare_camera_); - if (sendCommandAndWaitToAnswer == null || - sendCommandAndWaitToAnswer.JmsMessage.Status == MessageStatus.NACK) //correct if NACK & OPENED - { - log.Error("Restart close, prepare camera not successfully! see: " + - answerPrepare); - } - } - } - - //Status - JmsPacket answerStatus = await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.status_); - if (answerStatus == null || answerStatus.JmsMessage.Status != MessageStatus.ACK) - { - log.Error(answerStatus); - } - - //Get config - answerCfg = await SendCommandAndWaitToAnswerAsync(writer, stream, CommandM.get_cfg_); - if (answerCfg == null || answerCfg.JmsMessage.Status != MessageStatus.ACK) - { - log.Error(answerCfg); - } - else - { - result.IpAddress = IPAddress.Parse(ipAddressParam); - } - } - catch (Exception exc) - { - log.Error(exc.Message); - } - - - - result.Duration_ms = (DateTime.Now - detectionStart).TotalMilliseconds; - - result.Hardware = string.Empty; - result.Revision = string.Empty; - result.Serial = string.Empty; - result.SdCardVersion = string.Empty; - string origin = answerCfg != null ? answerCfg.MessageOrigin : "no cfg response"; - - string msg = string.Format("Camera {0} detected in {1} ms: HWAddress={2} IPAddress={3} {4}", - CameraCfg.Name, - result.Duration_ms, - CameraCfg.HardwareAddress, - this.ipAddress == null ? "not detected" : this.ipAddress.ToString(), - origin); - log.Debug(msg); - Console.WriteLine(msg); - - cameraTcpClient.Dispose(); - cameraTcpClient = null; - - result.IsDetected = true; - return result; /// Camera detected - } - } - } - catch (Exception exc) - { - log.ErrorFormat("Camera {0} with HWAddress={1} not detected : {2}", CameraCfg.Name, - CameraCfg.HardwareAddress, exc.Message); - } - } - - if (CameraCfg.DebugLevel == DebugMode.Simulate) - { - result.Hardware = null; - result.Revision = null; - result.Serial = null; - result.SdCardVersion = null; - result.IpAddress = null; - - result.IsDetected = true; - return result; - } - - result.Hardware = null; - result.Revision = null; - result.Serial = null; - result.SdCardVersion = null; - result.IpAddress = null; - - result.IsDetected = false; - return result; /// No camera detected - } - - private async Task ClearPendingResponsesAsync(StreamReader reader) + private async Task ClearPendingResponsesAsync(StreamReader reader) { await cameraReadLock.WaitAsync(); @@ -677,7 +947,8 @@ namespace TBF.Rig.Network.Camera.CJMS11 log.Debug("Handle CameraUICmd received - Live"); stopLiveStreamListenerFlag = false; stopUIListenerFlag = true; - StartLiveStreamListener(IPAddress, iPort); + StartLiveStreamListener(); + //StartLiveStreamListener(IPAddress, iPort); } else if (e.CameraUICmd == CameraUICmd.Stop) { @@ -688,7 +959,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 else if (e.CameraUICmd == CameraUICmd.Grab) { log.Debug($"Handle CameraUICmd received - GrabImage - camera idx: {cameraIdx}"); - stopUIListenerFlag = true; + //stopUIListenerFlag = true; StopUiThreads(); StartGrabImageListener(); } @@ -816,9 +1087,36 @@ namespace TBF.Rig.Network.Camera.CJMS11 saveImageListenerThread.Start(); } + private Task saveImageTask; + + public bool IsSaveImageInProgress => + saveImageTask != null && + !saveImageTask.IsCompleted; + + public Task StartSaveImageToFileAsync( Image image, string fileName) + { + if (image == null) + throw new ArgumentNullException(nameof(image)); + + if (string.IsNullOrWhiteSpace(fileName)) + throw new ArgumentException( + "File name is required.", + nameof(fileName)); + + if (saveImageTask != null && !saveImageTask.IsCompleted) + { + log.Warn( $"Image save already running: cameraIdx={cameraIdx}, file={fileName}"); + return saveImageTask; + } + + saveImageTask = Task.Run( () => SaveImageToFile(image, fileName)); + + return saveImageTask; + } + private void StartSaveImageListener() { - Task saveImageTask = Task.Run(() => SaveImageListener()); + Task saveImageTask = Task.Run(() => SaveImageListenerAsynch()); var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(1)); var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result; if (completedAnswerTask == timeoutAnswerTask) @@ -838,7 +1136,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 public void StartSaveImageListener(string filePath) { - Task saveImageTask = Task.Run(() => SaveImageListener()); + Task saveImageTask = Task.Run(() => SaveImageListenerAsynch()); var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(1)); var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result; if (completedAnswerTask == timeoutAnswerTask) @@ -895,7 +1193,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 } // Check if the connect task completed successfully - connectTask.GetAwaiter().GetResult(); // Throws if failed + //connectTask.GetAwaiter().GetResult()); // Throws if failed if (camTcpClient.Connected) { @@ -962,125 +1260,162 @@ namespace TBF.Rig.Network.Camera.CJMS11 } } + + + private async Task ConnectCameraAsync(int timeoutMs, CancellationToken ct) + { + var client = new TcpClient(); - public async Task SaveImageListener() + var connectTask = client.ConnectAsync(CameraCfg.IPAddressCJMS, iPort); + var timeoutTask = Task.Delay(timeoutMs, ct); + + var completed = await Task.WhenAny(connectTask, timeoutTask); + + if (completed == timeoutTask) + { + client.Close(); + throw new TimeoutException($"Connection timeout after {timeoutMs} ms."); + } + + await connectTask; + + log.Debug($"Camera connected to server {CameraCfg.IPAddressCJMS}:{iPort}."); + ipAddress = IPAddress.Parse(CameraCfg.IPAddressCJMS); + + return client; + } + + public async Task SaveImageListenerAsynch() + { + saveCts?.Cancel(); + saveCts = new CancellationTokenSource(); + + return await SaveImageListenerAsynch(saveCts.Token); + } + + public async Task SaveImageListenerAsynch(CancellationToken ct) { Image imageToReturn = null; - using (this.cameraTcpClient = new TcpClient()) + + await cameraOperationLock.WaitAsync(ct); + + try { - try + using (TcpClient client = await ConnectCameraAsync(1000, ct)) + using (NetworkStream stream = client.GetStream()) { - EstablishCameraConnection(ref this.cameraTcpClient, CameraCfg.IPAddressCJMS, iPort); - - log.Debug(string.Format("Camera connected to server {0}:{1}.", CameraCfg.IPAddressCJMS, iPort)); - ipAddress = IPAddress.Parse(CameraCfg.IPAddressCJMS); - - using (cameraStream = this.cameraTcpClient.GetStream()) - using (cameraWriter = new StreamWriter(cameraStream, Encoding.ASCII) { AutoFlush = true }) - using (cameraReader = new StreamReader(cameraStream, Encoding.ASCII)) - { - cameraStream.ReadTimeout = 1000; - - SendCommand(CommandM.prepare_camera_); - - bool inError = false; - stopSaveImageListenerFlag = false; - while (!stopSaveImageListenerFlag) - { - try - { - SendCommand(CommandM.grab_image_); - Task 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); - - switch (newPacket.JmsMessage.Command) - { - case CommandM.grab_image_: - if (newPacket.JmsMessage.Status != MessageStatus.ACK) - { - if (inError) - { - return null; // camera in error - finish thread - } - SendCommand(CommandM.prepare_camera_); - inError = true; - } - 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); - } - } - - imageToReturn = image; - stopSaveImageListenerFlag = - true; // mam obrazok ukoncim a zavriem thread - } - - break; - default: - break; - } - } - - } - catch (Exception exc) - { - string msg = string.Format("Exception in MJpeg RTP listener thread: {0}", exc.Message); - log.Error(msg); - } - } - - stopGrabImageListenerFlag = false; - SendCommand(CommandM.close_); - SendCommand(CommandM.disconnect_); - - CloseCameraResources(); - - return imageToReturn; + var reader = new CameraLineReader( + stream, + 64 * 1024); + JmsPacket prepareAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.prepare_camera_, + null, + 3000, + ct) + .ConfigureAwait(false); + bool alreadyOpened = + prepareAnswer != null && + prepareAnswer.JmsMessage.Status == + MessageStatus.NACK && + !string.IsNullOrEmpty( + prepareAnswer.JmsMessage.Payload) && + prepareAnswer.JmsMessage.Payload.Contains( + "OPENED"); + + if (alreadyOpened) + { + log.Debug( $"Camera already OPENED: " + $"cameraIdx={cameraIdx}, " + $"IP={CameraCfg.IPAddressCJMS}"); } - }catch(Exception exc) - { - log.Error($"Save Image exception: {exc.Message}"); + else if (prepareAnswer == null || prepareAnswer.JmsMessage.Status != MessageStatus.ACK) + { + log.Error( $"Prepare camera failed: " + $"cameraIdx={cameraIdx}, " + $"IP={CameraCfg.IPAddressCJMS}"); + return null; + } + + JmsPacket grabAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.grab_image_, + null, + 10000, + ct) + .ConfigureAwait(false); + + if (grabAnswer == null || grabAnswer.JmsMessage.Status != MessageStatus.ACK) + { + log.Error( $"Grab image failed: " + $"cameraIdx={cameraIdx}, " + $"IP={CameraCfg.IPAddressCJMS}"); + return null; + } + + ParsedImage parsedImage = grabAnswer.JmsMessage.getImage(); + + if (parsedImage == null) + { + log.Error( $"Parsed image is null: " + $"cameraIdx={cameraIdx}"); + return null; + } + + if (parsedImage.Encoding != ImageType.BASE_64) + { + log.Error( $"Unsupported image encoding: " + $"cameraIdx={cameraIdx}, " + $"encoding={parsedImage.Encoding}"); + return null; + } + + + + imageToReturn = parsedImage.Image; + + if (imageToReturn == null) + { + log.Error( $"Decoded image is null: " + $"cameraIdx={cameraIdx}"); + return null; + } + + DrawOverlay(imageToReturn); + + await TryCloseCameraAsync( stream, reader).ConfigureAwait(false); + + return imageToReturn; } } - return imageToReturn; + catch (OperationCanceledException) + { + log.Debug( $"Save image cancelled: cameraIdx={cameraIdx}"); + return null; + } + catch (TimeoutException exc) + { + log.Error( $"Save image timeout: cameraIdx={cameraIdx}, IP={CameraCfg.IPAddressCJMS}, error={exc.Message}"); + return null; + } + catch (Exception exc) + { + log.Error( $"Save image exception: cameraIdx={cameraIdx}, IP={CameraCfg.IPAddressCJMS}, error={exc.Message}", exc); + return null; + } + finally + { + cameraOperationLock.Release(); + } } void StopUiThreads() { + liveCts?.Cancel(); + grabCts?.Cancel(); + saveCts?.Cancel(); + stopLiveStreamListenerFlag = true; stopGrabImageListenerFlag = true; stopUIListenerFlag = true; + + log.Debug("Handle CameraUICmd StopUIListener"); var taskStopUi = Task.Run(StopUiListener); var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5)); @@ -1116,15 +1451,49 @@ namespace TBF.Rig.Network.Camera.CJMS11 //stopLiveStreamListenerFlag = false; //stopGrabImageListenerFlag = false; } + + + + public bool IsGrabImageInProgress + { + get + { + return grabImageTask != null && + !grabImageTask.IsCompleted; + } + } + + public Task GrabImageTask + { + get { return grabImageTask; } + } + public void StartGrabImageListener() { - grabImageListenerThread = new Thread(() => + + _ = StartGrabImageListenerAsync(); + } + + public Task StartGrabImageListenerAsync() + { + if (grabImageTask != null && !grabImageTask.IsCompleted) { - GrabImageListenerAsync().GetAwaiter().GetResult(); - }); + log.Warn( + $"Grab already running: cameraIdx={cameraIdx}, " + + $"IP={CameraCfg.IPAddressCJMS}"); - grabImageListenerThread.Start(); + return grabImageTask; + } + + grabCts?.Cancel(); + grabCts?.Dispose(); + + grabCts = new CancellationTokenSource(); + + grabImageTask = GrabImageListenerAsync(grabCts.Token); + + return grabImageTask; } private async Task EstablishCameraConnectionAsync( @@ -1144,70 +1513,255 @@ namespace TBF.Rig.Network.Camera.CJMS11 await connectTask; } - private async Task GrabImageListenerAsync() + private Task grabImageTask; + private CancellationTokenSource grabCts; + //private CancellationTokenSource liveCts; + private CancellationTokenSource saveCts; + + private readonly SemaphoreSlim cameraOperationLock = new SemaphoreSlim(1, 1); + + private async Task GrabImageListenerAsync(CancellationToken ct) { - using (this.cameraTcpClient = new TcpClient()) + await cameraOperationLock.WaitAsync(ct); + + Image resultImage = null; + try { - try + using (TcpClient client = await ConnectCameraAsync(5000, ct).ConfigureAwait(false)) + using (NetworkStream stream = client.GetStream()) { - await EstablishCameraConnectionAsync(this.cameraTcpClient, CameraCfg.IPAddressCJMS, iPort, 1000); + var reader = new CameraLineReader( stream, 64 * 1024); + + JmsPacket prepareAnswer = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.prepare_camera_, + null, + 3000, + ct); - using (cameraStream = this.cameraTcpClient.GetStream()) - using (cameraWriter = new StreamWriter(cameraStream, Encoding.ASCII) { AutoFlush = true }) - //using (cameraReader = new StreamReader(cameraStream, Encoding.ASCII)) + bool alreadyOpened = + prepareAnswer != null && + prepareAnswer.JmsMessage.Status == MessageStatus.NACK && + !string.IsNullOrEmpty(prepareAnswer.JmsMessage.Payload) && + prepareAnswer.JmsMessage.Payload.Contains("OPENED"); + + if (alreadyOpened) { - await SendCommandAndWaitToAnswerAsync(cameraWriter, cameraStream, CommandM.prepare_camera_, null, 3000); - await SendCommandAsync(cameraWriter, CommandM.grab_image_); + log.Debug("Camera already OPENED"); + } + else if (prepareAnswer == null || + prepareAnswer.JmsMessage == null || + prepareAnswer.JmsMessage.Status != + MessageStatus.ACK) + { + log.Error("Prepare camera failed."); + UiBridge.Bridge.OnImage(cameraIdx, null); + return null; + } - stopGrabImageListenerFlag = false; + await SendCommandAsync( stream, CommandM.grab_image_, ct, null).ConfigureAwait(false); + + while (!ct.IsCancellationRequested) + { + string response = await reader.ReadLineAsync( 10000, ct).ConfigureAwait(false); - while (!stopGrabImageListenerFlag) + if (string.IsNullOrWhiteSpace(response)) + continue; + + var packet = new JmsPacket(response); + + if (packet.JmsMessage == null) + continue; + + if (packet.JmsMessage.Command != CommandM.grab_image_) { - string responseAnswer = await ReadCameraLineAsync(cameraStream, 3000); - - if (string.IsNullOrWhiteSpace(responseAnswer)) - continue; - - JmsPacket newPacket = new JmsPacket(responseAnswer); - - if (newPacket.JmsMessage.Command == CommandM.grab_image_ && - newPacket.JmsMessage.Status == MessageStatus.ACK) - { - ParsedImage parsedImage = newPacket.JmsMessage.getImage(); - - if (parsedImage.Encoding == ImageType.BASE_64) - { - Image image = parsedImage.Image; - OnImageReceived(this, this.cameraIdx, image, RoiConfiguration.ImageRotation); - stopGrabImageListenerFlag = true; - } - } + log.Debug( $"Unexpected response during grab: cameraIdx={cameraIdx}, command={packet.JmsMessage.Command}"); + continue; } - await SendCommandAndWaitToAnswerAsync(cameraWriter, cameraStream, CommandM.close_, null, 2000); - await SendCommandAndWaitToAnswerAsync(cameraWriter, cameraStream, CommandM.disconnect_, null, 2000); + if (packet.JmsMessage.Status != MessageStatus.ACK) + { + log.Error( $"Grab image failed: cameraIdx={cameraIdx}, payload={packet.JmsMessage.Payload}"); + UiBridge.Bridge.OnImage(cameraIdx, null); + return null; + } + + ParsedImage parsedImage = packet.JmsMessage.getImage(); + + if (parsedImage.Encoding != ImageType.BASE_64) + { + log.Error( $"Unsupported image encoding: {parsedImage.Encoding}"); + return null; + } + + resultImage = parsedImage.Image; + + if (resultImage == null) + { + log.Error( $"Decoded image is null: cameraIdx={cameraIdx}"); + return null; + } + + DrawOverlay(resultImage); + + OnImageReceived( + this, + cameraIdx, + resultImage, + RoiConfiguration.ImageRotation); + + break; } - } - catch (Exception exc) - { - log.Error($"Grab Image exception: {exc.Message}"); - UiBridge.Bridge.OnImage(this.cameraIdx, null); - } - finally - { - CloseCameraResources(); + + if (resultImage == null) + { + return null; + } + + + log.Debug( $"Starting camera cleanup: cameraIdx={cameraIdx}"); + await TryCloseCameraAsync(stream,reader).ConfigureAwait(false); + log.Debug( $"Camera cleanup completed: cameraIdx={cameraIdx}"); + + return resultImage; } } + catch (OperationCanceledException) + { + log.Debug("Grab image cancelled."); + return null; + } + catch (TimeoutException exc) + { + log.Error( $"Grab image timeout: cameraIdx={cameraIdx}, error={exc.Message}"); + UiBridge.Bridge.OnImage(cameraIdx, null); + return null; + } + catch (Exception exc) + { + log.Error( $"Grab Image exception: cameraIdx={cameraIdx}, error={exc.Message}", exc); + UiBridge.Bridge.OnImage(cameraIdx, null); + return null; + } + finally + { + cameraOperationLock.Release(); + } } - - + + + private async Task TryCloseCameraAsync( + NetworkStream stream, + CameraLineReader reader) + { + /* + * CLOSE + * + * Na close ešte odpoveď očakávame. + */ + try + { + JmsPacket closeResponse = + await SendCommandAndWaitToAnswerAsync( + stream, + reader, + CommandM.close_, + null, + 2000, + CancellationToken.None) + .ConfigureAwait(false); + + if (closeResponse == null || + closeResponse.JmsMessage == null) + { + log.Warn( + $"No close response: " + + $"cameraIdx={cameraIdx}"); + + return; + } + + bool closeAccepted = + closeResponse.JmsMessage.Status == + MessageStatus.ACK; + + bool closeAlreadyInProgress = + closeResponse.JmsMessage.Status == + MessageStatus.NACK && + !string.IsNullOrEmpty( + closeResponse.JmsMessage.Payload) && + closeResponse.JmsMessage.Payload.Contains( + "Close command already in progress"); + + if (closeAlreadyInProgress) + { + log.Debug( + $"Camera close already in progress: " + + $"cameraIdx={cameraIdx}"); + } + else if (!closeAccepted) + { + log.Warn( + $"Camera close returned NACK: " + + $"cameraIdx={cameraIdx}, " + + $"payload={closeResponse.JmsMessage.Payload}"); + } + } + catch (TimeoutException exc) + { + log.Warn( + $"Close camera timeout: " + + $"cameraIdx={cameraIdx}, " + + $"error={exc.Message}"); + } + catch (Exception exc) + { + log.Warn( + $"Close camera failed: " + + $"cameraIdx={cameraIdx}, " + + $"error={exc.Message}"); + } + + /* + * DISCONNECT + * + * Dôležité: príkaz iba odošleme. + * Na odpoveď nečakáme. + */ + try + { + if (stream.CanWrite) + { + await SendCommandAsync( + stream, + CommandM.disconnect_, + CancellationToken.None, + null) + .ConfigureAwait(false); + + log.Debug( + $"Disconnect command sent: " + + $"cameraIdx={cameraIdx}"); + } + } + catch (Exception exc) + { + log.Warn( + $"Disconnect camera failed: " + + $"cameraIdx={cameraIdx}, " + + $"error={exc.Message}"); + } + } + private void CloseCameraResources() { cameraReader?.Close(); cameraReader = null; - cameraWriter?.Close(); - cameraWriter = null; + //cameraWriter?.Close(); + //cameraWriter = null; cameraStream?.Close(); cameraStream = null; cameraTcpClient?.Close(); @@ -1215,6 +1769,17 @@ namespace TBF.Rig.Network.Camera.CJMS11 cameraTcpClient = null; } + public void CancelGrabImage() + { + try + { + grabCts?.Cancel(); + } + catch (ObjectDisposedException) + { + // CTS už bolo uvoľnené. + } + } /// /// listener for multiple - repetetive image receiving @@ -1225,130 +1790,118 @@ namespace TBF.Rig.Network.Camera.CJMS11 /// /// - private void StartLiveStreamListener(IPAddress localIP, int localPort) + // private void StartLiveStreamListener(IPAddress localIP, int localPort) + // { + // liveStreamListenerThread = new Thread(new ThreadStart(LiveStreamListener)); + // liveStreamListenerThread.Start(); + // } + + private CancellationTokenSource liveCts; + + public void StartLiveStreamListener() { - liveStreamListenerThread = new Thread(new ThreadStart(LiveStreamListener)); - liveStreamListenerThread.Start(); + liveCts?.Cancel(); + liveCts = new CancellationTokenSource(); + + _ = LiveStreamListenerAsync(liveCts.Token); } + - void LiveStreamListener() + private async Task LiveStreamListenerAsync(CancellationToken ct) { - bool synced = false; + await cameraOperationLock.WaitAsync(ct); - using (this.cameraTcpClient = new TcpClient()) + try { - try + using var client = await ConnectCameraAsync(1000, ct); + using var stream = client.GetStream(); + var reader = new CameraLineReader(stream, 64 * 1024); + + var prepare = + await SendCommandAndWaitToAnswerAsync( stream,reader, CommandM.prepare_camera_, null, 3000, ct); + + if (prepare == null || prepare.JmsMessage.Status != MessageStatus.ACK) { - EstablishCameraConnection(ref this.cameraTcpClient, CameraCfg.IPAddressCJMS, iPort); - - log.Debug(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)) - { - cameraStream.ReadTimeout = 1000; - - SendCommand(CommandM.prepare_camera_); - SendCommand(CommandM.start_stream_images_); - - - stopLiveStreamListenerFlag = false; - while (!stopLiveStreamListenerFlag) - { - try - { - - //SendCommand(CommandM.grab_image_); - Task 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 (synced) - // { - /// Sychronized: process the new packet - 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,0,1 ); - - } - - break; - default: - break; - } - } - } - catch (Exception exc) - { - string msg = string.Format("Exception in MJpeg RTP listener thread: {0}", exc.Message); - log.Error(msg); - } - } - - stopLiveStreamListenerFlag = false; - //we need to wait to answer, it works on thread loop - properly access - var sendCommandAndWaitToAnswer = SendCommandAndWaitToAnswerAsync(cameraWriter, cameraStream, CommandM.prepare_camera_).GetAwaiter().GetResult(); - if (sendCommandAndWaitToAnswer == null || sendCommandAndWaitToAnswer.JmsMessage.Status == MessageStatus.NACK) //correct if NACK & OPENED - { - log.Error("Stop streaming not successfully! see: " + sendCommandAndWaitToAnswer.JmsMessage.Payload); - } - - //SendCommand(CommandM.close_); - //SendCommand(CommandM.disconnect_); - - CloseCameraResources(); - } - }catch(Exception exc) - { - log.Error($"Live Stream exception: {exc.Message}"); - UiBridge.Bridge.OnImage(this.cameraIdx, null); + log.Error("Prepare camera failed."); + return; } - } - return; + var start = + await SendCommandAndWaitToAnswerAsync( stream, reader, CommandM.start_stream_images_, null, 3000, ct); + + if (start == null || start.JmsMessage.Status != MessageStatus.ACK) + { + log.Error("Unable to start stream."); + return; + } + + while (!ct.IsCancellationRequested) + { + //string response = await ReadCameraLineAsync(stream, 5000, ct); + string response = await reader.ReadLineAsync(5000, ct); + + if (string.IsNullOrWhiteSpace(response)) + continue; + + var packet = new JmsPacket(response); + + if (packet.JmsMessage.Command != CommandM.grab_image_) + continue; + + if (packet.JmsMessage.Status != MessageStatus.ACK) + continue; + + var parsed = packet.JmsMessage.getImage(); + + if (parsed.Encoding != ImageType.BASE_64) + continue; + + Image image = parsed.Image; + + DrawOverlay(image); + + OnImageReceived( + this, + cameraIdx, + image, + RoiConfiguration.ImageRotation, + 0, + 1); + } + + await SendCommandAndWaitToAnswerAsync( stream, reader, CommandM.stop_stream_images_, null, 3000, ct); + await SendCommandAndWaitToAnswerAsync( stream, reader, CommandM.close_, null, 3000, ct); + await SendCommandAndWaitToAnswerAsync( stream, reader, CommandM.disconnect_, null, 3000, ct); + } + catch (OperationCanceledException) + { + log.Debug("Live stream cancelled."); + } + catch (Exception ex) + { + log.Error(ex); + UiBridge.Bridge.OnImage(cameraIdx, null); + } + finally + { + cameraOperationLock.Release(); + } } + private void DrawOverlay(Image image) + { + if (image == null) + return; + if (!showOverlayRect || overlayThickness <= 0) + return; - + using (Graphics g = Graphics.FromImage(image)) + using (Pen pen = new Pen(Color.Green, overlayThickness)) + { + g.DrawRectangle(pen, overlayRect); + } + } //public void OnMeasurementDataReceived(object sender, MeasuredDataEventArgs msrmtData) @@ -1405,12 +1958,16 @@ namespace TBF.Rig.Network.Camera.CJMS11 { try { - if (cameraWriter != null && cameraReader != null && cameraStream!=null && cameraStream.CanWrite) + if (cameraStream != null && cameraStream.CanWrite) { - cameraWriter.WriteLineAsync(CommandMEnum.GetVal(command)).Wait(); - cameraWriter.FlushAsync(); + string commandStr = CommandMEnum.GetVal(command) + "\n"; + byte[] bytes = Encoding.ASCII.GetBytes(commandStr); + + cameraStream.Write(bytes, 0, bytes.Length); + cameraStream.Flush(); } - }catch(Exception exc) + } + catch (Exception exc) { log.ErrorFormat("{0} SendCommand: {1}", Name, exc.Message); } @@ -1419,64 +1976,87 @@ namespace TBF.Rig.Network.Camera.CJMS11 // private async Task SendCommandAndWaitToAnswerAsync( - StreamWriter writer, - NetworkStream cameraStream, - CommandM command, - string payload = null, - int timeoutMs = 1000) + NetworkStream stream, + CameraLineReader reader, + CommandM command, + string payload = null, + int timeoutMs = 1000, + CancellationToken ct = default) +{ + await SendCommandAsync( stream, command, ct, payload).ConfigureAwait(false); + + try + { + for (int iReads = 0; iReads < 6; iReads++) + { + string responseAnswer = await reader.ReadLineAsync( timeoutMs, ct).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(responseAnswer)) + continue; + + log.Debug( $"Received from camera: idx={cameraIdx}, expected={command}, length={responseAnswer.Length}, prefix={GetLogPrefix(responseAnswer, 200)}"); + + JmsPacket result; + + try + { + result = new JmsPacket(responseAnswer); + } + catch (Exception exc) + { + log.Warn( $"Cannot parse camera response: idx={cameraIdx}, length={responseAnswer.Length}, error={exc.Message}"); + continue; + } + + if (result.JmsMessage == null) + { + log.Warn( $"Parsed packet contains no JmsMessage: idx={cameraIdx}"); + continue; + } + + if (result.JmsMessage.Command == command) + { + return result; + } + + log.Debug( $"Unexpected response command: idx={cameraIdx}, expected={command}, received={result.JmsMessage.Command}. Reading next response."); + } + + log.Error( $"Expected camera response not received: idx={cameraIdx}, command={command}"); + return null; + } + catch (OperationCanceledException) + { + throw; + } + catch (TimeoutException) + { + throw; + } + catch (IOException ioEx) + { + log.Error( $"IO error while reading from camera: idx={cameraIdx}, command={command}, error={ioEx.Message}"); + return null; + } + catch (Exception exc) + { + log.Error( $"Error while reading from camera: idx={cameraIdx}, command={command}, error={exc.Message}", exc); + return null; + } +} + + private static string GetLogPrefix( + string value, + int maximumLength) { + if (string.IsNullOrEmpty(value)) + return string.Empty; - - await SendCommandAsync(writer, command, payload); + if (value.Length <= maximumLength) + return value; - - - string responseAnswer = ""; - try - { - for (int iReads = 0; iReads <= 5; iReads++) - { - //get config - //responseAnswer = await ReadCameraLineAsync(cameraStream, timeoutMs); - responseAnswer = await Task.Run(() => ReadCameraLine(cameraStream, timeoutMs)); - - if (string.IsNullOrEmpty(responseAnswer)) - { - iReads++; - continue; - } - - log.Debug($"received from camera: {responseAnswer}"); - - JmsPacket result = null; - - try - { - result = new JmsPacket(responseAnswer); - } - catch (Exception exc) - { - continue; - } - - if (result.JmsMessage.Command == command) - { - return result; - } - } - - log.Error($"Maximum Count of unexcepted answers excited!"); - } - catch (IOException ioEx) - { - log.Error($"Timeout or IO error while reading from camera: {ioEx.Message}"); - } - catch (Exception e) - { - log.Error(string.Format("Error while reading from camera: {0}", e.Message)); - } - - return null; + return value.Substring(0, maximumLength) + + "..."; } public void Start() @@ -1560,7 +2140,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 public static void OnImageReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx = 0, int imageCount = 1) { - log.Info("Image received"); + log.Info($"Image received from camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}"); if (null != ImageCameraHandler) { @@ -1570,7 +2150,8 @@ namespace TBF.Rig.Network.Camera.CJMS11 ImageUtils.Rotate(image, imageRotation); //Send image to bridge - ImagesCameraHandler(sender, new PromptReceivedImagesEventArgs(idxImage, image,iImageIdx, imageCount)); + ImageCameraHandler(sender, new PromptReceivedImageEventArgs(idxImage, image)); + log.Info($"ImageCameraHandler reached camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}"); } catch (Exception ex) { @@ -1579,9 +2160,58 @@ namespace TBF.Rig.Network.Camera.CJMS11 } } - private async Task ReadCameraLineAsync(NetworkStream stream, int timeoutMs) + /// + /// This method is called from within this class when + /// a prompt string was received from the telnet server. + /// + public static void OnImagesReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx = 0, int imageCount = 1) { - await cameraReadLock.WaitAsync(); + + log.Info($"Images received from camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}"); + + if (null != ImageCameraHandler) + { + try + { + //rotation + ImageUtils.Rotate(image, imageRotation); + + //Send image to bridge + ImageCameraHandler(sender, new PromptReceivedImageEventArgs(idxImage, image)); + log.Info($"ImageCameraHandler reached camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}"); + } + catch (Exception ex) + { + log.Info("Internal error: PromptReceivedHandler exception: " + ex.Message); + } + } + + + + if (null != ImagesCameraHandler) + { + try + { + //rotation + ImageUtils.Rotate(image, imageRotation); + + //Send images to bridge + ImagesCameraHandler(sender, new PromptReceivedImagesEventArgs(idxImage, image,iImageIdx, imageCount)); + log.Info($"ImagesCameraHandler reached camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}"); + } + catch (Exception ex) + { + log.Info("Internal error: PromptReceivedHandler exception: " + ex.Message); + } + } + } + + private async Task ReadCameraLineAsync( + NetworkStream stream, + int timeoutMs, + CancellationToken ct) + { + await cameraReadLock.WaitAsync(ct); try { @@ -1590,15 +2220,15 @@ namespace TBF.Rig.Network.Camera.CJMS11 while (true) { - Task readTask = stream.ReadAsync(buffer, 0, 1); - Task timeoutTask = Task.Delay(timeoutMs); + Task readTask = stream.ReadAsync(buffer, 0, 1, ct); + Task timeoutTask = Task.Delay(timeoutMs, ct); Task completed = await Task.WhenAny(readTask, timeoutTask); if (completed == timeoutTask) { - try { cameraTcpClient?.Close(); } catch { } - throw new TimeoutException($"Camera read timeout after {timeoutMs} ms."); + ct.ThrowIfCancellationRequested(); + throw new TimeoutException($"Camera {cameraIdx} read timeout after {timeoutMs} ms."); } int read = await readTask; @@ -1650,7 +2280,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 } - private async Task SendCommandAsync(StreamWriter writer, CommandM command, string payload = null) + private async Task SendCommandAsync(NetworkStream stream, CommandM command, CancellationToken ct, string payload = null) { string commandStr = CommandMEnum.GetVal(command); @@ -1659,20 +2289,41 @@ namespace TBF.Rig.Network.Camera.CJMS11 commandStr += "=" + payload; } - commandStr += " "; + //commandStr += " "; + byte[] bytes = Encoding.ASCII.GetBytes(commandStr + "\n"); - await cameraWriteLock.WaitAsync(); + //await cameraWriteLock.WaitAsync(); - try + + log.Info($"Sending command: '{commandStr}'"); + await stream.WriteAsync(bytes, 0, bytes.Length); + await stream.FlushAsync(); + //stream.Write(bytes, 0, bytes.Length); + //stream.Flush(); + + //await stream.WriteLineAsync(commandStr); + //await stream.FlushAsync(); + log.Info($"Command sent: '{commandStr}'"); + + } + + + private async Task ReadCameraLineWithTimeout(NetworkStream stream, int timeoutMs) + { + var readTask = Task.Run(() => ReadCameraLine(stream, timeoutMs)); + var timeoutTask = Task.Delay(timeoutMs); + + var completed = await Task.WhenAny(readTask, timeoutTask); + + if (completed == timeoutTask) { - await writer.WriteLineAsync(commandStr); - await writer.FlushAsync(); - log.Info($"Command '{commandStr}' sent."); - } - finally - { - cameraWriteLock.Release(); + try { stream.Close(); } catch { } + try { cameraTcpClient?.Close(); } catch { } + + throw new TimeoutException($"Camera read timeout after {timeoutMs} ms."); } + + return await readTask; } diff --git a/TBF/Rig/Network/Camera/CJMS11/CameraLineReader.cs b/TBF/Rig/Network/Camera/CJMS11/CameraLineReader.cs new file mode 100644 index 000000000..da5fd2919 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/CameraLineReader.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace TBF.Rig.Network.Camera.CJMS11 +{ + internal sealed class CameraLineReader + { + private readonly Stream stream; + private readonly byte[] receiveBuffer; + + private int bufferStart; + private int bufferEnd; + + public CameraLineReader( + Stream stream, + int bufferSize = 64 * 1024) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (!stream.CanRead) + throw new ArgumentException( + "Stream must be readable.", + nameof(stream)); + + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException( + nameof(bufferSize)); + + this.stream = stream; + receiveBuffer = new byte[bufferSize]; + } + + public async Task ReadLineAsync( + int timeoutMs, + CancellationToken cancellationToken) + { + if (timeoutMs <= 0) + throw new ArgumentOutOfRangeException( + nameof(timeoutMs)); + + using (var lineBuffer = new MemoryStream()) + { + while (true) + { + int newLineIndex = FindNewLine(); + + if (newLineIndex >= 0) + { + int count = newLineIndex - bufferStart; + + if (count > 0) + { + lineBuffer.Write( + receiveBuffer, + bufferStart, + count); + } + + bufferStart = newLineIndex + 1; + + return ConvertLineToString(lineBuffer); + } + + if (bufferStart < bufferEnd) + { + lineBuffer.Write( + receiveBuffer, + bufferStart, + bufferEnd - bufferStart); + } + + bufferStart = 0; + bufferEnd = 0; + + int read = await ReadChunkAsync( + timeoutMs, + cancellationToken) + .ConfigureAwait(false); + + if (read == 0) + { + throw new IOException( + "Camera connection was closed."); + } + + bufferStart = 0; + bufferEnd = read; + } + } + } + + private int FindNewLine() + { + for (int i = bufferStart; i < bufferEnd; i++) + { + if (receiveBuffer[i] == (byte)'\n') + return i; + } + + return -1; + } + + private async Task ReadChunkAsync( + int timeoutMs, + CancellationToken cancellationToken) + { + using (var timeoutCts = + CancellationTokenSource.CreateLinkedTokenSource( + cancellationToken)) + { + timeoutCts.CancelAfter(timeoutMs); + + try + { + return await stream.ReadAsync( + receiveBuffer, + 0, + receiveBuffer.Length, + timeoutCts.Token) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + when (!cancellationToken.IsCancellationRequested) + { + throw new TimeoutException( + $"Camera read timeout after {timeoutMs} ms."); + } + } + } + + private static string ConvertLineToString( + MemoryStream lineBuffer) + { + int length = checked((int)lineBuffer.Length); + + if (length == 0) + return string.Empty; + + byte[] data = lineBuffer.GetBuffer(); + + if (data[length - 1] == (byte)'\r') + length--; + + return Encoding.ASCII.GetString( + data, + 0, + length); + } + } +} \ No newline at end of file diff --git a/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs b/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs index e6f71ab5d..05304d717 100644 --- a/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs +++ b/TBF/Rig/Network/Camera/CJMS11/GrabImagesOp.cs @@ -32,12 +32,13 @@ namespace TBF.Rig.Network.Camera.CJMS11 string command; bool grabImageCommandSent; string response; - bool grabPassed; - bool grabFailed; + bool transferringGrabbedImage; Image grabedImage; Dictionary grabedImages = new Dictionary(); + private Task grabImageTask; + private Task saveImageTask; /// @@ -58,20 +59,17 @@ namespace TBF.Rig.Network.Camera.CJMS11 grabImageCommandSent = false; transferringGrabbedImage = false; - /// image incomming event - if (camera.DebugLevel == DebugMode.Normal || camera.DebugLevel == DebugMode.DetectedOn) - { - Camera.ImageCameraHandler += ImageReceived; - } + // /// image incomming event + // if (camera.DebugLevel == DebugMode.Normal || camera.DebugLevel == DebugMode.DetectedOn) + // { + // Camera.ImageCameraHandler -= ImageReceived; + // Camera.ImageCameraHandler += ImageReceived; + // } //TODO BUMI improve by simulate image + Camera.ImageCameraHandler -= ImageReceived; Camera.ImageCameraHandler += ImageReceived; - /// inmages incomming event rotation - if (camera.DebugLevel == DebugMode.Normal || camera.DebugLevel == DebugMode.DetectedOn) - { - Camera.ImagesCameraHandler += ImagesReceived; - } - //TODO BUMI improve by simulate image + Camera.ImagesCameraHandler -= ImagesReceived; Camera.ImagesCameraHandler += ImagesReceived; } @@ -80,11 +78,17 @@ namespace TBF.Rig.Network.Camera.CJMS11 response = a.Response; } + private readonly object imageSync = new object(); + void ImageReceived(object sndr, PromptReceivedImageEventArgs a) { - if(a.idxCamera != camera.CameraIdx) return; - grabedImage = a.image; - grabPassed = true; + if (a == null || a.idxCamera != camera.CameraIdx) + return; + + lock (imageSync) + { + grabedImage = a.image; + } } void ImagesReceived(object sndr, PromptReceivedImagesEventArgs a) @@ -92,7 +96,6 @@ namespace TBF.Rig.Network.Camera.CJMS11 if(a.idxCamera != camera.CameraIdx) return; grabedImage = a.image; //upgraded image common grabedImages[a.idxImage] = a.image; // stored image from sent to list of images - grabPassed = true; } @@ -100,99 +103,166 @@ namespace TBF.Rig.Network.Camera.CJMS11 { grabImageCommandSent = false; transferringGrabbedImage = false; - grabPassed = false; + grabedImage = null; + grabImageTask = null; + saveImageTask = null; + grabedImages.Clear(); - if ((imgFileNames != null) && (imgFileNames.Length > 0) && File.Exists(imgFileNames[0])) - { - /// - /// An image specified, (1) delete previous image, (2) check if this is a simulation - /// - File.Delete(imgFileNames[0]); - - /* if (camera.CameraCfg.DebugLevel == DebugMode.Simulate || camera.CameraCfg.DebugLevel == DebugMode.DetectedOff) - { - /// - /// Camera is in simulation mode => create a simulated image - /// - File.Copy(string.Format("{0}\\Pictures\\sample.jpg", Program.ExecutableDir), imgFileNames[0]); - }*/ - } + if (imgFileNames != null && + imgFileNames.Length > 0 && + !string.IsNullOrEmpty(imgFileNames[0]) && + File.Exists(imgFileNames[0])) + { + File.Delete(imgFileNames[0]); + } } + public Event Run() { - if ((imgFileNames == null) || (imgFileNames.Length < 1) || (imgFileNames[0] == null)) + if (imgFileNames == null || + imgFileNames.Length < 1 || + string.IsNullOrEmpty(imgFileNames[0])) { - /// - /// No images to be grabbed => Done - /// - return Event.GrabPassed; + UnsubscribeEvents(); + return Event.GrabPassed; } - /*else if (camera.CameraCfg.DebugLevel == DebugMode.Simulate || - camera.CameraCfg.DebugLevel == DebugMode.DetectedOff) - { - /// - /// Camera is in simulation mode => create a simulated image and complete - /// - return Event.GrabPassed; - }*/ - else if (camera.IsGrabImageListenerThreadAlive) - { - return Event.CameraBusy; - } - else if (!grabImageCommandSent) + + if (!grabImageCommandSent) { - /// - /// Normal operation, no command sent yet => (1) wait until telnet state = Inactive, (2) send a command - /// - camera.StartGrabImageListener(); - grabImageCommandSent = true; + log.Debug( $"Starting asynchronous grab: cameraIdx={camera.CameraIdx}"); + + grabImageCommandSent = true; + grabImageTask = camera.StartGrabImageListenerAsync(); + return Event.CameraBusy; } - else if (grabPassed) + + if (grabImageTask == null) { - /// - /// Normal operation, grab passed => transfer/transferring the image - - - if (transferringGrabbedImage) - { - /// Grab passed and image transfer is in progress - return Event.GrabPassed; - } - else if (camera.IsSaveImageListenerThreadAlive) - { - /// Wait until the previous image transfer completes - return Event.CameraBusy; - } - else //if (0 == camera.DownloadFile(blackAndWhite ? "grabbed.jpg" : "grabbed.bmp", imgFileNames[0])) - { - camera.StartSaveImageToFileListener(grabedImage, imgFileNames[0]); - /// File transfer successfully started - transferringGrabbedImage = true; - return Event.GrabPassed; - } - } - else - { - /// - /// Normal operation, grab failed => there in no image to transfer - /// + log.Error( + $"Grab task was not created: cameraIdx={camera.CameraIdx}"); + + UnsubscribeEvents(); return Event.GrabFailed; } - + + if (!grabImageTask.IsCompleted) + { + return Event.CameraBusy; + } + + if (grabImageTask.IsCanceled) + { + log.Warn( + $"Grab task cancelled: cameraIdx={camera.CameraIdx}"); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + if (grabImageTask.IsFaulted) + { + log.Error( + $"Grab task failed: cameraIdx={camera.CameraIdx}", + grabImageTask.Exception); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + if (grabedImage == null) + { + grabedImage = grabImageTask.GetAwaiter().GetResult(); + } + + if (grabedImage == null) + { + log.Error( + $"Grab completed without valid image: cameraIdx={camera.CameraIdx}"); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + if (!transferringGrabbedImage) + { + saveImageTask = + camera.StartSaveImageToFileAsync( + grabedImage, + imgFileNames[0]); + + transferringGrabbedImage = true; + + return Event.CameraBusy; + } + + if (saveImageTask == null) + { + log.Error( + $"Save image task was not created: " + + $"cameraIdx={camera.CameraIdx}"); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + if (!saveImageTask.IsCompleted) + { + return Event.CameraBusy; + } + + if (saveImageTask.IsCanceled) + { + log.Warn( + $"Save image task cancelled: " + + $"cameraIdx={camera.CameraIdx}"); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + if (saveImageTask.IsFaulted) + { + log.Error( + $"Save image task failed: " + + $"cameraIdx={camera.CameraIdx}", + saveImageTask.Exception); + + UnsubscribeEvents(); + return Event.GrabFailed; + } + + bool fileSaved = File.Exists(imgFileNames[0]); + + UnsubscribeEvents(); + + return fileSaved + ? Event.GrabPassed + : Event.GrabFailed; } public void Stop() { //if (camera.CameraCfg.DebugLevel == DebugMode.Simulate) return; - if (camera.IsGrabImageListenerThreadAlive) - { - camera.SetStopGrabImageListenerFlag(); - } + // if (camera.IsGrabImageListenerThreadAlive) + // { + // camera.SetStopGrabImageListenerFlag(); + // } + + camera.CancelGrabImage(); + UnsubscribeEvents(); } + + private void UnsubscribeEvents() + { + Camera.ImageCameraHandler -= ImageReceived; + Camera.ImagesCameraHandler -= ImagesReceived; + } + + } } diff --git a/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs b/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs index 6bccf0ad7..678c2775f 100644 --- a/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs +++ b/TBF/Rig/Network/Camera/CJMS11/JmsMessage.cs @@ -1,3 +1,4 @@ +using System; using TBF.Rig.Network.Camera.CJMS11.POJO; namespace TBF.Rig.Network.Camera.CJMS11 @@ -11,6 +12,7 @@ namespace TBF.Rig.Network.Camera.CJMS11 private string orig_status; private string orig_command; private string orig_payload; + private DateTime timeStamp; public POJO.MessageStatus Status { @@ -35,6 +37,12 @@ namespace TBF.Rig.Network.Camera.CJMS11 public string OrigCommand => orig_command; public string OrigPayload => orig_payload; + + public DateTime TimeStamp + { + get => timeStamp; + set => timeStamp = value; + } public JmsMessage(string status, string command, string payload) { diff --git a/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs b/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs index 2cfccee07..e40c748f9 100644 --- a/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs +++ b/TBF/Rig/Network/Camera/CJMS11/JmsPacket.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; using log4net; +using Newtonsoft.Json; using TBF.Rig.Network.Camera.CJMS11.POJO; namespace TBF.Rig.Network.Camera.CJMS11 @@ -44,6 +45,16 @@ namespace TBF.Rig.Network.Camera.CJMS11 if (message.Length < 1) return false; this.jmsMessage = JmsMessageFromString(message); + + try + { + CameraResponse response = + JsonConvert.DeserializeObject(jmsMessage.Payload); + jmsMessage.TimeStamp = response.timeStamp; + }catch(Exception e) + { + log.Debug("Error parsing message timestamp: " + e.Message); + } return true; } @@ -70,23 +81,39 @@ namespace TBF.Rig.Network.Camera.CJMS11 return null; } - public static ParsedImage ImageParser(string payloadImage) + public static ParsedImage ImageParser( + string payloadImage) { - // Equivalent regex pattern - var imagePattern = new Regex(@"IMAGE:([A-Z0-9_]+):\s*(.*?)\s*~IMAGE_END", RegexOptions.Singleline); + const string prefix = "IMAGE:"; + const string endMarker = "~IMAGE_END"; - var match = imagePattern.Match(payloadImage); - if (match.Success) - { - string encoding = match.Groups[1].Value; - string imageData = match.Groups[2].Value; + int prefixIndex = payloadImage.IndexOf( prefix, StringComparison.Ordinal); + + if (prefixIndex < 0) + throw new ArgumentException( "IMAGE prefix was not found."); - return new ParsedImage(ImageTypeEnum.GetCommandM(encoding), imageData); - } - else - { - throw new ArgumentException($"Payload does not match expected image format: {payloadImage}"); - } + int encodingStart = + prefixIndex + prefix.Length; + + int encodingEnd = + payloadImage.IndexOf( + ':', + encodingStart); + + if (encodingEnd < 0) + throw new ArgumentException( + "Image encoding separator was not found."); + + int dataStart = encodingEnd + 1; + + int dataEnd = payloadImage.IndexOf( endMarker, dataStart, StringComparison.Ordinal); + + if (dataEnd < 0) + throw new ArgumentException( "IMAGE_END marker was not found."); + + string encoding = payloadImage .Substring( encodingStart, encodingEnd - encodingStart) .Trim(); + string imageData = payloadImage.Substring( dataStart,dataEnd - dataStart).Trim(); + return new ParsedImage( ImageTypeEnum.GetCommandM(encoding), imageData); } public override string ToString() diff --git a/TBF/Rig/Network/Camera/CJMS11/POJO/CameraResponse.cs b/TBF/Rig/Network/Camera/CJMS11/POJO/CameraResponse.cs new file mode 100644 index 000000000..fc07458c3 --- /dev/null +++ b/TBF/Rig/Network/Camera/CJMS11/POJO/CameraResponse.cs @@ -0,0 +1,12 @@ +using System; + +namespace TBF.Rig.Network.Camera.CJMS11.POJO +{ + public class CameraResponse + { + public string message { get; set; } + + public DateTime timeStamp { get; set; } + + } +} \ No newline at end of file diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index d50d93295..032b98f26 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -1016,11 +1016,13 @@ CameraCfgCtrl.cs + + diff --git a/TBF/UiBridge/Bridge.cs b/TBF/UiBridge/Bridge.cs index 79b0dad1d..226422de1 100644 --- a/TBF/UiBridge/Bridge.cs +++ b/TBF/UiBridge/Bridge.cs @@ -3,6 +3,7 @@ /// using System; using System.Collections.Generic; +using System.Threading.Tasks; using log4net; using NHibernate; using Common; @@ -229,7 +230,26 @@ namespace TBF.UiBridge public static void OnCameraUICmd(object sender, CameraUICmdEventArgs data) { if (CameraUICmdHandler == null) return; - try { CameraUICmdHandler(sender, data); } + try + { + log.DebugFormat("CameraUICmdHandler: {0}", data.CameraUICmd); + foreach (EventHandler handler in CameraUICmdHandler.GetInvocationList()) + { + var localHandler = handler; + + Task.Run(() => + { + try + { + localHandler(sender, data); + } + catch (Exception e) + { + log.Error("CameraUICmdHandler(...) failed", e); + } + }); + } + } catch (Exception e) { log.Error("CameraUICmdHandler(...) failed", e); } } public static event EventHandler CameraUICmdHandler; diff --git a/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs b/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs index 6f06d962d..10f9a7860 100644 --- a/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs +++ b/TBFTests/Rig/Network/Camera/CJMS11/CameraTest.cs @@ -131,7 +131,7 @@ namespace TBFTests.Rig.Network.Camera.CJMS11 var camera = GetCamera(IPCamera1); - Task saveImageTask = Task.Run(() => camera.SaveImageListener()); + Task saveImageTask = Task.Run(() => camera.SaveImageListenerAsynch()); var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(3)); var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result; if (completedAnswerTask == timeoutAnswerTask) @@ -152,7 +152,7 @@ namespace TBFTests.Rig.Network.Camera.CJMS11 // { // var camera = GetCamera(IPCamera2); // - // Task saveImageTask = Task.Run(() => camera.SaveImageListener()); + // Task saveImageTask = Task.Run(() => camera.SaveImageListenerAsynch()); // var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(3)); // var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result; // if (completedAnswerTask == timeoutAnswerTask)