Files
atvstdla dc66158497 Add QRValidation project to repository
- 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>
2025-10-02 11:38:38 +09:00

2324 lines
101 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Configuration;
namespace UIControl
{
public partial class Loader : UserControl
{
#region "Variable - Private"
Timer tm;
Boolean _ardebugmode = false;
eScean _scean = eScean.Nomal;
//기타 내부 변수
int AnimationStepConv = 30; //컨베어 이동 애니메이션 최대 값
//int AnimationStepPort = 9; //포트 이동 애니메이션 최대 값
Boolean bRemakeRect = true; //이값이 활성화되면 각 영역을 다시 그리게 된다
DateTime updatetime = DateTime.Now; //화면을 다시 그린 시간
Brush BRPortBg = new SolidBrush(Color.FromArgb(50, Color.DimGray));
Brush BRDetectDn = new SolidBrush(Color.FromArgb(50, Color.DimGray));
StringFormat sfCenter;
StringFormat sfLeft = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center };
SolidBrush brVacOff = new SolidBrush(Color.FromArgb(150, Color.White));
SolidBrush brVacOn = new SolidBrush(Color.FromArgb(200, Color.Lime));
Pen penVacOn = new Pen(Color.Red, 5);
Pen penVacOff = new Pen(Color.Black, 5);
//영역(큰 그림?) (bRemakeRect 에 의 해 생성된다)
RectangleF rect_main = RectangleF.Empty;
RectangleF rect_frontShuttle = RectangleF.Empty;
RectangleF rect_rearShuttle = RectangleF.Empty;
RectangleF rect_conveyor = RectangleF.Empty;
RectangleF rect_picker = RectangleF.Empty;
//영역(피커)
RectangleF rect_picker_rear = RectangleF.Empty;
RectangleF rect_picker_front = RectangleF.Empty;
RectangleF rect_picker_rear_vac1 = RectangleF.Empty;
RectangleF rect_picker_rear_vac2 = RectangleF.Empty;
RectangleF rect_picker_rear_vac3 = RectangleF.Empty;
RectangleF rect_picker_rear_vac4 = RectangleF.Empty;
RectangleF rect_picker_front_vac1 = RectangleF.Empty;
RectangleF rect_picker_front_vac2 = RectangleF.Empty;
RectangleF rect_picker_front_vac3 = RectangleF.Empty;
RectangleF rect_picker_front_vac4 = RectangleF.Empty;
//X축 포트 (F-L:0, F-R:1, R-L:2, R-R:3
//RectangleF[] rect_port = new RectangleF[4];
RectangleF[] rect_zone = new RectangleF[11];
CIcon[] icons = new CIcon[6];
List<CMenuButton> Buttons = new List<CMenuButton>();
List<CMenuButton> menuButtons = new List<CMenuButton>();
/// <summary>
/// 외부에서 입력된 릴의 갯수
/// </summary>
int ExtInputCount = 0;
#endregion
#region "Variable - Public"
public double[] arMotorPosition = new double[] { 0, 0, 0, 0, 0 };
public int ConveyorRunPoint = 1; //컨베어 모터 이동시 이동 화살표의 위치값(내부 타이머에의해 증가함)
public double arMcLengthW = 1460;
public double arMcLengthH = 1350;//
#endregion
#region "Property"
/// <summary>
/// 현재 메뉴가 표시되어있는가?
/// </summary>
public Boolean HasPopupMenu { get; private set; }
/// <summary>
/// 현재표시된 메뉴는 사용자의 입력을 반드시 받아야 하는가?
/// </summary>
public Boolean PopupMenuRequireInput { get; private set; }
public Font arFont_PortMessage { get; set; }
public Boolean LockXF { get; set; }
public Boolean LockXR { get; set; }
public Boolean LockYP { get; set; }
public eScean Scean { get { return _scean; } set { _scean = value; bRemakeRect = true; this.Invalidate(); } }
//컨베이어에 설치된 자재 감지 센서
public Boolean[] arDI_Cv_Detect { get; set; }
//컨베이어 입구 안전 센서
public Boolean arInitMOT { get; set; }
public Boolean arJobEND { get; set; }
public Boolean arLowDiskSpace { get; set; } //용량 부족 메세지 여부
public double arFreespace { get; set; } //남은 디스크 용량 비율
public Boolean arDI_Safty_CvIn { get; set; }
public Boolean arDI_SaftyOk { get; set; }
//작업을 선택하면 설정되는 작업일자와 차수이다
public string arJobDate { get; set; }
public string arJobSeq { get; set; }
public Boolean[] arMOT_LimUp = new bool[] { false, false, false, false, false };
public Boolean[] arMOT_LimDn = new bool[] { false, false, false, false, false };
public Boolean[] arMOT_Origin = new bool[] { false, false, false, false, false };
public Boolean[] arMOT_Alm = new bool[] { false, false, false, false, false };
public Boolean[] arMOT_HSet = new bool[] { false, false, false, false, false };
public Boolean[] arMOT_SVOn = new bool[] { false, false, false, false, false };
public Boolean arFlag_WaitPLC { get; set; }
public Boolean arFlag_WaitBCD2 { get; set; }
public Boolean arFlag_WaitBCD1 { get; set; }
public Boolean arFlag_BusyExtIn { get { return ExtInputCount > 0; } }
public Boolean arFlag_Minspace { get; set; }
public byte arUnloaderSeq { get; set; }
public Boolean arFlag_UnloaderBusy { get; set; }
public Boolean arFlag_UnloaderErr { get; set; }
public Boolean arConn_REM { get; set; }
public Boolean arFG_RDY_YP_FPICKON { get; set; }
public Boolean arFG_RDY_YP_FPICKOF { get; set; }
public Boolean arFG_RDY_YP_RPICKON { get; set; }
public Boolean arFG_RDY_YP_RPICKOF { get; set; }
public Boolean arFG_CMD_YP_FPICKON { get; set; }
//public Boolean arFG_CMD_YP_FPICKOF { get; set; }
public Boolean arFG_CMD_YP_RPICKON { get; set; }
//public Boolean arFG_CMD_YP_RPICKOF { get; set; }
//컨베이어 출구 안전 센서
public Boolean arDI_Safty_CvOut { get; set; }
//비상정지 센서 상태
public Boolean arDI_Emergency { get; set; }
public Boolean arDIAir { get; set; }
public Boolean arConn_MOT { get; set; }
public Boolean arConn_DIO { get; set; }
public Boolean arConn_BCD { get; set; }
public Boolean arConn_PLC { get; set; }
public int arLastDetectIndex { get; set; }
public Boolean arDebugMode { get { return _ardebugmode; } set { this._ardebugmode = value; Invalidate(); } }
public CPort[] arVar_Port { get; set; }
public CPicker[] arVar_Picker { get; set; }
public Boolean arConvRun { get; set; }
public Boolean arIsRunning { get; set; }
public Boolean arFGInputMode { get; set; }
public Boolean arFGInputFL { get; set; }
public Boolean arFGInputFR { get; set; }
public Boolean arFGInputRL { get; set; }
public Boolean arFGInputRR { get; set; }
private List<CItem> zitem = new List<CItem>();
public long[] zonetime = new long[11];
public Font arFont_count { get; set; }
public Font arFont_picker { get; set; }
public double arMotorLengthY { get; set; }
public double arMotorLengthXF { get; set; }
public double arMotorLengthXR { get; set; }
private CMenu[] _menus = null;
public CMenu[] arMenus { get { return _menus; } set { _menus = value; this.Invalidate(); } }
#endregion
public Loader()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.ContainerControl, false);
this.SetStyle(ControlStyles.Selectable, true);
this.Resize += Loader_Resize;
sfCenter = new StringFormat();
sfCenter.Alignment = StringAlignment.Center;
sfCenter.LineAlignment = StringAlignment.Center;
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
tm = new Timer();
tm.Interval = 50; //10frame;
tm.Tick += Tm_Tick;
arLastDetectIndex = -1;
arFont_PortMessage = new Font("Consolas", 11, FontStyle.Bold);
arFont_picker = new Font("Arial", 10, FontStyle.Bold);
arUnloaderSeq = 0;
arMotorLengthY = 400;
arMotorLengthXF = 580;
arMotorLengthXR = 590;
arJobDate = string.Empty;
arJobSeq = string.Empty;
arDI_Cv_Detect = new bool[8];
for (int i = 0; i < arDI_Cv_Detect.Length; i++)
arDI_Cv_Detect[i] = false;
bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
if (designMode == false) tm.Start();
arFont_count = new Font("Consolas", 30, FontStyle.Bold);
//기본수량설정됨
arVar_Port = new CPort[4];
arVar_Picker = new CPicker[2];
for (int i = 0; i < rect_zone.Length; i++)
rect_zone[i] = RectangleF.Empty;
for (int i = 0; i < zonetime.Length; i++)
zonetime[i] = 0;
for (int i = 0; i < icons.Length; i++)
icons[i] = new CIcon();
//미리 10개를 생성한다. 슬롯에 10개이상 생기기 않는다.
zitem = new List<CItem>(10);
for (int i = 0; i < zitem.Count; i++)
zitem[i] = new CItem();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (this.icons == null || icons.Length < 1) return;
var item = this.icons.Where(t => t.Rect.Contains(e.Location)).FirstOrDefault();
//선택된 것이 잇다면 모두 해제를 해준다.
icons.Where(t => t.Focus == true).ToList().ForEach(t => t.Focus = false);
if (item != null)
{
item.Focus = true;
this.Cursor = Cursors.Hand;
}
else
{
this.Cursor = Cursors.Arrow;
}
}
public class ZoneItemClickEventargs : EventArgs
{
public CItem item { get; private set; }
public ZoneItemClickEventargs(CItem item_)
{
this.item = item_;
}
}
public class IconClickEventargs : EventArgs
{
public CIcon item { get; private set; }
public IconClickEventargs(CIcon item_)
{
this.item = item_;
}
}
public class MenuItemClickEventargs : EventArgs
{
public CMenuButton item { get; private set; }
public MenuItemClickEventargs(CMenuButton item_)
{
this.item = item_;
}
}
public event EventHandler<MenuItemClickEventargs> ButtonClick;
public event EventHandler<IconClickEventargs> IConClick;
public event EventHandler<ZoneItemClickEventargs> ZoneItemClick;
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button == MouseButtons.Left)
{
var item = this.icons.Where(t => t.Rect.Contains(e.Location)).FirstOrDefault();
if (item != null)
{
//다른 메뉴가 선택되어잇다면 동작하지 않게 한다.
if (IConClick != null)
IConClick(this, new IconClickEventargs(item));
}
//삭제된 아이템은 선택되지 않게 한다 201013
lock (zitem)
{
var zitem = this.zitem.Where(t => t.Rect.Contains(e.Location) && t.Delete == false).FirstOrDefault();
if (zitem != null)
{
//특정 존의 아이템을 선택했다
if (ZoneItemClick != null)
ZoneItemClick(this, new ZoneItemClickEventargs(zitem));
}
}
//메뉴의해 생성된 버튼
var zbbut = this.menuButtons.Where(t => t.Rect.Contains(e.Location)).FirstOrDefault();
if (zbbut != null)
{
//특정 존의 아이템을 선택했다
if (ButtonClick != null)
ButtonClick(this, new MenuItemClickEventargs(zbbut));
}
//아이콘
var zbut = this.Buttons.Where(t => t.Rect.Contains(e.Location)).FirstOrDefault();
if (zbut != null)
{
//특정 존의 아이템을 선택했다
if (ButtonClick != null)
ButtonClick(this, new MenuItemClickEventargs(zbut));
}
}
}
void Loader_Resize(object sender, EventArgs e)
{
bRemakeRect = true;
}
public void RemakeRect()
{
bRemakeRect = true;
}
void makeRect_Input()
{
rect_main = new RectangleF(
DisplayRectangle.Left + DisplayRectangle.Width * 0.05f,
DisplayRectangle.Top + DisplayRectangle.Height * 0.05f,
DisplayRectangle.Width * 0.9f,
DisplayRectangle.Height * 0.9f);
//화면에 꽉차도록 전체 영역을 할당한다.
var cx = rect_main.Left + rect_main.Width / 2.0f;
var cy = rect_main.Top + rect_main.Height / 2.0f;
var padding = (int)(rect_main.Width * 0.03);
arVar_Port[2].Rect = new Rectangle(
(int)rect_main.Left + padding,
(int)rect_main.Top + padding,
(int)((cx - rect_main.Left) - padding * 2),
(int)((cy - rect_main.Top) - padding * 2));
arVar_Port[3].Rect = new Rectangle(
(int)cx + padding,
(int)rect_main.Top + padding,
(int)((cx - rect_main.Left) - padding * 2),
(int)((cy - rect_main.Top) - padding * 2));
arVar_Port[0].Rect = new Rectangle(
(int)rect_main.Left + padding,
(int)cy + padding,
(int)((cx - rect_main.Left) - padding * 2),
(int)((cy - rect_main.Top) - padding * 2));
arVar_Port[1].Rect = new Rectangle(
(int)cx + padding,
(int)cy + padding,
(int)((cx - rect_main.Left) - padding * 2),
(int)((cy - rect_main.Top) - padding * 2));
}
void makeRect_MotHome()
{
rect_main = new RectangleF(
DisplayRectangle.Left + DisplayRectangle.Width * 0.05f,
DisplayRectangle.Top + DisplayRectangle.Height * 0.05f,
DisplayRectangle.Width * 0.9f,
DisplayRectangle.Height * 0.9f);
}
double CvtMMtoPX_W(double PosMM, double StartX)
{
//컨베어 기준으로 값을 반환한ㄷ.
return StartX + rect_main.Width * (PosMM / arMcLengthW);
}
double CvtMMtoPX_H(double PosMM, double StartY)
{
//컨베어 기준으로 값을 반환한ㄷ.
return StartY + rect_main.Height * (PosMM / arMcLengthH);
}
#region "Menu Method"
public void ClearMenu()
{
_menus = null;
this.Invalidate();
}
public void AddMenu(CMenu menu)
{
var curCnt = 0;
if (this._menus != null) curCnt = this._menus.Length;
Array.Resize(ref _menus, curCnt + 1);
_menus[curCnt] = menu;
this.Invalidate();
}
public void DelMenu(CMenu menu)
{
List<CMenu> newlist = new List<CMenu>();
for (int i = 0; i < _menus.Length; i++)
{
if (_menus[i] != menu) newlist.Add(_menus[i]);
}
this._menus = newlist.ToArray();
this.Invalidate();
}
public void DelMenu(int idx)
{
List<CMenu> newlist = new List<CMenu>();
for (int i = 0; i < _menus.Length; i++)
{
if (i != idx) newlist.Add(_menus[i]);
}
this._menus = newlist.ToArray();
this.Invalidate();
}
public void DelMenu()
{
//제거할 아이템이 없다
if (_menus == null || _menus.Length < 1) return;
if (_menus.Length == 1) _menus = null;
else
{
//마지막요소를 제거 해주낟
Array.Resize(ref _menus, _menus.Length - 1);
}
this.Invalidate();
}
#endregion
/// <summary>
/// 각 영역을 현재 크기대비하여 재계산 한다
/// </summary>
void makeRect_Normal()
{
rect_main = new RectangleF(
DisplayRectangle.Left + DisplayRectangle.Width * 0.025f,
DisplayRectangle.Top + DisplayRectangle.Height * 0.025f,
DisplayRectangle.Width * 0.95f,
DisplayRectangle.Height * 0.8f);
//X축 모션(셔틀) 표시
var xPos1 = rect_main.Left + (rect_main.Width * 0.175);
var xPos2 = rect_main.Right - xPos1;
var yposR = rect_main.Height * 0.15;
var yposF = rect_main.Height * 0.85;
var h0p5 = rect_main.Height * 0.03;
var w0p5 = (rect_main.Width * 0.035) * ((rect_main.Height * 1.0) / rect_main.Width);
//var conv_height = rect_main.Height * 0.3;
var cx = (float)(rect_main.Top + (rect_main.Width / 2.0));
var cy = (float)(rect_main.Top + (rect_main.Height / 2.0));
var conv_height = CvtMMtoPX_H(350, 0); //컨베이어 높이
var conv_width = CvtMMtoPX_W(arMcLengthW, 0); //컨베어이어너비는 장비 너비와 같다
rect_conveyor = new RectangleF(rect_main.Left,
(float)(rect_main.Top + (rect_main.Height - conv_height) / 2.0f),
(float)conv_width,
(float)conv_height);
var h10p = rect_main.Height * 0.03;
var pickWidth = rect_main.Width * 0.02f;
//프론트셔틀의 영역(가동 영역) - 아래서 450mm 떨어진곳
var xAxisLengthMM = arMcLengthW - 200; // 컨베어 길이에서 좌우 100mm 씩 리밋센서가 있다
var xAxisLengthPX = CvtMMtoPX_W(xAxisLengthMM, rect_conveyor.Left);
rect_frontShuttle = new RectangleF(
(float)(CvtMMtoPX_W(100, rect_conveyor.Left)),
(float)(CvtMMtoPX_H(arMcLengthH - 410 - 10, rect_main.Top)),
(float)xAxisLengthPX,
(float)(CvtMMtoPX_H(20, 0)));
//리어셔틀의 영역(가동 영역) - 위에서 450mm
rect_rearShuttle = new RectangleF(
(float)(CvtMMtoPX_W(100, rect_conveyor.Left)),
(float)(CvtMMtoPX_H(410 - 10, rect_main.Top)),
(float)xAxisLengthPX,
(float)(CvtMMtoPX_H(20, 0)));
//세로축 총길이 1400mm Y축 모터는 양끝에 100mm 의 여유가 있으며, Y축
var pickerX = CvtMMtoPX_W(750, rect_conveyor.Left);
rect_picker = new RectangleF(
(float)(pickerX - (pickWidth / 2.0f)),
(float)(rect_rearShuttle.Top),
(float)(pickWidth),
(float)(rect_frontShuttle.Bottom - rect_rearShuttle.Top)
);
//Y축 피커 관련 세부 영역 설정 (VAC 와 원)
//전체영역의 80% 영역에 Y-로봇의 축을 그린다.
//var motorMax = 400; //전체 가동 길이 400mm
RectangleF rect = rect_picker;
var MotPosPx = rect.Top + rect.Height * (this.arMotorPosition[0] / (this.arMotorLengthY * 1.0f));
cx = rect.Left + rect.Width / 2.0f;
//상(Rear), 하(Front)로 영역을 그린다
var port_height = rect.Height * 0.25f;
var port_width = port_height;
//;// var port_width = rect.Width * 3f;
//port_width = port_height;
var port_space = CvtMMtoPX_H(350 / 2.0f, 0);
var port_spacex = CvtMMtoPX_W(10, 0); ;
var PickerSizeW = CvtMMtoPX_W(145, 0);// (float)(Math.Max(CvtMMtoPX_W(150, 0), CvtMMtoPX_H(15, 0)));
var PickerSizeH = PickerSizeW;// CvtMMtoPX_H(130, 0);//(float)(Math.Max(CvtMMtoPX_W(150, 0), CvtMMtoPX_H(15, 0)));
var PickerSpaceH = CvtMMtoPX_H(350, 0);
rect_picker_rear = new RectangleF(
(float)(cx + port_spacex),
(float)(MotPosPx - PickerSpaceH - PickerSizeH / 2.0f),
(float)PickerSizeW,
(float)PickerSizeH);
rect_picker_front = new RectangleF(
(float)(cx + port_spacex),
(float)(MotPosPx),
(float)PickerSizeW,
(float)PickerSizeH);
var pointoffset = 5;
rect_picker_rear_vac1 = new RectangleF(
(float)rect_picker_rear.Left + pointoffset,
(float)rect_picker_rear.Top + pointoffset,
(float)rect_picker_rear.Width * 0.2f,
(float)rect_picker_rear.Height * 0.2f);
rect_picker_rear_vac2 = new RectangleF(
(float)rect_picker_rear.Right - rect_picker_rear_vac1.Width - pointoffset,
(float)rect_picker_rear_vac1.Top,
(float)rect_picker_rear_vac1.Width,
(float)rect_picker_rear_vac1.Height);
rect_picker_rear_vac3 = new RectangleF(
(float)rect_picker_rear.Left + pointoffset,
(float)rect_picker_rear.Bottom - rect_picker_rear_vac1.Height - pointoffset,
(float)rect_picker_rear_vac1.Width,
(float)rect_picker_rear_vac1.Height);
rect_picker_rear_vac4 = new RectangleF(
(float)rect_picker_rear.Right - rect_picker_rear_vac1.Width - pointoffset,
(float)rect_picker_rear.Bottom - rect_picker_rear_vac1.Height - pointoffset,
(float)rect_picker_rear_vac1.Width,
(float)rect_picker_rear_vac1.Height);
rect_picker_front_vac1 = new RectangleF(
(float)rect_picker_front.Left + pointoffset,
(float)rect_picker_front.Top + pointoffset,
(float)rect_picker_front.Width * 0.2f,
(float)rect_picker_front.Height * 0.2f);
rect_picker_front_vac2 = new RectangleF(
(float)rect_picker_front.Right - rect_picker_front_vac1.Width - pointoffset,
(float)rect_picker_front_vac1.Top,
(float)rect_picker_front_vac1.Width,
(float)rect_picker_front_vac1.Height);
rect_picker_front_vac3 = new RectangleF(
(float)rect_picker_front.Left + pointoffset,
(float)rect_picker_front.Bottom - rect_picker_front_vac1.Height - pointoffset,
(float)rect_picker_front_vac1.Width,
(float)rect_picker_front_vac1.Height);
rect_picker_front_vac4 = new RectangleF(
(float)rect_picker_front.Right - rect_picker_front_vac1.Width - pointoffset,
(float)rect_picker_front.Bottom - rect_picker_front_vac1.Height - pointoffset,
(float)rect_picker_front_vac1.Width,
(float)rect_picker_front_vac1.Height);
//각 존의 영역 확인
//컨베어의 릴감지센서 위치를 표시한다
var senseW = rect_conveyor.Width * 0.02f;
var senseH = rect_conveyor.Height * 0.05f;
var slist = new double[] { 20, 340, 550, 890, 1110, 1440 };// new double[] { 0.02, 0.25, 0.4, 0.6, 0.75, 0.9 }; //센서의 위치정보(컨베어좌측기준)
//센서가 포함된 존의 영역을 생성한다
for (int i = 0; i < slist.Length; i++)
{
//선으로 영역을 표시해준다.
var PosMM = rect_conveyor.Width * (slist[i] / arMcLengthW);
var x = (float)(rect_conveyor.Left + PosMM);
var rx = x - senseW / 2.0f;
rect_zone[i * 2] = new RectangleF(rx, rect_conveyor.Top, senseW, rect_conveyor.Height);
}
var arraylis = new int[] { 1, 3, 5, 7, 9 };
var zterm = 4;
for (int i = 0; i < arraylis.Length; i++)
{
var idx = arraylis[i];
rect_zone[idx] = new RectangleF(
rect_zone[idx - 1].Right + zterm,
rect_zone[idx - 1].Top,
rect_zone[idx + 1].Left - rect_zone[idx - 1].Right - zterm * 2,
rect_zone[idx - 1].Height);
}
//아이콘영역
float ix = rect_main.Left;
float iy = rect_main.Bottom + 20;
Size iconSizeW = new Size(80, 80);
int idxIcon = 0;
ix = rect_main.Left;
iy = rect_main.Bottom + 20;
icons[idxIcon++] = new CIcon("bcd", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
ix += 85;
icons[idxIcon++] = new CIcon("plc", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
ix += 85;
icons[idxIcon++] = new CIcon("mot", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
ix += 85;
icons[idxIcon++] = new CIcon("emg", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
ix += 85;
icons[idxIcon++] = new CIcon("sft", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
ix += 85;
icons[idxIcon++] = new CIcon("air", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
//ix += 85;
//icons[idxIcon++] = new CIcon("debug", new RectangleF(ix, iy, iconSizeW.Width, iconSizeW.Height));
////버튼 생성해준다
//var ButL = this.Buttons.Where(t => t.Tag == "INPUTL").FirstOrDefault();
//var ButR = this.Buttons.Where(t => t.Tag == "INPUTR").FirstOrDefault();
//var butWidth = 64;
//var butPadding = 1;
//var butHeight = butWidth;// (int)(rect_conveyor.Height - butPadding * 2);
//if (ButL == null)
//{
// var newbutton = new CMenuButton("투입\n출구", "INPUTL");
// newbutton.BorderSize = 1;
// newbutton.Shape = eButtonType.Rectangle;
// newbutton.Rect = new Rectangle((int)rect_main.Left + butPadding, (int)rect_main.Bottom - butHeight - butPadding, butWidth, butHeight);
// this.Buttons.Add(newbutton);
//}
//else
//{
// ButL.Rect = new Rectangle((int)rect_main.Left + butPadding, (int)rect_main.Bottom - butHeight - butPadding, butWidth, butHeight);
// ButL.Shape = eButtonType.Rectangle;
//}
//if (ButR == null)
//{
// var newbutton = new CMenuButton("투입\n입구", "INPUTR");
// newbutton.BorderSize = 1;
// newbutton.Shape = eButtonType.Rectangle;
// newbutton.Rect = new Rectangle((int)rect_main.Right - butWidth - butPadding, (int)rect_main.Bottom - butHeight - butPadding, butWidth, butHeight);
// this.Buttons.Add(newbutton);
//}
//else
//{
// ButR.Rect = new Rectangle((int)rect_main.Right - butWidth - butPadding, (int)rect_main.Bottom - butHeight - butPadding, butWidth, butHeight);
// ButR.Shape = eButtonType.Rectangle;
//}
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = InterpolationMode.High;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
base.OnPaint(e);
if (bRemakeRect)
{
if (Scean == eScean.Nomal) makeRect_Normal();
else if (Scean == eScean.MotHome) makeRect_MotHome();
else if (Scean == eScean.Input) makeRect_Input();
bRemakeRect = false;
}
try
{
if (this.Scean == eScean.Nomal) Scean_Normal(e.Graphics);
else if (this.Scean == eScean.MotHome) Scean_MotHome(e.Graphics);
else if (this.Scean == eScean.Input) Scean_Input(e.Graphics);
}
catch (Exception ex)
{
//오류발생시 해당 오류를 표시한다 210110
e.Graphics.DrawString(ex.Message, this.Font, Brushes.Red, 100, 100);
}
//삭제대상 아이템을 삭제한다
ZoneItem_AutoClear();
//var str = "CAN DROP:" + isCanPickerDrop.ToString() + " : HOLD;" + zoneitemholding.ToString//() + ",TOT:" + ZoneItemCountTotal.ToString();
// e.Graphics.DrawString(str, this.Font, Brushes.White, 100, 100);
}
public double[] HomeProgress { get; set; }
public UIControl.CItem ZoneItem_Get(int zoneIndex)
{
lock (zitem)
return zitem.Where(t => t.ZoneIndex == zoneIndex).FirstOrDefault();
}
public UIControl.CItem ZoneItem_Get(string guid)
{
lock (zitem)
return zitem.Where(t => t.GUID == guid).FirstOrDefault();
}
public UIControl.CItem ZoneItem_GetJ(string jguid)
{
lock (zitem)
return zitem.Where(t => t.JGUID == jguid).FirstOrDefault();
}
//public int CountZoneItem(int zoneIndex)
//{
// return zoneitem.Where(t => t.Delete == false && t.ZoneIndex == zoneIndex).Count();
//}
private void ZoneItem_AutoClear()
{
//삭제된 아이템을 소거한다(작업중인녀석은 삭제되지 않게한다)
lock (zitem)
{
var delitems = this.zitem.Where(t => t.Delete == true && t.Processing == false).ToList();
for (int i = delitems.Count - 1; i >= 0; i--)
{
//이미 삭제된 아이템이므로 수량에는 적용하지 않는다
var item = delitems[i];
RaiseMessage("[{0}] 아이템소거 : Zone:{1},iPort:{2},Id:{3},Sid:{4},jguid:{5}", false, item.Seq, item.ZoneIndex, item.iPort, item.RID, item.SID, item.JGUID);
this.zitem.Remove(delitems[i]);
}
}
}
public void ZoneItem_Clear()
{
RaiseMessage("ClearZoneItem", false);
lock (zitem)
{
//직접모두 삭제하게 함
//this.zitem.ForEach((t) => { t.Delete = true; t.ZoneIndex = -1; });
this.zitem.Clear();
ExtInputCount = 0;
//this.ZoneItemCountTotal = 0;
}
}
public void ZoneItem_Delete(string itemguid)
{
lock (zitem)
{
var item = zitem.Where(t => t.GUID == itemguid).FirstOrDefault();
if (item != null)
{
if (item.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount -= 1;
item.Delete = true;
item.ZoneIndex = -1;
if (ExtInputCount < 0) ExtInputCount = 0;
}
// this.ZoneItemCountTotal -= 1;
}
}
public void ZoneItem_Delete(UIControl.CItem item)
{
RaiseMessage("[{0}] Delte Item : ZONE:{1},iPORT:{2},RID:{3},SID:{4}", false, item.Seq, item.ZoneIndex, item.iPort, item.RID, item.SID);
item.Delete = true;
if (item.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount -= 1;
item.ZoneIndex = -1;
if (ExtInputCount < 0) ExtInputCount = 0;
}
/// <summary>
/// 지정된 시간을 초과한 자료를 삭제한다
/// </summary>
/// <param name="timeSec"></param>
public List<string> ZoneItem_GetOldItemList(int timeSec)
{
var retval = new List<string>();
var basetime = DateTime.Now.AddSeconds(-timeSec);
lock (zitem)
{
var list = this.zitem.Where(t => t.Delete == false && t.InTime <= basetime).ToList();
foreach (var item in list)
retval.Add(item.GUID);
}
return retval;
}
public void ZoneItem_Add(UIControl.CItem item)
{
//해당 아이템을 지정한 존에 추가한다.
//만약 해당 존에 아이템이 있다면 정보를 변형만 한다
lock (zitem)
{
var curitems = this.zitem.Where(t => t.ZoneIndex == item.ZoneIndex && t.Delete == false).ToList();
if (curitems.Count == 0)
{
RaiseMessage("[{0}] ##### AddItem Zone:{1},iPort:{2},JGUID:{3}", false, item.Seq, item.ZoneIndex, item.iPort, item.JGUID);
item.Delete = false;
this.zitem.Add(item);
if (item.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount += 1;
//return item.GUID;
}
else
{
//기존 정보를 업데이트 해버린다.
var firstdr = curitems.FirstOrDefault();
if (firstdr != null)
{
if (firstdr.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount -= 1;
RaiseMessage("[{0}] ##### UpdateItem Zone:{1},iPort:{2},Id:{3},Sid:{4} => Zone:{5},iPort:{6},Id:{7},Sid:{8}", true,
firstdr.Seq,
firstdr.ZoneIndex, firstdr.iPort, firstdr.RID, firstdr.SID,
item.ZoneIndex, item.iPort, item.RID, item.SID);
firstdr.ZoneIndex = item.ZoneIndex;
firstdr.DropTime = item.DropTime;
firstdr.InTime = item.InTime;
firstdr.iPort = item.iPort;
firstdr.Size = item.Size;
firstdr.Delete = false;
if (item.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount += 1;
}
//return item.GUID;
}
if (ExtInputCount < 0) ExtInputCount = 0;
}
}
/// <summary>
/// 해당 존에 아이템이 들어왔다
/// </summary>
/// <param name="InputZoneIdx"></param>
public CItem ZoneItem_MoveIn(int InputZoneIdx, int offset = 1)
{
if (InputZoneIdx < 0 || InputZoneIdx > 9) throw new Exception("zonindex 값은 0~10 입니다");
//RaiseMessage("CVItem In : 존에 아이템 투입 입력존 : {0}", false, InputZoneIdx);
//해당 아이템 이후의 자재를 모두 Offset 만큼 shit 해준다
//Boolean shiftok = false;
//입력하려는 존 이후의 1개 데이터를 가져와서 존을 이동시켜준다
CItem target = null;
lock (zitem)
{
target = this.zitem.Where(t => t.Delete == false && t.ZoneIndex > InputZoneIdx && t.ZoneIndex <= InputZoneIdx + offset).OrderBy(t => t.ZoneIndex).FirstOrDefault();
if (target != null)
{
//RaiseMessage("CVItemIn : 아이템 존 설정 Zone:{0},iPort:{1},Id:{2},Sid:{3} => NewZone:{4}", false, target.ZoneIndex, target.iPort, target.RID, target.SID, InputZoneIdx);
if (target.ZoneIndex > ExitInputBusyBaseZone) ExtInputCount -= 1;
if (InputZoneIdx > ExitInputBusyBaseZone) ExtInputCount += 1;
target.ZoneIndex = InputZoneIdx;
target.ZoneIntime = DateTime.Now;
//shiftok = true;
}
else RaiseMessage("아이템이동 실패 대상 존 " + InputZoneIdx.ToString() + ": 아이템이 없습니다, 현재:" + ZoneItemCountTotal.ToString() + "개 있음", true, null);
}
if (ExtInputCount < 0) ExtInputCount = 0;
return target;
}
int ExitInputBusyBaseZone = 5;
public Boolean isCanPickerDrop
{
get
{
//현재 존에 1개까지는 놓을수 있지만 그 이상 있다면 놓을 수없다
var tsDrop = DateTime.Now - LastDropTime;
//마지막으로 놓은시간이후 3초 미만은 놓지 못하게 한다
//단 이경우는 컨베이어가 멈춰있는 경우에는 적용하지 못한다
if (tsDrop.TotalSeconds < 2) return false;
//절대 2개이상은 놓지못한다
if (ZoneItemCountTotal >= 2) return false;
else return zoneitemholding == 0;
//return this.zoneitem.Where(t => t.Delete == false && t.ZoneIndex > 1).Count() == 0;
}
}
public int ZoneItemCountTotal
{
get
{
int retval = -1;
lock (zitem)
{
retval = this.zitem.Where(t => t.Delete == false && t.ZoneIndex != -1).Count();
}
return retval;
}
}
public Boolean ZoneItemDoubleError
{
get
{
int retval = 0;
lock (zitem)
{
retval = this.zitem.Where(t => t.Delete == false && t.ZoneIndex >= 0 && t.ZoneIndex <= 1).Count(); //중복오류
}
return retval > 1;
}
}
/// <summary>
/// Z축이 아이템을 내려놓은 시간(연속으로 놓는 증상이 발생하여, 일단 이것으로 2초이상 빠르게 놓는일이 없도록 한다)
/// </summary>
public DateTime LastDropTime = DateTime.Now;
public int zoneitemholding
{
get
{
int retval = -1;
lock (zitem)
{
//int holdCount = 2;
int inch13cnt = this.zitem.Where(t => t.Delete == false && t.Size == "13").Count();
if (inch13cnt > 0)
retval = this.zitem.Where(t => t.Delete == false && t.ZoneIndex >= CanDropCount13).Count();
else
retval = this.zitem.Where(t => t.Delete == false && t.ZoneIndex >= CanDropCount7).Count();
}
return retval;
}
}
public int CanDropCount7 { get; set; }
public int CanDropCount13 { get; set; }
//private int ZoneItemCountTotal = 0;//
//private int zoneitemholding = 0;
/// <summary>
/// 컨베이어에 아이템이 있는가?
/// </summary>
public Boolean CVHasItem
{
get
{
//return ZoneItemCountTotal > 0;
Boolean cnt = false;
lock (zitem)
{
cnt = this.zitem.Where(t => t.Delete == false && t.ZoneIndex != -1).Count() > 0;
}
return cnt;
}
}
/// <summary>
/// 바코드 존의 데이터를 변경해준다.
/// </summary>
/// <param name="_reelid"></param>
/// <param name="_sid"></param>
/// <param name="_rawdata"></param>
public CItem SetBarcodeZone(string jguid, string _reelid, string _sid, string _lot, string _rawdata, int qty_, string _msg, DateTime _sTime)
{
//바코드 존에 있는 아이템을 확인한다
var item = ZoneItem_GetJ(jguid);
if (item == null)
{
RaiseMessage("바코드 존 아이템 찾기 실패 GUID:{0}", true, jguid);
return null;
}
else if (item.BarcodeDone)
{
if (item.RID == _reelid && item.SID == _sid)
{
RaiseMessage("[{0}] 바코드할당 스킵(동일 바코드값 확인) jguid:{1}", true, item.Seq, jguid);
}
else RaiseMessage("[{0}] 바코드할당 실패(이미 등록된 아이템) jguid:{1}", true, item.Seq, jguid);
return item;
}
item.RID = _reelid;
item.SID = _sid;
item.VLOT = _lot;
item.Qty = qty_;
item.BarcodeRaw = _rawdata;
item.BarcodeEnd = DateTime.Now;
item.BarcodeMsg = _msg;
item.BarcodeStart = _sTime;
item.BarcodeDone = true;
RaiseMessage("[{0}] SetBarcode Zone:{1},RID:{2},SID:{3},msg{4},time:{5},jguid:{6}", false, item.Seq, item.ZoneIndex, _reelid, _sid, _msg, _sTime, jguid);
return item;
}
//public int SetBarcodeZone(string _reelid, string _sid, string _rawdata, string _msg, DateTime _sTime)
//{
// RaiseMessage("SetBarcodeZone : id:{0},sid:{1},msg{2},time:{3}", false, _reelid, _sid, _msg, _sTime);
// //바코드 존에 있는 아이템을 확인한다
// int[] bcdzone = new int[] { 1, 2, 3 }; //1,2,3번 존에 할당한다
// for (int i = 0; i < bcdzone.Length; i++)
// {
// var item = ZoneItem_Get(bcdzone[i]);
// if (item == null || item.BarcodeDone) continue;
// if (string.IsNullOrEmpty(item.RID))
// {
// //존에서 아이템이 검출되었다
// //동일한 데이터가 있다면
// item.RID = _reelid;
// item.SID = _sid;
// item.BarcodeRaw = _rawdata;
// item.BarcodeEnd = DateTime.Now;
// item.BarcodeMsg = _msg;
// item.BarcodeStart = _sTime;
// item.BarcodeDone = true;
// return bcdzone[i];
// }
// else if (item.RID != _reelid)
// {
// //이미 등록된 아이템이다 (??)
// RaiseMessage("SetBarcodeZone : 지금 등록하려는 바코드가 이미 등록되었다! id:{0}, 등록되어있는 존 : {1},등록시간:{2} ", true, _reelid, bcdzone[i], item.BarcodeStart);
// continue;
// }
// else
// {
// //같은게 중복으로 인식되었으므로 처리하지 않아도 된다
// RaiseMessage("SetBarcodeZone : 중복건으로 처리하지 않는다", false);
// return bcdzone[i];
// }
// }
// RaiseMessage("1~3번 존 안에서 아이템을 찾지 못해 할당할 수 없습니다", true);
// return -1;
//}
void RaiseMessage(string m, Boolean iserr, params object[] args)
{
if (args != null && args.Length > 0) m = string.Format(m, args);
if (Message != null) Message(this, new MessageArgs(m, iserr));
}
//public void CVItemMove(int oldZoneIdx)
//{
// if (oldZoneIdx < 1 || oldZoneIdx > 10) throw new Exception("zonindex 값은 1~10 입니다");
// //해당 아이템 이하의 데이터를 모두 -1 한다.
// this.zoneitem.Where(t => t.Delete == false && t.ZoneIndex >= oldZoneIdx).ToList().ForEach((t) =>
// {
// t.ZoneIndex = t.ZoneIndex - 1;
// t.ZoneIntime = DateTime.Now;
// });
//}
/// <summary>
/// 아이템을 DROP해도 되는가? 존 3번 부터 아이템이 존재하면 drop 불가능으로 한다
/// </summary>
void Scean_Input(Graphics g)
{
//var sb = new System.Text.StringBuilder();
//sb.AppendLine("자재 투입 모드");
//for (int i = 0; i < 4; i++)
//{
// var p = arVar_Port[i];
// sb.AppendLine(string.Format("P{0} A:{1},DETU:{2},ENB:{3},LIML:{4},LIMH:{5},OVR:{6},RDY:{7}" +
// ",MDIR:{8},MRUN:{9},SFTY:{10} :: {11}",
// i + 1, p.AlignOK, p.DetectUp, p.Enable, p.LimitLower,
// p.LimitUpper, p.OverLoad, p.Ready, p.MotorDir, p.MotorRun, p.SaftyErr, p.title));
//}
//g.DrawString(sb.ToString(), this.arFont_PortMessage, Brushes.White, 100, 100);
//각 포트영역 테두리 그리기
for (int i = 0; i < arVar_Port.Length; i++)
{
var p = arVar_Port[i];
var inputActive = false;
if (i == 0) inputActive = arFGInputFL;
else if (i == 1) inputActive = arFGInputFR;
else if (i == 2) inputActive = arFGInputRL;
else if (i == 3) inputActive = arFGInputRR;
p.Display(g, this.arFont_count, this.arFont_PortMessage, this.arFGInputMode, inputActive);
}
}
void Scean_MotHome(Graphics g)
{
//g.DrawString("mot home", this.Font, Brushes.Black, 100, 100);
g.DrawRectangle(new Pen(Color.SteelBlue, 10), this.rect_main.Left, rect_main.Top, rect_main.Width, rect_main.Height);
Font f = new Font(this.Font.Name, 50f, FontStyle.Bold);
var rectTitle = new RectangleF(
rect_main.Left,
rect_main.Top,
rect_main.Width,
rect_main.Height * 0.25f);
g.DrawString("MOTION HOME", f, Brushes.White, rectTitle, sfCenter); ;
// g.DrawRectangle(Pens.Red, rectTitle.Left, rectTitle.Top, rectTitle.Width, rectTitle.Height);
var rectBody = new RectangleF(
rect_main.Left,
rectTitle.Bottom + 10,
rect_main.Width,
rect_main.Height - rectTitle.Height - 10);
// g.DrawRectangle(Pens.White, rectBody.Left, rectBody.Top, rectBody.Width, rectBody.Height);
var rectT = new Rectangle(
(int)(rectBody.Left + 20),
(int)(rectBody.Top + 10),
(int)(rectBody.Width * 0.23f),
(int)(rectBody.Height * 0.07));
var rectXF = new Rectangle(
(int)(rectT.Right + 20),
(int)(rectBody.Top + 10),
(int)(rectBody.Width - rectT.Width - rectT.Left - 10),
(int)(rectBody.Height * 0.07));
var titles = new string[] { "Y-PICKER", "Z-FRONT", "Z-REAR", "X-FRONT", "X-REAR" };
for (int i = 0; i < 5; i++)
{
var perc = HomeProgress[i];
var title = titles[i];
var offsetY = (rectBody.Height * 0.15f);
rectXF.Offset(0, (int)offsetY);
rectT.Offset(0, (int)offsetY);
//g.DrawRectangle(Pens.Yellow, rectT.Left, rectT.Top, rectT.Width, rectT.Height);
using (Font f2 = new Font(this.Font.Name, 20f, FontStyle.Bold))
{
g.DrawString("* " + title, f2, Brushes.Lime, rectT, sfLeft);
}
LinearGradientBrush brProgr = new LinearGradientBrush(rectXF, Color.Gold, Color.Yellow, LinearGradientMode.Vertical);
var rectXF_P = new Rectangle(rectXF.Left, rectXF.Top, (int)(rectXF.Width * (perc / 100.0)), rectXF.Height);
g.FillRectangle(brProgr, rectXF_P);
g.DrawRectangle(Pens.Gray, rectXF);
brProgr.Dispose();
}
f.Dispose();
}
void Scean_Normal(Graphics g)
{
///50mm 마다 그리드를 처리한다
//using (Font f = new Font("Consolas", 7))
//{
// for (float i = 0; i < arMcLengthW; i += 50.0f)
// {
// var fs = g.MeasureString(i.ToString(), f);
// var px = (float)(CvtMMtoPX_W(i, rect_main.Left));
// g.DrawLine(Pens.DimGray, px, (float)rect_main.Top, px, (float)rect_main.Bottom);
// g.DrawString(i.ToString(), f, Brushes.White, new PointF(px - (fs.Width / 2.0f), rect_main.Top - fs.Height));
// }
// for (float i = 0; i < arMcLengthH; i += 50.0f)
// {
// var fs = g.MeasureString(i.ToString(), f);
// var px = (float)(CvtMMtoPX_H(i, rect_main.Top));
// g.DrawLine(Pens.DimGray, (float)rect_main.Left, px, (float)rect_main.Right, px);
// if (i == 0) continue;
// g.DrawString(i.ToString(), f, Brushes.White, new PointF(rect_main.Left, px - (fs.Height / 2.0f)));
// }
//}
Draw_Icon(g);
//전체 영역 테두리
g.DrawRectangle(new Pen(Color.DimGray, 2), rect_main.Left, rect_main.Top, rect_main.Width, rect_main.Height);
//컨베어 그리기
Draw_Conveyor(g, rect_conveyor);
//셔틀표시
Draw_BallScrewRail(g, rect_frontShuttle, 50, 5, false, LockXF, this.arMOT_Origin[(int)eAxis.X_F], arMOT_LimDn[(int)eAxis.X_F], arMOT_LimUp[(int)eAxis.X_F]);
Draw_BallScrewRail(g, rect_rearShuttle, 50, 5, false, LockXR, this.arMOT_Origin[(int)eAxis.X_R], arMOT_LimDn[(int)eAxis.X_R], arMOT_LimUp[(int)eAxis.X_R]);
//포트표시(셔틀위에 표시됨)
Draw_Port(g, arMotorPosition[3], arMotorLengthXF, rect_frontShuttle, 0); //front
Draw_Port(g, arMotorPosition[4], arMotorLengthXR, rect_rearShuttle, 2); //rear
//Y축 레일표시
Draw_BallScrewRail(g, rect_picker, 50, 5, true, LockYP, this.arMOT_Origin[(int)eAxis.Y_P], arMOT_LimDn[(int)eAxis.Y_P], arMOT_LimUp[(int)eAxis.Y_P]);
//모터 Y축
Draw_PickerY(g, rect_picker);
Draw_Zone(g);
Draw_CVItem(g);
Draw_Button(g);
//우측상단에 작업일자와 차수를 표시한다
if (string.IsNullOrEmpty(arJobDate) == false && string.IsNullOrEmpty(arJobSeq) == false)
{
var msg = arJobDate + ":" + arJobSeq;
var fsizeseq = g.MeasureString(msg, this.Font);
var rectseqinfo = new RectangleF(rect_main.Right - fsizeseq.Width * 1.15f, rect_main.Bottom, fsizeseq.Width * 1.15f, fsizeseq.Height);
g.FillRectangle(new SolidBrush(Color.FromArgb(64, 64, 64)), rectseqinfo);
g.DrawString(arJobDate + ":" + arJobSeq, this.Font, Brushes.White, rectseqinfo, sfCenter);
g.DrawRectangle(Pens.White, rectseqinfo.Left, rectseqinfo.Top, rectseqinfo.Width, rectseqinfo.Height);
}
//정보 표시 (나중에 제거해야함) 별도 인포박스형태를 취해야 함
Draw_Info(g, rect_conveyor);
Draw_Error(g);
//메뉴는 최상위에 표시한다
Draw_Menu(g);
if (arDebugMode)
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("Loader Control");
sb.AppendLine(string.Format("Display {0} / pickup zone count {1} / total item count {2}", DisplayRectangle, zoneitemholding, ZoneItemCountTotal));
g.DrawString(sb.ToString(), this.Font, Brushes.White, 10, 10);
updatetime = DateTime.Now;
//작업 수량 및 전체수량을 표시함
var sb2 = new System.Text.StringBuilder();
if (arVar_Port != null && arVar_Port.Length > 0)
{
//sb2.AppendLine(string.Format("입(PICKER)/출(SS:DET1)/겹침(SS:DET1) : {0}/{1}/{2}",
// ar_cnt_in, ar_cnt_out, ar_cnt_outdup));
//sb2.AppendLine(string.Format("바코드 입/출:CMD/DUP/READ/ERR/중복수신/할당실패 {0}/{1}:{2}/{3}/{4}/{5}/{6}/{7}",
// ar_cnt_barcodein, ar_cnt_barcodeot, ar_cnt_barcodecmd, ar_cnt_barcodedup, ar_cnt_barcoderead, ar_cnt_barcodereaderr, ar_cnt_barcodereaddup, ar_cnt_barcodeAssignErr));
//sb2.AppendLine(string.Format("언로더 입/출:CMD/DUP {0}/{1}:{2}/{3}",
// ar_cnt_plcin, ar_cnt_plcot, ar_cnt_plccmd, ar_cnt_plcdup));
sb2.AppendLine(string.Format("DIO:{4},BCD:{5},PLC:{6}\n" +
"YP_RDY {7},{8},{9},{10}\n" +
"YP_CMD {7},{8},{9},{10}\n",
arVar_Port[0].reelCount,
arVar_Port[1].reelCount,
arVar_Port[2].reelCount,
arVar_Port[3].reelCount,
arConn_DIO,
arConn_BCD,
arConn_PLC,
arFG_RDY_YP_FPICKON, arFG_RDY_YP_FPICKOF, arFG_RDY_YP_RPICKON, arFG_RDY_YP_RPICKOF,
arFG_CMD_YP_FPICKON, arFG_CMD_YP_RPICKON));
}
using (var f = new Font("Consolas", 15f, FontStyle.Bold))
g.DrawString(sb.ToString(), f, Brushes.SkyBlue, rect_conveyor.Left + 20, rect_conveyor.Top + 50);
}
}
void Draw_Zone(Graphics g)
{
//
if (arFlag_WaitBCD2) //바코드 대기중이라면 해당 존을 강조해준다
{
var zone = rect_zone[2];
g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Violet)), zone.Left, rect_main.Top, zone.Width, rect_main.Height);
}
if (arFlag_WaitBCD1) //바코드 대기중이라면 해당 존을 강조해준다
{
var zone = rect_zone[3];
g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Gold)), zone.Left, zone.Top, zone.Width, zone.Height);
}
if (arFlag_WaitPLC)
{
var zone = rect_zone[0];
g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Violet)), zone.Left, this.rect_main.Top, zone.Width, this.rect_main.Height);
}
}
/// <summary>
/// 컨베이어의 아이템을 화면에 표시한ㄷ
/// </summary>
/// <param name="g"></param>
void Draw_CVItem(Graphics g)
{
//lock 개체적용 210110
lock (zitem)
{
var items = this.zitem.Where(t => t.Delete == false).ToList();
foreach (var item in items)
{
var zone = rect_zone[item.ZoneIndex];
var itemwidth = rect_conveyor.Width * 0.09f;
//해당 존의 중앙에 아이템을 그린다.
var cx = zone.Left + zone.Width / 2.0f;
var cy = zone.Top + zone.Height / 2.0f;
item.Rect = new Rectangle(
(int)(cx - itemwidth / 2.0f),
(int)(cy - itemwidth / 2.0f),
(int)itemwidth,
(int)itemwidth);
//원을 그리다.
if (item.hasBarcode)
{
if (item.Size == "13")
{
using (LinearGradientBrush br = new LinearGradientBrush(item.Rect, Color.DeepSkyBlue, Color.LightSkyBlue, LinearGradientMode.Vertical))
{
g.FillEllipse(br, item.Rect);
}
}
else
{
using (LinearGradientBrush br = new LinearGradientBrush(item.Rect, Color.LightSkyBlue, Color.DeepSkyBlue, LinearGradientMode.Vertical))
{
g.FillEllipse(br, item.Rect);
}
}
}
else
{
if (item.iPort == -1) //외부입력데이터
{
using (LinearGradientBrush br = new LinearGradientBrush(item.Rect, Color.Magenta, Color.DimGray, LinearGradientMode.BackwardDiagonal))
{
g.FillEllipse(br, item.Rect);
}
}
else
{
using (LinearGradientBrush br = new LinearGradientBrush(item.Rect, Color.Gray, Color.DimGray, LinearGradientMode.BackwardDiagonal))
{
g.FillEllipse(br, item.Rect);
}
}
}
// g.DrawRectangle(Pens.Blue, zone.Left,zone.Top,zone.Width,zone.Height);
// g.DrawRectangle(Pens.Red, item.Rect);
var ts = DateTime.Now - item.ZoneIntime;
if (ts.TotalSeconds > 7.0)
{
//오류발생 너무 오래됬음
g.DrawEllipse(new Pen(Color.Black, 7), item.Rect.OffsetRect(1, 1));
g.DrawEllipse(new Pen(Color.Magenta, 7), item.Rect);
}
else
{
g.DrawEllipse(new Pen(Color.DimGray, 5), item.Rect.OffsetRect(1, 1));
g.DrawEllipse(new Pen(Color.FromArgb(240, 240, 240), 5), item.Rect);
}
using (Font f = new Font("Consolas", 11, FontStyle.Bold))
{
var fsizet = g.MeasureString(item.SID, f);
var fsizeb = g.MeasureString(item.RID, f);
var itemW = Math.Max(fsizet.Width, fsizeb.Width);
var itemH = Math.Max(fsizet.Height, fsizeb.Height);
//padding
var paddingValue = (int)((item.Rect.Top - rect_conveyor.Top - itemH) / 2.0f + 5);
//상단에는 SID를
if (string.IsNullOrEmpty(item.SID) == false)
{
var zcx = zone.Left + zone.Width / 2.0f;
var rectTop = new RectangleF(zcx - itemW / 2.0f, (float)rect_conveyor.Top, itemW, itemH);
g.FillRectangle(new SolidBrush(Color.SteelBlue), rectTop);
g.DrawRect(rectTop, Color.FromArgb(30, 30, 30));
g.DrawString(item.SID, f, Brushes.Black, rectTop, sfCenter);
}
//하단에는 RID를
if (string.IsNullOrEmpty(item.RID) == false)
{
var zcx = zone.Left + zone.Width / 2.0f;
var rectTop = new RectangleF(zcx - itemW / 2.0f, (float)rect_conveyor.Bottom - itemH, itemW, itemH);
g.FillRectangle(new SolidBrush(Color.SteelBlue), rectTop);
g.DrawRect(rectTop, Color.FromArgb(30, 30, 30));
g.DrawString(item.RID, f, Brushes.Black, rectTop, sfCenter);
}
}
if (arDebugMode)
{
///크기 및 포트 drop 시간 정보를 표시한다.
var sb = new System.Text.StringBuilder();
sb.AppendLine("[" + item.Seq.ToString() + "]PORT:" + item.iPort.ToString());
sb.AppendLine("SIZE:" + item.Size);
sb.AppendLine("DROP:" + item.DropTime.ToString("HH:mm:ss"));
sb.AppendLine("IN:" + item.InTime.ToString("HH:mm:ss"));
sb.AppendLine("ZONE:" + item.ZoneIndex.ToString());
sb.AppendLine("존투입:" + item.ZoneIntime.ToString("yyyy-MM-dd HH:mmss"));
sb.AppendLine("REEL:" + item.RID.ToString());
sb.AppendLine("SID:" + item.SID.ToString());
using (Font f = new Font("맑은 고딕", 12, FontStyle.Bold))
{
if (item.Delete)
g.DrawString(sb.ToString(), f, Brushes.Red, item.Rect.Left, item.Rect.Top);
else if (item.RID.Trim() != "")
g.DrawString(sb.ToString(), f, Brushes.Blue, item.Rect.Left, item.Rect.Top);
else
g.DrawString(sb.ToString(), f, Brushes.Black, item.Rect.Left, item.Rect.Top);
}
}
else
{
//배출포트 설정전에는 크기를 중앙에 표시
//위에는 SID , 아래는 RID를 표시
var CenterString = (item.oPort == -1 ? item.Size + "\"" : "#" + item.oPort.ToString());
var fsize = item.oPort == -1 ? 40 : 30;
using (Font f = new Font("Consolas", fsize, FontStyle.Bold))
{
if (item.Delete)
g.DrawString(CenterString, f, Brushes.Red, item.Rect, sfCenter);
else if (item.RID.Trim() != "")
{
g.DrawString(CenterString, f, Brushes.Black, item.Rect, sfCenter);
}
else
g.DrawString(CenterString, f, Brushes.Black, item.Rect, sfCenter);
}
}
}
}
}
void Draw_Icon(Graphics g)
{
int iconOffsetX = 19;
int iconOffsetY = 20;
CIcon icon;
icon = this.icons[0];//.Rect;
if (arConn_BCD) g.DrawImage(UIControl.Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.bcd, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X,icon.Y,icon.W,icon.H);
icon = this.icons[1];//.Rect;
if (arConn_PLC) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.plc, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
icon = this.icons[2];//.Rect;
if (arConn_MOT) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.mot, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
icon = this.icons[3];//.Rect;
if (arDI_Emergency == false) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.emg, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
icon = this.icons[4];//.Rect;
if (this.arDI_SaftyOk) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.safty, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
icon = this.icons[5];//.Rect;
if (arDIAir) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
g.DrawImage(Properties.Resources.air, icon.X + iconOffsetX, icon.Y + iconOffsetY);
//if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
//icon = this.icons[6];//.Rect;
//if (arConn_REM) g.DrawImage(Properties.Resources.bg_blue, icon.X, icon.Y);
//else g.DrawImage(Properties.Resources.bg_red, icon.X, icon.Y);
//g.DrawImage(Properties.Resources.debug40, icon.X + iconOffsetX, icon.Y + iconOffsetY);
////if (icon.Focus) g.DrawRectangle(Pens.Gold, icon.X, icon.Y, icon.W, icon.H);
}
void Draw_Button(Graphics g)
{
foreach (var but in Buttons)
{
var font = but.Font;
if (font == null) font = new Font("맑은 고딕", 15);
if (NeedHomeSet() == false && arDI_Emergency == false && but.Tag == "INPUTL" && arIsRunning == false && arFGInputMode == false)
{
if (but.Shape == eButtonType.Rectangle)
{
g.FillRectangle(Brushes.White, but.Rect);
g.DrawRectangle(new Pen(but.BorderColor, but.BorderSize), but.Rect);
}
else
{
g.FillEllipse(Brushes.White, but.Rect);
g.DrawEllipse(new Pen(but.BorderColor, but.BorderSize), but.Rect);
}
g.DrawString(but.Text, font, Brushes.Black, but.Rect, sfCenter);
}
else if (NeedHomeSet() == false && arDI_Emergency == false && but.Tag == "INPUTR" && arIsRunning == false && arFGInputMode == false)
{
if (but.Shape == eButtonType.Rectangle)
{
g.FillRectangle(Brushes.White, but.Rect);
g.DrawRectangle(new Pen(but.BorderColor, but.BorderSize), but.Rect);
}
else
{
g.FillEllipse(Brushes.White, but.Rect);
g.DrawEllipse(new Pen(but.BorderColor, but.BorderSize), but.Rect);
}
g.DrawString(but.Text, font, Brushes.Black, but.Rect, sfCenter);
}
font.Dispose();
}
}
void Draw_Info(Graphics g, RectangleF rect)
{
//디자인 모드에서는 표시하지 않는다 200714
//if (DesignMode == true) return;
//if (this.arFlag_UnloaderBusy) ShowPopupMessage(g, "언로더 작업 대기 중\nBUSY", Properties.Resources.info);
//if (this.arIsRunning)
if (arFGInputMode)
{
ShowPopupMessage(g, "자재 투입 모드", "투입완료 후 'Re.Start' 하세요", Properties.Resources.info, false);
}
else
{
if (this.arFlag_UnloaderErr == true) ShowPopupMessage(g, "언로더 오류 발생", "STOP된 언로더를 확인하세요", Properties.Resources.error, false);
else if (this.arFlag_WaitPLC && arUnloaderSeq > 0)
{
if (arUnloaderSeq == 1) ShowPopupMessage(g, "언로더 작업 대기 중", "상태확인(#1)", Properties.Resources.info, false);
else if (arUnloaderSeq == 2) ShowPopupMessage(g, "언로더 작업 대기 중", "BUSY(#2)", Properties.Resources.info, false);
else if (arUnloaderSeq == 3) ShowPopupMessage(g, "언로더 작업 대기 중", "ITEM CHECK(#3)", Properties.Resources.info, false);
else if (arUnloaderSeq == 4) ShowPopupMessage(g, "언로더 작업 대기 중", "배출포트 설정(#4)", Properties.Resources.info, false);
else if (arUnloaderSeq == 5) ShowPopupMessage(g, "언로더 작업 대기 중", "릴크기 설정(#5)", Properties.Resources.info, false);
else if (arUnloaderSeq == 6) ShowPopupMessage(g, "언로더 작업 대기 중", "No.ZERO CHECK(#6)", Properties.Resources.info, false);
}
}
}
void Draw_PickerRail(Graphics g, RectangleF rect)
{
RectangleF rectRailL = new RectangleF(rect.Left, rect.Top, rect.Width / 2.0f + 1, rect.Height);
RectangleF rectRailR = new RectangleF(rect.Left + rect.Width / 2.0f, rect.Top, rect.Width / 2.0f, rect.Height);
var brR = new LinearGradientBrush(rect, Color.FromArgb(60, 60, 60), Color.FromArgb(80, 80, 80), LinearGradientMode.Horizontal);
var brL = new LinearGradientBrush(rect, Color.FromArgb(80, 80, 80), Color.FromArgb(60, 60, 60), LinearGradientMode.Horizontal);
g.FillRectangle(brL, rectRailL);
//g.FillRectangle(brR, rectRailR);
brL.Dispose();
brR.Dispose();
g.DrawRectangle(Pens.DimGray, rect.Left, rect.Top, rect.Width, rect.Height);
}
void Draw_PickerY(Graphics g, RectangleF rect)
{
//전체영역의 80% 영역에 Y-로봇의 축을 그린다.
//var motorMax = 400; //전체 가동 길이 400mm
this.arMotorLengthY = 600;
var py = rect.Top + rect.Height - (rect.Height * ((this.arMotorPosition[0] + 1) / (this.arMotorLengthY * 1.0f)));
var cx = rect.Left + rect.Width / 2.0f;
//상(Rear), 하(Front)로 영역을 그린다
var port_width = rect_picker_rear.Width;// * 3f;
var port_height = rect_picker_rear.Height; // rect.Height * 0.2f;
var port_space = (this.rect_picker_front.Top - this.rect_picker_rear.Bottom) / 2.0f;
//New Rear Position
var newYR = (float)(py - port_height - port_space);
if (newYR != rect_picker_rear.Y)
{
var offset = newYR - rect_picker_rear.Y;
this.rect_picker_rear.Offset(0, offset); //좌표가 변경되었다면 재계산
this.rect_picker_rear_vac1.Offset(0, offset);
this.rect_picker_rear_vac2.Offset(0, offset);
this.rect_picker_rear_vac3.Offset(0, offset);
this.rect_picker_rear_vac4.Offset(0, offset);
}
//New Front Position
var newYF = (float)(py + port_space);
if (newYF != rect_picker_front.Y)
{
var offset = newYF - rect_picker_front.Y;
this.rect_picker_front.Offset(0, offset); //좌표가 변경되었다면 재계산
this.rect_picker_front_vac1.Offset(0, offset);
this.rect_picker_front_vac2.Offset(0, offset);
this.rect_picker_front_vac3.Offset(0, offset);
this.rect_picker_front_vac4.Offset(0, offset);
}
//피커 #1 Circle 색상
var Bg1 = Color.FromArgb(100, 100, 100);
var Bg2 = Color.FromArgb(160, 160, 160);
if (this.arVar_Picker[0].Overload)
{
Bg1 = Color.Tomato;
Bg2 = Color.Red;
}
else
{
if (this.arVar_Picker[0].ItemOn)
{
if (this.arVar_Picker[0].isReelDetect)
{
Bg1 = Color.Lime; //.FromArgb(100, 100, 100);
Bg2 = Color.Green;//.FromArgb(160, 160, 160);
}
else
{
Bg1 = Color.Magenta; //.FromArgb(100, 100, 100);
Bg2 = Color.DarkMagenta;//.FromArgb(160, 160, 160);
}
}
else
{
Bg1 = Color.FromArgb(100, 100, 100);
Bg2 = Color.FromArgb(160, 160, 160);
}
}
using (var br = new LinearGradientBrush(rect_picker_front, Bg1, Bg2, LinearGradientMode.Vertical))
{
g.FillEllipse(br, rect_picker_front);
}
//피커 #2 Circle 색상
if (this.arVar_Picker[1].Overload)
{
Bg1 = Color.Tomato;
Bg2 = Color.Red;
}
else
{
if (this.arVar_Picker[1].ItemOn)
{
//실제 아이템 체크
if (this.arVar_Picker[1].isReelDetect)
{
Bg1 = Color.Lime; //.FromArgb(100, 100, 100);
Bg2 = Color.Green;//.FromArgb(160, 160, 160);
}
else
{
Bg1 = Color.Magenta; //.FromArgb(100, 100, 100);
Bg2 = Color.DarkMagenta;//.FromArgb(160, 160, 160);
}
}
else
{
Bg1 = Color.FromArgb(100, 100, 100);
Bg2 = Color.FromArgb(160, 160, 160);
}
}
using (var br = new LinearGradientBrush(rect_picker_rear, Bg1, Bg2, LinearGradientMode.Vertical))
{
g.FillEllipse(br, rect_picker_rear);
}
//피커 테두리
using (var bgPen = new Pen(Color.Black, 3))
{
g.DrawEllipse(bgPen, rect_picker_front);
g.DrawEllipse(bgPen, rect_picker_rear);
}
//피커 내부의 진공 표현
g.FillEllipse((this.arVar_Picker[0].VacDetect[0] ? brVacOn : brVacOff), rect_picker_front_vac1);
g.FillEllipse((this.arVar_Picker[0].VacDetect[1] ? brVacOn : brVacOff), rect_picker_front_vac2);
g.FillEllipse((this.arVar_Picker[0].VacDetect[2] ? brVacOn : brVacOff), rect_picker_front_vac3);
g.FillEllipse((this.arVar_Picker[0].VacDetect[3] ? brVacOn : brVacOff), rect_picker_front_vac4);
g.FillEllipse((this.arVar_Picker[1].VacDetect[0] ? brVacOn : brVacOff), rect_picker_rear_vac1);
g.FillEllipse((this.arVar_Picker[1].VacDetect[1] ? brVacOn : brVacOff), rect_picker_rear_vac2);
g.FillEllipse((this.arVar_Picker[1].VacDetect[2] ? brVacOn : brVacOff), rect_picker_rear_vac3);
g.FillEllipse((this.arVar_Picker[1].VacDetect[3] ? brVacOn : brVacOff), rect_picker_rear_vac4);
//피커설명 표시
if (arVar_Picker[0].Overload)
g.DrawString("OVL", arFont_picker, Brushes.Black, rect_picker_front, sfCenter);
else
g.DrawString(this.arVar_Picker[0].ReelSize, arFont_picker, Brushes.Black, rect_picker_front, sfCenter);
if (arVar_Picker[1].Overload)
g.DrawString("OVL", arFont_picker, Brushes.Black, rect_picker_rear, sfCenter);
else
g.DrawString(this.arVar_Picker[1].ReelSize, arFont_picker, Brushes.Black, rect_picker_rear, sfCenter);
//피커 진공표시 테두리 (진공출력상태에 따라서 색상을 달리 함)
g.DrawEllipse((this.arVar_Picker[0].VacOutput[0] ? penVacOn : penVacOff), rect_picker_front_vac1);
g.DrawEllipse((this.arVar_Picker[0].VacOutput[1] ? penVacOn : penVacOff), rect_picker_front_vac2);
g.DrawEllipse((this.arVar_Picker[0].VacOutput[2] ? penVacOn : penVacOff), rect_picker_front_vac3);
g.DrawEllipse((this.arVar_Picker[0].VacOutput[3] ? penVacOn : penVacOff), rect_picker_front_vac4);
g.DrawEllipse((this.arVar_Picker[1].VacOutput[0] ? penVacOn : penVacOff), rect_picker_rear_vac1);
g.DrawEllipse((this.arVar_Picker[1].VacOutput[1] ? penVacOn : penVacOff), rect_picker_rear_vac2);
g.DrawEllipse((this.arVar_Picker[1].VacOutput[2] ? penVacOn : penVacOff), rect_picker_rear_vac3);
g.DrawEllipse((this.arVar_Picker[1].VacOutput[3] ? penVacOn : penVacOff), rect_picker_rear_vac4);
//중앙부에 흰색구를 표시함
g.FillEllipse(Brushes.White, cx - 5, (float)(py - 5f), 10, 10);
}
void Draw_Screw(Graphics g, Rectangle rect)
{
//모터표시(X축)
g.FillRectangle(new SolidBrush(Color.FromArgb(50, 150, 150, 150)), rect);
//해다 영역에 사선으로그림을 그린다.
var termcount = 50;
var lineterm = rect.Width / termcount;
var skew = rect.Width * 0.01f;
Pen p = new Pen(Color.FromArgb(50, 120, 120, 120), 2);
for (int i = 0; i < termcount; i++)
{
var pt1 = new PointF(rect.Left + i * lineterm, rect.Top);
var pt2 = new PointF(pt1.X + skew, rect.Bottom);
g.DrawLine(p, pt1, pt2);
}
p.Dispose();
//e.Graphics.DrawRectangle(Pens.Gray, motr_rect.Left, motr_rect.Top, motr_rect.Width, motr_rect.Height);
//e.Graphics.DrawRectangle(Pens.Gray, motf_rect.Left, motf_rect.Top, motf_rect.Width, motf_rect.Height);
//RectangleF rectRailT = new RectangleF(rect.Left, rect.Top, rect.Width, rect.Height / 2.0f + 1);
//RectangleF rectRailB = new RectangleF(rect.Left, rect.Top + rect.Height / 2.0f, rect.Width, rect.Height / 2.0f);
//var brR = new LinearGradientBrush(rect, Color.FromArgb(100, 60, 60, 60), Color.FromArgb(100, 80, 80, 80), LinearGradientMode.Vertical);
//var brL = new LinearGradientBrush(rect, Color.FromArgb(100, 80, 80, 80), Color.FromArgb(100, 60, 60, 60), LinearGradientMode.Vertical);
////g.FillRectangle(brL, rectRailT);
////g.FillRectangle(brR, rectRailB);
//brL.Dispose();
//brR.Dispose();
g.DrawRectangle(new Pen(Color.FromArgb(50, Color.Gray)), rect.Left, rect.Top, rect.Width, rect.Height);
}
void Draw_BallScrewRail(Graphics g, RectangleF rect, int divCount, int alpha, Boolean downDirection, Boolean MLock, Boolean Org, Boolean LimDn, Boolean LimUp)
{
//모터표시(X축)
if (Org) g.FillRectangle(new SolidBrush(Color.FromArgb(alpha, Color.SkyBlue)), rect);
else if (LimUp) g.FillRectangle(new SolidBrush(Color.FromArgb(alpha, Color.Red)), rect);
else if (LimDn) g.FillRectangle(new SolidBrush(Color.FromArgb(alpha, Color.Blue)), rect);
else g.FillRectangle(new SolidBrush(Color.FromArgb(alpha, 150, 150, 150)), rect);
//해다 영역에 사선으로그림을 그린다.
var baseSize = (downDirection == false ? rect.Width : rect.Height);
var lineterm = baseSize / divCount;
var skew = baseSize * 0.01f;
Pen p = new Pen(Color.FromArgb(alpha, 120, 120, 120), 2);
PointF pt1 = PointF.Empty;
PointF pt2 = PointF.Empty;
for (int i = 0; i < divCount; i++)
{
if (downDirection)
{
pt1 = new PointF(rect.Left, rect.Top + i * lineterm);
pt2 = new PointF(rect.Right, pt1.Y + skew);
}
else
{
pt1 = new PointF(rect.Left + i * lineterm, rect.Top);
pt2 = new PointF(pt1.X + skew, rect.Bottom);
}
g.DrawLine(p, pt1, pt2);
}
p.Dispose();
//limi이 걸려있다면 해당 영역에 적색으로 표시한다.
var limwidth = 30;
if (LimUp)
{
RectangleF rectlu;
if (downDirection) rectlu = new RectangleF(rect.Left, rect.Top, rect.Width, limwidth);
else rectlu = new RectangleF(rect.Right - limwidth, rect.Top, limwidth, rect.Height);
g.FillRectangle(Brushes.Red, rectlu.Left, rectlu.Top, rectlu.Width, rectlu.Height);
}
if (LimDn)
{
RectangleF rectlu;
if (downDirection) rectlu = new RectangleF(rect.Left, rect.Bottom - limwidth, rect.Width, limwidth);
else rectlu = new RectangleF(rect.Left, rect.Top, limwidth, rect.Height);
g.FillRectangle(Brushes.Red, rectlu.Left, rectlu.Top, rectlu.Width, rectlu.Height);
}
//전체 테두리
g.DrawRectangle(new Pen(Color.FromArgb(alpha, Color.Gray)), rect.Left, rect.Top, rect.Width, rect.Height);
}
Boolean NeedHomeSet()
{
return arConn_MOT && (this.arMOT_HSet[0] == false || this.arMOT_HSet[1] == false || this.arMOT_HSet[2] == false || this.arMOT_HSet[3] == false || this.arMOT_HSet[4] == false);
}
byte errstep = 0;
bool errstepR = true;
void Draw_Error(Graphics g)
{
//디자인 모드에서는 표시하지 않는다 200714
if (DesignMode == true) return;
if (arConn_DIO && this.arDI_Emergency == true) ShowPopupMessage(g, "EMERGENCY BUTTON", "비상정지 확인\nEMERGENCY or POWER LOSS", Properties.Resources.error, true);
else if (this.arDI_SaftyOk == false) ShowPopupMessage(g, "SAFTY SENSOR", "안전 센서 확인", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_Alm[0] == true) ShowPopupMessage(g, "SERVO ALARM", "Y-PICKER 모터 알람 발생", Properties.Resources.error, true);
else if (arConn_MOT && this.arMOT_Alm[1] == true) ShowPopupMessage(g, "SERVO ALARM", "Z-FRONT 모터 알람 발생", Properties.Resources.error, true);
else if (arConn_MOT && this.arMOT_Alm[2] == true) ShowPopupMessage(g, "SERVO ALARM", "Z-REAR 모터 알람 발생", Properties.Resources.error, true);
else if (arConn_MOT && this.arMOT_Alm[3] == true) ShowPopupMessage(g, "SERVO ALARM", "X-FRONT 모터 알람 발생", Properties.Resources.error, true);
else if (arConn_MOT && this.arMOT_Alm[4] == true) ShowPopupMessage(g, "SERVO ALARM", "X-REAR 모터 알람 발생", Properties.Resources.error, true);
else if (arConn_MOT && this.arMOT_SVOn[0] == false) ShowPopupMessage(g, "SERVO ALARM", "Y-PICKER SERVO-OFF", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_SVOn[1] == false) ShowPopupMessage(g, "SERVO ALARM", "Z-FRONT SERVO-OFF", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_SVOn[2] == false) ShowPopupMessage(g, "SERVO ALARM", "Z-REAR SERVO-OFF", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_SVOn[3] == false) ShowPopupMessage(g, "SERVO ALARM", "X-FRONT SERVO-OFF", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_SVOn[4] == false) ShowPopupMessage(g, "SERVO ALARM", "X-REAR SERVO-OFF", Properties.Resources.alert, true);
else if (arConn_MOT && (this.arMOT_HSet[0] == false || this.arMOT_HSet[1] == false || this.arMOT_HSet[2] == false || this.arMOT_HSet[3] == false || this.arMOT_HSet[4] == false))
{
//안전오류도 표시해줘야한다
var SaftyMessage = string.Empty;
if (arVar_Port[0].SaftyErr == true) SaftyMessage += "PORT-FL";
if (arVar_Port[1].SaftyErr == true) SaftyMessage += (string.IsNullOrEmpty(SaftyMessage) == false ? "," : string.Empty) + "PORT-FR";
if (arVar_Port[2].SaftyErr == true) SaftyMessage += (string.IsNullOrEmpty(SaftyMessage) == false ? "," : string.Empty) + "PORT-RL";
if (arVar_Port[3].SaftyErr == true) SaftyMessage += (string.IsNullOrEmpty(SaftyMessage) == false ? "," : string.Empty) + "PORT-RR";
if (string.IsNullOrEmpty(SaftyMessage) == false)
{
ShowPopupMessage(g, "SYSTEM NOT READY", "장치 초기화가 필요 합니다\n포트안전센서 확인 필요\n" + SaftyMessage, Properties.Resources.error, true);
}
else ShowPopupMessage(g, "SYSTEM NOT READY", "장치 초기화가 필요 합니다\n홈 검색이 완료되지 않았습니다", Properties.Resources.error, true);
}
else if (arConn_MOT && this.arMOT_HSet[0] == false) ShowPopupMessage(g, "SERVO ALARM", "Y-PICKER 홈 검색 필요", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_HSet[1] == false) ShowPopupMessage(g, "SERVO ALARM", "Z-FRONT 홈 검색 필요", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_HSet[2] == false) ShowPopupMessage(g, "SERVO ALARM", "Z-REAR 홈 검색 필요", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_HSet[3] == false) ShowPopupMessage(g, "SERVO ALARM", "X-FRONT 홈 검색 필요", Properties.Resources.alert, true);
else if (arConn_MOT && this.arMOT_HSet[4] == false) ShowPopupMessage(g, "SERVO ALARM", "X-REAR 홈 검색 필요", Properties.Resources.alert, true);
else if (arVar_Port[0].OverLoad) ShowPopupMessage(g, "## OVERLOAD ##", "FRONT-LEFT", Properties.Resources.alert, true);
else if (arVar_Port[1].OverLoad) ShowPopupMessage(g, "## OVERLOAD ##", "FRONT-RIGHT", Properties.Resources.alert, true);
else if (arVar_Port[2].OverLoad) ShowPopupMessage(g, "## OVERLOAD ##", "REAR-LEFT", Properties.Resources.alert, true);
else if (arVar_Port[3].OverLoad) ShowPopupMessage(g, "## OVERLOAD ##", "REAR-RIGHT", Properties.Resources.alert, true);
//else if (arFlag_UnloaderBusy) ShowPopupMessage(g, "## UNLOADER ##\nBUSY", Properties.Resources.alert);
//else if (arFlag_UnloaderErr) ShowPopupMessage(g, "## UNLOADER ##\nERROR", Properties.Resources.alert);
}
void Draw_Menu(Graphics g)
{
//var buttonOk = new CMenuButton("OK", "1");
//var buttonNo = new CMenuButton("CANCLE", "0");
//var newmenu = new CMenu("body str", "title", eMsgIcon.Error, buttonOk, buttonNo)
//{
// BorderColor = Color.Gray
//};
//this.menus.Push(newmenu);
if (arMenus == null || arMenus.Length < 1) { this.HasPopupMenu = false; return; }
else HasPopupMenu = true;
ShowMaskLayer(g, Color.FromArgb(250, Color.Black));
var item = this.arMenus.Last();//.Peek();
//이 메뉴를 표시 합니다.
PopupMenuRequireInput = item.RequireInput;
var buttonSpace = 10;
var hSpace = 5;
var vSpace = 10;
var iconSize = 80;
var menuheight = 64;
var padding = 10;
var msgW = 900;// (int)(this.rect_main.Width * 0.65f);// 640;// (int)(rect_main.Width * 0.7f);
var msgH = 400;
var rect = new RectangleF(
rect_main.Left + (rect_main.Width - msgW) / 2.0f,
rect_main.Top + (rect_main.Height - msgH) / 2.0f,
msgW, msgH);
Rectangle rectT = Rectangle.Empty; //title
Rectangle rectI = Rectangle.Empty; //icon
Rectangle rectC = Rectangle.Empty; //content
Rectangle rectB = Rectangle.Empty; //button
rectT = new Rectangle((int)rect.Left + padding, (int)rect.Top + padding, (int)rect.Width - (padding * 2), (int)(rect.Height * 0.1));
rectI = new Rectangle((int)rect.Left + padding + 10, (int)rectT.Bottom + vSpace, iconSize, iconSize); //icon size
rectB = new Rectangle((int)(rect.Left + padding * 2), (int)(rect.Bottom - menuheight - padding), (int)rect.Width - (padding * 4), menuheight);
rectC = new Rectangle((int)rectI.Right + 20 + hSpace * 2, (int)rectT.Bottom + 10 + vSpace,
(int)(rect.Width - hSpace - (padding * 2) - rectI.Width),
(int)(rect.Height - rectT.Height - rectB.Height - (padding * 2) - vSpace * 2));
g.FillRectangle(new SolidBrush(Color.FromArgb(220, item.BackColor)), rect);
//제목줄 표시
using (LinearGradientBrush sb = new LinearGradientBrush(rectT,
Color.FromArgb(160, 160, 160),
Color.FromArgb(180, 180, 180),
LinearGradientMode.Vertical))
{
g.FillRectangle(sb, rectT);
}
g.DrawString(item.Title, item.Font, new SolidBrush(item.ForeColor), rectT, sfCenter);
//버튼표시
if (item.buttons != null && item.buttons.Length > 0)
{
//현재 버튼 영역의 갯수가 다르면 다시 생성한다.
if (menuButtons.Count != item.buttons.Length)
{
menuButtons = new List<CMenuButton>();
foreach (var bt in item.buttons)
menuButtons.Add(bt);
g.DrawString("!!", this.Font, Brushes.Red, rectB.Left + 10, rectB.Top + 10);
}
else
{
for (int i = 0; i < menuButtons.Count; i++)
menuButtons[i] = item.buttons[i];
}
g.DrawString(item.buttons.Length.ToString() + "/" + menuButtons.Count.ToString(), this.Font, Brushes.Red, rectB);
var butidx = 0;
var butwid = (rectB.Width - (item.buttons.Length - 1) * buttonSpace) / item.buttons.Length;
foreach (var but in item.buttons)
{
but.menutag = item.Tag;
but.Rect = new Rectangle(rectB.Left + butwid * butidx + buttonSpace * butidx, rectB.Top, butwid, rectB.Height);
g.FillRectangle(new SolidBrush(but.BackColor), but.Rect);
g.DrawRectangle(new Pen(but.BorderColor, but.BorderSize), but.Rect);
g.DrawString(but.Text, item.Font, new SolidBrush(but.ForeColor), but.Rect, sfCenter);
butidx++;
}
}
else menuButtons.Clear();
//아이콘 영역에 그림표시
if (rectI.IsEmpty == false)
{
g.DrawImage(Properties.Resources.info, rectI);
}
//본문데이터표시
if (string.IsNullOrEmpty(item.Text) == false) //contec
{
g.DrawString(item.Text, item.Font, new SolidBrush(item.ForeColor), rectC);
}
//외각 테두리
//g.DrawRectangle(new Pen(Color.FromArgb(20,20,20), 10) { Alignment = PenAlignment.Center }, rect.Left + 1, rect.Top + 1, rect.Width, rect.Height);
g.DrawRectangle(new Pen(Color.FromArgb(180, 180, 180), 10) { Alignment = PenAlignment.Center }, rect.Left, rect.Top, rect.Width, rect.Height);
//g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width, rect.Height);
//g.DrawRectangle(Pens.Red, rectT); //제목표시줄
//g.DrawRectangle(Pens.Blue, rectI); //아이콘
//g.DrawRectangle(Pens.Green, rectB); //버튼영역
//g.DrawRectangle(Pens.Black, rectC); //본문영역
}
void ShowMaskLayer(Graphics g, Color maskColor)
{
g.FillRectangle(new SolidBrush(maskColor), this.DisplayRectangle.Left, DisplayRectangle.Top, DisplayRectangle.Width, DisplayRectangle.Height);
}
void ShowPopupMessage(Graphics g, string title, string msg, Image icon, Boolean isError)
{
//팝업표시할때마다 배경 마스킹을 한다
//var maskColor = Color.FromArgb(100, Color.White);
//ShowMaskLayer(g, maskColor);
if (isError == false)
{
//팝업표시할때마다 배경 마스킹을 한다
var maskColor = Color.FromArgb(50, Color.Gray);
ShowMaskLayer(g, maskColor);
var msgW = (int)(this.rect_main.Width * 0.65f);// 640;// (int)(rect_main.Width * 0.7f);
var msgH = 105;
var rect = new RectangleF(
rect_main.Left + (rect_main.Width - msgW) / 2.0f,
rect_main.Top + (rect_main.Height - msgH) / 2.0f,
msgW, msgH);
var TitleHeight = 25;
var rectT = new Rectangle((int)rect.Left, (int)rect.Bottom - TitleHeight, (int)rect.Width, TitleHeight);
var rectI = new Rectangle((int)rect.Left, (int)rect.Top, (int)rect.Width, (int)rect.Height - rectT.Height);
//g.FillRectangle(new SolidBrush(Color.FromArgb(220, Color.Black)), rect);
g.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.White)), rect);
var rectTL = new RectangleF(rectT.Left, rectT.Top, rectT.Width / 2.0f, rectT.Height);
var rectTR = new RectangleF(rectTL.Right, rectT.Top, rectTL.Width, rectTL.Height);
using (var sb = new LinearGradientBrush(rectT, Color.Transparent, Color.White, LinearGradientMode.Horizontal))
g.FillRectangle(sb, rectTL);
using (var sb = new LinearGradientBrush(rectT, Color.White, Color.Transparent, LinearGradientMode.Horizontal))
g.FillRectangle(sb, rectTR);
//g.DrawImage(icon,
// (int)(rect.Left + 20),
// (int)(rect.Top + (rect.Height - icon.Height) / 2.0f));
g.DrawString(title, new Font("맑은 고딕", 10f, FontStyle.Bold), Color.Black, rectT, ContentAlignment.MiddleCenter);
g.DrawString(msg, new Font("맑은 고딕", 30f, FontStyle.Bold), Color.White, rectI, ContentAlignment.MiddleCenter, Color.FromArgb(24, 24, 24));
if (errstep % 5 == 0) errstepR = !errstepR;
if (errstepR)
g.DrawRectangle(new Pen(Color.Gold, 2), rect.Left, rect.Top, rect.Width, rect.Height);
else
g.DrawRectangle(new Pen(Color.White, 2), rect.Left, rect.Top, rect.Width, rect.Height);
if (errstep < 255) errstep += 1;
else errstep = 0;
}
else
{
//팝업표시할때마다 배경 마스킹을 한다
var maskColor = Color.FromArgb(253, 15, 15, 15);
ShowMaskLayer(g, maskColor);
var msgW = (int)(this.rect_main.Width * 0.65f);// 640;// (int)(rect_main.Width * 0.7f);
var msgH = 400;
var rect = new RectangleF(
rect_main.Left + (rect_main.Width - msgW) / 2.0f,
rect_main.Top + (rect_main.Height - msgH) / 2.0f,
msgW, msgH);
var rectT = new Rectangle((int)rect.Left, (int)rect.Bottom - 200, (int)rect.Width, 200);
var rectI = new Rectangle((int)rect.Left, (int)rect.Top, (int)rect.Width, (int)rect.Height - rectT.Height);
//g.FillRectangle(new SolidBrush(Color.FromArgb(220, Color.Black)), rect);
g.FillRectangle(new SolidBrush(Color.FromArgb(253, Color.Black)), rect);
g.DrawImage(icon,
(int)(rectI.Left + rectI.Width / 2.0f) - 40,
(int)(rectI.Top + rectI.Height / 2.0f) + 10);
g.DrawString(msg, new Font("맑은 고딕", 30f, FontStyle.Bold), Brushes.Gold, rectT, sfCenter);
if (errstep % 5 == 0) errstepR = !errstepR;
if (errstepR)
g.DrawRectangle(new Pen(Color.Red, 10), rect.Left, rect.Top, rect.Width, rect.Height);
else
g.DrawRectangle(new Pen(Color.Gold, 10), rect.Left, rect.Top, rect.Width, rect.Height);
if (errstep < 255) errstep += 1;
else errstep = 0;
}
}
void Draw_Conveyor(Graphics g, RectangleF rect)
{
//컨베어표시
g.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Black)), rect);
if (arDI_Safty_CvIn || arDI_Cv_Detect[6])
{
//입구쪽 감지센서
var rectdetIn = new RectangleF(rect.Right - 10, rect.Top + 9, 5, rect.Height - 10);
g.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Blue)), rectdetIn);
}
if (arDI_Safty_CvOut || arDI_Cv_Detect[7])
{
//출구쪽 감지센서
var rectdetOut = new RectangleF(rect.Left, rect.Top + 9, 5, rect.Height - 10);
g.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Blue)), rectdetOut);
}
//컨베어의 릴감지센서 위치를 표시한다
var gridsize = rect.Width / (6 + 1f);
var senseW = rect.Width * 0.02f;
var senseH = rect.Height * 0.05f;
var slist = new double[] { 50, 350, 470, 890, 1110, 1440 };
for (int i = 0; i < slist.Length; i++)
{
//선으로 영역을 표시해준다.
var PosMM = rect.Width * (slist[i] / arMcLengthW);
var x = (float)(rect.Left + PosMM);
var rx = x - senseW / 2.0f;
//센서모양을 그려준다
var rectT = new RectangleF(rx, rect.Top + 3, senseW, senseH);
var rectB = new RectangleF(rx, rect.Bottom - senseH - 3, senseW, senseH);
if (arDI_Cv_Detect[i] == true)
{
g.DrawLine(Pens.SkyBlue, x, rect.Top, x, rect.Bottom);
arLastDetectIndex = i;
}
if (i == arLastDetectIndex) //마지막으로 지나간 것은 색상을 변경 해준다.
{
g.FillRectangle(Brushes.Gold, rectT);
g.FillRectangle(Brushes.Gold, rectB);
}
else
{
g.FillRectangle(Brushes.White, rectT);
g.FillRectangle(Brushes.White, rectB);
}
// g.DrawString(i.ToString(), this.Font, Brushes.White, x, rect.Top - 20);
}
Color borderColor = CVHasItem ? Color.Gold : (arConvRun ? Color.Red : Color.Gray);
using (var p = new Pen(borderColor, 6))
{
g.DrawLine(p, rect.Left, rect.Top, rect.Right, rect.Top);
g.DrawLine(p, rect.Left, rect.Bottom, rect.Right, rect.Bottom);
//g.DrawLine(new Pen(Color.Red), rect.Left, rect.Top , rect.Right, rect.Top );
//g.DrawLine(new Pen(Color.Red), rect.Left, rect.Bottom, rect.Right, rect.Bottom);
}
//동작시 모터의 진행방ㅎㅇ을 표시한다
if (arConvRun)
UIControl.Common.Draw_Arrow(g, rect,
eDirection.RightToLeft,
ConveyorRunPoint,
AnimationStepConv,
Color.FromArgb(50, 50, 50), this.Font);
}
void Draw_Port(Graphics g, double motposition, double maxLength, RectangleF area, int portindex)
{
CPort portL = arVar_Port[portindex + 0];
CPort portR = arVar_Port[portindex + 1];
maxLength = arMcLengthW - 200;
var PosX = area.Left + (area.Width * (motposition / maxLength));
// g.DrawLine(Pens.DarkViolet, (int)PosX, (int)(area.Top - 10), (int)PosX, (int)(area.Bottom + 10));
var portwidth = CvtMMtoPX_W(350, 0); // area.Width * 0.25;
var port_space = CvtMMtoPX_W(60, 0); //각 포트사이가 60mm
var port_height = CvtMMtoPX_H(250, 0);
var position_front_px = area.Left + ((area.Width * (motposition / maxLength)));
var borderSize = 7;
var offsetY = CvtMMtoPX_H(10, 0); //포트는 축으로 부터 10mm 이격되어있음
var portY = area.Top + (portindex < 2 ? (offsetY + borderSize - area.Height / 2.0f) : (-offsetY - port_height + area.Height / 2.0f));
//left port
var NewX0 = PosX; // 기준부터 좌측 셔틀이 시작한다 (PosX - portwidth - port_space);
this.arVar_Port[portindex + 0].Rect = new RectangleF(
(float)NewX0,
(float)portY,
(float)portwidth,
(float)port_height);
//right port
var NewX1 = (float)(PosX + portwidth + port_space);
this.arVar_Port[portindex + 1].Rect = new RectangleF(
NewX1,
(float)portY,
(float)portwidth,
(float)port_height);
var Rect_L = arVar_Port[portindex + 0].Rect;
var Rect_R = arVar_Port[portindex + 1].Rect;
using (Font fCnt = new Font("consolas", 30, FontStyle.Bold))
{
using (Font fMSg = new Font("맑은 고딕", 12, FontStyle.Bold))
{
portL.Display(g, fCnt, fMSg, this.arFGInputMode, (portindex == 0 ? this.arFGInputFL : this.arFGInputRL));
portR.Display(g, fCnt, fMSg, this.arFGInputMode, (portindex == 0 ? this.arFGInputFR : this.arFGInputRR));
}
}
//g.DrawRect(area, Color.DarkViolet, 1);
}
private void Tm_Tick(object sender, EventArgs e)
{
if (ConveyorRunPoint < (AnimationStepConv - 3)) ConveyorRunPoint += 1;
else ConveyorRunPoint = 1;
for (int i = 0; i < 4; i++)
{
if (this.arVar_Port[i].arrowIndex < (arVar_Port[i].AnimationStepPort - 3)) this.arVar_Port[i].arrowIndex += 1;
else this.arVar_Port[i].arrowIndex = 1;
}
this.Invalidate();
// var ts = DateTime.Now - updatetime;
//if (ts.TotalMilliseconds >= tm.Interval) this.Invalidate();
}
}
}