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
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
[assembly: AssemblyVersion("3.9.3133.0")]
|
[assembly: AssemblyVersion("3.9.3134.0")]
|
||||||
[assembly: AssemblyFileVersion("3.9.3133.0")]
|
[assembly: AssemblyFileVersion("3.9.3134.0")]
|
||||||
|
|||||||
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using log4net;
|
using log4net;
|
||||||
@ -70,6 +71,9 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
int lastWmNr1;
|
int lastWmNr1;
|
||||||
Bitmap lastImage; /// non-zoomed image
|
Bitmap lastImage; /// non-zoomed image
|
||||||
Bitmap zoomedImage;
|
Bitmap zoomedImage;
|
||||||
|
private readonly List<string> currentImageGroup = new List<string>();//multiple pictures
|
||||||
|
private int currentImageGroupIndex;
|
||||||
|
private bool mouseOverPictureBox;
|
||||||
|
|
||||||
Thread imageProcessingThread; /// Thread to process images
|
Thread imageProcessingThread; /// Thread to process images
|
||||||
public delegate void OcrTextObtainedDelegate(int ix, bool isEnd, string text);
|
public delegate void OcrTextObtainedDelegate(int ix, bool isEnd, string text);
|
||||||
@ -96,6 +100,12 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
public TestStartEndForm()
|
public TestStartEndForm()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
KeyPreview = true;
|
||||||
|
|
||||||
|
pictureBox1.MouseWheel += pictureBox1_MouseWheel;
|
||||||
|
pictureBox1.MouseEnter += pictureBox1_MouseEnter;
|
||||||
|
pictureBox1.MouseLeave += pictureBox1_MouseLeave;
|
||||||
|
|
||||||
this.Icon = Properties.Resources.TBF_icon;
|
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].TextChanged += new System.EventHandler(this.startTextBoxX_TextChanged);
|
||||||
startTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.startTextBoxX_KeyPress);
|
startTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.startTextBoxX_KeyPress);
|
||||||
startTextBoxes[i].MouseDown += new System.Windows.Forms.MouseEventHandler(this.startTextBoxX_MouseDown);
|
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)
|
private void EndTextBox_EnableHanders(int i)
|
||||||
{
|
{
|
||||||
endTextBoxes[i].AcceptsTabChanged += new System.EventHandler(this.endTextBoxX_AcceptsTabChanged);
|
endTextBoxes[i].AcceptsTabChanged += new System.EventHandler(this.endTextBoxX_AcceptsTabChanged);
|
||||||
endTextBoxes[i].TextChanged += new System.EventHandler(this.endTextBoxX_TextChanged);
|
endTextBoxes[i].TextChanged += new System.EventHandler(this.endTextBoxX_TextChanged);
|
||||||
endTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.endTextBoxX_KeyPress);
|
endTextBoxes[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.endTextBoxX_KeyPress);
|
||||||
endTextBoxes[i].MouseDown += new System.Windows.Forms.MouseEventHandler(this.endTextBoxX_AcceptsTabChanged);
|
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)
|
private void TestStartEndForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -763,7 +804,7 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
TextBox clickedTextBox = sender as TextBox;
|
TextBox clickedTextBox = sender as TextBox;
|
||||||
if (clickedTextBox != null)
|
if (clickedTextBox != null)
|
||||||
{
|
{
|
||||||
var physPos = PhysPos(clickedTextBox, startTextBoxes);
|
var physPos = PhysPos(clickedTextBox, endTextBoxes);//startTextBoxes);
|
||||||
|
|
||||||
if (physPos < 0)
|
if (physPos < 0)
|
||||||
{
|
{
|
||||||
@ -1069,7 +1110,7 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
TextBox clickedTextBox = sender as TextBox;
|
TextBox clickedTextBox = sender as TextBox;
|
||||||
if (clickedTextBox != null)
|
if (clickedTextBox != null)
|
||||||
{
|
{
|
||||||
var physPos = PhysPos(clickedTextBox, startTextBoxes);
|
var physPos = PhysPos(clickedTextBox, endTextBoxes);//startTextBoxes);
|
||||||
|
|
||||||
if (physPos < 0)
|
if (physPos < 0)
|
||||||
{
|
{
|
||||||
@ -1134,65 +1175,479 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startOrEnd">Tst.Start or Tst.End</param>
|
/// <param name="startOrEnd">Tst.Start or Tst.End</param>
|
||||||
/// <param name="physPos">0..23</param>
|
/// <param name="physPos">0..23</param>
|
||||||
void LoadImage(object sender, Tst startOrEnd, int physPos)
|
void LoadImage( object sender, Tst startOrEnd, int physPos)
|
||||||
{
|
{
|
||||||
int wmNr1b = phys2wm[physPos]; /// 0 means no WM
|
if (physPos < 0 || physPos >= phys2wm.Length)
|
||||||
if (wmNr1b <= 0) return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wmNr0b = wmNr1b - 1;
|
int wmNr1b = phys2wm[physPos];
|
||||||
|
|
||||||
string imageFileName;
|
// Hodnota 0 znamená, že na tejto fyzickej pozícii nie je vodomer.
|
||||||
if (startOrEnd == Tst.Start && startImages[wmNr0b] != null)
|
if (wmNr1b <= 0)
|
||||||
imageFileName = startImages[wmNr0b];
|
{
|
||||||
else if (startOrEnd == Tst.End && endImages[wmNr0b] != null)
|
return;
|
||||||
imageFileName = endImages[wmNr0b];
|
}
|
||||||
else
|
|
||||||
imageFileName = string.Empty;
|
|
||||||
|
|
||||||
if (imageFileName == lastImageName)
|
wmNr0b = wmNr1b - 1;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastImageName = imageFileName;
|
string imageFileName = GetBaseImageFileName(
|
||||||
lastWmNr1 = wmNr1b;
|
startOrEnd,
|
||||||
|
wmNr0b);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(imageFileName) || !File.Exists(imageFileName))
|
lastWmNr1 = wmNr1b;
|
||||||
{
|
|
||||||
lastImage = null;
|
if (string.IsNullOrWhiteSpace(imageFileName))
|
||||||
}
|
{
|
||||||
else
|
ClearImageGroup();
|
||||||
{
|
LoadImageFile(null);
|
||||||
using (Bitmap tempBmp = Image.FromFile(imageFileName) as Bitmap)
|
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;
|
return null;
|
||||||
if (tempBmp.Height < tempBmp.Width)
|
}
|
||||||
|
|
||||||
|
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;
|
currentImageGroup.Add(timedImage);
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
/// <summary>
|
||||||
/// Left click = Zoom, Right click = Unzoom
|
/// Left click = Zoom, Right click = Unzoom
|
||||||
@ -1276,7 +1731,9 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
{
|
{
|
||||||
if (lastImage == null)
|
if (lastImage == null)
|
||||||
{
|
{
|
||||||
|
Image previousImage = pictureBox1.Image;
|
||||||
pictureBox1.Image = new Bitmap(TBF.Properties.Resources.Empty);
|
pictureBox1.Image = new Bitmap(TBF.Properties.Resources.Empty);
|
||||||
|
previousImage?.Dispose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1312,10 +1769,108 @@ namespace TBF.Rig.DataEntry.StandartCameraPurchaseOrder
|
|||||||
Brush brush = (brightness > 160) ? darkBrush : lightBrush;
|
Brush brush = (brightness > 160) ? darkBrush : lightBrush;
|
||||||
|
|
||||||
grph.DrawString(lastWmNr1.ToString(), font, brush, margin, margin);
|
grph.DrawString(lastWmNr1.ToString(), font, brush, margin, margin);
|
||||||
|
|
||||||
|
DrawImageInfo(grph, tempBmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image oldPictureBoxImage = pictureBox1.Image;
|
||||||
pictureBox1.Image = tempBmp;
|
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)
|
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 log4net;
|
||||||
using Renci.SshNet;
|
using Renci.SshNet;
|
||||||
using Common;
|
using Common;
|
||||||
|
using Config.Entities;
|
||||||
using FluentNHibernate.Conventions;
|
using FluentNHibernate.Conventions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using TBF.Rig.Network.Telnet;
|
using TBF.Rig.Network.Telnet;
|
||||||
@ -69,10 +70,20 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
|||||||
{
|
{
|
||||||
get {
|
get {
|
||||||
if (roiConfiguration!=null) {return roiConfiguration;}
|
if (roiConfiguration!=null) {return roiConfiguration;}
|
||||||
else { roiConfiguration = GetFirstRoiChild(); return roiConfiguration; }
|
else { roiConfiguration = GetFirstRoiCfgChild(); return roiConfiguration; }
|
||||||
}
|
}
|
||||||
set { if (roiConfiguration == null) roiConfiguration = value;}
|
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; } }
|
public IPAddress IPAddress { get { return ipAddress; } }
|
||||||
IPAddress ipAddress;
|
IPAddress ipAddress;
|
||||||
@ -154,6 +165,14 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
|||||||
private bool stopSaveImageListenerFlag;
|
private bool stopSaveImageListenerFlag;
|
||||||
public bool IsSaveImageListenerThreadAlive { get { return saveImageListenerThread != null && saveImageListenerThread.IsAlive; } }
|
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
|
/// ROI related parameters
|
||||||
///
|
///
|
||||||
@ -482,7 +501,6 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
|||||||
if (completedTask == timeoutTask)
|
if (completedTask == timeoutTask)
|
||||||
{
|
{
|
||||||
client.Close();
|
client.Close();
|
||||||
|
|
||||||
throw new TimeoutException( $"Camera connection timeout after 1 second: {ipAddressParam}:{portParam}");
|
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
|
/// Load the list of components from the database
|
||||||
var session = TBF.DB.CreateSession(DBKind.Config);
|
var session = TBF.DB.CreateSession(DBKind.Config);
|
||||||
var cmptnEntities = session.QueryOver<Config.Entities.Component>().OrderBy(x => x.ItemNr).Asc.List();
|
var cmptnEntities = session.QueryOver<Config.Entities.Component>().OrderBy(x => x.ItemNr).Asc.List();
|
||||||
|
|
||||||
|
|
||||||
List<RoiCfg> cfgs = new List<RoiCfg>();
|
List<RoiCfg> cfgs = new List<RoiCfg>();
|
||||||
IList<IComponent> components = new List<Rig.Generic.IComponent>();
|
IList<IComponent> components = new List<Rig.Generic.IComponent>();
|
||||||
foreach (var component in cmptnEntities)
|
foreach (var component in cmptnEntities)
|
||||||
@ -943,25 +961,136 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
|||||||
{
|
{
|
||||||
IComponentFactory cmpntFactory = Rig.TbfComponents.CmpntFactoryFromClassName(component.ClassName);
|
IComponentFactory cmpntFactory = Rig.TbfComponents.CmpntFactoryFromClassName(component.ClassName);
|
||||||
if (cmpntFactory == null) continue;
|
if (cmpntFactory == null) continue;
|
||||||
|
|
||||||
IList<IComponent> tbfComponents = Rig.TbfComponents.LoadComponentsFromDB(cmptnEntities);
|
IList<IComponent> tbfComponents = Rig.TbfComponents.LoadComponentsFromDB(cmptnEntities);
|
||||||
IComponentCfg cmpntCfgFromCmpntEntity = cmpntFactory.CmpntCfgFromCmpntEntity(component);
|
IComponentCfg cmpntCfgFromCmpntEntity = cmpntFactory.CmpntCfgFromCmpntEntity(component);
|
||||||
|
|
||||||
IComponent cmpnt = cmpntFactory.GetComponent(cmpntCfgFromCmpntEntity, components);
|
IComponent cmpnt = cmpntFactory.GetComponent(cmpntCfgFromCmpntEntity, components);
|
||||||
|
|
||||||
RoiForFixedStartCJMS11.RoiCfg roi = cmpntCfgFromCmpntEntity as RoiForFixedStartCJMS11.RoiCfg;
|
RoiForFixedStartCJMS11.RoiCfg roi = cmpntCfgFromCmpntEntity as RoiForFixedStartCJMS11.RoiCfg;
|
||||||
|
|
||||||
if (roi != null)
|
if (roi != null)
|
||||||
{
|
{
|
||||||
cfgs.Add(roi);
|
cfgs.Add(roi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get fist found configuration
|
/// get fist found configuration
|
||||||
return cfgs.First();
|
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()
|
private string getConfigMessage()
|
||||||
{
|
{
|
||||||
//CameraCfg
|
//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)
|
if (imageToSave == null)
|
||||||
{
|
{
|
||||||
log.Error("Nothing to save in SaveImageToFile");
|
throw new ArgumentNullException(nameof(imageToSave));
|
||||||
return; // nothing to save
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileNamePath == null || fileNamePath.IsEmpty())
|
if (string.IsNullOrWhiteSpace(fileNamePath))
|
||||||
{
|
{
|
||||||
log.Error("Path to save missing!");
|
throw new ArgumentException(
|
||||||
return; // nothing to save
|
"Path to save is missing.",
|
||||||
|
nameof(fileNamePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try
|
string directory =
|
||||||
{
|
Path.GetDirectoryName(fileNamePath);
|
||||||
string filePath = fileNamePath;
|
|
||||||
|
|
||||||
string path = Path.GetDirectoryName(filePath);
|
|
||||||
if (path != null && path.IsNotEmpty())
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var clonedImage = CloneImageToBitmap(imageToSave))
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
{
|
|
||||||
clonedImage.Save(filePath, ImageFormat.Png); // encoder, encoderParams);
|
|
||||||
}
|
|
||||||
log.Debug("Image saved to: " + filePath);
|
|
||||||
}catch(Exception exc)
|
|
||||||
{
|
{
|
||||||
log.Error("Error saving image: " + exc.Message);
|
Directory.CreateDirectory(directory);
|
||||||
throw;
|
}
|
||||||
|
|
||||||
|
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();
|
liveCts?.Cancel();
|
||||||
grabCts?.Cancel();
|
grabCts?.Cancel();
|
||||||
saveCts?.Cancel();
|
saveCts?.Cancel();
|
||||||
|
sendImagesCts?.Cancel();
|
||||||
|
|
||||||
stopLiveStreamListenerFlag = true;
|
stopLiveStreamListenerFlag = true;
|
||||||
stopGrabImageListenerFlag = true;
|
stopGrabImageListenerFlag = true;
|
||||||
@ -2773,45 +2922,48 @@ namespace TBF.Rig.Network.Camera.CJMS11
|
|||||||
/// This method is called from within this class when
|
/// This method is called from within this class when
|
||||||
/// a prompt string was received from the telnet server.
|
/// a prompt string was received from the telnet server.
|
||||||
/// </summary>
|
/// </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)
|
||||||
{
|
{
|
||||||
|
if (image == null)
|
||||||
log.Info($"Images received from camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}");
|
|
||||||
|
|
||||||
if (null != ImageCameraHandler)
|
|
||||||
{
|
{
|
||||||
try
|
return;
|
||||||
{
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
image.Dispose();
|
||||||
{
|
return;
|
||||||
//rotation
|
}
|
||||||
ImageUtils.Rotate(image, imageRotation);
|
|
||||||
|
try
|
||||||
//Send images to bridge
|
{
|
||||||
ImagesCameraHandler(sender, new PromptReceivedImagesEventArgs(idxImage, image,iImageIdx, imageCount));
|
handler( sender,
|
||||||
log.Info($"ImagesCameraHandler reached camera: {idxImage}, iImageIdx: {iImageIdx}, imageCount: {imageCount}");
|
new PromptReceivedImagesEventArgs(
|
||||||
}
|
cameraIndex,
|
||||||
catch (Exception ex)
|
imageIndex,
|
||||||
{
|
imageCount,
|
||||||
log.Info("Internal error: PromptReceivedHandler exception: " + ex.Message);
|
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;
|
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>
|
/// </summary>
|
||||||
/// <description>delta time for send parameter 500ms is minimum</description>
|
/// <description>delta time for send parameter 500ms is minimum</description>
|
||||||
public int RoiDeltaTimeMs;
|
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[]
|
string[] paramNames = new string[]
|
||||||
{
|
{
|
||||||
@ -35,11 +41,7 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
|||||||
Strings.DelaySendImage,
|
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()
|
public override void InitializeAll()
|
||||||
{
|
{
|
||||||
@ -68,8 +70,8 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
|||||||
switch (i)
|
switch (i)
|
||||||
{
|
{
|
||||||
case 0: PulsesPerLtr = Utils.ParseUDouble(strValue); return CfgUpdateFlags.None;
|
case 0: PulsesPerLtr = Utils.ParseUDouble(strValue); return CfgUpdateFlags.None;
|
||||||
case 1: PulsesPerLtr = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
case 1: RoiPicCount = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||||
case 2: PulsesPerLtr = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
case 2: RoiDeltaTimeMs = Int32.Parse(strValue); return CfgUpdateFlags.None;
|
||||||
default: 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;
|
if (Utils.TryParseUDouble(strValue, out dummy)) return true;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (Int32.TryParse(strValue, out dummyInt)) return true;
|
if (!Int32.TryParse(strValue, out dummyInt) || (dummyInt < 0 || dummyInt > 50))
|
||||||
if (dummyInt < 0 || dummyInt < 50)
|
|
||||||
{
|
{
|
||||||
message = ParamName(i) + " is invalid. Param must be between 0 and 50!";
|
message = ParamName(i) + " is invalid. Param must be between 0 and 50!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
return true;
|
||||||
case 2:
|
case 2:
|
||||||
if (Int32.TryParse(strValue, out dummyInt)) return true;
|
if (!Int32.TryParse(strValue, out dummyInt) || (dummyInt < 500 || dummyInt < 5500))
|
||||||
if (dummyInt < 500 || dummyInt < 5500)
|
|
||||||
{
|
{
|
||||||
message = ParamName(i) + " is invalid. Param must be between 500 and 5500!";
|
message = ParamName(i) + " is invalid. Param must be between 500 and 5500!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
return true;
|
||||||
default:
|
default:
|
||||||
message = "Invalid index";
|
message = "Invalid index";
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
///
|
///
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using log4net;
|
using log4net;
|
||||||
using Common;
|
using Common;
|
||||||
@ -41,6 +42,8 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
|||||||
get { return roiCfg.ProcParams.PulsesPerLtr; }
|
get { return roiCfg.ProcParams.PulsesPerLtr; }
|
||||||
set { roiCfg.ProcParams.PulsesPerLtr = value; }
|
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 double LtrsPerPulse { get { return (PulsesPerLtr <= float.Epsilon) ? 1.0 : (1 / PulsesPerLtr); } }
|
||||||
public string QuantityUnits { get; set; }
|
public string QuantityUnits { get; set; }
|
||||||
|
|
||||||
@ -123,26 +126,65 @@ namespace TBF.Rig.Network.Camera.RoiForFixedStartCJMS11
|
|||||||
return GrabImageOp(imageFileName, roiCfg.BlackAndWhite, roiCfg.LowResolution, roiCfg.ImageRotation);
|
return GrabImageOp(imageFileName, roiCfg.BlackAndWhite, roiCfg.LowResolution, roiCfg.ImageRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOperation GrabImageOp(string imageFileName,
|
// Roi.cs
|
||||||
bool blackAndWhite, bool lowResolution, ImageRotation imageRotation)
|
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;
|
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()
|
public IValve GetValve()
|
||||||
|
|||||||
@ -94,17 +94,24 @@ namespace TBF.Rig.Network.Telnet
|
|||||||
|
|
||||||
public class PromptReceivedImagesEventArgs : EventArgs
|
public class PromptReceivedImagesEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public Image image; /// server image.
|
public int idxCamera;
|
||||||
public int idxCamera; /// camera index.
|
public int idxImage;
|
||||||
public int idxImage; /// image index.
|
public int imageCount;
|
||||||
public int imageCounts; /// number of images.
|
public Image image;
|
||||||
|
public DateTimeOffset? frameTime;
|
||||||
public PromptReceivedImagesEventArgs(int idxCamera, Image image, int idxImage, int imageCounts)
|
|
||||||
|
public PromptReceivedImagesEventArgs(
|
||||||
|
int idxCamera,
|
||||||
|
int idxImage,
|
||||||
|
int imageCount,
|
||||||
|
Image image,
|
||||||
|
DateTimeOffset? frameTime)
|
||||||
{
|
{
|
||||||
this.image = image;
|
|
||||||
this.idxCamera = idxCamera;
|
this.idxCamera = idxCamera;
|
||||||
this.idxImage = idxImage;
|
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\MessageStatus.cs"/>
|
||||||
<Compile Include="Rig\Network\Camera\CJMS11\POJO\MessageStatusEnum.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\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\RoisAndResults.cs"/>
|
||||||
<Compile Include="Rig\Network\Camera\CJMS11\TerminalDlg.cs">
|
<Compile Include="Rig\Network\Camera\CJMS11\TerminalDlg.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user