using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Drawing.Drawing2D; using System.Linq; using System.Security.Authentication.ExtendedProtection; using System.Security.Policy; namespace TrendCtrlII { public enum EDRAGTYPE { SCREEN, ZOOM, UC, NONE } public enum ESCREENMODE { INIT, TEMPDATA, GRAPH } public enum EBUTTONTYPE { LEFTMON, RIGHTMON } public enum EALIGN { LEFT, CENTER, RIGHT } public partial class TrendCtrlII : UserControl { private ChartStyle _Style = new ChartStyle(); public CMouseinfo Mouseinfo; //마우스위치정보 public Boolean Refresh_BackGround = true; public Boolean Refresh_Grpah = true; //화면이동모드 public Boolean ScreenMove = false; //Boolean Make_BackT = false; Bitmap layer_bakT; //전체영역의 배경 Bitmap layer_bak; Bitmap layer_graph; Bitmap layer_graphT; //전체영역의 그래프 public float MaxValueY { get; set; } = 15; public Single LineWidth = 1; private Rectangle UserZoom; //사용자줌영역 //실값 public Single[] values; // public int[] times; private CChinfo[] chinfo; public Boolean Key_Control = false; // private Rectangle Cursorrect; //2015-02-17 : 시간정보를 chinfo 에서 분리 public Dictionary Time; public void RemoveAt(int idx) { //배열갯수를 벗어나면 처리하지 않는다. if (idx >= this.Time.Count) return; //삭제대상의 키를 확인한다. var timeKey = this.Time.Take(idx).First().Key; //시간정보삭제 this.Time.Remove(timeKey); //채널정보 중 타임키를 공유하는 자료를 삭제한다. this.chinfo.ToList().ForEach(t => t.Value.Remove(timeKey)); } public void AddData(long val_time, int ch, float val_volt, string ch_grp, string ch_title, Color ch_color) { //존재하지 않는 시간이라면 추가한다. var ttime = Time.Where(t => t.Value == val_time).FirstOrDefault(); if (ttime.Key is default(UInt32) && ttime.Value is default(Int64)) // 기존에 값이 존재하지 않는경우 { this.Time.Add((uint)this.Time.Count + 1, val_time); ttime = Time.Where(t => t.Value == val_time).FirstOrDefault(); } //해당 채널이 존재하는지 확인한다 var chdata = this.CHInfo.Where(t => t.CH == ch).FirstOrDefault(); if (chdata == null) { Array.Resize(ref chinfo, chinfo.Length + 1); chdata = new CChinfo { CH = ch, Idx = (ushort)ch, GROUP = ch_grp, TITLE = ch_title, Color = ch_color, Show = true, }; chinfo[chinfo.Length - 1] = chdata; } //해당 시간대의 데이터가 없다면 추가한다. if (chdata.Value.ContainsKey(ttime.Key) == false) chdata.Value.Add(ttime.Key, val_volt); } public void AddData(long val_time, int ch, float val_volt) { //존재하지 않는 시간이라면 추가한다. var ttime = Time.Where(t => t.Value == val_time).FirstOrDefault(); if (ttime.Key is default(UInt32)) { this.Time.Add((uint)this.Time.Count + 1, val_time); } //해당 채널이 존재하는지 확인한다 var chdata = this.CHInfo.Where(t => t.CH == ch).FirstOrDefault(); if (chdata != null) { //해당 시간대의 데이터가 없다면 추가한다. if (chdata.Value.ContainsKey(ttime.Key) == false) chdata.Value.Add(ttime.Key, val_volt); } } private Boolean init; //그리드초기화완료여부 public String initmsg = "initializing..."; public int initpercent = 0; public List uc = new List(0); //사용자커서 최대2개까지한다. //미정리 //MY EVENT //public event OnClickProbeSensorHandlerL OnClickLEFT; //셀클릭 //public delegate void OnClickProbeSensorHandlerL(); //public event OnClickProbeSensorHandlerR OnClickRIGHT; //셀클릭 //public delegate void OnClickProbeSensorHandlerR(); public event OnUpdateUserControlHandler OnUpdateUserCursor; //UPDATE USER CURSOR public delegate void OnUpdateUserControlHandler(int idx); //화면디자인관련 private Cursor cursor = Cursors.Default; //현재커서의모양 private StringBuilder Warn_msg = new StringBuilder(""); //경고메세지(화면 최상단중앙에 표시됨) private Font Warn_Font; //기타설정' public RectangleF WindowRect; //머릿부분 public RectangleF ChartRect; /// /// 현재데이터중 가장 마지막의 시간정보를 반환합니다. (없는경우 1900년도가 반화노딤) /// /// public Int64 LastDate() { if (Time == null) return DateTime.Parse("1900-01-01").ToFileTime(); if (Time.Count < 1) return DateTime.Parse("1900-01-01").ToFileTime(); return Time.Last().Value;// [Time.Count - 1].Value; } public Int64 FirstDate() { if (Time == null) return DateTime.Parse("1900-01-01").ToFileTime(); if (Time.Count < 1) return DateTime.Parse("1900-01-01").ToFileTime(); return Time.First().Value; } public TrendCtrlII() { InitializeComponent(); // Initialize Variables // Set Optimized Double Buffer to reduce flickering this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); // Redraw when resized this.SetStyle(ControlStyles.ResizeRedraw, true); this.Font = SystemInformation.MenuFont; this.Warn_Font = new Font("나눔고딕", 20, FontStyle.Bold); this.Warn_msg.Append("메세지가 없습니다"); this.Mouseinfo = new CMouseinfo(new PointF(0, 0)); Time = new Dictionary(); } public ChartStyle Style { get { return this._Style; } set { this._Style = value; } } /// /// Draws the background gradient and the grid into Graphics /// /// Graphic public void DrawBackgroundWindow(Graphics g, RectangleF crect, RectangleF winrect) { //using (Brush gradientBrush = new LinearGradientBrush(this.WindowRect, Style.design_backcolor_start, Style.design_backcolor_bottom, LinearGradientMode.Vertical)) //{ // g.FillRectangle(gradientBrush, this.WindowRect); //} //g.FillRectangle(Brushes.Black, this.WindowRect); //전체창에대한 보더 RectangleF borderrect = new RectangleF(winrect.Left, winrect.Top, winrect.Width - 1, winrect.Height - 1); g.DrawRectangle(Pens.Gray, borderrect.Left, borderrect.Top, borderrect.Width, borderrect.Height); //실제차트정보 //g.FillRectangle(Brushes.Gray, ChartRect.Left + 2, ChartRect.Top + 2, ChartRect.Width, ChartRect.Height); //g.FillRectangle(Brushes.Black, ChartRect); //Display Collection TIME //String Str = "Collection Time : " + GetDateTimeStr(startview, false) + " - " + GetDateTimeStr(endview, false); //SizeF fontszie = g.MeasureString(Str, this.Font); //// Rectangle rect = new Rectangle((int)winrect.Left, (int)winrect.Top, (int)winrect.Width, (int)(fontszie.Height * 2)); // g.DrawString(Str, this.Font, Brushes.Black, rect.Left + 10, 3+rect.Top + (rect.Height - fontszie.Height) / 2); // //g.DrawLine(Pens.Black, winrect.Left, fontszie.Height * 2, winrect.Left + winrect.Width, fontszie.Height * 2); //CHART BORDER g.DrawRectangle(new Pen(Color.Black, 2), crect.Left, crect.Top, crect.Width, crect.Height); } public void Set_Refresh() { Console.WriteLine("Graph : Set_Refresh"); Refresh_BackGround = true; Refresh_Grpah = true; } /// Override OnPaint method protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // this.SuspendLayout(); // AntiAliasing e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; //표시아이템이없다면 오류를 낸다. if (!init || chinfo == null) { //progress int bw = (int)(this.Width * 0.8); int bh = (int)(this.Height * 0.05); Rectangle rr = new Rectangle((this.Width - bw) / 2, (this.Height - bh) / 2 + 10, bw, bh); //Display Initial Message Font nf = new Font("나눔고딕", 20, FontStyle.Bold); Rectangle FullRect = new Rectangle(0, 0, this.Width, this.Height); SizeF initsize = e.Graphics.MeasureString(initmsg, nf); e.Graphics.DrawString(initmsg, nf, Brushes.Black, this.Width / 2 - initsize.Width / 2, this.Height / 2 - initsize.Height / 2 - bh); //Display progress Bar if (initpercent > 100) initpercent = 100; int perc = (int)(rr.Width * initpercent / 100); e.Graphics.FillRectangle(Brushes.Green, new Rectangle(rr.Left, rr.Top, perc, rr.Height)); //Display Control Border e.Graphics.DrawRectangle(Pens.DarkGray, rr); //using (Brush gradientBrush = new LinearGradientBrush(FullRect, Color.Gray, Color.Gray, LinearGradientMode.Vertical)) //{ // // e.Graphics.FillRectangle(gradientBrush, FullRect); // // String initstr = "initializing..."; // //if (initmsg != "") initstr += "\n\n" + initmsg; //} //LinearGradientBrush lb = new LinearGradientBrush(rr, Color.Gray, Color.WhiteSmoke,LinearGradientMode.Vertical); //lb.Dispose(); //e.Graphics.DrawRectangle(Pens.DarkGoldenrod, FullRect.Left, FullRect.Top, FullRect.Width - 1, FullRect.Height - 1); //this.ResumeLayout(); return; } Graphics g_bak; Graphics g_graph; if (Style.XV1 == Style.X1 && Style.XV2 == Style.X2) { //전체영역이면(전체용 비트맵을 사용함) g_bak = Graphics.FromImage(this.layer_bakT); g_graph = Graphics.FromImage(this.layer_graphT); } else { g_bak = Graphics.FromImage(this.layer_bak); g_graph = Graphics.FromImage(this.layer_graph); } // e.Graphics.CompositingMode = CompositingMode.SourceOver; if (Refresh_BackGround) { // g_bak.CompositingMode = CompositingMode.SourceOver; g_bak.Clear(Color.White); DrawBackgroundWindow(g_bak, ChartRect, WindowRect); Draw_Grid(g_bak, ChartRect); //x축,y축의 눈금과 그리드표시 Refresh_BackGround = false; } if (Refresh_Grpah) { // g_graph.CompositingMode = CompositingMode.SourceOver; g_graph.Clear(Color.Transparent); Draw_channel(g_graph, ChartRect, WindowRect); Refresh_Grpah = false; } g_bak.Dispose(); g_graph.Dispose(); //전체영역의경우 별도 할당된 이미지를 사용한ㄷ(최초 1번만 그리게된다) if (Style.XV1 == Style.X1 && Style.XV2 == Style.X2) { e.Graphics.DrawImage(layer_bakT, 0, 0); e.Graphics.DrawImage(layer_graphT, 0, 0); } else { e.Graphics.DrawImage(layer_bak, 0, 0); e.Graphics.DrawImage(layer_graph, 0, 0); } //기타정보그림 Draw_ZoomSelect(e.Graphics); Draw_Cursor(e.Graphics, ChartRect); Draw_MouseInfo(e.Graphics); Draw_Debug(e.Graphics); if (ShowLegend) Draw_legend(e.Graphics); g_bak.Dispose(); g_graph.Dispose(); //StringBuilder sb = new StringBuilder(); //sb.AppendLine("Startv View : " + startview.ToString() + "/" + endview.ToString()); //sb.AppendLine("Start Time : " + starttime.ToString() + "/" + endtime.ToString()); //sb.AppendLine("Start View O : " + Style.XV1.ToString() + "/" + endviewo.ToString()); //sb.AppendLine("Start Volt : " + Style.Y1.ToString() + "/" + Style.Y2.ToString()); //sb.AppendLine("Start Volt O : " + Style.Y1o.ToString() + "/" + Style.Y2o.ToString()); //e.Graphics.DrawString(sb.ToString(), this.Font, Brushes.Black, 100, 100); //e.Graphics.DrawRectangle(Pens.Red, ChartRect.Left, ChartRect.Top, ChartRect.Width, ChartRect.Height); //e.Graphics.DrawRectangle(Pens.Blue, LegendRect.Left, LegendRect.Top, LegendRect.Width, LegendRect.Height); //this.Update(); //this.ResumeLayout(); } public bool ShowLegend { get; set; } = false; void Draw_legend(Graphics g) { if (chinfo == null || chinfo.Any() == false) return; var maxcnt = 10; var h = LegendRect.Height / maxcnt; var w = LegendRect.Width; var showitem = this.chinfo.Where(t => t.Show).ToList(); var itemcnt = Math.Min(10, showitem.Count()); for (int i = 0; i < itemcnt; i++) { var y = LegendRect.Y + i * h; var item = showitem[i]; var rect = new RectangleF(LegendRect.X, y, w, h); var rectflag = new RectangleF(rect.X, rect.Y, rect.Width * 0.15f, rect.Height); var rectttitle = new RectangleF(rectflag.Right + 3, rect.Y, rect.Width - rectflag.Width - 3, rect.Height); using (var bgcolor = new SolidBrush(item.Color)) g.FillRectangle(bgcolor, rectflag); var title = $"{item.TITLE}"; if (item.Value.Any()) title = $"{item.TITLE} ({item.Value.Last().Value}v)"; g.DrawString(title, this.Font, Brushes.Black, rectttitle, new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center }); ; g.DrawRectangle(Pens.Black, rect.X, rect.Y, rect.Width, rect.Height); } } public void Draw_Cursor(Graphics g, RectangleF Crect) { if (this.uc.Count == 0) return; Font cfont = new Font("나눔고딕", 10, FontStyle.Bold); //커서는 기본 2개를 둔다(전체사이즈를 측정하기위해서 임시로 크기를 테스트한다.) String cursorstr = "▦ C1 2012-00-00- 00:00:00 ▦ C1 2012-00-00- 00:00:00 ▦ C1 2012-00-00- 00:00:00"; SizeF FontSize = g.MeasureString(cursorstr.ToString(), cfont); //커서가표시될영역 //Cursorrect = new Rectangle(0, 0, 50,50); //Cursorrect.X = (int)(Crect.Left + Crect.Width - FontSize.Width * 1.1) - 1; //Cursorrect.Y = (int)(Crect.Top - 2 - FontSize.Height); //Cursorrect.Width = (int)(FontSize.Width * 1.1); //Cursorrect.Height = (int)(FontSize.Height * 1.3); //g.FillRectangle(Brushes.DimGray, Cursorrect); //g.DrawRectangle(Pens.Gray, Cursorrect); //Single newy = Cursorrect.Top + ((Cursorrect.Height - FontSize.Height)/2) ; //Single Maxwidth = 0; //String fulltext = ""; var qry = from CUserCursor cs in uc orderby cs.Idx select cs; cursorstr = ""; foreach (CUserCursor uctl in qry) { Single newx = GetXfromTime(uctl.Time, Crect); uctl.Newx = newx; Single imgsizew = Properties.Resources.down_16.Width; Single imgsizeh = Properties.Resources.down_16.Height; cursorstr += " ▦ C" + (uctl.Idx + 1).ToString() + " " + GetTimeStr(uctl.Time, true, true, true, true); FontSize = g.MeasureString(cursorstr.ToString(), cfont); // if (FontSize.Width > Maxwidth) Maxwidth = FontSize.Width; if (uctl.Idx < 1) { g.DrawImage(Properties.Resources.down_16, (newx - imgsizew / 2) - 1, Crect.Top - imgsizeh - 5); g.DrawLine(new Pen(Color.Green, 1), newx, Crect.Top, newx, Crect.Top + Crect.Height); // fulltext = cursorstr; } else { TimeSpan ts = DateTime.FromFileTime(uctl.Time) - DateTime.FromFileTime(uc[0].Time); cursorstr += " △t " + ts.Days.ToString() + "d " + ts.Hours.ToString() + ":" + ts.Minutes.ToString() + ":" + ts.Seconds.ToString(); g.DrawImage(Properties.Resources.down_orange, (newx - imgsizew / 2) - 1, Crect.Top - imgsizeh - 5); g.DrawLine(new Pen(Color.Orange, 1), newx, Crect.Top, newx, Crect.Top + Crect.Height); // if (fulltext == "") fulltext = cursorstr; // else fulltext += "\n" + cursorstr; } // newy += FontSize.Height + 1; } if (cursorstr != "") { FontSize = g.MeasureString(cursorstr, cfont); g.DrawString(cursorstr, cfont, Brushes.Black, Crect.Right - FontSize.Width - 2, Crect.Top - FontSize.Height - 2); } // // Cursorrect = new Rectangle((int)(ChartRect.Left + ChartRect.Width - Maxwidth), (int)ChartRect.Top, (int)Maxwidth, (int)(newy - FontSize.Height)); } public void Draw_MouseInfo(Graphics g) { if (this.Mouseinfo.Showinfo) //마웃위치정보를 화면에 표시를 한다. { String tm = GetTimeStr(Mouseinfo.Time, true, true, true, true) + "\n" + Mouseinfo.Volt.ToString("#0.00") + "v"; if (Style.Show_DebugMsg) tm += "\n" + Mouseinfo.Time.ToString(); SizeF tmsize = g.MeasureString(tm, Style._mouseinfofont); RectangleF rect = new RectangleF(Mouseinfo.Position.X + 5, Mouseinfo.Position.Y + 5, tmsize.Width + 10, tmsize.Height + 10); if (Mouseinfo.Position.X >= (this.ChartRect.Left + ChartRect.Width / 2)) { //마우스위치에따라서 표시정보 위치를 좌/우 교환한다 150223 rect = new RectangleF(Mouseinfo.Position.X - (tmsize.Width + 10) - 5, Mouseinfo.Position.Y + 5, tmsize.Width + 10, tmsize.Height + 10); } //g.FillRectangle(new SolidBrush(Color.FromArgb(140,Color.Green)), rect); g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width, rect.Height); g.DrawString(tm, Style._mouseinfofont, new SolidBrush(Style.design_mouseinfocolor), rect.Left + (rect.Width - tmsize.Width) / 2, rect.Top + (rect.Height - tmsize.Height) / 2); } } public void Draw_Grid(Graphics g, RectangleF crect) { DateTime SDVDate = DateTime.FromFileTime(Style.XV1); DateTime SDate = DateTime.FromFileTime(Style.XV1); DateTime EDate = DateTime.FromFileTime(Style.XV2); var (normalizedStart, normalizedEnd) = NormalizeTimeRangeHalfHourIntervals(SDate, EDate); SDVDate = normalizedStart; SDate = normalizedStart; EDate = normalizedEnd; TimeSpan TS = EDate - SDate; //timespan if (TS.TotalSeconds > Style.MaxZoomX) { //표시간격 최소 10초 int tm = 10; if (Style.XGap == 0) { tm = (int)(TS.TotalSeconds / 5); } else { tm = Style.XGap; } var termcnt = (int)(TS.TotalSeconds / tm); if (termcnt < 10) { tm = (int)(TS.TotalSeconds / 10f); } else if (termcnt > 10) //간격이 너무 크다 { tm = (int)(TS.TotalSeconds / 10f); } tm = GetClosestInterval(tm); for (Int64 i = 0; i <= TS.TotalSeconds; i += tm) { DateTime NewDate = SDVDate.AddSeconds(i); //시작시간에서 if (SDate.Day != NewDate.Day) { //일자가 바귀엇으므로 해당 일즤 세로축을 표시 DateTime LineDay = DateTime.Parse(NewDate.ToString("yyyy-MM-dd 00:00:00")); Single newx2 = GetXfromTime(LineDay.ToFileTime(), crect); g.DrawLine(new Pen(Color.FromArgb(50, Color.Black)), newx2, crect.Top, newx2, crect.Top + crect.Height); SDate = NewDate; } Single newx = GetXfromTime(NewDate.ToFileTime(), crect); String timestr = ""; if (i == 0) timestr = GetDateTimeStr(NewDate.ToFileTime(), true); else timestr = GetTimeStr(NewDate.ToFileTime(), i == 0 ? true : false, false); timestr = NewDate.ToString("yy-MM-dd") + "\n" + NewDate.ToString("HH:mm:ss"); DrawString(g, timestr, Style.FontX, Brushes.Black, new PointF(newx, crect.Top + crect.Height + 5), EALIGN.CENTER); if (i > 0) g.DrawLine(new Pen(Color.Gray), newx, crect.Top, newx, crect.Top + crect.Height); } } else { //Y축 값을 표시할 값이 없으면 오류를 표시한다. DrawString(g, "ERRX", Style.FontX, Brushes.Black, new PointF(crect.Left - 2, crect.Bottom), EALIGN.RIGHT, EALIGN.CENTER); } //세로(VOLT)표시 //g.DrawString("start="+Style._startvolt.ToString(), this.Font, Brushes.Red, 100, 100); //g.DrawString("end="+Style._endvolt.ToString(), this.Font, Brushes.Red, 100, 120); //g.DrawString("최소="+Style.최소값.ToString(), this.Font, Brushes.Red, 100, 140); //g.DrawString("최대="+Style.최대값.ToString(), this.Font, Brushes.Red, 100, 160); if (Math.Abs(Style.YV2 - Style.YV1) >= Style.MaxZoomY) { Single term; if (Style.YGap == 0) { term = (Style.YV2 - Style.YV1) / 10; } else { term = Style.YGap; } for (Single i = Style.YV1; i <= Style.YV2; i += term) { // 세로길이 = 10v : Single newy = GetYfromVolt(i, crect); if (Style.YV1 == 0 && i == 0) { } else g.DrawLine(new Pen(Color.Gray), crect.Left, newy, crect.Left + crect.Width, newy); //소수점없는경우에눈금표시한다. DrawString(g, i.ToString("#0.00"), Style.FontY, Brushes.Black, new PointF(crect.Left - 2, newy), EALIGN.RIGHT, EALIGN.CENTER); } } else { //Y축 값을 표시할 값이 없으면 오류를 표시한다. DrawString(g, "ERRY", Style.FontY, Brushes.Black, new PointF(crect.Left - 2, crect.Top + 2), EALIGN.RIGHT, EALIGN.CENTER); } } public (DateTime normalizedStart, DateTime normalizedEnd) NormalizeTimeRangeHalfHourIntervals(DateTime start, DateTime end) { const int intervalInSeconds = 1800; // 30 minutes DateTime normalizedStart = NormalizeTimeToInterval(start, intervalInSeconds, roundDown: true); DateTime normalizedEnd = NormalizeTimeToInterval(end, intervalInSeconds, roundDown: false); return (normalizedStart, normalizedEnd); } private DateTime NormalizeTimeToInterval(DateTime time, int intervalInSeconds, bool roundDown) { long ticks = time.Ticks / TimeSpan.TicksPerSecond; if (roundDown) { ticks = (ticks / intervalInSeconds) * intervalInSeconds; // Round down } else { ticks = ((ticks + intervalInSeconds - 1) / intervalInSeconds) * intervalInSeconds; // Round up } return new DateTime(ticks * TimeSpan.TicksPerSecond); } private readonly int[] TimeIntervals = new int[] { 1, 10, 30, 60, 300, 600, 1800, 3600, 21600, 43200, 86400, 432000, 864000, 2592000 }; public int GetClosestInterval(int seconds) { // Handle cases where seconds is less than the smallest interval or more than the largest interval if (seconds <= TimeIntervals[0]) return TimeIntervals[0]; if (seconds >= TimeIntervals[TimeIntervals.Length - 1]) return TimeIntervals[TimeIntervals.Length - 1]; // Find the closest interval int closestInterval = TimeIntervals[0]; int minDifference = Math.Abs(seconds - closestInterval); foreach (int interval in TimeIntervals) { int difference = Math.Abs(seconds - interval); if (difference < minDifference) { minDifference = difference; closestInterval = interval; } } return closestInterval; } public void Draw_Debug(Graphics g) { if (!Style.Show_DebugMsg) return; //Single newy = 50; //String newstr = ""; SizeF fontsize; StringBuilder DebugMsg = new StringBuilder(); DebugMsg.AppendLine("Mouseinfo=" + this.Mouseinfo.Position.ToString() + " " + this.Mouseinfo.Showinfo.ToString()); DebugMsg.AppendLine("Mouseinfo0=" + this.Mouseinfo.Position0.ToString() + " idx=" + this.Mouseinfo.DragIndex.ToString()); DebugMsg.AppendLine("Mouse Volt =" + this.Mouseinfo.Volt.ToString() + " ,Time=" + DateTime.FromFileTime(Mouseinfo.Time).ToString()); DebugMsg.AppendLine("Mouse Drag=" + Mouseinfo.Drag.ToString() + " " + Mouseinfo.DragType.ToString()); DebugMsg.AppendLine("Mouse DragStart=" + Mouseinfo.DragStart.ToString()); DebugMsg.AppendLine("TimeCount =" + Time.Count.ToString()); TimeSpan NowTerm = DateTime.FromFileTime(Style.X2) - DateTime.FromFileTime(Style.X1); DebugMsg.AppendLine("XGap =" + Style.XGap.ToString() + ",YGap=" + Style.YGap.ToString() + ",Xterm=" + Style.X표시범위.ToString() + ",NowTerm=" + NowTerm.ToString()); foreach (String file in Files) { DebugMsg.AppendLine("File = " + file); } foreach (CUserCursor ucitem in uc) { DebugMsg.AppendLine("uc" + ucitem.Idx.ToString() + ",x=" + ucitem.Newx.ToString() + ",time=" + ucitem.Time.ToString()); } DebugMsg.AppendLine("======================"); DebugMsg.AppendLine("시간축=" + Style.X1.ToString() + "~" + Style.X2.ToString() + " : " + DateTime.FromFileTime(Style.X1).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.X2).ToString("yy-MM-dd HH:mm:ss")); DebugMsg.AppendLine("시간축V=" + Style.XV1.ToString() + "/" + Style.XV2.ToString() + " : " + DateTime.FromFileTime(Style.XV1).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.XV2).ToString("yy-MM-dd HH:mm:ss")); DebugMsg.AppendLine("시간축VO=" + Style.XV1o.ToString() + "/" + Style.XV2o.ToString() + " : " + DateTime.FromFileTime(Style.XV1o).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.XV2o).ToString("yy-MM-dd HH:mm:ss")); DebugMsg.AppendLine("======================"); DebugMsg.AppendLine("전압축=" + Style.Y1.ToString() + "~" + Style.Y2.ToString()); DebugMsg.AppendLine("전압축V=" + Style.YV1.ToString() + "~" + Style.YV2.ToString()); DebugMsg.AppendLine("전압축VO=" + Style.YV1o.ToString() + "~" + Style.YV2o.ToString()); DebugMsg.AppendLine("======================"); DebugMsg.AppendLine("UserZoom=" + UserZoom.ToString()); // newstr = "CHinfo=" + chinfo.GetUpperBound(0); foreach (CChinfo ch in chinfo) { DebugMsg.AppendLine("idx=" + ch.Idx.ToString() + ",visible:" + ch.Show.ToString() + ",cnt=" + ch.Value.Count.ToString() + ",Color=" + ch.Color.ToString()); } fontsize = g.MeasureString(DebugMsg.ToString(), this.Font); g.FillRectangle(new SolidBrush(Color.FromArgb(180, Color.Black)), new Rectangle(100, 100, (int)(fontsize.Width * 1.3), (int)(fontsize.Height * 1.3))); g.DrawString(DebugMsg.ToString(), this.Font, Brushes.Tomato, 110, 110); } public List Files = new List(); public void DataClear() { if (uc != null) uc.Clear(); if (chinfo != null) Array.Clear(chinfo, 0, chinfo.Length); if (Time != null) this.Time.Clear(); if (Files != null) Files.Clear(); } /// /// 지정된 시간값이 현재 존재하는가? /// /// int64 : time value /// public Boolean ExistTime(Int64 _value) { // if (Time.Count < 1) return false; return Time.Select(t => t.Value).ToList().Contains(_value); //for (int i = 1; i <= Time.Count; i++) //{ // if (_value == Time[i - 1].Value) return true; //} //return false; } public void ScaleFitX() { var xrange = this.RangeX; this.Style.XV1 = xrange.min; this.Style.XV2 = xrange.max; this.Invalidate(); } public void ScaleFitY() { var xrange = this.RangeY; this.Style.YV1 = xrange.min; this.Style.YV2 = xrange.max; this.Invalidate(); } public void ScaleFitXY() { ScaleFitX(); ScaleFitY(); } public void Draw_channel(Graphics g, RectangleF crect, RectangleF wrect) { //지정된 시간에 속한 타임만 취듯한다. var rangeX = this.RangeX; var minTime = this.Time[rangeX.min]; var maxTime = this.Time[rangeX.max]; var qry = Time .Where(t => t.Value >= Style.XV1 && t.Value <= Style.XV2) .OrderBy(t => t.Value) .ToDictionary(t => t.Key, t => t.Value); if (qry.Any() == false) return; //지정된 시간영역의 자료의 범위를 측정 var time_min = qry.Min(t => t.Key); //최소시간정보 var time_max = qry.Max(t => t.Key); //최대시간정보 //각채널의 데이터를 확인한다(시간정보[최소~최대]에 포함된것만 표시한다) var chlist = chinfo.Where(t => t != null && t.Show && (t.Value.Max(s => s.Key) >= time_min && t.Value.Min(s => s.Key) <= time_max)).ToList(); foreach (var ch in chinfo) { if (ch == null || ch.Show == false) continue; //var minX = ch.Value.Min(s => s.Key); //var maxX = ch.Value.Max(s => s.Key); //해당채널내에서 지정된 영역의 자료만 가져온다. var valueList = ch.Value.Where(t => t.Key >= time_min && t.Key <= time_max).ToDictionary(t => t.Key, t => t.Value); if (valueList.Any() == false) continue; List linearrya = new List(0); Single PreX = 0; Single PreY = 0; Boolean fc1 = false, fc2 = false; int idx = 0; //저장된 모든 값을 출력한다. foreach (var cdata in valueList) { //시간정보는 동일하다면 조회하지 않게한다. var time = qry[cdata.Key]; var volt = cdata.Value; //좌표계산 Single newx = GetXfromTime(time, crect) - 1; Single newy = GetYfromVolt(volt, crect) - 1; //커서값확인 foreach (CUserCursor cs in uc) { if (cs.Idx == 0 && time >= cs.Time && fc1 == false) { if (volt > 60) ch.C1value = "OVER"; else ch.C1value = volt.ToString(); fc1 = true; } else if (cs.Idx == 1 && time >= cs.Time && fc2 == false) { if (volt > 60) ch.C2value = "OVER"; else ch.C2value = volt.ToString(); fc2 = true; } } //너무가까운데이터는 그리지 않는다. //if (Math.Abs(newx - PreX) < 1 && Math.Abs(newy - PreY) < 1) //{ // //Console.WriteLine("SKip Pixel"); // continue; //} //else //{ PreX = newx; PreY = newy; //} if (newx >= wrect.Left && newx <= wrect.Left + wrect.Width) //y축은 구분없이 모두 그리게한다. linearrya.Add(new PointF(newx, newy)); if (Style.Show_DataPoint) { var size = 2; g.FillEllipse(new SolidBrush(Color.FromArgb(180, ch.Color)), new RectangleF(newx - size, newy - size, size * 2, size * 2)); g.DrawEllipse(Pens.Black, new RectangleF(newx - size, newy - size, size * 2, size * 2)); } idx += 1; } if (linearrya.Count > 1) g.DrawLines(new Pen(ch.Color, 10), linearrya.ToArray()); } } public void Draw_ZoomSelect(Graphics g) { //사용자줌영역표시 if (this.UserZoom.Width != 0) { int x, y, w, h; w = Math.Abs(UserZoom.Width); h = Math.Abs(UserZoom.Height); if (UserZoom.Width > 0) x = UserZoom.Left; else x = UserZoom.Width + UserZoom.Left; if (UserZoom.Height > 0) y = UserZoom.Top; else y = UserZoom.Height + UserZoom.Top; //Y축 줌을 사용하지 않으므로 높이부분은 모두로 처리한다. if (!Style.UseZoomY) { y = (int)ChartRect.Top; h = (int)ChartRect.Height; } Rectangle rect = new Rectangle(x, y, w, h); Int64 startx = GetTimeFromX(rect.Left - this.ChartRect.Left, ChartRect); //줌영역의 시작시간정보 Int64 endx = GetTimeFromX(rect.Left + rect.Width - ChartRect.Left, ChartRect); //줌영역의 종료시간정보 Single starty = (Single)GetVoltfromY(rect.Top, ChartRect); Single endy = (Single)GetVoltfromY(rect.Top + rect.Height, ChartRect); String ZommMsg = ""; //선택된 영역 Drawing TimeSpan zoomts = DateTime.FromFileTime(startx) - DateTime.FromFileTime(endx); if (Math.Abs(zoomts.TotalSeconds) >= Style.MaxZoomX && Math.Abs(starty - endy) >= Style.MaxZoomY && rect.Width > 20) //150224 줌영역자체의 크기는 10px를 넘도록 { //시간은 최소1분 , 전압값은 최소 1v g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Lime)), rect); ZommMsg = Math.Abs(zoomts.TotalSeconds).ToString("N0") + Style.UnitX; } else { g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Red)), rect); if (rect.Width <= 20) { ZommMsg = "줌 선택영역을 넓게 지정하세요"; } else { if (Math.Abs(zoomts.TotalSeconds) < Style.MaxZoomX) { ZommMsg = "줌을 활성화 하려면 최소(" + Style.MaxZoomX.ToString() + Style.UnitX + "의 구간을 선택해야합니다"; } else { ZommMsg = "줌을 활성화 하려면 최소(" + Style.MaxZoomY.ToString() + Style.UnitY + "의 구간을 선택해야합니다"; } } } g.DrawRectangle(Pens.Black, rect); //시간정보 Display g.DrawString(GetTimeStr(startx, true, true) + "," + (Style.Y2 - starty).ToString("#0.00") + Style.UnitY, this.Font, Brushes.Black, rect.Left, rect.Top); String fontstr = GetTimeStr(endx, true, true) + "," + (Style.Y2 - endy).ToString("#0.00") + Style.UnitY; SizeF fontsize = g.MeasureString(fontstr, this.Font); g.DrawString(fontstr, this.Font, Brushes.Black, rect.Left - 5 + rect.Width - fontsize.Width, rect.Top + rect.Height - fontsize.Height); if (ZommMsg != "") DrawString(g, ZommMsg, this.Font, Brushes.Black, new PointF(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2), EALIGN.CENTER, EALIGN.CENTER); } } /// /// 화면의 좌,우 측에 존재하는 이돈버튼(월을 움직이는 버튼이며 월 이동을위해서 이벤트를 발생한다) /// /// //public void Draw_NaviLR(Graphics G, RectangleF crect) //{ // return; // //LEFT NAVI // Single x, y, xw = 30; // x = crect.Left - 3; // y = crect.Top + crect.Height / 2 - xw / 2; // PointF[] aa = new PointF[4]; // aa[0] = new PointF(x, y); // aa[1] = new PointF(x - xw, y + xw); // aa[2] = new PointF(x, y + xw + xw); // aa[3] = new PointF(x, y); // PointF[] aa1 = new PointF[4]; // aa1[0] = new PointF(x + 2, y + 2); // aa1[1] = new PointF(x + 2 - xw, y + xw + 2); // aa1[2] = new PointF(x + 2, y + xw + xw + 2); // aa1[3] = new PointF(x + 2, y + 2); // G.FillPolygon(Brushes.Black, aa1); // G.FillPolygon(Brushes.DarkRed, aa); // G.DrawPolygon(Pens.Black, aa); // Buttons.Add(new RectangleF(x - xw, y, xw, xw*2)); // Buttonstag.Add("LBUTTON"); // Buttonstype.Add(EBUTTONTYPE.LEFTMON); // //RIGHT NAVI // x = crect.Left + crect.Width + 3; // y = crect.Top + crect.Height / 2 - xw / 2; // aa = new PointF[4]; // aa[0] = new PointF(x, y); // aa[1] = new PointF(x + xw, y + xw); // aa[2] = new PointF(x, y + xw + xw); // aa[3] = new PointF(x, y); // aa1 = new PointF[4]; // aa1[0] = new PointF(x + 2, y + 2); // aa1[1] = new PointF(x + 2 + xw, y + xw + 2); // aa1[2] = new PointF(x + 2, y + xw + xw + 2); // aa1[3] = new PointF(x + 2, y + 2); // G.FillPolygon(Brushes.Black, aa1); // G.FillPolygon(Brushes.DarkRed, aa); // G.DrawPolygon(Pens.Black, aa); // Buttons.Add(new RectangleF(x, y, xw, xw*2)); // Buttonstag.Add("RBUTTON"); // Buttonstype.Add(EBUTTONTYPE.RIGHTMON); //} public String GetDateTimeStr(Int64 Filetime, Boolean linefeed) { DateTime TimeDate = DateTime.FromFileTime(Filetime); String timestr = ""; if (linefeed) timestr = TimeDate.ToShortDateString() + "\n" + TimeDate.ToShortTimeString(); //' yymm + "-" + day.ToString("00") + "\n" + hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00"); else timestr = TimeDate.ToString();// yymm + "-" + day.ToString("00") + " " + hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00"); return timestr; } public String GetTimeStr(Int64 Filetime, Boolean withday, Boolean withseconed, Boolean nolf = false, Boolean showyear = false) { if (Filetime < 0D) return "-Err-"; DateTime TimeDate = DateTime.FromFileTime(Filetime); String timestr = ""; if (TimeDate.Second == 0 || !withseconed) timestr = TimeDate.ToString("HH:mm");// TimeDate.Hour.ToString("00") + ":" + TimeDate.Minute.ToString("00"); else timestr = TimeDate.ToString("HH:mm:ss"); // hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00"); if (withday) { if (showyear) { timestr = TimeDate.ToString("yy-MM-dd") + " " + timestr; } else { if (nolf) timestr = TimeDate.ToString("dd") + "일 " + timestr; else timestr = timestr + "\n" + TimeDate.ToString("dd") + "일"; } } return timestr; } /// /// (전체시간 * 현재X) / 전체너비 /// /// /// public Int64 GetTimeFromX(Single X, RectangleF crect) { DateTime sd = DateTime.FromFileTime(Style.XV1); DateTime ed = DateTime.FromFileTime(Style.XV2); TimeSpan ts = ed - sd; Int64 gap = (Int64)((ts.TotalMilliseconds * X) / crect.Width); return sd.AddMilliseconds(gap).ToFileTime(); } public Single GetXfromTime(Int64 t) { return (((this.ChartRect.Width) * (t - Style.XV1)) / (Style.XV2 - Style.XV1)) + ChartRect.Left; } public Single GetXfromTime(Int64 t, RectangleF crect) { return (((crect.Width) * (t - Style.XV1)) / (Style.XV2 - Style.XV1)) + crect.Left; } public Single GetVoltfromY(Single Y, RectangleF crect) { return ((Style.Y2 - Style.Y1) * Y) / crect.Height; } public Single GetYfromVolt(Single volt, RectangleF crect) { //volt = (Style._endvolt - Style._startvolt) - volt; 150223 : Y값도 X축처럼 표시변수 YV 값으로 수정함 return (crect.Height - ((crect.Height * (volt - Style.YV1)) / (Style.YV2 - Style.YV1))) + crect.Top; } private void DrawString(Graphics G, String str, Font font, Brush br, PointF ops, EALIGN XCenter = EALIGN.LEFT, EALIGN YCenter = EALIGN.LEFT) { SizeF fsize = G.MeasureString(str, font); Single newx = ops.X; Single newy = ops.Y; if (XCenter == EALIGN.CENTER) newx = ops.X - fsize.Width / 2; else if (XCenter == EALIGN.RIGHT) newx = ops.X - fsize.Width; if (YCenter == EALIGN.CENTER) newy = ops.Y - fsize.Height / 2; else if (YCenter == EALIGN.RIGHT) newy = ops.Y - fsize.Height; G.DrawString(str, font, br, newx, newy); } public Boolean INIT { get { return this.init; } set { this.init = value; } } public CChinfo[] CHInfo { get { return this.chinfo; } set { this.chinfo = value; } } public (uint min, uint max) RangeX { get { var min = (CHInfo.Min(t => t.Value.Where(s => s.Key > 0).Min(v => v.Key))); var max = (CHInfo.Max(t => t.Value.Where(s => s.Key > 0).Max(v => v.Key))); return (min, max); } } public (float min, float max) RangeY { get { //100을 초과한 범위는 제한한다 var min = CHInfo.Min(t => t.Value.Where(s => s.Value < MaxValueY && s.Value > -MaxValueY).Min(u => u.Value)); var max = CHInfo.Max(t => t.Value.Where(s => s.Value < MaxValueY && s.Value > -MaxValueY).Max(u => u.Value)); return (min, max); } } public int LegendWidth { get; set; } = 200; public RectangleF LegendRect { get; private set; } protected override void OnResize(EventArgs e) { base.OnResize(e); LegendWidth = 100; this.WindowRect = new RectangleF(0, 0, this.Width, this.Height); if (ShowLegend) { this.ChartRect = new RectangleF(Padding.Left, Padding.Top, this.Width - (Padding.Left + 10) - LegendWidth - 20, (Single)(this.Height - (Padding.Top + Padding.Bottom))); LegendRect = new RectangleF(ChartRect.Right + 10, ChartRect.Y, LegendWidth + 10, ChartRect.Height); } else { this.ChartRect = new RectangleF(Padding.Left, Padding.Top, this.Width - (Padding.Left + Padding.Right) - 20, (Single)(this.Height - (Padding.Top + Padding.Bottom))); LegendRect = RectangleF.Empty; // new RectangleF(ChartRect.Right + 20, ChartRect.Y, LegendWidth, ChartRect.Height); } int SW = this.Width; int SH = this.Height; if (this.Width == 0 || this.Height == 0) { SW = 600; SH = 600; } layer_bak = new Bitmap(SW, SH); layer_graph = new Bitmap(SW, SH); layer_bakT = new Bitmap(SW, SH); layer_graphT = new Bitmap(SW, SH); Graphics.FromImage(layer_bak).Clear(Color.White); Graphics.FromImage(layer_bakT).Clear(Color.White); Set_Refresh(); Invalidate(); } private int check_userpoint(MouseEventArgs e) { if (this.uc.Count == 0) return -1; for (int i = 0; i < this.uc.Count; i++) { CUserCursor uuc = this.uc[i]; //현재위치가 해당 Rectagle 안이라면 속해있다. if ((int)this.Mouseinfo.Position.X >= (int)uuc.Newx - 2 && (int)this.Mouseinfo.Position.X <= (int)(uuc.Newx) + 2 && this.Mouseinfo.Position.Y <= ChartRect.Top) return i; } return -1; } //private int check_buttons(MouseEventArgs e) //{ // if (this.Buttons == null) return -1; // for (int i = 0; i < this.Buttons.Count; i++) // { // RectangleF rect = this.Buttons[i]; // //현재위치가 해당 Rectagle 안이라면 속해있다. // if (e.X > rect.X && e.X < rect.X + rect.Width) // { // if (e.Y > rect.Y && e.Y < rect.Y + rect.Height) return i; // } // } // return -1; //} private void InitializeComponent() { this.SuspendLayout(); // // TrendCtrl // this.Name = "TrendCtrl"; this.Size = new System.Drawing.Size(287, 321); this.Load += new System.EventHandler(this.DispCtrl_Load); this.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseDoubleClick); this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseDown); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseMove); this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseUp); this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseWhell); this.ResumeLayout(false); } public void Graph_Reset() { Style.ResetXxis(); Style.ResetYxis(); Set_Refresh(); Console.WriteLine("Graph : Graph_Reset"); } private void TrendCtrl_MouseDown(object sender, MouseEventArgs e) { switch (e.Button) { case System.Windows.Forms.MouseButtons.Left: //드래그판단을위한 위치값을 저장한다. Mouseinfo.DragStart = new PointF(e.X - this.WindowRect.Left, e.Y - WindowRect.Top); //화면이동모드일때는 좌측버튼도 중앙버튼처럼 처리해야한다. if (ScreenMove) { //X축 줌을 사용한경우에만 가능하게 한다. if (Style.UseZoomX) { //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다. if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) { if (Style.XV2 == Style.X2 && Style.XV1 == Style.X1) { //데이터영역과 표시영역이 동일하다(이동할 영역이 없는경우이다) Mouseinfo.DragType = EDRAGTYPE.NONE; } else { //이동할영역이 존재하므로 이동시킨다. Mouseinfo.DragType = EDRAGTYPE.SCREEN; this.UserZoom = new Rectangle(0, 0, 0, 0); } } } } else { //사용자포인트관련 int idx = check_userpoint(e); if (idx != -1) { Mouseinfo.DragType = EDRAGTYPE.UC; Mouseinfo.DragIndex = idx; return; } //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다. if (idx == -1 && e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) { //좌측버튼은 항상 줌영역을 활성화한다 if (Style.UseZoomX) { Mouseinfo.DragType = EDRAGTYPE.ZOOM; this.UserZoom = new Rectangle(e.X, e.Y, 0, 0); } else { //줌을 사용하지 않기로 하여 ㅆ다면? Mouseinfo.DragType = EDRAGTYPE.NONE; } } } break; case System.Windows.Forms.MouseButtons.Middle: //X축 줌을 사용한경우에만 가능하게 한다. if (Style.UseZoomX) { //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다. if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) { if (Style.XV2 == Style.X2 && Style.XV1 == Style.X1) { //데이터영역과 표시영역이 동일하다(이동할 영역이 없는경우이다) Mouseinfo.DragType = EDRAGTYPE.NONE; } else { //이동할영역이 존재하므로 이동시킨다. Mouseinfo.DragType = EDRAGTYPE.SCREEN; this.UserZoom = new Rectangle(0, 0, 0, 0); } } } break; case System.Windows.Forms.MouseButtons.Right: // 줌 초기화 // if (Message != null) Message("줌영역을 초기화합니다"); Style.XV1 = Style.XV1o; Style.XV2 = Style.XV2o; Style.Y1 = Style.YV1o; Style.Y2 = Style.YV2o; Set_Refresh(); //전체화면용 데이터를 바로 사용하면된다) Console.WriteLine("Mouse R Click : REFRESH"); Refresh(); //if(Message != null) Message(""); break; } } private void TrendCtrl_MouseMove(object sender, MouseEventArgs e) { //마우스의 위치가 실시간 저장된다. if (this.Mouseinfo == null) this.Mouseinfo = new CMouseinfo(new PointF(e.X - this.WindowRect.Left, e.Y - this.WindowRect.Top)); else this.Mouseinfo.Position = new PointF(e.X - this.WindowRect.Left, e.Y - this.WindowRect.Top); this.Mouseinfo.Time = GetTimeFromX(Mouseinfo.Position.X - ChartRect.Left, ChartRect); // +this.startview; this.Mouseinfo.Volt = this._Style.Y2 - GetVoltfromY(Mouseinfo.Position.Y - ChartRect.Top, ChartRect); this.Mouseinfo.Showinfo = false; //MouseStatus initialize... Mouseinfo.Hand = false; Mouseinfo.Cross = false; Mouseinfo.Move = false; Mouseinfo.ChangePos = false; // if (e.X >= ChartRect.X && e.X <= ChartRect.X + ChartRect.Width) if (e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) Mouseinfo.Showinfo = true; else Mouseinfo.Showinfo = false; //이동중인데 마우스가 눌려있다면 DRAG다 if (e.Button == MouseButtons.Left) Mouseinfo.Drag = true; else Mouseinfo.Drag = false; if (Mouseinfo.Drag) //드래그중이다 { Mouseinfo.Move = true; //화면이동으로 } else { if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width) { if (e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) { Mouseinfo.Cross = true; //화면내부라면 크로스 } else Mouseinfo.Cross = false; } else Mouseinfo.Cross = false; } //사용자포인터위라면? int idx = this.check_userpoint(e); if (idx != -1) { Mouseinfo.Cross = false; Mouseinfo.Hand = false; Mouseinfo.Drag = false; Mouseinfo.ChangePos = true; Mouseinfo.DragType = EDRAGTYPE.UC; } //드래그중판단 if (Mouseinfo.Drag) { switch (Mouseinfo.DragType) { case EDRAGTYPE.NONE: break; case EDRAGTYPE.UC: Single newx = (e.X); Int64 newtime = GetTimeFromX(newx - ChartRect.Left, ChartRect); if (Mouseinfo.DragIndex < uc.Count) { //데이터가 없는경우이다. this.uc[Mouseinfo.DragIndex].Newx = newx; this.uc[Mouseinfo.DragIndex].Time = newtime; } break; case EDRAGTYPE.ZOOM: this.UserZoom.Width = (int)(e.X - WindowRect.X - Mouseinfo.DragStart.X); if (Key_Control || Style.UseZoomY) //컨트롤이 눌려져있다면 줌을 사용한다. { this.UserZoom.Height = (int)(e.Y - WindowRect.Y - Mouseinfo.DragStart.Y); } else { this.UserZoom.Height = (int)ChartRect.Height;// (int)(e.Y - Mouseinfo.DragStart.Y - ChartRect.Left); } if (Mouseinfo.Cross) this.Cursor = Cursors.Cross; break; case EDRAGTYPE.SCREEN: if (Mouseinfo.Move) { this.Cursor = Cursors.NoMove2D; Mouseinfo.Showinfo = false; } //이동한pixcel을 시간정보로 변환한다. //현재마우스위치값 - 드래그시작마우스위치값을 뺸 픽셀을 초로환산한다. Single term = Mouseinfo.DragStart.X - Mouseinfo.Position.X; //양수라면 오른쪽으로 이동했다. TimeSpan termtime = DateTime.FromFileTime(GetTimeFromX(Mouseinfo.Position.X, ChartRect)) - DateTime.FromFileTime(GetTimeFromX(Mouseinfo.DragStart.X, ChartRect)); Int64 NewSD = DateTime.FromFileTime(Style.XV1).AddSeconds(-1 * termtime.TotalSeconds).ToFileTime();// + (int)mstime; Int64 NewED = DateTime.FromFileTime(Style.XV2o).AddSeconds(-1 * termtime.TotalSeconds).ToFileTime(); //endviewo + (int)mstime; //해당 시간영역의 전체 데이터 영역에 들어있어야 이동이 가능하다 if ((NewSD > Style.X1 && NewED < Style.X2)) { Style.XV1 = NewSD; Style.XV2 = NewED; //y축 사용시에만 설정되게한다. if (Key_Control || Style.UseZoomY) { Single termy = Mouseinfo.Position.Y - Mouseinfo.DragStart.Y - ChartRect.Top; Single termvolt = GetVoltfromY(termy, ChartRect); Single mvolt = termvolt; Single SV = Style.YV1o + mvolt; Single EV = Style.YV2o + mvolt; Style.YV1 = SV; Style.YV2 = EV; } Mouseinfo.DragStart = Mouseinfo.Position; //현재위치부터 드래그가 다시되도록 Set_Refresh(); } break; } } else { if (Mouseinfo.Hand) this.Cursor = Cursors.Hand; if (Mouseinfo.Cross) this.Cursor = Cursors.Cross; if (Mouseinfo.ChangePos) this.Cursor = Cursors.SizeWE; } this.Refresh(); } public void Graph_Left(int perc) { DateTime newStartview = DateTime.FromFileTime(this.Style.XV1); DateTime newEndview = DateTime.FromFileTime(this.Style.XV2); TimeSpan term = newEndview - newStartview; DateTime NewStart = newStartview.AddMinutes(-1 * (term.TotalMinutes / perc)); DateTime NewEnd = newEndview.AddMinutes(-1 * (term.TotalMinutes / perc)); if (NewStart < DateTime.FromFileTime(Style.X1)) return; this.Style.XV1 = NewStart.ToFileTime(); this.Style.XV2 = NewEnd.ToFileTime(); Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_Left"); } public void Graph_Right(int perc) { DateTime newStartview = DateTime.FromFileTime(this.Style.XV1); DateTime newEndview = DateTime.FromFileTime(this.Style.XV2); TimeSpan term = newEndview - newStartview; DateTime NewStart = newStartview.AddMinutes(term.TotalMinutes / perc); DateTime NewEnd = newEndview.AddMinutes(term.TotalMinutes / perc); if (NewEnd > DateTime.FromFileTime(Style.X2)) return; this.Style.XV1 = NewStart.ToFileTime(); this.Style.XV2 = NewEnd.ToFileTime(); Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_Right"); } /// /// 현재 그래프의 Y축 좌표영역을 10% 위로 이동합니다. /// public void Graph_Up() { Single v = (Style.YV2 - Style.YV1) / 10; Style.YV2 -= v; Style.YV1 -= v; Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_Up"); } /// /// 현재 그래프의 Y축 좌표영역을 10% 아래로 이동합니다. /// public void Graph_Down() { Single v = (Style.YV2 - Style.YV1) / 10; Style.YV2 += v; Style.YV1 += v; Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_Down"); } /// /// 현재 그래프의 Y축 좌표영역을 10% 확대합니다. /// public void Graph_ZoomInY() { Single v = (Style.YV2 - Style.YV1) / 10; Style.YV2 -= v; Style.YV1 += v; Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_ZoomInY"); } /// /// 현재 그래프의 Y축 좌표영역을 10% 축소합니다. /// public void Graph_ZommOutY() { Single v = (Style.YV2 - Style.YV1) / 10; Style.YV2 += v; Style.YV1 -= v; Set_Refresh(); Refresh(); Console.WriteLine("Graph : Graph_ZommOutY"); } public void ZoomIN() { DateTime newStartview = DateTime.FromFileTime(this.Style.XV1); DateTime newEndview = DateTime.FromFileTime(this.Style.XV2); TimeSpan term = newEndview - newStartview; DateTime NewStart = newStartview.AddMinutes(term.TotalMinutes / 20); DateTime NewEnd = newEndview.AddMinutes(-1 * (term.TotalMinutes / 20)); if (NewStart >= DateTime.FromFileTime(Style.X2).AddMinutes(-10)) NewStart = DateTime.FromFileTime(Style.X2).AddMinutes(-10); if (NewEnd <= DateTime.FromFileTime(Style.X1).AddMinutes(10)) NewEnd = DateTime.FromFileTime(Style.X1).AddMinutes(10); Style.XV1 = NewStart.ToFileTime(); this.Style.XV2 = NewEnd.ToFileTime(); Set_Refresh(); Refresh(); Console.WriteLine("Graph : ZoomIN"); } public void ZoomOUT() { DateTime newStartview = DateTime.FromFileTime(Style.XV1); DateTime newEndview = DateTime.FromFileTime(Style.XV2); TimeSpan term = newEndview - newStartview; DateTime NewStart = newStartview.AddMinutes(-1 * (term.TotalMinutes / 20)); DateTime NewEnd = newEndview.AddMinutes(term.TotalMinutes / 20); if (newStartview < DateTime.FromFileTime(Style.X1)) NewStart = DateTime.FromFileTime(Style.X1); if (newEndview > DateTime.FromFileTime(Style.X2)) NewEnd = DateTime.FromFileTime(Style.X2); Style.XV1 = NewStart.ToFileTime(); Style.XV2 = NewEnd.ToFileTime(); Set_Refresh(); Refresh(); Console.WriteLine("Graph : ZoomOUT"); } private void TrendCtrl_MouseWhell(object sender, MouseEventArgs e) { if (Key_Control) { //온도축 변경(Y) Single news, newe; news = Style.Y2; newe = Style.Y1; Single TimerTerm = newe - news; if (e.Delta > 0) { //확대 news = news + (TimerTerm / 10); newe = newe + (-1 * (TimerTerm / 10)); } else { //축소(down) news = news + (-1 * (TimerTerm / 10)); newe = newe + (1 * (TimerTerm / 10)); } if (news > 400) news = 400; if (news < -200) news = -200; Style.Y2 = news; Style.Y1 = newe; } else { //시간축 변경(X) DateTime news, newe; news = DateTime.FromFileTime(Style.XV1); newe = DateTime.FromFileTime(Style.XV2); TimeSpan TimerTerm = newe - news; if (e.Delta > 0) { //확대 if (TimerTerm.TotalSeconds < Style.MaxZoomX) return; news = news.AddSeconds(TimerTerm.TotalSeconds / 10); newe = newe.AddSeconds(-1 * (TimerTerm.TotalSeconds / 10)); } else { //축소(down) news = news.AddSeconds(-1 * (TimerTerm.TotalSeconds / 10)); newe = newe.AddSeconds(1 * (TimerTerm.TotalSeconds / 10)); } if (news > DateTime.FromFileTime(Style.X2)) news = DateTime.FromFileTime(Style.X2); if (news < DateTime.FromFileTime(Style.X1)) news = DateTime.FromFileTime(Style.X1); if (newe > DateTime.FromFileTime(Style.X2)) newe = DateTime.FromFileTime(Style.X2); if (newe < DateTime.FromFileTime(Style.X1)) newe = DateTime.FromFileTime(Style.X1); if (news < DateTime.FromFileTime(Style.X1) || newe > DateTime.FromFileTime(Style.X2)) { //MessageBox.Show("데이터가 없으므로 줌을 사용할 수 없습니다"); } else { Style.XV1 = news.ToFileTime(); Style.XV2 = newe.ToFileTime(); Style.XV1 = news.ToFileTime(); Style.XV2o = newe.ToFileTime(); } } this.Set_Refresh(); this.Refresh(); Console.WriteLine("Graph : Mouse Wheel"); } private void TrendCtrl_MouseUp(object sender, MouseEventArgs e) { Mouseinfo.Drag = false; if (Mouseinfo.DragType == EDRAGTYPE.UC) { Refresh_Grpah = true; //다시값을 계산하려면 실행해야함 this.Refresh(); Console.WriteLine("Graph : MouseUp1"); if (OnUpdateUserCursor != null) OnUpdateUserCursor(Mouseinfo.DragIndex); } else { if (UserZoom.Width != 0 && e.Button == System.Windows.Forms.MouseButtons.Left) //줌영역이 있다면 줌을 활성화한다. { int x1, x2, y1, y2; x1 = UserZoom.Left; x2 = UserZoom.Left + UserZoom.Width; if (!Style.UseZoomY) { y1 = (int)ChartRect.Top; y2 = (int)ChartRect.Bottom; } else { y1 = UserZoom.Top; y2 = UserZoom.Bottom; } Int64 startx = GetTimeFromX(x1 - ChartRect.Left, ChartRect); Int64 endx = GetTimeFromX(x2 - ChartRect.Left, ChartRect); Single endy = Style.Y2 - GetVoltfromY(y1 - ChartRect.Top, ChartRect); Single starty = Style.Y2 - GetVoltfromY(y2 - ChartRect.Top, ChartRect); if (endx < startx) //교환 { Int64 t = startx; startx = endx; endx = t; } if (endy < starty) //교환 { Single t = starty; starty = endy; endy = t; } TimeSpan tst = DateTime.FromFileTime(endx) - DateTime.FromFileTime(startx); Single VoltTerm = endy - starty; if (tst.TotalSeconds >= Style.MaxZoomX && VoltTerm >= Style.MaxZoomY && UserZoom.Width > 20) //보기영역이 5초는 넘어야 줌이 가능한다. { this.Style.XV1 = startx; this.Style.XV2 = endx; this.Style.YV1 = starty;// +this.Style.최소값; this.Style.YV2 = endy;// +this.Style.최대값; UserZoom.Width = 0; Set_Refresh(); this.Refresh(); Console.WriteLine("Graph : MouseUp2"); } else { UserZoom.Width = 0; Set_Refresh(); this.Refresh(); Console.WriteLine("Graph : MouseUp3"); } } } } private void TrendCtrl_MouseDoubleClick(object sender, MouseEventArgs e) { ////마우스더블클릭 //int idx = check_userpoint(e); //if (idx != -1) //{ // //삭제한다. // uc.Remove(uc[Mouseinfo.DragIndex]); // if (uc.Count == 1) // { // uc[0].Idx = 0; // foreach (CChinfo c in this.chinfo) // { // c.C2value = ""; // } // } // Refresh_Grpah = true; //다시갱신해야 값이 계산됨 // this.Refresh(); // if(OnUpdateUserCursor != null) // OnUpdateUserCursor(Mouseinfo.DragIndex); // return; //} //if (e.Button == System.Windows.Forms.MouseButtons.Left) //{ // if (this.uc.Count < 2) // { // Single newx = GetXfromTime(Mouseinfo.Time, ChartRect); // this.uc.Add(new CUserCursor((short)this.uc.Count, Mouseinfo.Time, newx)); // Refresh_Grpah = true; //다시갱신해야 값이 계산됨 // this.Refresh(); // if (OnUpdateUserCursor != null) // OnUpdateUserCursor(this.uc.Count); // } //} } private void DispCtrl_Load(object sender, EventArgs e) { } } //end class } //end namespace