2332 lines
62 KiB
C#
2332 lines
62 KiB
C#
///
|
|
/// Copyright (c) 2017-2022 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using log4net;
|
|
using Renci.SshNet;
|
|
using Common;
|
|
using FluentNHibernate.Conventions;
|
|
using Newtonsoft.Json;
|
|
using TBF.Rig.Network.Telnet;
|
|
using TBF.Resources;
|
|
using TBF.Rig.Generic;
|
|
using TBF.Rig.Network.Camera.CJMS11.POJO;
|
|
using TBF.Rig.Network.Camera.common;
|
|
using TBF.Rig.Network.Camera.RoiForFixedStartCJMS11;
|
|
using TBF.UiBridge;
|
|
|
|
namespace TBF.Rig.Network.Camera.CJMS11
|
|
{
|
|
/// <summary>
|
|
/// This class implements: (1) Camera device, (2) Measurement operation
|
|
/// </summary>
|
|
public class Camera : ComponentBase, GenericDevices.ICamera, Generic.IDevice, IOperation
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(Camera));
|
|
public override string ToString()
|
|
{
|
|
return string.Format("{0}: Name={1}, Addr={2}, IP={3}, s/n={4}, SD={5}",
|
|
this.GetType().Namespace.Substring(8),
|
|
CameraCfg.Name,
|
|
CameraCfg.HardwareAddress,
|
|
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : ((ipAddress == null) ? "not detected" : ipAddress.ToString()),
|
|
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : (string.IsNullOrEmpty(cpuSerialNr) ? "not detected" : cpuSerialNr),
|
|
CameraCfg.DebugLevel == DebugMode.Simulate ? "simulated" : (string.IsNullOrEmpty(sdCardVer) ? "not detected" : sdCardVer));
|
|
}
|
|
|
|
|
|
private const int iPort = 32456;
|
|
string ClpUserName = string.Empty; /// Either "tbf" or "pi" after a successful detection
|
|
const string ClpPassword = "C1ern4V0d4";
|
|
|
|
|
|
private readonly SemaphoreSlim cameraReadLock = new SemaphoreSlim(1, 1);
|
|
//private readonly SemaphoreSlim cameraWriteLock = new SemaphoreSlim(1, 1);
|
|
|
|
///
|
|
/// Properties
|
|
///
|
|
public readonly CameraCfg CameraCfg;
|
|
public readonly AdapterJMS.Netadapter NetAdapter;
|
|
|
|
private RoiCfg roiConfiguration;
|
|
public RoiCfg RoiConfiguration
|
|
{
|
|
get {
|
|
if (roiConfiguration!=null) {return roiConfiguration;}
|
|
else { roiConfiguration = GetFirstRoiChild(); return roiConfiguration; }
|
|
}
|
|
set { if (roiConfiguration == null) roiConfiguration = value;}
|
|
}
|
|
|
|
public IPAddress IPAddress { get { return ipAddress; } }
|
|
IPAddress ipAddress;
|
|
|
|
public string CpuHardware { get { return cpuHardware; } }
|
|
string cpuHardware;
|
|
|
|
public string CpuRevision { get { return cpuRevision; } }
|
|
string cpuRevision;
|
|
|
|
public string CpuSerialNr { get { return cpuSerialNr; } }
|
|
string cpuSerialNr;
|
|
|
|
public string SDCardVer { get { return sdCardVer; } }
|
|
string sdCardVer;
|
|
|
|
public bool Running { get { return running; } }
|
|
bool running;
|
|
|
|
public bool ShowOverlayRect { set { showOverlayRect = value; } }
|
|
bool showOverlayRect = false;
|
|
|
|
public Rectangle OverlayRect { set { overlayRect = value; } }
|
|
Rectangle overlayRect;
|
|
|
|
public int OverlayThickness { set { overlayThickness = value; } }
|
|
int overlayThickness;
|
|
|
|
public int CameraIdx { get { return cameraIdx; } }
|
|
int cameraIdx;
|
|
///
|
|
static int nextCameraIdx = 0;
|
|
|
|
static int GetCameraIdx() { return nextCameraIdx++; } /// Used in Initialize()
|
|
|
|
///
|
|
/// Telnet support
|
|
///
|
|
const string TelnetPrompt = "$ ";
|
|
const bool TelnetSkipFirstLine = true;
|
|
|
|
private TerminalDlg terminalDlg;
|
|
string sha1sumResponse;
|
|
JmsMessage jmsMessage;
|
|
bool sha1sumResponseProcessed;
|
|
|
|
///
|
|
/// scp support
|
|
///
|
|
|
|
PasswordConnectionInfo connectionInfo;
|
|
|
|
|
|
|
|
///
|
|
/// MJpeg RTP protocol support
|
|
///
|
|
public bool CameraConnected{ get {return cameraTcpClient != null && cameraTcpClient.Connected;} }
|
|
int cameraTcpLocalPort;
|
|
TcpClient cameraTcpClient;
|
|
private NetworkStream cameraStream = null;
|
|
//private StreamWriter cameraWriter = null;
|
|
private StreamReader cameraReader = null;
|
|
private Image lastImage;
|
|
private bool lockCameraInitialisation = false;
|
|
|
|
Thread liveStreamListenerThread;
|
|
private bool stopLiveStreamListenerFlag;
|
|
|
|
public bool IsGrabImageListenerThreadAlive { get { return grabImageListenerThread != null && grabImageListenerThread.IsAlive; } }
|
|
Thread grabImageListenerThread;
|
|
private bool stopGrabImageListenerFlag;
|
|
public void SetStopGrabImageListenerFlag() { stopGrabImageListenerFlag = true; }
|
|
|
|
Thread stopUIListenerThread;
|
|
private bool stopUIListenerFlag;
|
|
|
|
Thread saveImageListenerThread;
|
|
private bool stopSaveImageListenerFlag;
|
|
public bool IsSaveImageListenerThreadAlive { get { return saveImageListenerThread != null && saveImageListenerThread.IsAlive; } }
|
|
|
|
///
|
|
/// ROI related parameters
|
|
///
|
|
RoisAndResults roisAndResults;
|
|
///
|
|
public void ClearRoiParams()
|
|
{
|
|
roisAndResults.ClearRoiParams();
|
|
}
|
|
|
|
public int RegisterRoi(string roiParams)
|
|
{
|
|
return roisAndResults.RegisterRoi(roiParams);
|
|
}
|
|
|
|
public int GetResult(int roiHandle, out long timeMs)
|
|
{
|
|
return roisAndResults.GetResult(roiHandle, out timeMs);
|
|
}
|
|
|
|
|
|
public Camera() { }
|
|
|
|
public Camera(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
|
: base(cfg)
|
|
{
|
|
CameraCfg = cfg as CameraCfg;
|
|
|
|
// NetAdapter = TbfComponents.FindComponent(cfg.ParentName, components) as TBF.Rig.Network.AdapterJMS.Netadapter;
|
|
//if (NetAdapter == null) throw new ArgumentNullException("no network adapter");
|
|
}
|
|
|
|
|
|
public override void Initialize()
|
|
{
|
|
cameraIdx = GetCameraIdx();
|
|
|
|
running = false;
|
|
terminalDlg = null;
|
|
roisAndResults = new RoisAndResults(); /// object for measurement results, writes/reads to/from this object are locked
|
|
|
|
// if (CameraCfg.DebugLevel == DebugMode.Simulate)
|
|
// {
|
|
// UiBridge.Bridge.OnCameraInfo(cameraIdx, string.Format("{0} s/n {1} si simulated", Name, CameraCfg.HardwareAddress));
|
|
// return;
|
|
// }
|
|
|
|
/// Detect a camera
|
|
CameraDetectionResult cameraDetectionResult = null;
|
|
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);
|
|
// var detectJmsCamera2 = DetectJMSCamera("192.168.0.168",32456);
|
|
// Task.WaitAll(detectJmsCamera, detectJmsCamera1, detectJmsCamera2);
|
|
|
|
|
|
if (CameraCfg.DebugLevel == DebugMode.AutoDetect)
|
|
{
|
|
CameraCfg.DebugLevel = cameraDetectionResult.IsDetected ? DebugMode.DetectedOn : DebugMode.DetectedOff;
|
|
}
|
|
else if (!cameraDetectionResult.IsDetected)
|
|
{
|
|
throw new Exception(string.Format("{0}={1}", Strings.Address, CameraCfg.HardwareAddress));
|
|
}
|
|
|
|
if (cameraDetectionResult.IsDetected)
|
|
{
|
|
ipAddress = cameraDetectionResult.IpAddress;
|
|
|
|
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
|
|
{
|
|
UiBridge.Bridge.OnCameraInfo(this.cameraIdx, string.Format("{0} s/n {1} was not detected", Name, CameraCfg.HardwareAddress));
|
|
log.FatalFormat("'{0}' NOT detected: Id={1} addr={2}", Name, CameraIdx, CameraCfg.HardwareAddress);
|
|
}
|
|
}
|
|
|
|
private void OnPromptImageReceived(object sender, PromptReceivedImageEventArgs 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!");
|
|
}
|
|
}
|
|
}
|
|
|
|
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!");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void StopDevice()
|
|
{
|
|
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
|
{
|
|
running = false;
|
|
|
|
|
|
stopLiveStreamListenerFlag = true;
|
|
stopGrabImageListenerFlag = true;
|
|
|
|
}
|
|
}
|
|
|
|
public void StopDevice2()
|
|
{
|
|
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
|
{
|
|
|
|
if (liveStreamListenerThread != null) liveStreamListenerThread.Join(50);
|
|
if (grabImageListenerThread != null) grabImageListenerThread.Join(50);
|
|
|
|
if (cameraTcpClient != null)
|
|
{
|
|
cameraTcpClient.Close();
|
|
cameraTcpClient = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RunDeviceBefore()
|
|
{
|
|
if ((CameraCfg.DebugLevel != DebugMode.Simulate) && (CameraCfg.DebugLevel != DebugMode.DetectedOff))
|
|
{
|
|
if (sha1sumResponse != null && !sha1sumResponseProcessed)
|
|
{
|
|
|
|
|
|
sha1sumResponseProcessed = true;
|
|
}
|
|
}
|
|
}
|
|
public void RunDeviceAfter() { }
|
|
|
|
|
|
void OnPromptReceived(object sndr, PromptReceivedJMSEventArgs a)
|
|
{
|
|
if (sha1sumResponse == null)
|
|
{
|
|
sha1sumResponse = a.Response;
|
|
jmsMessage = a.Message;
|
|
}
|
|
}
|
|
|
|
void OnReceivedImageX(object sndr, PromptReceivedImageEventArgs a)
|
|
{
|
|
if (a != null)
|
|
{
|
|
lastImage = a.image;
|
|
}
|
|
}
|
|
|
|
void OnPromptReceivedCamera(object sndr, PromptReceivedJMSEventArgs a)
|
|
{
|
|
try
|
|
{
|
|
JmsPacket packet = new JmsPacket(a.Response);
|
|
jmsMessage = packet.JmsMessage;
|
|
}catch(Exception exc)
|
|
{
|
|
log.Error(exc.Message);
|
|
}
|
|
}
|
|
|
|
void OnReceivedPackage(object sndr, PromptReceivedEventArgs a)
|
|
{
|
|
if (sha1sumResponse == null)
|
|
{
|
|
sha1sumResponse = a.Response;
|
|
try
|
|
{
|
|
JmsPacket packet = new JmsPacket(a.Response);
|
|
jmsMessage = packet.JmsMessage;
|
|
}catch(Exception exc)
|
|
{
|
|
log.Error(exc.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Configuration Change Handling
|
|
|
|
public static void OnCfgChange(object sender, CfgChangeArgs args)
|
|
{
|
|
if (CfgChangeHandler == null) return;
|
|
try { CfgChangeHandler(sender, args); }
|
|
catch (Exception e) { log.Error("CfgChangeHandler(...) failed", e); }
|
|
}
|
|
|
|
public static event EventHandler<CfgChangeArgs> CfgChangeHandler;
|
|
|
|
public override void StartChangeHandler()
|
|
{
|
|
CfgChangeHandler += delegate(object sender, CfgChangeArgs args)
|
|
{
|
|
CameraCfg tmpcfg = args.Cfg as CameraCfg;
|
|
if (tmpcfg != null && tmpcfg.Name.Equals(Name))
|
|
{
|
|
if (args.Command == CfgChangeCmd.CfgChange)
|
|
{
|
|
CameraCfg.TestImagesMode = tmpcfg.TestImagesMode;
|
|
CameraCfg.TestImagesCount = tmpcfg.TestImagesCount;
|
|
CameraCfg.IPAddressCJMS = tmpcfg.IPAddressCJMS;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#endregion Configuration Change Handling
|
|
|
|
|
|
public class CameraDetectionResult
|
|
{
|
|
public bool IsDetected { get; set; }
|
|
public IPAddress IpAddress { get; set; }
|
|
public string Hardware { get; set; }
|
|
public string Revision { get; set; }
|
|
public string Serial { get; set; }
|
|
public string SdCardVersion { get; set; }
|
|
public string Response { get; set; }
|
|
public double Duration_ms { get; set; }
|
|
}
|
|
|
|
async Task<CameraDetectionResult> DetectJMSCamera()
|
|
{
|
|
return await DetectJMSCamera(CameraCfg.IPAddressCJMS, iPort);
|
|
}
|
|
|
|
async Task<CameraDetectionResult> 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;
|
|
}
|
|
|
|
private static string FormatPacketForLog( JmsPacket packet)
|
|
{
|
|
if (packet == null)
|
|
return "<null>";
|
|
|
|
if (packet.JmsMessage == null)
|
|
return "<packet without message>";
|
|
|
|
string payload = packet.JmsMessage.Payload;
|
|
|
|
if (!string.IsNullOrEmpty(payload) && payload.Length > 200)
|
|
{
|
|
payload = payload.Substring(0, 200) + "...";
|
|
}
|
|
|
|
return $"command={packet.JmsMessage.Command}, " + $"status={packet.JmsMessage.Status}, " + $"payload={payload}";
|
|
}
|
|
|
|
private async Task ClearPendingResponsesAsync(StreamReader reader)
|
|
{
|
|
await cameraReadLock.WaitAsync();
|
|
|
|
try
|
|
{
|
|
while (reader.Peek() >= 0)
|
|
{
|
|
string oldLine = reader.ReadLine();
|
|
log.Debug("Discarded old camera response: " + oldLine);
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
log.Warn("ClearPendingResponses failed: " + exc.Message);
|
|
}
|
|
finally
|
|
{
|
|
cameraReadLock.Release();
|
|
}
|
|
}
|
|
|
|
class SensorConfig{
|
|
public string type { get; set; } = "all";
|
|
public int width { get; set; } = 640;
|
|
public int height { get; set; } = 480;
|
|
public int fps { get; set; } = 10;
|
|
public int exposure { get; set; } = 2000;
|
|
public float brightness { get; set; } = 0.5f;
|
|
public float contrast { get; set; } = 1.5f;
|
|
public int nBuffersOverload { get; set; } = 5;
|
|
|
|
public static string getJsonString(SensorConfig sensorConfig)
|
|
{
|
|
string returnStr = JsonConvert.SerializeObject(sensorConfig, Formatting.Indented);
|
|
|
|
returnStr = returnStr.Replace("\n", "");
|
|
returnStr = returnStr.Replace("\r", "");
|
|
return returnStr;
|
|
}
|
|
}
|
|
|
|
private RoiCfg GetFirstRoiChild()
|
|
{
|
|
/// Load the list of components from the database
|
|
var session = TBF.DB.CreateSession(DBKind.Config);
|
|
var cmptnEntities = session.QueryOver<Config.Entities.Component>().OrderBy(x => x.ItemNr).Asc.List();
|
|
|
|
|
|
List<RoiCfg> cfgs = new List<RoiCfg>();
|
|
IList<IComponent> components = new List<Rig.Generic.IComponent>();
|
|
foreach (var component in cmptnEntities)
|
|
{
|
|
if (String.Compare(component.Parent, Name, StringComparison.Ordinal) == 0)
|
|
{
|
|
IComponentFactory cmpntFactory = Rig.TbfComponents.CmpntFactoryFromClassName(component.ClassName);
|
|
if (cmpntFactory == null) continue;
|
|
|
|
IList<IComponent> tbfComponents = Rig.TbfComponents.LoadComponentsFromDB(cmptnEntities);
|
|
IComponentCfg cmpntCfgFromCmpntEntity = cmpntFactory.CmpntCfgFromCmpntEntity(component);
|
|
|
|
IComponent cmpnt = cmpntFactory.GetComponent(cmpntCfgFromCmpntEntity, components);
|
|
|
|
RoiForFixedStartCJMS11.RoiCfg roi = cmpntCfgFromCmpntEntity as RoiForFixedStartCJMS11.RoiCfg;
|
|
|
|
if (roi != null)
|
|
{
|
|
cfgs.Add(roi);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// get fist found configuration
|
|
return cfgs.First();
|
|
}
|
|
|
|
private string getConfigMessage()
|
|
{
|
|
//CameraCfg
|
|
SensorConfig sensorConfig = new SensorConfig();
|
|
sensorConfig.height = RoiConfiguration.ImageFrame.Height;
|
|
sensorConfig.width = RoiConfiguration.ImageFrame.Width;
|
|
return SensorConfig.getJsonString(sensorConfig);
|
|
}
|
|
|
|
|
|
void CameraInfoRequest(UdpClient udpClient, int hardwareAddress)
|
|
{
|
|
string strToSend = string.Format("getinfo {0}", hardwareAddress);
|
|
|
|
}
|
|
|
|
void SetDateTime(UdpClient udpClient, DateTime dateTime)
|
|
{
|
|
string strToSend = string.Format("setdatetime {0:yyMMddHHmmss}", dateTime);
|
|
|
|
}
|
|
|
|
|
|
private CameraUICmd lockBrich = CameraUICmd.None;
|
|
|
|
// Define the handler method
|
|
private void HandleCameraUiCmd(object sender, CameraUICmdEventArgs e)
|
|
{
|
|
if(lockBrich != CameraUICmd.None && e.CameraUICmd != CameraUICmd.Stop)
|
|
return;
|
|
|
|
if (!(e.CameraIdx == -1 || e.CameraIdx == cameraIdx))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
//lock
|
|
lockBrich = e.CameraUICmd;
|
|
// Example reaction to event
|
|
if (e.CameraUICmd == CameraUICmd.Live)
|
|
{
|
|
if (stopLiveStreamListenerFlag == false && liveStreamListenerThread != null &&
|
|
liveStreamListenerThread.IsAlive)
|
|
return;
|
|
log.Debug("Handle CameraUICmd received - Live");
|
|
stopLiveStreamListenerFlag = false;
|
|
stopUIListenerFlag = true;
|
|
StartLiveStreamListener();
|
|
//StartLiveStreamListener(IPAddress, iPort);
|
|
}
|
|
else if (e.CameraUICmd == CameraUICmd.Stop)
|
|
{
|
|
StopUiThreads();
|
|
stopUIListenerFlag = true;
|
|
log.Debug("Handle CameraUICmd received - Stop Live");
|
|
}
|
|
else if (e.CameraUICmd == CameraUICmd.Grab)
|
|
{
|
|
log.Debug($"Handle CameraUICmd received - GrabImage - camera idx: {cameraIdx}");
|
|
//stopUIListenerFlag = true;
|
|
StopUiThreads();
|
|
StartGrabImageListener();
|
|
}
|
|
else if (e.CameraUICmd == CameraUICmd.SaveImage)
|
|
{
|
|
log.Debug("Handle CameraUICmd received - SaveImage - will end Live Stream");
|
|
StopUiThreads();
|
|
StartSaveImageListener();
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
|
|
//Unlock - if is not in live - live will finish only by CameraUICmd.Stop
|
|
if (lockBrich != CameraUICmd.Live)
|
|
lockBrich = CameraUICmd.None;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lockBrich = CameraUICmd.None; //Unlock - solve hard lock
|
|
stopUIListenerFlag = true;
|
|
}
|
|
|
|
}
|
|
|
|
void SaveImageToFile(Image imageToSave)
|
|
{
|
|
|
|
|
|
if (imageToSave == null)
|
|
{
|
|
log.Error("Nothing to save in SaveImageToFile");
|
|
return; // nothing to save
|
|
}
|
|
|
|
using (SaveFileDialog dialog = new SaveFileDialog())
|
|
{
|
|
dialog.Title = "Save Image As";
|
|
dialog.Filter = "PNG Image|*.png|JPEG Image|*.jpg|Bitmap Image|*.bmp";
|
|
dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
|
|
dialog.FileName = "my_image.png";
|
|
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
// Choose format based on extension
|
|
var ext = System.IO.Path.GetExtension(dialog.FileName).ToLower();
|
|
System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Png;
|
|
if (ext == ".jpg") format = System.Drawing.Imaging.ImageFormat.Jpeg;
|
|
else if (ext == ".bmp") format = System.Drawing.Imaging.ImageFormat.Bmp;
|
|
|
|
try
|
|
{
|
|
SaveImageToFile(imageToSave, dialog.FileName);
|
|
MessageBox.Show("Image saved to: " + dialog.FileName);
|
|
}catch(Exception exc)
|
|
{
|
|
MessageBox.Show("Error saving image: " + exc.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SaveImageToFile(Image imageToSave, string fileNamePath)
|
|
{
|
|
if (imageToSave == null)
|
|
{
|
|
log.Error("Nothing to save in SaveImageToFile");
|
|
return; // nothing to save
|
|
}
|
|
|
|
if (fileNamePath == null || fileNamePath.IsEmpty())
|
|
{
|
|
log.Error("Path to save missing!");
|
|
return; // nothing to save
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
string filePath = fileNamePath;
|
|
|
|
string path = Path.GetDirectoryName(filePath);
|
|
if (path != null && path.IsNotEmpty())
|
|
{
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
|
|
using (var clonedImage = CloneImageToBitmap(imageToSave))
|
|
{
|
|
clonedImage.Save(filePath, ImageFormat.Png); // encoder, encoderParams);
|
|
}
|
|
log.Debug("Image saved to: " + filePath);
|
|
}catch(Exception exc)
|
|
{
|
|
log.Error("Error saving image: " + exc.Message);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private static ImageCodecInfo GetEncoder(ImageFormat format)
|
|
{
|
|
return ImageCodecInfo.GetImageDecoders().FirstOrDefault(codec => codec.FormatID == format.Guid);
|
|
}
|
|
|
|
|
|
private Bitmap CloneImageToBitmap(Image originalImage)
|
|
{
|
|
return new Bitmap(originalImage);
|
|
}
|
|
private Image CloneImage(Image originalImage)
|
|
{
|
|
|
|
using (var ms = new MemoryStream())
|
|
{
|
|
originalImage.Save(ms, ImageFormat.Png); // Use a safe format for copying
|
|
ms.Position = 0;
|
|
return Image.FromStream(ms);
|
|
}
|
|
}
|
|
|
|
public void StartSaveImageToFileListener(Image imageToSave, string fileName)
|
|
{
|
|
saveImageListenerThread = new Thread(() => SaveImageToFile(imageToSave, fileName));
|
|
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<Image> saveImageTask = Task.Run(() => SaveImageListenerAsynch());
|
|
var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(1));
|
|
var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result;
|
|
if (completedAnswerTask == timeoutAnswerTask)
|
|
{
|
|
log.Error("Connection attempt timed out after 1 second.");
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
SaveImageToFile(saveImageTask.Result);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
MessageBox.Show("Error saving image: " + exc.Message);
|
|
}
|
|
}
|
|
|
|
public void StartSaveImageListener(string filePath)
|
|
{
|
|
Task<Image> saveImageTask = Task.Run(() => SaveImageListenerAsynch());
|
|
var timeoutAnswerTask = Task.Delay(TimeSpan.FromSeconds(1));
|
|
var completedAnswerTask = Task.WhenAny(saveImageTask, timeoutAnswerTask).Result;
|
|
if (completedAnswerTask == timeoutAnswerTask)
|
|
{
|
|
log.Error("Connection attempt timed out after 1 second.");
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
SaveImageToFile(saveImageTask.Result);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
MessageBox.Show("Error saving image: " + exc.Message);
|
|
}
|
|
}
|
|
|
|
private void EstablishCameraConnection(ref TcpClient camTcpClient, string localIP, int localPort)
|
|
{
|
|
|
|
var connectTask = camTcpClient.ConnectAsync(localIP, localPort);
|
|
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(1));
|
|
|
|
var completedTask = Task.WhenAny(connectTask, timeoutTask).Result;
|
|
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
throw new TimeoutException("Connection attempt timed out after 1 second.");
|
|
}
|
|
}
|
|
|
|
private void EstablishCameraConnectionRepeticaly(ref TcpClient camTcpClient, string localIP, int localPort)
|
|
{
|
|
int[] timeoutIntervals = new int[] { 500, 1000 };
|
|
|
|
foreach (int interval in timeoutIntervals)
|
|
{
|
|
// Dispose previous TcpClient if needed
|
|
//camTcpClient?.Close();
|
|
//camTcpClient = new TcpClient();
|
|
|
|
try
|
|
{
|
|
// Begin connection attempt
|
|
var connectTask = camTcpClient.ConnectAsync(localIP, localPort);
|
|
var timeoutTask = Task.Delay(interval);
|
|
|
|
var completedTask = Task.WhenAny(connectTask, timeoutTask).GetAwaiter().GetResult();
|
|
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
// Timed out
|
|
continue;
|
|
}
|
|
|
|
// Check if the connect task completed successfully
|
|
//connectTask.GetAwaiter().GetResult()); // Throws if failed
|
|
|
|
if (camTcpClient.Connected)
|
|
{
|
|
// Connected successfully
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// Ignore and continue to next retry interval
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If we reach here, all attempts failed
|
|
throw new TimeoutException("Connection attempt timed out after multiple retries.");
|
|
}
|
|
private void EstablishCameraConnectionX(ref TcpClient camTcpClient, string localIP, int localPort)
|
|
{
|
|
Task connectTask = null;
|
|
Task timeoutTask = null;
|
|
Task completedTask = null;
|
|
|
|
int[] timeoutIntervals = new int[] {100, 300, 500, 1000};
|
|
|
|
foreach (int interval in timeoutIntervals)
|
|
{
|
|
if (camTcpClient.Connected)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
connectTask = camTcpClient.ConnectAsync(localIP, localPort);
|
|
timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(interval));
|
|
|
|
completedTask = Task.WhenAny(connectTask, timeoutTask).Result;
|
|
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
throw new TimeoutException("Connection attempt timed out after 1 second.");
|
|
}
|
|
|
|
if (connectTask == null || connectTask.IsFaulted || connectTask.IsCanceled)
|
|
{
|
|
throw new Exception("Connection attempt failed: " + connectTask.Exception.Message);
|
|
}
|
|
|
|
if (camTcpClient.Connected == false)
|
|
{
|
|
throw new Exception("Connection attempt failed: " + camTcpClient.Client.RemoteEndPoint.ToString());
|
|
}
|
|
|
|
}
|
|
|
|
|
|
private async Task<TcpClient> ConnectCameraAsync(int timeoutMs, CancellationToken ct)
|
|
{
|
|
var client = new TcpClient();
|
|
|
|
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<Image> SaveImageListenerAsynch()
|
|
{
|
|
saveCts?.Cancel();
|
|
saveCts = new CancellationTokenSource();
|
|
|
|
return await SaveImageListenerAsynch(saveCts.Token);
|
|
}
|
|
|
|
public async Task<Image> SaveImageListenerAsynch(CancellationToken ct)
|
|
{
|
|
Image imageToReturn = null;
|
|
|
|
await cameraOperationLock.WaitAsync(ct);
|
|
|
|
try
|
|
{
|
|
using (TcpClient client = await ConnectCameraAsync(1000, ct))
|
|
using (NetworkStream stream = client.GetStream())
|
|
{
|
|
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}");
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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));
|
|
|
|
var completedTask = Task.WhenAny(taskStopUi, timeoutTask).Result;
|
|
|
|
if (completedTask == timeoutTask)
|
|
{
|
|
stopUIListenerFlag = true;
|
|
throw new TimeoutException("Connection attempt timed out after 5 second.");
|
|
}
|
|
CloseCameraResources();
|
|
}
|
|
|
|
void StopUiListener()
|
|
{
|
|
stopUIListenerFlag = false;
|
|
while (!stopUIListenerFlag)
|
|
{
|
|
stopLiveStreamListenerFlag = true;
|
|
stopGrabImageListenerFlag = true;
|
|
|
|
if ((liveStreamListenerThread != null && liveStreamListenerThread.IsAlive) ||
|
|
(grabImageListenerThread != null && grabImageListenerThread.IsAlive))
|
|
{
|
|
Thread.Sleep(100);
|
|
}
|
|
else
|
|
{
|
|
stopUIListenerFlag = true;
|
|
}
|
|
}
|
|
//stopLiveStreamListenerFlag = false;
|
|
//stopGrabImageListenerFlag = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsGrabImageInProgress
|
|
{
|
|
get
|
|
{
|
|
return grabImageTask != null &&
|
|
!grabImageTask.IsCompleted;
|
|
}
|
|
}
|
|
|
|
public Task GrabImageTask
|
|
{
|
|
get { return grabImageTask; }
|
|
}
|
|
|
|
public void StartGrabImageListener()
|
|
{
|
|
|
|
_ = StartGrabImageListenerAsync();
|
|
}
|
|
|
|
public Task<Image> StartGrabImageListenerAsync()
|
|
{
|
|
if (grabImageTask != null && !grabImageTask.IsCompleted)
|
|
{
|
|
log.Warn(
|
|
$"Grab already running: cameraIdx={cameraIdx}, " +
|
|
$"IP={CameraCfg.IPAddressCJMS}");
|
|
|
|
return grabImageTask;
|
|
}
|
|
|
|
grabCts?.Cancel();
|
|
grabCts?.Dispose();
|
|
|
|
grabCts = new CancellationTokenSource();
|
|
|
|
grabImageTask = GrabImageListenerAsync(grabCts.Token);
|
|
|
|
return grabImageTask;
|
|
}
|
|
|
|
private async Task EstablishCameraConnectionAsync(
|
|
TcpClient client,
|
|
string ip,
|
|
int port,
|
|
int timeoutMs)
|
|
{
|
|
Task connectTask = client.ConnectAsync(ip, port);
|
|
Task timeoutTask = Task.Delay(timeoutMs);
|
|
|
|
Task completedTask = await Task.WhenAny(connectTask, timeoutTask);
|
|
|
|
if (completedTask == timeoutTask)
|
|
throw new TimeoutException($"Connection attempt timed out after {timeoutMs} ms.");
|
|
|
|
await connectTask;
|
|
}
|
|
|
|
private Task<Image> grabImageTask;
|
|
private CancellationTokenSource grabCts;
|
|
//private CancellationTokenSource liveCts;
|
|
private CancellationTokenSource saveCts;
|
|
|
|
private readonly SemaphoreSlim cameraOperationLock = new SemaphoreSlim(1, 1);
|
|
|
|
private async Task<Image> GrabImageListenerAsync(CancellationToken ct)
|
|
{
|
|
await cameraOperationLock.WaitAsync(ct);
|
|
|
|
Image resultImage = null;
|
|
try
|
|
{
|
|
using (TcpClient client = await ConnectCameraAsync(5000, ct).ConfigureAwait(false))
|
|
using (NetworkStream stream = client.GetStream())
|
|
{
|
|
var reader = new CameraLineReader( stream, 64 * 1024);
|
|
|
|
JmsPacket prepareAnswer =
|
|
await SendCommandAndWaitToAnswerAsync(
|
|
stream,
|
|
reader,
|
|
CommandM.prepare_camera_,
|
|
null,
|
|
3000,
|
|
ct);
|
|
|
|
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");
|
|
}
|
|
else if (prepareAnswer == null ||
|
|
prepareAnswer.JmsMessage == null ||
|
|
prepareAnswer.JmsMessage.Status !=
|
|
MessageStatus.ACK)
|
|
{
|
|
log.Error("Prepare camera failed.");
|
|
UiBridge.Bridge.OnImage(cameraIdx, null);
|
|
return null;
|
|
}
|
|
|
|
await SendCommandAsync( stream, CommandM.grab_image_, ct, null).ConfigureAwait(false);
|
|
|
|
while (!ct.IsCancellationRequested)
|
|
{
|
|
string response = await reader.ReadLineAsync( 10000, ct).ConfigureAwait(false);
|
|
|
|
if (string.IsNullOrWhiteSpace(response))
|
|
continue;
|
|
|
|
var packet = new JmsPacket(response);
|
|
|
|
if (packet.JmsMessage == null)
|
|
continue;
|
|
|
|
if (packet.JmsMessage.Command != CommandM.grab_image_)
|
|
{
|
|
log.Debug( $"Unexpected response during grab: cameraIdx={cameraIdx}, command={packet.JmsMessage.Command}");
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
cameraStream?.Close();
|
|
cameraStream = null;
|
|
cameraTcpClient?.Close();
|
|
cameraTcpClient?.Dispose();
|
|
cameraTcpClient = null;
|
|
}
|
|
|
|
public void CancelGrabImage()
|
|
{
|
|
try
|
|
{
|
|
grabCts?.Cancel();
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{
|
|
// CTS už bolo uvoľnené.
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// listener for multiple - repetetive image receiving
|
|
/// </summary>
|
|
/// <detail>
|
|
/// method uses reader to receive data from camera
|
|
/// </detail>
|
|
/// <param name="localIP"></param>
|
|
/// <param name="localPort"></param>
|
|
|
|
// private void StartLiveStreamListener(IPAddress localIP, int localPort)
|
|
// {
|
|
// liveStreamListenerThread = new Thread(new ThreadStart(LiveStreamListener));
|
|
// liveStreamListenerThread.Start();
|
|
// }
|
|
|
|
private CancellationTokenSource liveCts;
|
|
|
|
public void StartLiveStreamListener()
|
|
{
|
|
liveCts?.Cancel();
|
|
liveCts = new CancellationTokenSource();
|
|
|
|
_ = LiveStreamListenerAsync(liveCts.Token);
|
|
}
|
|
|
|
|
|
private async Task LiveStreamListenerAsync(CancellationToken ct)
|
|
{
|
|
await cameraOperationLock.WaitAsync(ct);
|
|
|
|
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)
|
|
{
|
|
log.Error("Prepare camera failed.");
|
|
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)
|
|
//{
|
|
// string[] pulsesArr = msrmtData.MeasuredData.Split(new char[] { ';' });
|
|
|
|
// uint uiVal;
|
|
// if (result != null && result.Length + 1 == pulsesArr.Length && uint.TryParse(pulsesArr[0], out uiVal))
|
|
// {
|
|
// cameraTimeMs = (long)uiVal;
|
|
|
|
// for (int i = 0; i < result.Length; i++)
|
|
// {
|
|
// int val;
|
|
// if (int.TryParse(pulsesArr[i + 1], out val))
|
|
// {
|
|
// result[i] = Math.Abs(val);
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
|
|
public IOperation LiveStreamOp(bool hiRes)
|
|
{
|
|
int wid = hiRes ? 1280 : 640;
|
|
int hgh = hiRes ? 960 : 480;
|
|
|
|
//string command = string.Format("~/userland/build/bin/raspivid -t 0 -cd MJPEG -w {0} -h {1} -fps 30 -b 8000000 -o - | gst-launch-1.0 fdsrc ! \"image/jpeg,framerate=30/1\" ! jpegparse ! rtpjpegpay ! udpsink host={2} port={3}",
|
|
// wid, hgh, NetAdapter.IPAddress, rtpUdpLocalPort);
|
|
|
|
return new LiveStreamOp(this, command);
|
|
}
|
|
|
|
|
|
public IOperation GrabImagesOp(string[] imgFileNames, bool blackAndWhite, bool lowResolution, ImageRotation imageRotation)
|
|
{
|
|
return new GrabImagesOp(this, imgFileNames, blackAndWhite, lowResolution, imageRotation);
|
|
}
|
|
|
|
|
|
public IOperation MeasurementOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string command;
|
|
bool measurementCommandSent;
|
|
|
|
|
|
|
|
|
|
private void SendCommand(CommandM command)
|
|
{
|
|
try
|
|
{
|
|
if (cameraStream != null && cameraStream.CanWrite)
|
|
{
|
|
string commandStr = CommandMEnum.GetVal(command) + "\n";
|
|
byte[] bytes = Encoding.ASCII.GetBytes(commandStr);
|
|
|
|
cameraStream.Write(bytes, 0, bytes.Length);
|
|
cameraStream.Flush();
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
log.ErrorFormat("{0} SendCommand: {1}", Name, exc.Message);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
private async Task<JmsPacket> SendCommandAndWaitToAnswerAsync(
|
|
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;
|
|
|
|
if (value.Length <= maximumLength)
|
|
return value;
|
|
|
|
return value.Substring(0, maximumLength) +
|
|
"...";
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
///Prepare communication with the camera
|
|
log.DebugFormat("Camera Start() - JMCameras.CameraCfg.IPAddressCJMS: {0}, CameraCfg.Port: {1}! ");
|
|
StopUiListener();
|
|
this.cameraTcpClient = new TcpClient();
|
|
lockCameraInitialisation = true;
|
|
|
|
try
|
|
{
|
|
EstablishCameraConnection(ref this.cameraTcpClient, CameraCfg.IPAddressCJMS, iPort);
|
|
}catch(Exception exc)
|
|
{
|
|
log.ErrorFormat("Camera Start() - JMCameras.CameraCfg.IPAddressCJMS: {0}, CameraCfg.Port: {1}! Exception: {2}", CameraCfg.IPAddressCJMS, iPort, exc.Message);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
public Event Run()
|
|
{
|
|
|
|
return Event.None;
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (measurementCommandSent)
|
|
{
|
|
log.WarnFormat("{0} SEND_COMMAND: cancel", Name);
|
|
if (cameraStream.CanWrite)
|
|
{
|
|
SendCommand(CommandM.close_);
|
|
SendCommand(CommandM.disconnect_);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
/// PromptReceviedEventHandler delegate and handler
|
|
///
|
|
public delegate void PromptCameraReceivedEventHandler(object sender, PromptReceivedJMSEventArgs e);
|
|
public static event PromptCameraReceivedEventHandler PromptCameraReceivedHandler;
|
|
|
|
/// <summary>
|
|
/// This method is called from within this class when
|
|
/// a prompt string was received from the telnet server.
|
|
/// </summary>
|
|
public static void OnPromptReceived(object sender, string response, JmsPacket package)
|
|
{
|
|
|
|
log.Info("Prompt received, response:" + Environment.NewLine + response);
|
|
|
|
if (null != PromptCameraReceivedHandler)
|
|
{
|
|
try
|
|
{
|
|
PromptCameraReceivedHandler(sender, new PromptReceivedJMSEventArgs(response, package.JmsMessage));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.Info("Internal error: PromptReceivedHandler exception: " + e.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
/// PromptReceviedEventHandler delegate and handler
|
|
///
|
|
public delegate void PromptImagesReceivedEventHandler(object sender, PromptReceivedImagesEventArgs e);
|
|
public static event EventHandler<PromptReceivedImagesEventArgs> ImagesCameraHandler;
|
|
public static event EventHandler<PromptReceivedImageEventArgs> ImageCameraHandler;
|
|
|
|
/// <summary>
|
|
/// 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 = 0, int imageCount = 1)
|
|
{
|
|
|
|
log.Info($"Image 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is called from within this class when
|
|
/// a prompt string was received from the telnet server.
|
|
/// </summary>
|
|
public static void OnImagesReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx = 0, int imageCount = 1)
|
|
{
|
|
|
|
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<string> ReadCameraLineAsync(
|
|
NetworkStream stream,
|
|
int timeoutMs,
|
|
CancellationToken ct)
|
|
{
|
|
await cameraReadLock.WaitAsync(ct);
|
|
|
|
try
|
|
{
|
|
var line = new List<byte>();
|
|
var buffer = new byte[1];
|
|
|
|
while (true)
|
|
{
|
|
Task<int> readTask = stream.ReadAsync(buffer, 0, 1, ct);
|
|
Task timeoutTask = Task.Delay(timeoutMs, ct);
|
|
|
|
Task completed = await Task.WhenAny(readTask, timeoutTask);
|
|
|
|
if (completed == timeoutTask)
|
|
{
|
|
ct.ThrowIfCancellationRequested();
|
|
throw new TimeoutException($"Camera {cameraIdx} read timeout after {timeoutMs} ms.");
|
|
}
|
|
|
|
int read = await readTask;
|
|
|
|
if (read == 0)
|
|
throw new IOException("Camera connection closed.");
|
|
|
|
if (buffer[0] == '\n')
|
|
break;
|
|
|
|
if (buffer[0] != '\r')
|
|
line.Add(buffer[0]);
|
|
}
|
|
|
|
return Encoding.ASCII.GetString(line.ToArray());
|
|
}
|
|
finally
|
|
{
|
|
cameraReadLock.Release();
|
|
}
|
|
}
|
|
|
|
private readonly object cameraReadSyncLock = new object();
|
|
|
|
private string ReadCameraLine(NetworkStream stream, int timeoutMs)
|
|
{
|
|
lock (cameraReadSyncLock)
|
|
{
|
|
stream.ReadTimeout = timeoutMs;
|
|
|
|
var line = new List<byte>();
|
|
|
|
while (true)
|
|
{
|
|
int b = stream.ReadByte(); // tu funguje ReadTimeout
|
|
|
|
if (b == -1)
|
|
throw new IOException("Camera connection closed.");
|
|
|
|
if (b == '\n')
|
|
break;
|
|
|
|
if (b != '\r')
|
|
line.Add((byte)b);
|
|
}
|
|
|
|
return Encoding.ASCII.GetString(line.ToArray());
|
|
}
|
|
}
|
|
|
|
|
|
private async Task SendCommandAsync(NetworkStream stream, CommandM command, CancellationToken ct, string payload = null)
|
|
{
|
|
string commandStr = CommandMEnum.GetVal(command);
|
|
|
|
if (!string.IsNullOrEmpty(payload))
|
|
{
|
|
commandStr += "=" + payload;
|
|
}
|
|
|
|
//commandStr += " ";
|
|
byte[] bytes = Encoding.ASCII.GetBytes(commandStr + "\n");
|
|
|
|
//await cameraWriteLock.WaitAsync();
|
|
|
|
|
|
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<string> 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)
|
|
{
|
|
try { stream.Close(); } catch { }
|
|
try { cameraTcpClient?.Close(); } catch { }
|
|
|
|
throw new TimeoutException($"Camera read timeout after {timeoutMs} ms.");
|
|
}
|
|
|
|
return await readTask;
|
|
}
|
|
|
|
|
|
}
|
|
}
|