camera send implemenatation, dialog support, ver 3.9.3134
This commit is contained in:
parent
34141481e3
commit
71ff74122e
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.3133.0")]
|
||||
[assembly: AssemblyFileVersion("3.9.3133.0")]
|
||||
[assembly: AssemblyVersion("3.9.3134.0")]
|
||||
[assembly: AssemblyFileVersion("3.9.3134.0")]
|
||||
|
||||
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using log4net;
|
||||
@ -70,6 +71,9 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
int lastWmNr1;
|
||||
Bitmap lastImage; /// non-zoomed image
|
||||
Bitmap zoomedImage;
|
||||
private readonly List<string> currentImageGroup = new List<string>();//multiple pictures
|
||||
private int currentImageGroupIndex;
|
||||
private bool mouseOverPictureBox;
|
||||
|
||||
Thread imageProcessingThread; /// Thread to process images
|
||||
public delegate void OcrTextObtainedDelegate(int ix, bool isEnd, string text);
|
||||
@ -96,6 +100,12 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
public TestStartEndForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
KeyPreview = true;
|
||||
|
||||
pictureBox1.MouseWheel += pictureBox1_MouseWheel;
|
||||
pictureBox1.MouseEnter += pictureBox1_MouseEnter;
|
||||
pictureBox1.MouseLeave += pictureBox1_MouseLeave;
|
||||
|
||||
this.Icon = Properties.Resources.TBF_icon;
|
||||
|
||||
@ -483,15 +493,46 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
startTextBoxes[i].TextChanged += new System.EventHandler(this.startTextBoxX_TextChanged);
|
||||
startTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.startTextBoxX_KeyPress);
|
||||
startTextBoxes[i].MouseDown += new System.Windows.Forms.MouseEventHandler(this.startTextBoxX_MouseDown);
|
||||
startTextBoxes[i].Enter += startTextBoxX_Enter;
|
||||
}
|
||||
|
||||
private void startTextBoxX_Enter(object sender, EventArgs e)
|
||||
{
|
||||
var textBox = sender as TextBox;
|
||||
if (textBox == null) { return; }
|
||||
|
||||
int physPos = PhysPos(textBox, startTextBoxes);
|
||||
|
||||
if (physPos >= 0)
|
||||
{
|
||||
LoadImage(sender, Tst.Start, physPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void EndTextBox_EnableHanders(int i)
|
||||
{
|
||||
endTextBoxes[i].AcceptsTabChanged += new System.EventHandler(this.endTextBoxX_AcceptsTabChanged);
|
||||
endTextBoxes[i].TextChanged += new System.EventHandler(this.endTextBoxX_TextChanged);
|
||||
endTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.endTextBoxX_KeyPress);
|
||||
endTextBoxes[i].MouseDown += new System.Windows.Forms.MouseEventHandler(this.endTextBoxX_AcceptsTabChanged);
|
||||
endTextBoxes[i].Enter += endTextBoxX_Enter;
|
||||
}
|
||||
|
||||
private void endTextBoxX_Enter(object sender, EventArgs e)
|
||||
{
|
||||
var textBox = sender as TextBox;
|
||||
if (textBox == null) { return; }
|
||||
|
||||
int physPos = PhysPos(textBox, endTextBoxes);
|
||||
|
||||
if (physPos >= 0)
|
||||
{
|
||||
LoadImage(sender, Tst.End, physPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void TestStartEndForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
@ -763,7 +804,7 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
TextBox clickedTextBox = sender as TextBox;
|
||||
if (clickedTextBox != null)
|
||||
{
|
||||
var physPos = PhysPos(clickedTextBox, startTextBoxes);
|
||||
var physPos = PhysPos(clickedTextBox, endTextBoxes);//startTextBoxes);
|
||||
|
||||
if (physPos < 0)
|
||||
{
|
||||
@ -1069,7 +1110,7 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
TextBox clickedTextBox = sender as TextBox;
|
||||
if (clickedTextBox != null)
|
||||
{
|
||||
var physPos = PhysPos(clickedTextBox, startTextBoxes);
|
||||
var physPos = PhysPos(clickedTextBox, endTextBoxes);//startTextBoxes);
|
||||
|
||||
if (physPos < 0)
|
||||
{
|
||||
@ -1134,65 +1175,479 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
/// </summary>
|
||||
/// <param name="startOrEnd">Tst.Start or Tst.End</param>
|
||||
/// <param name="physPos">0..23</param>
|
||||
void LoadImage(object sender, Tst startOrEnd, int physPos)
|
||||
{
|
||||
int wmNr1b = phys2wm[physPos]; /// 0 means no WM
|
||||
if (wmNr1b <= 0) return;
|
||||
void LoadImage( object sender, Tst startOrEnd, int physPos)
|
||||
{
|
||||
if (physPos < 0 || physPos >= phys2wm.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wmNr0b = wmNr1b - 1;
|
||||
int wmNr1b = phys2wm[physPos];
|
||||
|
||||
string imageFileName;
|
||||
if (startOrEnd == Tst.Start && startImages[wmNr0b] != null)
|
||||
imageFileName = startImages[wmNr0b];
|
||||
else if (startOrEnd == Tst.End && endImages[wmNr0b] != null)
|
||||
imageFileName = endImages[wmNr0b];
|
||||
else
|
||||
imageFileName = string.Empty;
|
||||
// Hodnota 0 znamená, že na tejto fyzickej pozícii nie je vodomer.
|
||||
if (wmNr1b <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (imageFileName == lastImageName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
wmNr0b = wmNr1b - 1;
|
||||
|
||||
lastImageName = imageFileName;
|
||||
lastWmNr1 = wmNr1b;
|
||||
string imageFileName = GetBaseImageFileName(
|
||||
startOrEnd,
|
||||
wmNr0b);
|
||||
|
||||
if (string.IsNullOrEmpty(imageFileName) || !File.Exists(imageFileName))
|
||||
{
|
||||
lastImage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Bitmap tempBmp = Image.FromFile(imageFileName) as Bitmap)
|
||||
lastWmNr1 = wmNr1b;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(imageFileName))
|
||||
{
|
||||
ClearImageGroup();
|
||||
LoadImageFile(null);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ak používateľ zvolil iný vodomer alebo Start/End obrázok,
|
||||
* vytvoríme novú skupinu:
|
||||
*
|
||||
* TestAdvance-1-start.bmp
|
||||
* TestAdvance-1-start_img01_....bmp
|
||||
* TestAdvance-1-start_img02_....bmp
|
||||
*/
|
||||
if (!string.Equals(
|
||||
imageFileName,
|
||||
lastImageName,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lastImageName = imageFileName;
|
||||
|
||||
LoadImageGroup(imageFileName);
|
||||
|
||||
// Pôvodný kompatibilný obrázok bude zobrazený ako prvý.
|
||||
currentImageGroupIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Súbory mohli vzniknúť až po prvom otvorení dialógu,
|
||||
* preto skupinu pri opätovnom výbere obnovíme.
|
||||
*/
|
||||
RefreshCurrentImageGroup();
|
||||
}
|
||||
|
||||
if (currentImageGroup.Count == 0)
|
||||
{
|
||||
LoadImageFile(null);
|
||||
return;
|
||||
}
|
||||
|
||||
currentImageGroupIndex = Math.Max(
|
||||
0,
|
||||
Math.Min(
|
||||
currentImageGroupIndex,
|
||||
currentImageGroup.Count - 1));
|
||||
|
||||
LoadImageFile(
|
||||
currentImageGroup[currentImageGroupIndex]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// base name of image file
|
||||
/// </summary>
|
||||
/// <param name="startOrEnd"></param>
|
||||
/// <param name="waterMeterIndex"></param>
|
||||
/// <returns></returns>
|
||||
private string GetBaseImageFileName(
|
||||
Tst startOrEnd,
|
||||
int waterMeterIndex)
|
||||
{
|
||||
if (waterMeterIndex < 0 ||
|
||||
waterMeterIndex >= WaterMetersCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (startOrEnd == Tst.Start)
|
||||
{
|
||||
if (startImages == null ||
|
||||
waterMeterIndex >= startImages.Length)
|
||||
{
|
||||
Rectangle rect;
|
||||
if (tempBmp.Height < tempBmp.Width)
|
||||
return null;
|
||||
}
|
||||
|
||||
return startImages[waterMeterIndex];
|
||||
}
|
||||
|
||||
if (startOrEnd == Tst.End)
|
||||
{
|
||||
if (endImages == null ||
|
||||
waterMeterIndex >= endImages.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return endImages[waterMeterIndex];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// load group of images
|
||||
/// </summary>
|
||||
/// <param name="baseImageFileName"></param>
|
||||
private void LoadImageGroup(
|
||||
string baseImageFileName)
|
||||
{
|
||||
currentImageGroup.Clear();
|
||||
currentImageGroupIndex = 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(baseImageFileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string fullBaseFileName;
|
||||
|
||||
try
|
||||
{
|
||||
fullBaseFileName =
|
||||
Path.GetFullPath(baseImageFileName);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Warn(
|
||||
$"Cannot create image group from invalid path: " +
|
||||
$"file='{baseImageFileName}', " +
|
||||
$"error={exc.Message}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string directory =
|
||||
Path.GetDirectoryName(fullBaseFileName);
|
||||
|
||||
string baseName =
|
||||
Path.GetFileNameWithoutExtension(fullBaseFileName);
|
||||
|
||||
string extension =
|
||||
Path.GetExtension(fullBaseFileName);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(directory) ||
|
||||
string.IsNullOrWhiteSpace(baseName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pôvodný obrázok pridáme ako prvý.
|
||||
*/
|
||||
if (File.Exists(fullBaseFileName))
|
||||
{
|
||||
currentImageGroup.Add(fullBaseFileName);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string searchPattern =
|
||||
$"{baseName}_img*{extension}";
|
||||
|
||||
try
|
||||
{
|
||||
List<string> timedImages =
|
||||
Directory
|
||||
.GetFiles(
|
||||
directory,
|
||||
searchPattern,
|
||||
SearchOption.TopDirectoryOnly)
|
||||
.OrderBy(GetTimedImageIndex)
|
||||
.ThenBy(
|
||||
fileName => fileName,
|
||||
StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (string timedImage in timedImages)
|
||||
{
|
||||
if (!currentImageGroup.Contains(
|
||||
timedImage,
|
||||
StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
int hght = tempBmp.Height;
|
||||
int leftMargin = (tempBmp.Width - hght) / 2;
|
||||
rect = new Rectangle(leftMargin, 0, hght, hght);
|
||||
}
|
||||
else
|
||||
{
|
||||
int hght = tempBmp.Height;
|
||||
int wdth = tempBmp.Width;
|
||||
int margin = (hght - wdth) / 2;
|
||||
rect = new Rectangle(0, margin, wdth, wdth);
|
||||
}
|
||||
//int hght = tempBmp.Height;
|
||||
//int leftMargin = (tempBmp.Width - hght) / 2;
|
||||
//lastImage = new Bitmap(tempBmp.Clone(new Rectangle(leftMargin, 0, hght, hght), tempBmp.PixelFormat));
|
||||
lastImage = new Bitmap(tempBmp.Clone(rect, tempBmp.PixelFormat));
|
||||
if (isZoomed[wmNr0b])
|
||||
{
|
||||
zoomedImage = MakeZoomedImage(lastImage, zoomCenterLoc[wmNr0b]);
|
||||
currentImageGroup.Add(timedImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShowImage();
|
||||
}
|
||||
log.Debug(
|
||||
$"Image group loaded: " +
|
||||
$"baseFile='{fullBaseFileName}', " +
|
||||
$"searchPattern='{searchPattern}', " +
|
||||
$"imagesCount={currentImageGroup.Count}");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Warn(
|
||||
$"Cannot load image group: " +
|
||||
$"baseFile='{fullBaseFileName}', " +
|
||||
$"pattern='{searchPattern}', " +
|
||||
$"error={exc.Message}");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// grouping by time
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <returns></returns>
|
||||
private static int GetTimedImageIndex(
|
||||
string fileName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
string nameWithoutExtension =
|
||||
Path.GetFileNameWithoutExtension(fileName);
|
||||
|
||||
Match match = Regex.Match(
|
||||
nameWithoutExtension,
|
||||
@"_img(?<index>\d+)_",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
int imageIndex;
|
||||
|
||||
return int.TryParse(
|
||||
match.Groups["index"].Value,
|
||||
out imageIndex)
|
||||
? imageIndex
|
||||
: int.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// refreshing current image group
|
||||
/// </summary>
|
||||
private void RefreshCurrentImageGroup()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lastImageName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string currentlySelectedFile =
|
||||
currentImageGroupIndex >= 0 &&
|
||||
currentImageGroupIndex < currentImageGroup.Count
|
||||
? currentImageGroup[currentImageGroupIndex]
|
||||
: null;
|
||||
|
||||
LoadImageGroup(lastImageName);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentlySelectedFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int restoredIndex =
|
||||
currentImageGroup.FindIndex(
|
||||
fileName =>
|
||||
string.Equals(
|
||||
fileName,
|
||||
currentlySelectedFile,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (restoredIndex >= 0)
|
||||
{
|
||||
currentImageGroupIndex = restoredIndex;
|
||||
}
|
||||
}
|
||||
/// --- picture load ---
|
||||
private void LoadImageFile(
|
||||
string imageFileName)
|
||||
{
|
||||
DisposeLoadedImages();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(imageFileName) ||
|
||||
!File.Exists(imageFileName))
|
||||
{
|
||||
log.Debug(
|
||||
$"Image cannot be displayed because the file does not exist: " +
|
||||
$"file='{imageFileName}'");
|
||||
|
||||
ShowImage();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var tempBmp =
|
||||
new Bitmap(imageFileName))
|
||||
{
|
||||
Rectangle rect =
|
||||
CreateSquareCropRectangle(tempBmp);
|
||||
|
||||
using (Bitmap croppedImage =
|
||||
tempBmp.Clone(
|
||||
rect,
|
||||
tempBmp.PixelFormat))
|
||||
{
|
||||
lastImage =
|
||||
new Bitmap(croppedImage);
|
||||
}
|
||||
}
|
||||
|
||||
if (isZoomed[wmNr0b])
|
||||
{
|
||||
zoomedImage =
|
||||
MakeZoomedImage(
|
||||
lastImage,
|
||||
zoomCenterLoc[wmNr0b]);
|
||||
}
|
||||
|
||||
log.Debug(
|
||||
$"Image displayed from group: " +
|
||||
$"waterMeter={lastWmNr1}, " +
|
||||
$"groupIndex={currentImageGroupIndex + 1}/" +
|
||||
$"{currentImageGroup.Count}, " +
|
||||
$"file='{imageFileName}'");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Error(
|
||||
$"Cannot load image: " +
|
||||
$"file='{imageFileName}', " +
|
||||
$"error={exc.Message}",
|
||||
exc);
|
||||
|
||||
DisposeLoadedImages();
|
||||
}
|
||||
|
||||
ShowImage();
|
||||
}
|
||||
|
||||
/// --- block picture Crop help method ---
|
||||
private static Rectangle CreateSquareCropRectangle( Bitmap bitmap)
|
||||
{
|
||||
int size = Math.Min( bitmap.Width, bitmap.Height);
|
||||
int x = (bitmap.Width - size) / 2;
|
||||
int y = (bitmap.Height - size) / 2;
|
||||
|
||||
return new Rectangle( x, y, size, size);
|
||||
}
|
||||
|
||||
// --- block release old pictures ---
|
||||
private void DisposeLoadedImages()
|
||||
{
|
||||
if (lastImage != null)
|
||||
{
|
||||
lastImage.Dispose();
|
||||
lastImage = null;
|
||||
}
|
||||
|
||||
if (zoomedImage != null)
|
||||
{
|
||||
zoomedImage.Dispose();
|
||||
zoomedImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearImageGroup()
|
||||
{
|
||||
currentImageGroup.Clear();
|
||||
currentImageGroupIndex = 0;
|
||||
lastImageName = null;
|
||||
}
|
||||
|
||||
// --- block picture exchange ---
|
||||
private void ShowPreviousImageFromGroup()
|
||||
{
|
||||
ShowImageFromGroup(-1);
|
||||
}
|
||||
|
||||
private void ShowNextImageFromGroup()
|
||||
{
|
||||
ShowImageFromGroup(1);
|
||||
}
|
||||
|
||||
private void ShowImageFromGroup(
|
||||
int direction)
|
||||
{
|
||||
if (direction == 0 ||
|
||||
currentImageGroup.Count <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int newIndex =
|
||||
currentImageGroupIndex + direction;
|
||||
|
||||
/*
|
||||
* Cyklické prepínanie:
|
||||
*
|
||||
* posledný -> prvý
|
||||
* prvý -> posledný
|
||||
*/
|
||||
if (newIndex < 0)
|
||||
{
|
||||
newIndex =
|
||||
currentImageGroup.Count - 1;
|
||||
}
|
||||
else if (newIndex >= currentImageGroup.Count)
|
||||
{
|
||||
newIndex = 0;
|
||||
}
|
||||
|
||||
currentImageGroupIndex = newIndex;
|
||||
|
||||
LoadImageFile(
|
||||
currentImageGroup[currentImageGroupIndex]);
|
||||
}
|
||||
|
||||
|
||||
// --- mouse events ---
|
||||
|
||||
private void pictureBox1_MouseEnter(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
mouseOverPictureBox = true;
|
||||
|
||||
/*
|
||||
* PictureBox štandardne nemusí mať focus.
|
||||
* Focus pomôže zachytiť klávesy aj koliesko.
|
||||
*/
|
||||
pictureBox1.Focus();
|
||||
}
|
||||
|
||||
private void pictureBox1_MouseLeave(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
mouseOverPictureBox = false;
|
||||
}
|
||||
|
||||
private void pictureBox1_MouseWheel(
|
||||
object sender,
|
||||
MouseEventArgs e)
|
||||
{
|
||||
if (!mouseOverPictureBox ||
|
||||
currentImageGroup.Count <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Delta > 0)
|
||||
{
|
||||
ShowPreviousImageFromGroup();
|
||||
}
|
||||
else if (e.Delta < 0)
|
||||
{
|
||||
ShowNextImageFromGroup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left click = Zoom, Right click = Unzoom
|
||||
@ -1276,7 +1731,9 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
{
|
||||
if (lastImage == null)
|
||||
{
|
||||
Image previousImage = pictureBox1.Image;
|
||||
pictureBox1.Image = new Bitmap(TBF.Properties.Resources.Empty);
|
||||
previousImage?.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1312,10 +1769,108 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
Brush brush = (brightness > 160) ? darkBrush : lightBrush;
|
||||
|
||||
grph.DrawString(lastWmNr1.ToString(), font, brush, margin, margin);
|
||||
|
||||
DrawImageInfo(grph, tempBmp);
|
||||
}
|
||||
|
||||
Image oldPictureBoxImage = pictureBox1.Image;
|
||||
pictureBox1.Image = tempBmp;
|
||||
oldPictureBoxImage?.Dispose();
|
||||
}
|
||||
|
||||
private void DrawImageInfo( Graphics graphics, Bitmap bitmap)
|
||||
{
|
||||
if (graphics == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(graphics));
|
||||
}
|
||||
|
||||
if (bitmap == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bitmap));
|
||||
}
|
||||
|
||||
string currentImageFileName = GetCurrentImageFileName();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentImageFileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string fileName = Path.GetFileName(currentImageFileName);
|
||||
|
||||
string displayText = fileName;
|
||||
|
||||
Match match = Regex.Match( fileName, @"_img(?<index>\d+)_(?<date>\d{8})_(?<time>\d{6})_(?<ms>\d{3})", RegexOptions.IgnoreCase);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
string imageIndex = match.Groups["index"].Value;
|
||||
|
||||
string date = match.Groups["date"].Value;
|
||||
|
||||
string time = match.Groups["time"].Value;
|
||||
|
||||
string milliseconds = match.Groups["ms"].Value;
|
||||
|
||||
string formattedDate =
|
||||
$"{date.Substring(6, 2)}." +
|
||||
$"{date.Substring(4, 2)}." +
|
||||
$"{date.Substring(0, 4)}";
|
||||
|
||||
string formattedTime =
|
||||
$"{time.Substring(0, 2)}:" +
|
||||
$"{time.Substring(2, 2)}:" +
|
||||
$"{time.Substring(4, 2)}." +
|
||||
milliseconds;
|
||||
|
||||
string baseFileName =
|
||||
Regex.Replace(
|
||||
fileName,
|
||||
@"_img\d+_\d{8}_\d{6}_\d{3}",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
displayText =
|
||||
$"{baseFileName} [img{imageIndex}]" +
|
||||
Environment.NewLine +
|
||||
$"{formattedDate} {formattedTime}";
|
||||
}
|
||||
|
||||
using (Font font = new Font( "Arial", isZoomed[wmNr0b] ? 8 : 10, FontStyle.Bold))
|
||||
{
|
||||
SizeF textSize =
|
||||
graphics.MeasureString(
|
||||
displayText,
|
||||
font);
|
||||
|
||||
float margin = 5;
|
||||
|
||||
float x = bitmap.Width - textSize.Width - margin;
|
||||
float y = bitmap.Height - textSize.Height - margin;
|
||||
|
||||
graphics.DrawString( displayText, font, Brushes.Black, x + 1, y + 1);
|
||||
|
||||
graphics.DrawString( displayText, font, Brushes.White, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCurrentImageFileName()
|
||||
{
|
||||
if (currentImageGroup == null ||
|
||||
currentImageGroup.Count == 0)
|
||||
{
|
||||
return lastImageName;
|
||||
}
|
||||
|
||||
if (currentImageGroupIndex < 0 ||
|
||||
currentImageGroupIndex >= currentImageGroup.Count)
|
||||
{
|
||||
return lastImageName;
|
||||
}
|
||||
|
||||
return currentImageGroup[currentImageGroupIndex];
|
||||
}
|
||||
|
||||
|
||||
private void checkBoxEnableStartBox_CheckStateChanged(object sender, EventArgs e)
|
||||
@ -1345,5 +1900,35 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Page Down/Up evenst
|
||||
|
||||
protected override bool ProcessCmdKey(
|
||||
ref Message msg,
|
||||
Keys keyData)
|
||||
{
|
||||
if (mouseOverPictureBox &&
|
||||
currentImageGroup.Count > 1)
|
||||
{
|
||||
if (keyData == Keys.PageUp)
|
||||
{
|
||||
ShowPreviousImageFromGroup();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyData == Keys.PageDown)
|
||||
{
|
||||
ShowNextImageFromGroup();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return base.ProcessCmdKey(
|
||||
ref msg,
|
||||
keyData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ using System.Windows.Forms;
|
||||
using log4net;
|
||||
using Renci.SshNet;
|
||||
using Common;
|
||||
using Config.Entities;
|
||||
using FluentNHibernate.Conventions;
|
||||
using Newtonsoft.Json;
|
||||
using TBF.Rig.Network.Telnet;
|
||||
@ -69,10 +70,20 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
{
|
||||
get {
|
||||
if (roiConfiguration!=null) {return roiConfiguration;}
|
||||
else { roiConfiguration = GetFirstRoiChild(); return roiConfiguration; }
|
||||
else { roiConfiguration = GetFirstRoiCfgChild(); return roiConfiguration; }
|
||||
}
|
||||
set { if (roiConfiguration == null) roiConfiguration = value;}
|
||||
}
|
||||
|
||||
private RoiForFixedStartCJMS11.Roi _roi;
|
||||
public RoiForFixedStartCJMS11.Roi CamRoi
|
||||
{
|
||||
get {
|
||||
if (_roi!=null) {return _roi;}
|
||||
else { _roi = GetFirstRoiChild(); return _roi; }
|
||||
}
|
||||
set { if (_roi == null) _roi = value;}
|
||||
}
|
||||
|
||||
public IPAddress IPAddress { get { return ipAddress; } }
|
||||
IPAddress ipAddress;
|
||||
@ -154,6 +165,14 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
private bool stopSaveImageListenerFlag;
|
||||
public bool IsSaveImageListenerThreadAlive { get { return saveImageListenerThread != null && saveImageListenerThread.IsAlive; } }
|
||||
|
||||
/// <summary>
|
||||
/// Send images part
|
||||
/// </summary>
|
||||
private Task<IReadOnlyList<TimedCameraImage>> sendImagesTask;
|
||||
private CancellationTokenSource sendImagesCts;
|
||||
|
||||
public bool IsSendImagesInProgress => sendImagesTask != null && !sendImagesTask.IsCompleted;
|
||||
|
||||
///
|
||||
/// ROI related parameters
|
||||
///
|
||||
@ -482,7 +501,6 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
client.Close();
|
||||
|
||||
throw new TimeoutException( $"Camera connection timeout after 1 second: {ipAddressParam}:{portParam}");
|
||||
}
|
||||
|
||||
@ -928,13 +946,13 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
}
|
||||
}
|
||||
|
||||
private RoiCfg GetFirstRoiChild()
|
||||
private RoiCfg GetFirstRoiCfgChild()
|
||||
{
|
||||
/// 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)
|
||||
@ -943,25 +961,136 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
{
|
||||
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 RoiForFixedStartCJMS11.Roi GetFirstRoiChild()
|
||||
{
|
||||
using (var session = TBF.DB.CreateSession(DBKind.Config))
|
||||
{
|
||||
var componentEntities = session
|
||||
.QueryOver<Config.Entities.Component>()
|
||||
.OrderBy(x => x.ItemNr)
|
||||
.Asc
|
||||
.List();
|
||||
|
||||
IList<IComponent> components =
|
||||
Rig.TbfComponents.LoadComponentsFromDB(componentEntities);
|
||||
|
||||
var roi = components
|
||||
.OfType<RoiForFixedStartCJMS11.Roi>()
|
||||
.FirstOrDefault(x =>
|
||||
x.Cfg != null &&
|
||||
string.Equals(
|
||||
x.Cfg.ParentName,
|
||||
Name,
|
||||
StringComparison.Ordinal));
|
||||
|
||||
if (roi == null)
|
||||
{
|
||||
log.WarnFormat(
|
||||
"No ROI child was found for camera '{0}'.",
|
||||
Name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var roiCfg = roi.Cfg as RoiForFixedStartCJMS11.RoiCfg;
|
||||
|
||||
if (roiCfg == null)
|
||||
{
|
||||
log.WarnFormat(
|
||||
"ROI '{0}' has an invalid configuration type.",
|
||||
roi.Name);
|
||||
|
||||
return roi;
|
||||
}
|
||||
|
||||
LoadProcedureParams(session, roiCfg);
|
||||
|
||||
return roi;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadProcedureParams(
|
||||
NHibernate.ISession session,
|
||||
RoiForFixedStartCJMS11.RoiCfg roiCfg)
|
||||
{
|
||||
if (roiCfg == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentProcedure = StateMachine.Procedure;
|
||||
|
||||
if (currentProcedure == null)
|
||||
{
|
||||
log.WarnFormat(
|
||||
"Cannot load procedure parameters for ROI '{0}': " +
|
||||
"current procedure is null.",
|
||||
roiCfg.Name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var dbParams = session
|
||||
.QueryOver<ComponentProcedure>()
|
||||
.Where(x => x.CmpntName == roiCfg.Name)
|
||||
.And(x => x.Procedure == currentProcedure)
|
||||
.SingleOrDefault();
|
||||
|
||||
if (dbParams == null)
|
||||
{
|
||||
log.WarnFormat(
|
||||
"No procedure parameters found for ROI '{0}', procedure '{1}'. " +
|
||||
"Using default values.",
|
||||
roiCfg.Name,
|
||||
currentProcedure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (roiCfg.ProcParams == null)
|
||||
{
|
||||
roiCfg.ProcParams = new ProcedureParams();
|
||||
}
|
||||
|
||||
if (!roiCfg.ProcParams.UpdateFromDbEntity(dbParams))
|
||||
{
|
||||
log.WarnFormat(
|
||||
"Failed to deserialize procedure parameters for ROI '{0}', " +
|
||||
"procedure '{1}'.",
|
||||
roiCfg.Name,
|
||||
currentProcedure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log.DebugFormat(
|
||||
"Loaded ROI procedure parameters: ROI='{0}', procedure='{1}', " +
|
||||
"RoiPicCount={2}, RoiDeltaTimeMs={3}.",
|
||||
roiCfg.Name,
|
||||
currentProcedure,
|
||||
roiCfg.ProcParams.RoiPicCount,
|
||||
roiCfg.ProcParams.RoiDeltaTimeMs);
|
||||
}
|
||||
|
||||
private string getConfigMessage()
|
||||
{
|
||||
//CameraCfg
|
||||
@ -1087,40 +1216,59 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveImageToFile(Image imageToSave, string fileNamePath)
|
||||
public void SaveImageToFile(
|
||||
Image imageToSave,
|
||||
string fileNamePath)
|
||||
{
|
||||
if (imageToSave == null)
|
||||
{
|
||||
log.Error("Nothing to save in SaveImageToFile");
|
||||
return; // nothing to save
|
||||
throw new ArgumentNullException(nameof(imageToSave));
|
||||
}
|
||||
|
||||
if (fileNamePath == null || fileNamePath.IsEmpty())
|
||||
if (string.IsNullOrWhiteSpace(fileNamePath))
|
||||
{
|
||||
log.Error("Path to save missing!");
|
||||
return; // nothing to save
|
||||
throw new ArgumentException(
|
||||
"Path to save is missing.",
|
||||
nameof(fileNamePath));
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
string filePath = fileNamePath;
|
||||
|
||||
string path = Path.GetDirectoryName(filePath);
|
||||
if (path != null && path.IsNotEmpty())
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
string directory =
|
||||
Path.GetDirectoryName(fileNamePath);
|
||||
|
||||
using (var clonedImage = CloneImageToBitmap(imageToSave))
|
||||
{
|
||||
clonedImage.Save(filePath, ImageFormat.Png); // encoder, encoderParams);
|
||||
}
|
||||
log.Debug("Image saved to: " + filePath);
|
||||
}catch(Exception exc)
|
||||
if (!string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
log.Error("Error saving image: " + exc.Message);
|
||||
throw;
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
ImageFormat format =
|
||||
GetImageFormatFromFileName(fileNamePath);
|
||||
|
||||
using (var clonedImage = CloneImageToBitmap(imageToSave))
|
||||
{
|
||||
clonedImage.Save(fileNamePath, format);
|
||||
}
|
||||
|
||||
log.Debug("Image saved to: " + fileNamePath);
|
||||
}
|
||||
|
||||
private static ImageFormat GetImageFormatFromFileName(
|
||||
string fileName)
|
||||
{
|
||||
string extension =
|
||||
Path.GetExtension(fileName)?.ToLowerInvariant();
|
||||
|
||||
switch (extension)
|
||||
{
|
||||
case ".bmp":
|
||||
return ImageFormat.Bmp;
|
||||
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
return ImageFormat.Jpeg;
|
||||
|
||||
case ".png":
|
||||
default:
|
||||
return ImageFormat.Png;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1555,6 +1703,7 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
liveCts?.Cancel();
|
||||
grabCts?.Cancel();
|
||||
saveCts?.Cancel();
|
||||
sendImagesCts?.Cancel();
|
||||
|
||||
stopLiveStreamListenerFlag = true;
|
||||
stopGrabImageListenerFlag = true;
|
||||
@ -2773,45 +2922,48 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
/// This method is called from within this class when
|
||||
/// a prompt string was received from the telnet server.
|
||||
/// </summary>
|
||||
public static void OnImagesReceived(object sender, int idxImage, Image image, ImageRotation imageRotation, int iImageIdx = 0, int imageCount = 1)
|
||||
public static void OnImagesReceived(
|
||||
object sender,
|
||||
int cameraIndex,
|
||||
Image image,
|
||||
int imageIndex,
|
||||
int imageCount,
|
||||
DateTimeOffset? frameTime)
|
||||
{
|
||||
|
||||
log.Info($"Images received from camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}");
|
||||
|
||||
if (null != ImageCameraHandler)
|
||||
if (image == null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
log.Info( $"Images received from camera: {cameraIndex}, imageIndex={imageIndex}, " +
|
||||
$"imageCount={imageCount}, frameTime={frameTime:O}");
|
||||
|
||||
if (null != ImagesCameraHandler)
|
||||
EventHandler<PromptReceivedImagesEventArgs> handler = ImagesCameraHandler;
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
image.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
handler( sender,
|
||||
new PromptReceivedImagesEventArgs(
|
||||
cameraIndex,
|
||||
imageIndex,
|
||||
imageCount,
|
||||
image,
|
||||
frameTime));
|
||||
|
||||
log.Info( $"ImagesCameraHandler reached camera: {cameraIndex}, imageIndex={imageIndex}, " +
|
||||
$"imageCount={imageCount}");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Error( $"ImagesCameraHandler failed: cameraIdx={cameraIndex}, imageIndex={imageIndex}, " +
|
||||
$"error={exc.Message}", exc);
|
||||
image.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2934,6 +3086,517 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
||||
|
||||
return await readTask;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------ SEND PART --------------------------------------
|
||||
|
||||
public Task<IReadOnlyList<TimedCameraImage>> StartSendImagesPreviewAsync(
|
||||
int count,
|
||||
int delayMs,
|
||||
string taskId)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(count),
|
||||
"Image count must be greater than zero.");
|
||||
}
|
||||
|
||||
if (delayMs < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(delayMs),
|
||||
"Delay cannot be negative.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(taskId))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Task ID is required.",
|
||||
nameof(taskId));
|
||||
}
|
||||
|
||||
if (sendImagesTask != null &&
|
||||
!sendImagesTask.IsCompleted)
|
||||
{
|
||||
log.Warn(
|
||||
$"Send images preview already running: " +
|
||||
$"cameraIdx={cameraIdx}, taskId={taskId}");
|
||||
|
||||
return sendImagesTask;
|
||||
}
|
||||
|
||||
sendImagesCts?.Cancel();
|
||||
sendImagesCts?.Dispose();
|
||||
|
||||
sendImagesCts =
|
||||
new CancellationTokenSource();
|
||||
|
||||
sendImagesTask =
|
||||
SendImagesPreviewListenerAsync(
|
||||
count,
|
||||
delayMs,
|
||||
taskId,
|
||||
sendImagesCts.Token);
|
||||
|
||||
return sendImagesTask;
|
||||
}
|
||||
|
||||
public void CancelSendImagesPreview()
|
||||
{
|
||||
try
|
||||
{
|
||||
sendImagesCts?.Cancel();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<TimedCameraImage>> SendImagesPreviewListenerAsync(
|
||||
int requestedCount,
|
||||
int delayMs,
|
||||
string taskId,
|
||||
CancellationToken ct)
|
||||
{
|
||||
bool lockAcquired = false;
|
||||
TcpClient client = null;
|
||||
NetworkStream stream = null;
|
||||
CameraLineReader reader = null;
|
||||
|
||||
var imagesByIndex = new Dictionary<int, TimedCameraImage>();
|
||||
|
||||
try
|
||||
{
|
||||
await cameraOperationLock.WaitAsync(ct).ConfigureAwait(false);
|
||||
|
||||
lockAcquired = true;
|
||||
|
||||
client = await ConnectCameraAsync( timeoutMs: 5000, ct: ct).ConfigureAwait(false);
|
||||
stream = client.GetStream();
|
||||
|
||||
reader = new CameraLineReader( stream, 64 * 1024);
|
||||
|
||||
/*
|
||||
* Kamera musí byť pripravená.
|
||||
*/
|
||||
JmsPacket prepareAnswer = await SendCommandAndWaitToAnswerAsync(
|
||||
stream,
|
||||
reader,
|
||||
CommandM.prepare_camera_,
|
||||
null,
|
||||
3000,
|
||||
ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
bool prepared = prepareAnswer != null &&
|
||||
prepareAnswer.JmsMessage != null &&
|
||||
prepareAnswer.JmsMessage.Status == MessageStatus.ACK;
|
||||
|
||||
bool alreadyOpened =
|
||||
prepareAnswer != null &&
|
||||
prepareAnswer.JmsMessage != null &&
|
||||
prepareAnswer.JmsMessage.Status == MessageStatus.NACK &&
|
||||
!string.IsNullOrEmpty( prepareAnswer.JmsMessage.Payload) &&
|
||||
prepareAnswer.JmsMessage.Payload.Contains( "OPENED");
|
||||
|
||||
if (!prepared && !alreadyOpened)
|
||||
{
|
||||
throw new IOException( $"Prepare camera for send failed: cameraIdx={cameraIdx}, " +
|
||||
$"answer={FormatPacketForLog(prepareAnswer)}");
|
||||
}
|
||||
|
||||
var request = new SendImagesRequest { Count = requestedCount, Delay = delayMs, TaskId = taskId };
|
||||
string requestPayload = JsonConvert.SerializeObject( request, Formatting.None);
|
||||
|
||||
log.Debug( $"Starting send images preview: cameraIdx={cameraIdx}, count={requestedCount}, " +
|
||||
$"delay={delayMs}, taskId={taskId}");
|
||||
|
||||
/*
|
||||
* Pri send_ neposielame cez
|
||||
* SendCommandAndWaitToAnswerAsync(),
|
||||
* pretože očakávame viac odpovedí.
|
||||
*/
|
||||
await SendCommandAsync(
|
||||
stream,
|
||||
CommandM.send_,
|
||||
ct,
|
||||
requestPayload)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
/*
|
||||
* Timeout musí zahŕňať plánovaný čas snímania.
|
||||
*
|
||||
* count=5, delay=2050:
|
||||
* približne 4 * 2050 ms medzi prvým a posledným frame
|
||||
* plus rezerva na prenos Base64 obrázkov.
|
||||
*/
|
||||
int totalTimeoutMs = CalculateSendTimeout( requestedCount, delayMs);
|
||||
|
||||
using (var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
|
||||
{
|
||||
timeoutCts.CancelAfter(totalTimeoutMs);
|
||||
|
||||
while (imagesByIndex.Count < requestedCount)
|
||||
{
|
||||
timeoutCts.Token.ThrowIfCancellationRequested();
|
||||
|
||||
string response = await reader.ReadLineAsync( 10000, timeoutCts.Token).ConfigureAwait(false);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(response))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JmsPacket packet;
|
||||
|
||||
try
|
||||
{
|
||||
packet = new JmsPacket(response);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Warn( $"Cannot parse send response: cameraIdx={cameraIdx}, error={exc.Message}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet.JmsMessage == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet.JmsMessage.Command != CommandM.send_)
|
||||
{
|
||||
log.Debug( $"Unexpected packet during send: cameraIdx={cameraIdx}, command={packet.JmsMessage.Command}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet.JmsMessage.Status != MessageStatus.ACK)
|
||||
{
|
||||
log.Warn( $"Send image returned NACK: cameraIdx={cameraIdx}, payload={packet.JmsMessage.Payload}");
|
||||
continue;
|
||||
}
|
||||
|
||||
string payload = packet.JmsMessage.Payload;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Posledný sumarizačný packet nemá
|
||||
* pictureBase64 ani workerId.
|
||||
*/
|
||||
if (!PayloadContainsImage(payload))
|
||||
{
|
||||
TryLogSendSummary( payload, cameraIdx);
|
||||
continue;
|
||||
}
|
||||
|
||||
SendImagePayload imagePayload;
|
||||
|
||||
try
|
||||
{
|
||||
imagePayload = JsonConvert.DeserializeObject<SendImagePayload>(payload);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Warn( $"Cannot deserialize send image: cameraIdx={cameraIdx}, error={exc.Message}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (imagePayload == null || string.IsNullOrWhiteSpace( imagePayload.PictureBase64))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.Equals( imagePayload.Status, "SUCCESSFUL", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
log.Warn( $"Send image status is not successful: cameraIdx={cameraIdx}, " +
|
||||
$"workerId={imagePayload.WorkerId}, status={imagePayload.Status}");
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* workerId je podľa logu 1..count.
|
||||
*/
|
||||
int imageIndex = imagePayload.WorkerId;
|
||||
|
||||
if (imageIndex <= 0)
|
||||
{
|
||||
log.Warn( $"Invalid send image index: cameraIdx={cameraIdx}, workerId={imageIndex}");
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicitný packet ignorujeme.
|
||||
*/
|
||||
if (imagesByIndex.ContainsKey( imageIndex))
|
||||
{
|
||||
log.Warn( $"Duplicate send image: cameraIdx={cameraIdx}, workerId={imageIndex}");
|
||||
continue;
|
||||
}
|
||||
|
||||
Image image = DecodeBase64Image( imagePayload.PictureBase64);
|
||||
|
||||
if (!IsValidGrabbedImage(image))
|
||||
{
|
||||
image?.Dispose();
|
||||
|
||||
log.Warn(
|
||||
$"Invalid send image: " +
|
||||
$"cameraIdx={cameraIdx}, " +
|
||||
$"workerId={imageIndex}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
DateTimeOffset? frameTime =
|
||||
ParseFrameTime(
|
||||
imagePayload.FrameTime);
|
||||
|
||||
/*
|
||||
* Úpravy obrázka dokončíme ešte predtým,
|
||||
* než ho odovzdáme UI.
|
||||
*/
|
||||
ImageUtils.Rotate(
|
||||
image,
|
||||
RoiConfiguration.ImageRotation);
|
||||
|
||||
DrawOverlay(image);
|
||||
|
||||
var timedImage =
|
||||
new TimedCameraImage
|
||||
{
|
||||
Index = imageIndex,
|
||||
TaskId =
|
||||
imagePayload.TaskId,
|
||||
FrameTime = frameTime,
|
||||
FrameTimeMillis =
|
||||
imagePayload.FrameTimeMillis,
|
||||
FrameGap =
|
||||
imagePayload.FrameGap,
|
||||
Image = image
|
||||
};
|
||||
|
||||
imagesByIndex.Add(
|
||||
imageIndex,
|
||||
timedImage);
|
||||
|
||||
/*
|
||||
* UI dostane klon.
|
||||
* Originál zostáva vo výslednom zozname.
|
||||
*/
|
||||
Image imageForUi = CloneImageToBitmap(image);
|
||||
|
||||
OnImagesReceived(
|
||||
this,
|
||||
cameraIdx,
|
||||
imageForUi,
|
||||
imageIndex,
|
||||
requestedCount,
|
||||
frameTime);
|
||||
|
||||
log.Debug( $"Send image received: cameraIdx={cameraIdx}, workerId={imageIndex}, " +
|
||||
$"received={imagesByIndex.Count}/{requestedCount}, frameTime={frameTime:O}, " +
|
||||
$"size={image.Width}x{image.Height}");
|
||||
}
|
||||
}
|
||||
|
||||
List<TimedCameraImage> result =
|
||||
imagesByIndex
|
||||
.OrderBy(x => x.Key)
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
|
||||
log.Debug(
|
||||
$"Send images preview completed: " +
|
||||
$"cameraIdx={cameraIdx}, " +
|
||||
$"received={result.Count}, " +
|
||||
$"taskId={taskId}");
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
foreach (TimedCameraImage image
|
||||
in imagesByIndex.Values)
|
||||
{
|
||||
image.Image?.Dispose();
|
||||
}
|
||||
|
||||
log.Debug(
|
||||
$"Send images preview cancelled: " +
|
||||
$"cameraIdx={cameraIdx}, " +
|
||||
$"taskId={taskId}");
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
foreach (TimedCameraImage image
|
||||
in imagesByIndex.Values)
|
||||
{
|
||||
image.Image?.Dispose();
|
||||
}
|
||||
|
||||
log.Error(
|
||||
$"Send images preview failed: " +
|
||||
$"cameraIdx={cameraIdx}, " +
|
||||
$"taskId={taskId}, " +
|
||||
$"error={exc.Message}",
|
||||
exc);
|
||||
|
||||
UiBridge.Bridge.OnImage(
|
||||
cameraIdx,
|
||||
null);
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null &&
|
||||
reader != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await TryCloseCameraAsync(
|
||||
stream,
|
||||
reader)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Warn(
|
||||
$"Send preview cleanup failed: " +
|
||||
$"cameraIdx={cameraIdx}, " +
|
||||
$"error={exc.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
stream?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
client?.Close();
|
||||
client?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (lockAcquired)
|
||||
{
|
||||
cameraOperationLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int CalculateSendTimeout(
|
||||
int count,
|
||||
int delayMs)
|
||||
{
|
||||
/*
|
||||
* Medzi count obrázkami je count - 1 intervalov.
|
||||
* Pridáme 15 sekúnd rezervu na capture,
|
||||
* Base64 a sieťový prenos.
|
||||
*/
|
||||
long captureDuration =
|
||||
(long)Math.Max(0, count - 1) *
|
||||
delayMs;
|
||||
|
||||
long timeout =
|
||||
captureDuration + 15000L;
|
||||
|
||||
return timeout > int.MaxValue
|
||||
? int.MaxValue
|
||||
: (int)timeout;
|
||||
}
|
||||
|
||||
private static Image DecodeBase64Image( string pictureBase64)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace( pictureBase64))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
byte[] imageBytes = Convert.FromBase64String( pictureBase64);
|
||||
|
||||
using (var stream = new MemoryStream(imageBytes))
|
||||
using (Image decoded = Image.FromStream( stream, useEmbeddedColorManagement: false, validateImageData: true))
|
||||
{
|
||||
/*
|
||||
* Bitmap odpojí výsledok od MemoryStream.
|
||||
*/
|
||||
return new Bitmap(decoded);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PayloadContainsImage( string payload)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(payload))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return payload.IndexOf( "\"pictureBase64\"", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
private static DateTimeOffset? ParseFrameTime( string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeOffset parsed;
|
||||
|
||||
if (DateTimeOffset.TryParse(
|
||||
value,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
System.Globalization.DateTimeStyles.RoundtripKind,
|
||||
out parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void TryLogSendSummary( string payload, int cameraIdx)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendSummaryPayload summary = JsonConvert.DeserializeObject<SendSummaryPayload>(payload);
|
||||
|
||||
if (summary == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log.Debug( $"Send summary received: cameraIdx={cameraIdx}, picCount={summary.PictureCount}, " +
|
||||
$"delay={summary.Delay}, timeStamp={summary.TimeStamp}");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
log.Debug( $"Payload is not a send summary: cameraIdx={cameraIdx}, error={exc.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
37
TBF/Rig/Network/Camera/CJMS11/POJO/SendImagePayload.cs
Normal file
37
TBF/Rig/Network/Camera/CJMS11/POJO/SendImagePayload.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CJMS11.POJO
|
||||
{
|
||||
public class SendImagePayload
|
||||
{
|
||||
[JsonProperty("frameGap")]
|
||||
public int FrameGap { get; set; }
|
||||
|
||||
[JsonProperty("frameTime")]
|
||||
public string FrameTime { get; set; }
|
||||
|
||||
[JsonProperty("frameTimeMillis")]
|
||||
public long FrameTimeMillis { get; set; }
|
||||
|
||||
[JsonProperty("pictureBase64")]
|
||||
public string PictureBase64 { get; set; }
|
||||
|
||||
[JsonProperty("picturesComplete")]
|
||||
public int PicturesComplete { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("taskId")]
|
||||
public string TaskId { get; set; }
|
||||
|
||||
[JsonProperty("taskPath")]
|
||||
public string TaskPath { get; set; }
|
||||
|
||||
[JsonProperty("taskTime")]
|
||||
public string TaskTime { get; set; }
|
||||
|
||||
[JsonProperty("workerId")]
|
||||
public int WorkerId { get; set; }
|
||||
}
|
||||
}
|
||||
16
TBF/Rig/Network/Camera/CJMS11/POJO/SendImagesRequest.cs
Normal file
16
TBF/Rig/Network/Camera/CJMS11/POJO/SendImagesRequest.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CJMS11.POJO
|
||||
{
|
||||
public class SendImagesRequest
|
||||
{
|
||||
[JsonProperty("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[JsonProperty("delay")]
|
||||
public int Delay { get; set; }
|
||||
|
||||
[JsonProperty("taskId")]
|
||||
public string TaskId { get; set; }
|
||||
}
|
||||
}
|
||||
16
TBF/Rig/Network/Camera/CJMS11/POJO/SendSummaryPayload.cs
Normal file
16
TBF/Rig/Network/Camera/CJMS11/POJO/SendSummaryPayload.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CJMS11.POJO
|
||||
{
|
||||
public class SendSummaryPayload
|
||||
{
|
||||
[JsonProperty("delay")]
|
||||
public int Delay { get; set; }
|
||||
|
||||
[JsonProperty("pic_count")]
|
||||
public int PictureCount { get; set; }
|
||||
|
||||
[JsonProperty("timeStamp")]
|
||||
public string TimeStamp { get; set; }
|
||||
}
|
||||
}
|
||||
20
TBF/Rig/Network/Camera/CJMS11/POJO/TimedCameraImage.cs
Normal file
20
TBF/Rig/Network/Camera/CJMS11/POJO/TimedCameraImage.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace TBF.Rig.Network.Camera.CJMS11.POJO
|
||||
{
|
||||
public class TimedCameraImage
|
||||
{
|
||||
public int Index { get; set; }
|
||||
|
||||
public string TaskId { get; set; }
|
||||
|
||||
public DateTimeOffset? FrameTime { get; set; }
|
||||
|
||||
public long FrameTimeMillis { get; set; }
|
||||
|
||||
public int FrameGap { get; set; }
|
||||
|
||||
public Image Image { get; set; }
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,12 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
/// </summary>
|
||||
/// <description>delta time for send parameter 500ms is minimum</description>
|
||||
public int RoiDeltaTimeMs;
|
||||
|
||||
/// <summary>
|
||||
/// count of picture for send > 1 / grabImage == 0|1 parameter - switch method
|
||||
/// </summary>
|
||||
/// <description>count of picture for send > 1 / grabImage == 0|1 parameter - switch method</description>
|
||||
public int RoiPicCount;
|
||||
|
||||
string[] paramNames = new string[]
|
||||
{
|
||||
@ -35,11 +41,7 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
Strings.DelaySendImage,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// count of picture for send > 1 / grabImage == 0|1 parameter - switch method
|
||||
/// </summary>
|
||||
/// <description>count of picture for send > 1 / grabImage == 0|1 parameter - switch method</description>
|
||||
public int RoiPicCount;
|
||||
|
||||
|
||||
public override void InitializeAll()
|
||||
{
|
||||
@ -68,8 +70,8 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
switch (i)
|
||||
{
|
||||
case 0: PulsesPerLtr = Utils.ParseUDouble(strValue); return CfgUpdateFlags.None;
|
||||
case 1: PulsesPerLtr = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||
case 2: PulsesPerLtr = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||
case 1: RoiPicCount = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||
case 2: RoiDeltaTimeMs = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||
default: return CfgUpdateFlags.None;
|
||||
}
|
||||
}
|
||||
@ -87,21 +89,19 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
if (Utils.TryParseUDouble(strValue, out dummy)) return true;
|
||||
break;
|
||||
case 1:
|
||||
if (Int32.TryParse(strValue, out dummyInt)) return true;
|
||||
if (dummyInt < 0 || dummyInt < 50)
|
||||
if (!Int32.TryParse(strValue, out dummyInt) || (dummyInt < 0 || dummyInt > 50))
|
||||
{
|
||||
message = ParamName(i) + " is invalid. Param must be between 0 and 50!";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case 2:
|
||||
if (Int32.TryParse(strValue, out dummyInt)) return true;
|
||||
if (dummyInt < 500 || dummyInt < 5500)
|
||||
if (!Int32.TryParse(strValue, out dummyInt) || (dummyInt < 500 || dummyInt < 5500))
|
||||
{
|
||||
message = ParamName(i) + " is invalid. Param must be between 500 and 5500!";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
default:
|
||||
message = "Invalid index";
|
||||
return false;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using log4net;
|
||||
using Common;
|
||||
@ -41,6 +42,8 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
get { return roiCfg.ProcParams.PulsesPerLtr; }
|
||||
set { roiCfg.ProcParams.PulsesPerLtr = value; }
|
||||
}
|
||||
public int RoiDeltaTimeMs { get { return roiCfg.ProcParams.RoiDeltaTimeMs; } }
|
||||
public int RoiPicCount { get { return roiCfg.ProcParams.RoiPicCount; } }
|
||||
public double LtrsPerPulse { get { return (PulsesPerLtr <= float.Epsilon) ? 1.0 : (1 / PulsesPerLtr); } }
|
||||
public string QuantityUnits { get; set; }
|
||||
|
||||
@ -123,26 +126,65 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
||||
return GrabImageOp(imageFileName, roiCfg.BlackAndWhite, roiCfg.LowResolution, roiCfg.ImageRotation);
|
||||
}
|
||||
|
||||
public IOperation GrabImageOp(string imageFileName,
|
||||
bool blackAndWhite, bool lowResolution, ImageRotation imageRotation)
|
||||
// Roi.cs
|
||||
public IOperation GrabImageOp(
|
||||
string imageFileName,
|
||||
bool blackAndWhite,
|
||||
bool lowResolution,
|
||||
ImageRotation imageRotation)
|
||||
{
|
||||
if (NetCamera != null)
|
||||
if (NetCamera == null)
|
||||
{
|
||||
///TODO BUMI - implementing grabing multiple images
|
||||
/// 1. Create a list of image file names
|
||||
string[] imageNames = new string[roiCfg.ImageOpacity.Count];
|
||||
for (int i = 0; i < imageNames.Length; i++)
|
||||
{
|
||||
imageNames[i] = imageFileName + string.Format("{0}_{1}",imageNames.Length, i);
|
||||
}
|
||||
/// 2. implement count of images to grab
|
||||
/// 3. implement grabing multiple images
|
||||
|
||||
/// TODO: Implement support for sharing one camera by multiple ROI-s
|
||||
return NetCamera.GrabImagesOp(new string[] { imageFileName }, blackAndWhite, lowResolution, imageRotation);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
int imageCount = Math.Max(1, RoiPicCount);
|
||||
|
||||
string[] imageNames = CreateImageFileNames(
|
||||
imageFileName,
|
||||
imageCount);
|
||||
|
||||
return NetCamera.GrabImagesOp(
|
||||
imageNames,
|
||||
blackAndWhite,
|
||||
lowResolution,
|
||||
imageRotation);
|
||||
}
|
||||
|
||||
private static string[] CreateImageFileNames(
|
||||
string originalFileName,
|
||||
int imageCount)
|
||||
{
|
||||
if (imageCount <= 1)
|
||||
{
|
||||
return new[] { originalFileName };
|
||||
}
|
||||
|
||||
string directory = Path.GetDirectoryName(originalFileName);
|
||||
string baseName = Path.GetFileNameWithoutExtension(originalFileName);
|
||||
string extension = Path.GetExtension(originalFileName);
|
||||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");
|
||||
|
||||
var result = new string[imageCount];
|
||||
|
||||
// Prvý obrázok zostáva kompatibilný s dialógom.
|
||||
result[0] = originalFileName;
|
||||
|
||||
for (int i = 1; i < imageCount; i++)
|
||||
{
|
||||
string fileName = string.Format(
|
||||
"{0}_{1}_{2:D2}{3}",
|
||||
baseName,
|
||||
timestamp,
|
||||
i + 1,
|
||||
extension);
|
||||
|
||||
result[i] = string.IsNullOrEmpty(directory)
|
||||
? fileName
|
||||
: Path.Combine(directory, fileName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IValve GetValve()
|
||||
|
||||
@ -94,17 +94,24 @@ namespace TBF.Rig.Network.Telnet
|
||||
|
||||
public class PromptReceivedImagesEventArgs : EventArgs
|
||||
{
|
||||
public Image image; /// server image.
|
||||
public int idxCamera; /// camera index.
|
||||
public int idxImage; /// image index.
|
||||
public int imageCounts; /// number of images.
|
||||
|
||||
public PromptReceivedImagesEventArgs(int idxCamera, Image image, int idxImage, int imageCounts)
|
||||
public int idxCamera;
|
||||
public int idxImage;
|
||||
public int imageCount;
|
||||
public Image image;
|
||||
public DateTimeOffset? frameTime;
|
||||
|
||||
public PromptReceivedImagesEventArgs(
|
||||
int idxCamera,
|
||||
int idxImage,
|
||||
int imageCount,
|
||||
Image image,
|
||||
DateTimeOffset? frameTime)
|
||||
{
|
||||
this.image = image;
|
||||
this.idxCamera = idxCamera;
|
||||
this.idxImage = idxImage;
|
||||
this.imageCounts = imageCounts;
|
||||
this.imageCount = imageCount;
|
||||
this.image = image;
|
||||
this.frameTime = frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1030,6 +1030,10 @@
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\MessageStatus.cs"/>
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\MessageStatusEnum.cs"/>
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\ParsedImage.cs"/>
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\SendImagePayload.cs" />
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\SendImagesRequest.cs" />
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\SendSummaryPayload.cs" />
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\TimedCameraImage.cs" />
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\RoisAndResults.cs"/>
|
||||
<Compile Include="Rig\Network\Camera\CJMS11\TerminalDlg.cs">
|
||||
<SubType>Form</SubType>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user