Files
vms2016_kadisp/TrendCtrlII/TrendCtrlII.cs
2024-11-26 20:15:16 +09:00

1807 lines
73 KiB
C#

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<UInt32, Int64> 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<CUserCursor> uc = new List<CUserCursor>(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;
/// <summary>
/// 현재데이터중 가장 마지막의 시간정보를 반환합니다. (없는경우 1900년도가 반화노딤)
/// </summary>
/// <returns></returns>
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<uint, long>();
}
public ChartStyle Style
{
get { return this._Style; }
set { this._Style = value; }
}
/// <summary>
/// Draws the background gradient and the grid into Graphics <paramref name="g"/>
/// </summary>
/// <param name="g">Graphic</param>
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<String> Files = new List<string>();
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();
}
/// <summary>
/// 지정된 시간값이 현재 존재하는가?
/// </summary>
/// <param name="value">int64 : time value</param>
/// <returns></returns>
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<PointF> linearrya = new List<PointF>(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);
}
}
/// <summary>
/// 화면의 좌,우 측에 존재하는 이돈버튼(월을 움직이는 버튼이며 월 이동을위해서 이벤트를 발생한다)
/// </summary>
/// <param name="G"></param>
//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;
}
/// <summary>
/// (전체시간 * 현재X) / 전체너비
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
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");
}
/// <summary>
/// 현재 그래프의 Y축 좌표영역을 10% 위로 이동합니다.
/// </summary>
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");
}
/// <summary>
/// 현재 그래프의 Y축 좌표영역을 10% 아래로 이동합니다.
/// </summary>
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");
}
/// <summary>
/// 현재 그래프의 Y축 좌표영역을 10% 확대합니다.
/// </summary>
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");
}
/// <summary>
/// 현재 그래프의 Y축 좌표영역을 10% 축소합니다.
/// </summary>
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