- Added QRValidation vision control system - Includes CapCleaningControl UI components - WebSocket-based barcode validation system - Support for Crevis PLC integration - Test projects for PLC emulator, motion, IO panel, and Modbus 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1143 lines
45 KiB
C#
1143 lines
45 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Text;
|
|
using System.Management;
|
|
using System.Data.SqlClient;
|
|
using System.Data;
|
|
using System.Threading.Tasks;
|
|
using System.Net.NetworkInformation;
|
|
using System.Windows.Forms;
|
|
using System.Net;
|
|
using WatsonWebsocket;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using Euresys.Open_eVision_22_12;
|
|
using Crevis.VirtualFG40Library;
|
|
using AR;
|
|
|
|
namespace Project
|
|
{
|
|
|
|
public enum eTarget
|
|
{
|
|
Left = 0,
|
|
Right,
|
|
None = 9,
|
|
}
|
|
public enum remotelist
|
|
{
|
|
barcodeupdate,
|
|
}
|
|
public class remoteargs : EventArgs
|
|
{
|
|
public string strdata { get; set; }
|
|
public int CamIdx { get; set; }
|
|
public remotelist Command { get; set; }
|
|
public remoteargs(remotelist _cmd, int _camidx, string _strdata)
|
|
{
|
|
this.strdata = _strdata;
|
|
this.CamIdx = _camidx;
|
|
this.Command = _cmd;
|
|
}
|
|
}
|
|
|
|
public static class PUB
|
|
{
|
|
public static Class.WebSocket[] wsock_ = new Class.WebSocket[2];
|
|
public static AR.Log[] log_ = new AR.Log[2];
|
|
|
|
public static DateTime LastInputTime = DateTime.Now;
|
|
public static CSetting setting;
|
|
public static Boolean VisionLicense;
|
|
public static DateTime parsetime = DateTime.Now;
|
|
public static Flag flag;
|
|
|
|
public static bool[] DetectReel = new bool[] { false, false };
|
|
public static bool[] DetectConv = new bool[] { false, false };
|
|
public static int[] DetectReelValue = new int[] { 0, 0 };
|
|
public static int[] DetectConvValue = new int[] { 0, 0 };
|
|
public static double[] ProcessTime = new double[] { 0, 0 };
|
|
|
|
public static string[] lastguid = new string[] { string.Empty, string.Empty };
|
|
public static string[] lastdata = new string[] { string.Empty, string.Empty };
|
|
public static string[] lastcmd = new string[] { string.Empty, string.Empty };
|
|
public static string[] lastip = new string[] { string.Empty, string.Empty };
|
|
|
|
public static DateTime[] lastsend = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
public static DateTime[] lastsendStatus = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
public static Stack<string> imgque = new Stack<string>();
|
|
public static Boolean[] _isCrevisOpen = new bool[] { false, false, false };
|
|
public static Boolean[] _isCrevisACQ = new bool[] { false, false, false };
|
|
public static bool[] IsLive = new bool[] { false, false };
|
|
|
|
public static eTarget GetTarget(int camidx)
|
|
{
|
|
if (camidx < 0 || camidx > 900) return eTarget.None;
|
|
if (camidx == PUB.setting.CameraIndexL) return eTarget.Left;
|
|
else if (camidx == PUB.setting.CameraIndexR && camidx != PUB.setting.CameraIndexL) return eTarget.Right;
|
|
else return eTarget.None;
|
|
}
|
|
// public static System.Windows.Forms.Panel pIvLeft, pIvRight;
|
|
public static event EventHandler<remoteargs> RemoteCommand;
|
|
public static void RaiseRemoteCommand(remotelist cmd, int camidx, string data)
|
|
{
|
|
RemoteCommand?.Invoke(null, new remoteargs(cmd, camidx, data));
|
|
}
|
|
|
|
|
|
public static void initCore()
|
|
{
|
|
//setting
|
|
setting = new CSetting();
|
|
setting.Load();
|
|
|
|
//log
|
|
log_[0] = new AR.Log();
|
|
log_[0].FileNameFormat = "{yyyyMMdd}_L";
|
|
log_[0].TimeFormat = "yyyy-MM-dd HH:mm:ss.fff";
|
|
|
|
log_[1] = new AR.Log();
|
|
log_[1].FileNameFormat = "{yyyyMMdd}_R";
|
|
log_[1].TimeFormat = "yyyy-MM-dd HH:mm:ss.fff";
|
|
|
|
flag = new Flag();
|
|
}
|
|
|
|
|
|
public static void LogFlush()
|
|
{
|
|
PUB.log_[0].Flush();
|
|
PUB.log_[1].Flush();
|
|
}
|
|
|
|
public static string GetStackTraceInfo(
|
|
[CallerFilePath] string filenpath = null,
|
|
[CallerLineNumber] int linenumer = -1,
|
|
[CallerMemberName] string callMember = null)
|
|
{
|
|
|
|
|
|
var stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
|
|
string fileName = stackFrame.GetFileName();
|
|
if (fileName.isEmpty()) fileName = filenpath;
|
|
|
|
string methodName = stackFrame.GetMethod().ToString();
|
|
if (methodName.isEmpty()) methodName = callMember;
|
|
|
|
int lineNumber = stackFrame.GetFileLineNumber();
|
|
if (linenumer != -1) lineNumber = linenumer;
|
|
|
|
var TraceInfo = stackFrame.ToString();
|
|
|
|
return $"File:{fileName},Line:{lineNumber}\nMethod:{methodName}\n{TraceInfo}";
|
|
|
|
}
|
|
|
|
public static Dictionary<string, string> ComputerInfo()
|
|
{
|
|
string ip = "";
|
|
string mac = "";
|
|
// string prgmName = Application.ProductName;
|
|
|
|
var nif = NetworkInterface.GetAllNetworkInterfaces();
|
|
var host = Dns.GetHostEntry(Dns.GetHostName());
|
|
string fullname = System.Net.Dns.GetHostEntry("").HostName;
|
|
foreach (IPAddress r in host.AddressList)
|
|
{
|
|
string str = r.ToString();
|
|
|
|
if (str != "" && str.Substring(0, 3) == "10.")
|
|
{
|
|
ip = str;
|
|
break;
|
|
}
|
|
}
|
|
|
|
string rtn = string.Empty;
|
|
ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled='TRUE'");
|
|
ManagementObjectSearcher query1 = new ManagementObjectSearcher(oq);
|
|
foreach (ManagementObject mo in query1.Get())
|
|
{
|
|
string[] address = (string[])mo["IPAddress"];
|
|
if (address[0] == ip && mo["MACAddress"] != null)
|
|
{
|
|
mac = mo["MACAddress"].ToString();
|
|
break;
|
|
}
|
|
}
|
|
|
|
var username = System.Environment.UserName;
|
|
var retval = new Dictionary<string, string>();
|
|
retval.Add("IP", ip);
|
|
retval.Add("MAC", mac);
|
|
retval.Add("USER", username);
|
|
retval.Add("HOST", fullname);
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
public static Rectangle GetVisionOrientSearchArea(
|
|
Rectangle baserect,
|
|
int offsetX,
|
|
int offsetY,
|
|
out string VisionOrientMessage)
|
|
{
|
|
VisionOrientMessage = "";
|
|
var newORectW = baserect.Width + offsetX;
|
|
var newORectH = baserect.Height + offsetY;
|
|
|
|
var newORectX = baserect.X - ((newORectW - baserect.Width) / 2.0);
|
|
var newORectY = baserect.Y - ((newORectH - baserect.Height) / 2.0);
|
|
if (newORectX < 1)
|
|
{
|
|
newORectW += (int)newORectY;
|
|
newORectX = 1;
|
|
}
|
|
if (newORectY < 1)
|
|
{
|
|
newORectH += (int)newORectY;
|
|
newORectY = 1;
|
|
}
|
|
if (newORectW < 4) newORectW = 0;
|
|
if (newORectH < 4) newORectH = 0;
|
|
var newORect = new Rectangle((int)newORectX, (int)newORectY, (int)newORectW, (int)newORectH);
|
|
if (newORect.Width < 1 || newORect.Height < 1)
|
|
{
|
|
VisionOrientMessage = string.Format("Reference area size error({0},{1},{2},{3})", newORect.Left, newORect.Top, newORect.Width, newORect.Height);
|
|
return Rectangle.Empty;
|
|
}
|
|
else return newORect;
|
|
}
|
|
public static double GetFreeSpace(string driveletter)
|
|
{
|
|
try
|
|
{
|
|
var di = new System.IO.DriveInfo(driveletter);
|
|
var freespace = di.TotalFreeSpace;
|
|
var totalspace = di.TotalSize;
|
|
var freeSpaceRate = (freespace * 1.0 / totalspace) * 100.0;
|
|
return freeSpaceRate;
|
|
}
|
|
catch
|
|
{
|
|
return 100.0;
|
|
}
|
|
}
|
|
|
|
public static string getSavePath(out double freespace)
|
|
{
|
|
Boolean path1Exist = false;
|
|
double freespace1 = 100.0;
|
|
string savePath1 = "";
|
|
|
|
freespace = 100.0;
|
|
|
|
savePath1 = System.IO.Path.Combine(PUB.setting.Path_Data, "Images");
|
|
if (savePath1 != "" && System.IO.Directory.Exists(savePath1))
|
|
{
|
|
path1Exist = true;
|
|
//이폴더를 사용
|
|
if (savePath1.StartsWith("\\")) return savePath1;
|
|
//남은잔량을 체크한다.
|
|
return savePath1;
|
|
}
|
|
|
|
if (path1Exist)
|
|
{
|
|
freespace = freespace1;
|
|
return savePath1;
|
|
}
|
|
|
|
//이제 문제가 좀 심각? (폴더가 없다)
|
|
var savePath = System.IO.Path.Combine(Util.CurrentPath, "Images");
|
|
if (System.IO.Directory.Exists(savePath) == false)
|
|
System.IO.Directory.CreateDirectory(savePath);
|
|
|
|
freespace = GetFreeSpace(savePath.Substring(0, 1));
|
|
return savePath;
|
|
}
|
|
public static double ChangeValuePopup(double value, string title)
|
|
{
|
|
var f = AR.UTIL.InputBox(title, value.ToString());
|
|
if (f.Item1)
|
|
{
|
|
var val = double.Parse(f.Item2);
|
|
return val;
|
|
}
|
|
else return value;
|
|
|
|
}
|
|
public static VirtualFG40Library _virtualFG40;
|
|
public static DateTime[] _CrevisRunTime = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
public static DateTime[] _CrevisGrabTime = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
static System.Threading.ManualResetEvent[] mre_grab = new System.Threading.ManualResetEvent[] {
|
|
new System.Threading.ManualResetEvent(true),
|
|
new System.Threading.ManualResetEvent(true)
|
|
};
|
|
public static EImageBW8[] OrgImage = new EImageBW8[] { null, null };
|
|
public static Int32[] _hDevice = new Int32[] { -1, -1 };
|
|
public static Int32[] _width = new Int32[] { 0, 0, 0 };
|
|
public static Int32[] _height = new Int32[] { 0, 0, 0 };
|
|
public static Int32[] _stride = new Int32[] { 0, 0, 0 };
|
|
public static Int32[] _bufferSize = new Int32[] { 0, 0, 0 };
|
|
public static IntPtr[] camPtr = new IntPtr[] { IntPtr.Zero, IntPtr.Zero };
|
|
public static bool[] IsProcess = new bool[] { false, false };
|
|
/// <summary>
|
|
/// 카메라버퍼에서 이미지를 읽는다
|
|
/// </summary>
|
|
/// <param name="camIdx"></param>
|
|
/// <param name="DisplayImage"></param>
|
|
/// <returns></returns>
|
|
public static Boolean CrevisGrab(int camIdx, Boolean DisplayImage, Panel Piv)
|
|
{
|
|
// 1. Excute Acquisition Start command
|
|
// 2. Grab Image using GrabImage
|
|
// 3. Image Display
|
|
// 4. Excute Acquisition Stop command
|
|
var camTarget = PUB.GetTarget(camIdx);// = eTarget.Left ? PUB.setting.CameraIndexL : PUB.setting.CameraIndexR;
|
|
if (camTarget == eTarget.None) return false;
|
|
var logIdx = camTarget == eTarget.Left ? 0 : 1;
|
|
|
|
//작업가능여부확인
|
|
if (mre_grab[camIdx].WaitOne(100) == false) return false;
|
|
|
|
//너무빨리 실행되지 않게 한다
|
|
var ts = DateTime.Now - _CrevisGrabTime[camIdx];
|
|
var fpsterm = (int)(1000f / 12f);
|
|
if (ts.TotalMilliseconds < fpsterm)
|
|
{
|
|
System.Threading.Thread.Sleep((int)(fpsterm - ts.TotalMilliseconds));
|
|
return false;
|
|
}
|
|
_CrevisGrabTime[camIdx] = DateTime.Now;
|
|
|
|
Int32 status = VirtualFG40Library.MCAM_ERR_SUCCESS;
|
|
try
|
|
{
|
|
mre_grab[camIdx].Reset();
|
|
|
|
//데이터수집확인
|
|
if (PUB._isCrevisACQ[camIdx] == false)
|
|
{
|
|
// Acqusition Start
|
|
status = _virtualFG40.AcqStart(_hDevice[camIdx]);
|
|
if (status == VirtualFG40Library.MCAM_ERR_RESOURCE_IN_USE)
|
|
{
|
|
if (_hDevice[camIdx] > 0)
|
|
_virtualFG40.AcqStop(_hDevice[camIdx]);
|
|
PUB._isCrevisACQ[camIdx] = false;
|
|
}
|
|
else if (status != VirtualFG40Library.MCAM_ERR_SUCCESS)
|
|
{
|
|
PUB.log_[logIdx].AddE(String.Format("Camera ACQ failed : {0}", status));
|
|
if (_hDevice[camIdx] > 0) _virtualFG40.AcqStop(_hDevice[camIdx]); //실패하면 멈춘다
|
|
PUB._isCrevisACQ[camIdx] = false;
|
|
}
|
|
else
|
|
{
|
|
PUB._isCrevisACQ[camIdx] = true;
|
|
PUB.log_[logIdx].AddI("Camera ACQ start");
|
|
}
|
|
|
|
//이미지수집에 실패했다면 진행하지 않는다.
|
|
if (PUB._isCrevisACQ[camIdx] == false) return false;
|
|
}
|
|
|
|
//이미지 수집
|
|
status = _virtualFG40.GrabImage(_hDevice[camIdx], camPtr[camIdx], (UInt32)_bufferSize[camIdx]);
|
|
if (status != VirtualFG40Library.MCAM_ERR_SUCCESS)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Camera capture({camIdx}) error : {status}");
|
|
if (_hDevice[camIdx] > 0) _virtualFG40.AcqStop(_hDevice[camIdx]); //실패하면 멈춘다
|
|
PUB._isCrevisACQ[camIdx] = false;
|
|
PUB._isCrevisOpen[camIdx] = false; //연결여부도 끊는다
|
|
_virtualFG40.CloseDevice(_hDevice[camIdx]);
|
|
return false;
|
|
}
|
|
else _CrevisGrabTime[camIdx] = DateTime.Now;
|
|
|
|
//이미지생성 확인
|
|
if (mre[camIdx].WaitOne(100))
|
|
{
|
|
mre[camIdx].Reset();
|
|
if (OrgImage[camIdx] == null || OrgImage[camIdx].IsVoid)
|
|
{
|
|
OrgImage[camIdx] = new EImageBW8(_width[camIdx], _height[camIdx]);
|
|
}
|
|
|
|
//생성된 이미지 복사
|
|
using (var grabimg = new EImageBW8())
|
|
{
|
|
grabimg.SetImagePtr(_width[camIdx], _height[camIdx], camPtr[camIdx]);
|
|
grabimg.CopyTo(OrgImage[camIdx]);
|
|
//this.iv[camIdx].Invalidate(); //image update
|
|
DrawImage(camTarget, Piv, grabimg);
|
|
}
|
|
mre[camIdx].Set();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (_hDevice[camIdx] > 0) _virtualFG40.AcqStop(_hDevice[camIdx]); //실패하면 멈춘다
|
|
PUB._isCrevisACQ[camIdx] = false;
|
|
//var stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
|
|
//string fileName = stackFrame.GetFileName();
|
|
//int lineNumber = stackFrame.GetFileLineNumber();
|
|
//var TraceInfo = stackFrame.ToString();
|
|
//var tracemsg = $"File:{fileName},Line:{lineNumber}\n{TraceInfo}";
|
|
PUB.log_[logIdx].AddE($"Crevis Grab({camIdx}):{ex.Message}");
|
|
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
mre_grab[camIdx].Set();
|
|
}
|
|
|
|
|
|
}
|
|
public static string lastlogbarcode = string.Empty;
|
|
public static string[] lastbcd = new string[] { string.Empty, string.Empty };
|
|
public static Boolean RequestNewRead = false;
|
|
|
|
|
|
public static ManualResetEvent[] mre = new ManualResetEvent[2];
|
|
|
|
static string[] prebarcodedata = new string[] { "", "" };
|
|
/// <summary>
|
|
/// 이미지를 처리합니다
|
|
/// 트리거가 없는 경우에는 릴존재여부와/컨베이어확인을 합니다.
|
|
/// 트리거가 있다면 릴 영역을 찾아서 ID를 스캔합니다
|
|
/// </summary>
|
|
/// <param name="camIdx"></param>
|
|
/// <returns></returns>
|
|
public static bool CreavisProcess(int camIdx, bool isTrigger, Panel Piv)
|
|
{
|
|
var camTarget = PUB.GetTarget(camIdx);
|
|
if (camTarget == eTarget.None) return false;
|
|
var logIdx = camTarget == eTarget.Left ? 0 : 1;
|
|
//이미지사용여부확인
|
|
if (mre[camIdx].WaitOne(100) == false) return false;
|
|
mre[camIdx].Reset();
|
|
|
|
//이미지가 유효한 경우 처리한다
|
|
if (OrgImage[camIdx] == null || OrgImage[camIdx].IsVoid)
|
|
{
|
|
mre[camIdx].Set();
|
|
return false;
|
|
}
|
|
mre[camIdx].Reset();
|
|
|
|
System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch();
|
|
wat.Restart();
|
|
|
|
//검사영역확인
|
|
var rect_reeldetect = camIdx == PUB.setting.CameraIndexL ? PUB.setting.ROI_ReelDetect_L : PUB.setting.ROI_ReelDetect_R;
|
|
var rect_convdetect = camIdx == PUB.setting.CameraIndexL ? PUB.setting.ROI_ConvDetect_L : PUB.setting.ROI_ConvDetect_R;
|
|
|
|
try
|
|
{
|
|
//
|
|
//iv[camIdx].Shapes.Clear();
|
|
string msg;
|
|
string bcdlist = string.Empty;
|
|
int DataCount = 0;// data1.Count;
|
|
Util_Vision.SCodeData codedata = new Util_Vision.SCodeData();
|
|
List<string> datalist = new List<string>();
|
|
List<Point[]> ptlist = new List<Point[]>();
|
|
|
|
|
|
var prccnt = 0;
|
|
string barcodeMessage = string.Empty;
|
|
|
|
//erode 값이 1과 3일때 차이가 발생함, 어두운 녹색라벨혹은, QR이 border에 붙은경우에 erode 3 은 서로 붙게되어서 필터링 되버림
|
|
//CvInvoke.CvtColor(OrgImage, OrgCImage, ColorConversion.Gray2Bgr);
|
|
|
|
//릴을 확인 한다
|
|
//if (IsProcess[camidx])
|
|
//{
|
|
using (EImageEncoder enc = new EImageEncoder())
|
|
{
|
|
using (var seg = enc.GrayscaleSingleThresholdSegmenter)
|
|
{
|
|
seg.WhiteLayerEncoded = true;
|
|
seg.BlackLayerEncoded = false;
|
|
seg.Mode = EGrayscaleSingleThreshold.Absolute;
|
|
seg.AbsoluteThreshold = 50;
|
|
}
|
|
|
|
using (ECodedImage2 encimg = new ECodedImage2())
|
|
{
|
|
using (EROIBW8 roiimge = new EROIBW8())
|
|
{
|
|
roiimge.Attach(OrgImage[camIdx]);
|
|
roiimge.SetPlacement(500, 70, 3008, 2640); //컨베이어전체영역이ㄴ
|
|
enc.Encode(roiimge, encimg);
|
|
using (EObjectSelection selection = new EObjectSelection())
|
|
{
|
|
selection.Clear();
|
|
selection.AddObjects(encimg);
|
|
selection.RemoveUsingUnsignedIntegerFeature(EFeature.Area, 1000, ESingleThresholdMode.LessEqual);
|
|
PUB.DetectReelValue[camIdx] = (int)selection.ElementCount;
|
|
PUB.DetectReel[camIdx] = selection.ElementCount > 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
//
|
|
//else Pub.DetectReel = false;
|
|
|
|
//컨베이어 확인여부 체크필요
|
|
var roi_conv = camIdx == PUB.setting.CameraIndexL ? PUB.setting.ROI_ConvDetect_L : PUB.setting.ROI_ConvDetect_R;
|
|
if (roi_conv.IsEmpty == false)
|
|
{
|
|
using (EImageBW8 thimage = new EImageBW8(OrgImage[camIdx].Width, OrgImage[camIdx].Height))
|
|
{
|
|
EasyImage.Copy(OrgImage[camIdx], thimage);
|
|
|
|
var minresidu = EasyImage.AutoThreshold(thimage, EThresholdMode.MinResidue);
|
|
EasyImage.Threshold(thimage, thimage, minresidu.Value);
|
|
|
|
using (EROIBW8 roimage = new EROIBW8())
|
|
{
|
|
roimage.Attach(thimage);
|
|
roimage.SetPlacement(roi_conv.X, roi_conv.Y, roi_conv.Width, roi_conv.Height);
|
|
|
|
EasyImage.PixelCount(roimage, new EBW8(64), new EBW8(86), out int val_below,
|
|
out int val_between, out int val_above);
|
|
PUB.DetectConvValue[camIdx] = val_below;
|
|
|
|
var det_value = camIdx == PUB.setting.CameraIndexL ? PUB.setting.ConvDetectValueL : PUB.setting.ConvDetectValueR;
|
|
PUB.DetectConv[camIdx] = val_below < det_value;
|
|
}
|
|
}
|
|
}
|
|
else PUB.DetectConv[camIdx] = false;
|
|
|
|
//QR코드를 읽는다
|
|
List<Util_Vision.SCodeData> qrdataList = new List<Util_Vision.SCodeData>();
|
|
|
|
if (isTrigger)
|
|
{
|
|
var qrrlt = Util_Vision.DetectQR(OrgImage[camIdx],
|
|
null, prccnt++, out msg,
|
|
PUB.setting.erodevaluestr,
|
|
PUB.setting.GainOffsetListStr,
|
|
PUB.setting.blob_area_min,
|
|
PUB.setting.blob_area_max,
|
|
PUB.setting.blob_sigmaxy,
|
|
PUB.setting.blob_sigmayy,
|
|
PUB.setting.blob_sigmaxx,
|
|
PUB.setting.blob_minw,
|
|
PUB.setting.blob_maxw,
|
|
PUB.setting.blob_minh,
|
|
PUB.setting.blob_maxh, finddata: PUB.lastdata[camIdx]);
|
|
|
|
qrdataList = qrrlt.Item1;
|
|
|
|
PUB.ProcessTime[camIdx] = qrrlt.Item4;
|
|
var target = camIdx == PUB.setting.CameraIndexL ? eTarget.Left : eTarget.Right;
|
|
// var pan = target == eTarget.Left ? pIvLeft : pIvRight;
|
|
DrawImage(target, Piv, OrgImage[camIdx], qrrlt.Item1, qrrlt.Item2, qrrlt.Item3);
|
|
|
|
DataCount = qrdataList.Count;
|
|
}
|
|
else DataCount = 0;
|
|
|
|
//데이터가 확인되었다면 읽은 경우이다
|
|
if (DataCount > 0)
|
|
{
|
|
var bidx = 0;
|
|
foreach (var item in qrdataList.OrderByDescending(t => t.sid))
|
|
{
|
|
datalist.Add(item.data);
|
|
ptlist.Add(item.corner);
|
|
codedata = item;
|
|
|
|
if (bcdlist.isEmpty() == false) bcdlist += "\n";
|
|
bcdlist += $"[#{bidx}] {item.data}";
|
|
bidx += 1;
|
|
}
|
|
}
|
|
|
|
var tsrun = DateTime.Now - _CrevisRunTime[camIdx];
|
|
var dispMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " (" + tsrun.TotalMilliseconds.ToString("N0") + "ms)";
|
|
if (barcodeMessage.isEmpty() == false) dispMessage += "\n" + barcodeMessage;
|
|
|
|
if (bcdlist.isEmpty() == false && lastlogbarcode != bcdlist)
|
|
{
|
|
if(bcdlist.Equals(prebarcodedata[camIdx])==false)
|
|
{
|
|
PUB.log_[logIdx].Add("BARCODE", "New recognition:" + bcdlist);
|
|
prebarcodedata[camIdx] = bcdlist;
|
|
}
|
|
|
|
lastlogbarcode = bcdlist;
|
|
RaiseRemoteCommand(remotelist.barcodeupdate, camIdx, bcdlist); //값이 변경되었으니 표시해준다
|
|
|
|
}
|
|
else if (bcdlist.isEmpty() && lastlogbarcode.isEmpty() == false)
|
|
{
|
|
RaiseRemoteCommand(remotelist.barcodeupdate, camIdx, string.Empty);
|
|
lastlogbarcode = string.Empty;
|
|
}
|
|
|
|
var tsparse = DateTime.Now - parsetime;
|
|
if (tsparse.TotalMilliseconds >= PUB.setting.GrabDelaySlow)
|
|
{
|
|
//var data = codedata.data;//: String.Empty;
|
|
if (PUB.RequestNewRead)
|
|
{
|
|
//신규 요청받고 처음들어온 데이터는 처리하지 말자 - 211213
|
|
PUB.RequestNewRead = false;
|
|
}
|
|
else
|
|
{
|
|
if (PUB.setting.SendRawData) //211210
|
|
{
|
|
if (PUB.BarcodeParsing(camIdx, qrdataList, "CREVIS"))
|
|
parsetime = DateTime.Now;
|
|
else
|
|
parsetime = DateTime.Now.AddMilliseconds(50);
|
|
}
|
|
else
|
|
{
|
|
if (PUB.BarcodeParsing(camIdx, datalist, "CREVIS"))
|
|
parsetime = DateTime.Now;
|
|
else
|
|
parsetime = DateTime.Now.AddMilliseconds(50);
|
|
}
|
|
}
|
|
}
|
|
else //이떄에는 용량을 체크한다. 210805
|
|
{
|
|
try
|
|
{
|
|
if (PUB.setting.AutoDeleteSeconds > 0 && PUB.setting.AutoDeleteSeconds < 99)
|
|
{
|
|
var di = new System.IO.DirectoryInfo(PUB.setting.ImageSavePath);
|
|
var ttime = DateTime.Now.AddSeconds(-PUB.setting.AutoDeleteSeconds);
|
|
var dellist = di.GetFiles().Where(t => t.CreationTime <= ttime).FirstOrDefault();
|
|
if (dellist != null) dellist.Delete();
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
//iv[camIdx].Invalidate();
|
|
|
|
wat.Stop();
|
|
PUB.ProcessTime[camIdx] = wat.ElapsedMilliseconds;
|
|
_CrevisRunTime[camIdx] = DateTime.Now;
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//var tracemsg = $"File:{fileName},Line:{lineNumber}\n{TraceInfo}";
|
|
PUB.log_[logIdx].AddE($"Crevis Process({camIdx}):" + ex.Message);
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
wat.Stop();
|
|
mre[camIdx].Set();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 판넬이미지 변경
|
|
/// </summary>
|
|
/// <param name="target"></param>
|
|
/// <param name="p"></param>
|
|
/// <param name="grabimg"></param>
|
|
/// <param name="qrdata">확인된 qr이 있다면 표시합니다.</param>
|
|
/// <param name="rect_area">흰색영역이 마킹된 영역</param>
|
|
/// <param name="rect_reel">QR로 추정되는 영역</param>
|
|
public static void DrawImage(eTarget target, Panel p, EImageBW8 grabimg,
|
|
List<Util_Vision.SCodeData> qrdata = null,
|
|
List<Rectangle> rect_area = null,
|
|
List<Rectangle> rect_reel = null)
|
|
{
|
|
|
|
var roi_reel = target == eTarget.Left ? PUB.setting.ROI_ReelDetect_L : PUB.setting.ROI_ReelDetect_R;
|
|
var roi_conv = target == eTarget.Left ? PUB.setting.ROI_ConvDetect_L : PUB.setting.ROI_ConvDetect_R;
|
|
var camIdx = target == eTarget.Left ? PUB.setting.CameraIndexL : PUB.setting.CameraIndexR;
|
|
|
|
var detect_reel = PUB.DetectReel[camIdx];
|
|
var detect_conv = PUB.DetectConv[camIdx];
|
|
|
|
var zoomx = (p.Width * 1f) / grabimg.Width;
|
|
var zoomy = (p.Height * 1f) / grabimg.Height;
|
|
|
|
using (var g = p.CreateGraphics())
|
|
{
|
|
grabimg.Draw(g, zoomx, zoomy);
|
|
|
|
|
|
//이미지의 중앙부분으 포커스를 계산한다.
|
|
float focus = 0f;
|
|
if (OrgImage[camIdx].IsVoid == false)
|
|
{
|
|
var fw = (int)(OrgImage[camIdx].Width * 0.4f);
|
|
var fh = (int)(OrgImage[camIdx].Height * 0.4f);
|
|
var fx = (int)((OrgImage[camIdx].Width - fw) / 2f);
|
|
var fy = (int)((OrgImage[camIdx].Height - fw) / 2f);
|
|
//var frect = new Rectangle(fx, fy, fw, fh);
|
|
g.DrawRectangle(Pens.Gold, fx * zoomx, fy * zoomy, fw * zoomx, fh * zoomy);
|
|
using (var img = new EROIBW8())
|
|
{
|
|
img.Attach(OrgImage[camIdx]);
|
|
img.SetPlacement(fx, fy, fw, fh);
|
|
focus = EasyImage.Focusing(img);
|
|
}
|
|
}
|
|
|
|
using (Font f = new Font("Consolas", 10, FontStyle.Bold))
|
|
{
|
|
var msg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") +
|
|
$"\ncam:{camIdx},focus:{focus}" +
|
|
$"\nReel:{detect_reel},Value={PUB.DetectReelValue[camIdx]}" +
|
|
$"\nConv:{detect_conv},Value={PUB.DetectConvValue[camIdx]}";
|
|
|
|
g.DrawString(msg, f,
|
|
Brushes.White, 10, 10);
|
|
}
|
|
|
|
if (roi_reel.IsEmpty == false)
|
|
{
|
|
var rect = roi_reel;
|
|
var x = rect.X * zoomx;
|
|
var y = rect.Y * zoomy;
|
|
var w = rect.Width * zoomx;
|
|
var h = rect.Height * zoomy;
|
|
g.DrawRectangle(Pens.Lime, x, y, w, h);
|
|
}
|
|
if (roi_conv.IsEmpty == false)
|
|
{
|
|
var rect = roi_conv;
|
|
var x = rect.X * zoomx;
|
|
var y = rect.Y * zoomy;
|
|
var w = rect.Width * zoomx;
|
|
var h = rect.Height * zoomy;
|
|
g.DrawRectangle(Pens.Magenta, x, y, w, h);
|
|
}
|
|
|
|
//border
|
|
g.DrawRectangle(Pens.White, p.DisplayRectangle);
|
|
|
|
//검색영역표시(영역검사)
|
|
if (rect_area != null && rect_area.Any())
|
|
{
|
|
foreach (var item in rect_area)
|
|
{
|
|
var x = item.X * zoomx;
|
|
var y = item.Y * zoomy;
|
|
var w = item.Width * zoomx;
|
|
var h = item.Height * zoomy;
|
|
g.DrawRectangle(Pens.SkyBlue, x, y, w, h);
|
|
}
|
|
}
|
|
|
|
//검색영역표시(QR검사)
|
|
if (rect_reel != null && rect_reel.Any())
|
|
{
|
|
foreach (var item in rect_reel)
|
|
{
|
|
var x = item.X * zoomx;
|
|
var y = item.Y * zoomy;
|
|
var w = item.Width * zoomx;
|
|
var h = item.Height * zoomy;
|
|
g.DrawRectangle(Pens.Blue, x, y, w, h);
|
|
}
|
|
}
|
|
|
|
|
|
//바코드가 검색되었다면?
|
|
if (qrdata != null && qrdata.Any())
|
|
{
|
|
using (Font f = new Font("Consolas", 10, FontStyle.Bold))
|
|
{
|
|
foreach (var item in qrdata)
|
|
{
|
|
//각코너의 점을 표시한다.
|
|
var firstX = 0f;
|
|
var firstY = 0f;
|
|
foreach (var cn in item.corner)
|
|
{
|
|
var x = cn.X * zoomx;
|
|
var y = cn.Y * zoomy;
|
|
if (firstX == 0 && firstY == 0)
|
|
{
|
|
firstX = x;
|
|
firstY = y;
|
|
}
|
|
}
|
|
//첫번째 좌표에 데이터를 표시해준다
|
|
g.DrawString(item.data, f, Brushes.Lime, firstX, firstY);
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Dispose();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
public static string[] lastlog = new string[] { string.Empty, string.Empty };
|
|
public static DateTime[] logtime = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
|
|
|
|
public static bool[] _trigger = new bool[] { false, false };
|
|
public static DateTime[] triggerTime = new DateTime[] { DateTime.Now, DateTime.Now };
|
|
public static Boolean[] IsTrigger = new bool[] { false, false };
|
|
|
|
public static DateTime TriggerStart = DateTime.Now;
|
|
public static Boolean BarcodeParsing(int camIdx, List<Util_Vision.SCodeData> data, string source, Boolean force = false)
|
|
{
|
|
var camTarget = PUB.GetTarget(camIdx);
|
|
if (camTarget == eTarget.None) return false;
|
|
var logIdx = camTarget == eTarget.Left ? 0 : 1;
|
|
var datatagstr = string.Empty;
|
|
foreach (var item in data)
|
|
if (item.data.isEmpty() == false)
|
|
{
|
|
if (datatagstr.Length > 0) datatagstr += "\n";
|
|
datatagstr += item.data;
|
|
}
|
|
|
|
PUB.lastbcd[camIdx] = datatagstr;
|
|
|
|
//스트림데이터가 꺼져있다면 처리하지 않는다.
|
|
if (force == false && PUB.setting.DisableStreamData)
|
|
{
|
|
//트리거모드일때 보내는 플래그가 없다면 보내지 않는다.
|
|
if (PUB.IsTrigger[camIdx] == false)
|
|
{
|
|
PUB.log_[logIdx].AddAT($"[SKIP-SEND] No Trigger Signal");
|
|
return false;
|
|
}
|
|
}
|
|
//TriggerSend = false;
|
|
|
|
//통신이 연결되어있지 않다면 처리하지 않는다
|
|
if (PUB.wsock_[logIdx].IsListening == false || PUB.wsock_[logIdx].ListClients().Count() < 1)
|
|
{
|
|
PUB.log_[logIdx].AddAT($"[SKIP-SEND] Disconnected");
|
|
return false;
|
|
}
|
|
|
|
//파일을 저장한다.
|
|
var path = "";
|
|
if (PUB.mre[camIdx].WaitOne(100))
|
|
{
|
|
try
|
|
{
|
|
PUB.mre[camIdx].Reset();
|
|
path = System.IO.Path.Combine(PUB.setting.ImageSavePath, $"[{camIdx}]barcode_{DateTime.Now.ToString("HHmmssfff")}.jpg");
|
|
var fi = new System.IO.FileInfo(path);
|
|
if (fi.Directory.Exists == false) fi.Directory.Create();
|
|
PUB.OrgImage[camIdx].Save(fi.FullName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Save failed {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
PUB.mre[camIdx].Set();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PUB.log_[logIdx].AddE($"Save failed (object is locked)");
|
|
}
|
|
|
|
//데이터 생성
|
|
var msg = new
|
|
{
|
|
guid = PUB.lastguid[camIdx],
|
|
command = PUB.lastcmd[camIdx],
|
|
data = data,
|
|
time = DateTime.Now.ToString(),
|
|
file = path
|
|
};
|
|
|
|
|
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(msg);
|
|
Boolean sendok = false;
|
|
try
|
|
{
|
|
if (PUB.setting.Log_BarcodeTx)
|
|
{
|
|
if (lastlog[camIdx] == datatagstr)
|
|
{
|
|
var tsee = DateTime.Now - logtime[camIdx];
|
|
if (tsee.TotalSeconds > 60)
|
|
{
|
|
PUB.log_[logIdx].AddI(json);
|
|
lastlog[camIdx] = datatagstr;
|
|
logtime[camIdx] = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PUB.log_[logIdx].AddI(json);
|
|
lastlog[camIdx] = datatagstr;
|
|
}
|
|
}
|
|
//PUB.ws[camIdx].ListClients().ToList().ForEach(t => PUB.ws[camIdx].SendAsync(t, json).Wait());
|
|
PUB.lastsend[camIdx] = DateTime.Now;
|
|
Send_WSock(camTarget, json);
|
|
if (msg.guid.isEmpty() == false)
|
|
PUB.log_[logIdx].Add("Tx", json);
|
|
sendok = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Send failed {ex.Message}");
|
|
sendok = false;
|
|
}
|
|
|
|
if (sendok == false)
|
|
{
|
|
try
|
|
{
|
|
PUB.log_[logIdx].AddAT("Closing server due to send failure");
|
|
Time_WS_Listen[camIdx] = DateTime.Now;
|
|
PUB.wsock_[logIdx].Stop();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Server shutdown failed:{ex.Message}");
|
|
}
|
|
return false;
|
|
}
|
|
else return true;
|
|
}
|
|
public static ManualResetEvent[] mre_send = new ManualResetEvent[2];
|
|
public static bool Send_WSock(eTarget camTarget, string data)
|
|
{
|
|
if (camTarget == eTarget.None) return false;
|
|
var camIdx = camTarget == eTarget.Left ? PUB.setting.CameraIndexL : PUB.setting.CameraIndexR;
|
|
var logIdx = camTarget == eTarget.Left ? 0 : 1;
|
|
if (PUB.wsock_[logIdx].IsListening == false || PUB.wsock_[logIdx].ListClients().Count() < 1)
|
|
{
|
|
PUB.log_[logIdx].AddAT($"[IGNORE-SEND] Not Listening or No Clients");
|
|
return false;
|
|
}
|
|
|
|
if (mre_send[camIdx].WaitOne(1000) == false)
|
|
{
|
|
PUB.log_[logIdx].AddE($"send error by mre-lock");
|
|
return false;
|
|
}
|
|
mre_send[camIdx].Reset();
|
|
var ws = PUB.wsock_[logIdx];
|
|
try
|
|
{
|
|
ws.ListClients().ToList().ForEach(t => ws.SendAsync(t, data).Wait());
|
|
PUB.log_[logIdx].AddI($"send Complete {data}");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"send error by {ex.Message}");
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
mre_send[camIdx].Set();
|
|
}
|
|
|
|
}
|
|
//마지막으로 전송에 사용한 데이터를 임시 저장한다. -211213 // 데이터 missing 현상 관련 처리 작업
|
|
public static string[] lastTxGuid = new string[] { string.Empty, string.Empty };
|
|
public static string[] lastTxRID = new string[] { string.Empty, string.Empty };
|
|
public static DateTime lastTxTime = DateTime.Now;
|
|
|
|
public static Boolean BarcodeParsing(int camIdx, List<string> data, string source)
|
|
{
|
|
if (data == null || data.Count < 1) return false;
|
|
|
|
PUB.lastbcd[camIdx] = string.Join("|", data);
|
|
var camTarget = PUB.GetTarget(camIdx);
|
|
if (camTarget == eTarget.None) return false;
|
|
var logIdx = camTarget == eTarget.Left ? 0 : 1;
|
|
|
|
//스트림데이터가 꺼져있다면 처리하지 않는다.
|
|
if (PUB.setting.DisableStreamData)
|
|
{
|
|
//트리거모드일때 보내는 플래그가 없다면 보내지 않는다.
|
|
if (IsTrigger[camIdx] == false) return false;
|
|
}
|
|
|
|
|
|
//통신이 연결되어있지 않다면 처리하지 않는다
|
|
if (PUB.wsock_[logIdx].IsListening == false || PUB.wsock_[logIdx].ListClients().Count() < 1) return false;
|
|
|
|
//파일을 저장한다.
|
|
var path = "";
|
|
if (PUB.mre[camIdx].WaitOne(100))
|
|
{
|
|
|
|
try
|
|
{
|
|
path = System.IO.Path.Combine(PUB.setting.ImageSavePath, $"[{camIdx}]barcode_{DateTime.Now.ToString("HHmmssfff")}.jpg");
|
|
PUB.mre[camIdx].Reset();
|
|
var fi = new System.IO.FileInfo(path);
|
|
if (fi.Directory.Exists == false) fi.Directory.Create();
|
|
PUB.OrgImage[camIdx].Save(fi.FullName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Save failed {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
PUB.mre[camIdx].Set();
|
|
}
|
|
}
|
|
|
|
|
|
//데이터 생성
|
|
var msg = new
|
|
{
|
|
guid = PUB.lastguid[camIdx],
|
|
command = PUB.lastcmd[camIdx],
|
|
data = data,
|
|
time = DateTime.Now.ToString(),
|
|
file = path
|
|
};
|
|
|
|
//마지막으로전송한 RID값이 동일한경우
|
|
if (lastTxRID[camIdx].Trim() == PUB.lastbcd[camIdx].Trim())
|
|
{
|
|
if (lastTxGuid[camIdx].Trim() != PUB.lastguid[camIdx].Trim())
|
|
{
|
|
var tsTx = DateTime.Now - lastTxTime;
|
|
if (tsTx.TotalSeconds < 5)
|
|
{
|
|
//GUID가 다른데 rid가 같다 /
|
|
//missing data 의 패턴잉ㅆ다. 2113
|
|
//이때는 전송하지 않고 그냥 skip하게한다.
|
|
PUB.lastguid[camIdx] = string.Empty;
|
|
PUB.lastcmd[camIdx] = string.Empty;
|
|
PUB.log_[logIdx].Add($"Only GUID of last transmission data differs, processing skip. (within 5 seconds only)");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//마지막으로 전송한 데이터 값 저장
|
|
lastTxGuid[camIdx] = PUB.lastguid[camIdx];
|
|
lastTxRID = PUB.lastbcd;
|
|
lastTxTime = DateTime.Now;
|
|
|
|
|
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(msg);
|
|
Boolean sendok = false;
|
|
try
|
|
{
|
|
if (PUB.lastbcd[camIdx].isEmpty() == false && PUB.setting.Log_BarcodeTx)
|
|
{
|
|
if (lastlog[camIdx] == PUB.lastbcd[camIdx])
|
|
{
|
|
var tsee = DateTime.Now - logtime[camIdx];
|
|
if (tsee.TotalSeconds > 60)
|
|
{
|
|
PUB.log_[logIdx].AddI(json);
|
|
lastlog[camIdx] = PUB.lastbcd[camIdx];
|
|
logtime[camIdx] = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PUB.log_[logIdx].AddI(json);
|
|
lastlog[camIdx] = PUB.lastbcd[camIdx];
|
|
}
|
|
}
|
|
//PUB.ws[camIdx].ListClients().ToList().ForEach(t => PUB.ws[camIdx].SendAsync(t, json).Wait());
|
|
PUB.lastsend[camIdx] = DateTime.Now;
|
|
Send_WSock(camTarget, json);
|
|
if (msg.guid.isEmpty() == false)
|
|
PUB.log_[logIdx].Add("Tx", json);
|
|
|
|
sendok = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].Add($"Send failed {ex.Message}");
|
|
sendok = false;
|
|
}
|
|
|
|
if (sendok == false)
|
|
{
|
|
try
|
|
{
|
|
PUB.log_[logIdx].AddAT("Closing server due to send failure");
|
|
PUB.Time_WS_Listen[camIdx] = DateTime.Now;
|
|
PUB.wsock_[logIdx].Stop();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Server shutdown failed:{ex.Message}");
|
|
}
|
|
try
|
|
{
|
|
PUB.log_[logIdx].AddAT("Closing socket due to send failure");
|
|
PUB.wsock_[logIdx].Stop();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PUB.log_[logIdx].AddE($"Socket shutdown failed:{ex.Message}");
|
|
}
|
|
return false;
|
|
}
|
|
else return true;
|
|
}
|
|
|
|
public static DateTime[] Time_WS_Connected = new DateTime[2];
|
|
public static DateTime[] Time_WS_Disconnected = new DateTime[2];
|
|
public static DateTime[] Time_WS_Listen = new DateTime[2];
|
|
public static DateTime[] Time_WS_Listen_Try = new DateTime[2];
|
|
public static DateTime[] Time_WS_Recv = new DateTime[2];
|
|
public static string[] WS_LastRecv = new string[] { string.Empty, string.Empty };
|
|
public static string[] WS_LastSend = new string[] { string.Empty, string.Empty };
|
|
|
|
|
|
public static void ChangeUIPopup(System.Windows.Forms.NumericUpDown valueCtl)
|
|
{
|
|
var value = valueCtl.Value.ToString();
|
|
var f = AR.UTIL.InputBox("input value", value);// new Dialog.fInput(value);
|
|
if(f.Item1 == true)
|
|
{
|
|
var val = decimal.Parse(f.Item2);
|
|
if (val < valueCtl.Minimum)
|
|
{
|
|
UTIL.MsgE(string.Format("Minimum input value is {0}.", valueCtl.Minimum));
|
|
val = valueCtl.Minimum;
|
|
}
|
|
if (val > valueCtl.Maximum)
|
|
{
|
|
UTIL.MsgE(string.Format("Maximum input value is {0}.", valueCtl.Maximum));
|
|
val = valueCtl.Maximum;
|
|
}
|
|
valueCtl.Value = val;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|