tbf/TBF/Rig/Network/Camera/CJMS11/Camera.cs

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;
}
}
}