1348 lines
58 KiB
C#
1348 lines
58 KiB
C#
using System.Collections.Generic;
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Diagnostics;
|
|
using System.Data;
|
|
using System.Collections;
|
|
using System.Windows.Forms;
|
|
using System.Linq;
|
|
using System.Data.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using TrendCtrlII;
|
|
using AR;
|
|
using System.Windows.Media.Animation;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics.Eventing.Reader;
|
|
using System.Windows.Forms.DataVisualization.Charting;
|
|
using System.Text.RegularExpressions;
|
|
using OpenTK.Graphics.ES20;
|
|
using ScottPlot.WinForms;
|
|
using ScottPlot.Control;
|
|
|
|
namespace vmsnet
|
|
{
|
|
|
|
public partial class Frm_trend
|
|
{
|
|
ChartListData[] ChartListData = new ChartListData[10];
|
|
string SelectedGRPChValue = "";
|
|
List<int> totalChlist = new List<int>();
|
|
DocumentElement.CHANNELDataTable DTCHLIST;
|
|
|
|
readonly ScottPlot.WinForms.FormsPlot formsPlot1;
|
|
|
|
/* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 데이터그리드 행의 전체선택인지 결정하는 변수 선언 */
|
|
private bool selALL = true;
|
|
|
|
List<string> LoadFileList = new List<string>();
|
|
List<int> LoadCHList = new List<int>();
|
|
ScottPlot.Plottables.Crosshair CrossHair;
|
|
ScottPlot.Plottables.Scatter[] myPlots;
|
|
int RecordCount = 0;
|
|
ScottPlot.Plottables.VerticalLine[] CursorLine;
|
|
ScottPlot.Plottables.AxisLine PlottableBeingDragged = null;
|
|
List<DateTime>[] myPlotDataX = new List<DateTime>[0];
|
|
List<float>[] myPlotDataY = new List<float>[0];
|
|
|
|
public Frm_trend()
|
|
{
|
|
// 디자이너에서 이 호출이 필요합니다.
|
|
InitializeComponent();
|
|
|
|
PUB.INIT(out List<string> message);
|
|
|
|
//차트 채널 및 파일데이터 변수
|
|
for (int i = 0; i < ChartListData.Length; i++)
|
|
ChartListData[i] = new ChartListData();
|
|
|
|
CursorLine = new ScottPlot.Plottables.VerticalLine[2];
|
|
|
|
formsPlot1 = new ScottPlot.WinForms.FormsPlot() { Dock = DockStyle.Fill };
|
|
formsPlot1.MouseDown += FormsPlot1_MouseDown;
|
|
formsPlot1.MouseUp += FormsPlot1_MouseUp;
|
|
formsPlot1.MouseMove += FormsPlot1_MouseMove;
|
|
|
|
panel8.Controls.Add(formsPlot1);
|
|
|
|
// 초기 데이터 설정
|
|
FormsPlot_Init(formsPlot1); /* 작성자: 이재웅, 작성일: 2024-09-09 */
|
|
|
|
/* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 사용자에 의한 셀수정 차단 */
|
|
this.dv_chlist.EditMode = DataGridViewEditMode.EditProgrammatically;
|
|
}
|
|
|
|
public void Frm_trend_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
|
|
{
|
|
if (this.dv_grp.SelectedRows.Count > 0)
|
|
{
|
|
PUB.TREND.tv_selectgroup0 = this.cmb_group.SelectedIndex.ToString();
|
|
PUB.TREND.tv_selectgroup = this.bs_grp.Position.ToString();
|
|
}
|
|
}
|
|
|
|
public void Frm_trend_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
|
|
{
|
|
switch (e.KeyCode)
|
|
{
|
|
case Keys.D:
|
|
if (e.Control) this.formsPlot1.Refresh();
|
|
break;
|
|
case Keys.F10:
|
|
this.bt_cursor1.PerformClick();
|
|
break;
|
|
case Keys.F11:
|
|
this.bt_cursor2.PerformClick();
|
|
break;
|
|
}
|
|
}
|
|
public void Frm_trend_Load(System.Object sender, System.EventArgs e)
|
|
{
|
|
this.Show();
|
|
Application.DoEvents();
|
|
|
|
////화면좌측의 그룹목록을 갱신
|
|
RefreshGroupList();
|
|
|
|
////마지막으로 선택한 그룹을 선택해준다.
|
|
var selidx0 = 0;
|
|
int.TryParse(PUB.TREND.tv_selectgroup0, out selidx0);
|
|
if (selidx0 < cmb_group.Items.Count)
|
|
this.cmb_group.SelectedIndex = selidx0;
|
|
else
|
|
this.cmb_group.SelectedIndex = this.cmb_group.Items.Count > 0 ? 0 : -1;
|
|
|
|
var selidx = 0;
|
|
int.TryParse(PUB.TREND.tv_selectgroup, out selidx); // XMl.Data("trendview", "selectgroup", "0")
|
|
this.bs_grp.Position = selidx;
|
|
|
|
////모든채널정보를 가져온다.
|
|
PUB.smsg("Refresh GroupName");
|
|
DTCHLIST = new DocumentElement.CHANNELDataTable();// pub.DS.CHANNEL.Clone(); // DBC.GetTable("Select * from CHANNEL ORDER BY IDX")
|
|
DTCHLIST.Merge(PUB.DS.CHANNEL);
|
|
//DTCHLIST.Columns.Add("GRPNAME");
|
|
foreach (DocumentElement.CHANNELRow Dr in DTCHLIST.Rows)
|
|
{
|
|
Dr.GRPNAME = PUBC.GetGrpName(Dr.GIDX);
|
|
}
|
|
DTCHLIST.AcceptChanges();
|
|
PUB.smsg("");
|
|
toolStripLabel3.Text = $"<PATH:{PUB.CONFIG.GetDatabasePath()}>";
|
|
|
|
//scootplot();
|
|
}
|
|
|
|
private ScottPlot.Plottables.AxisLine GetLineUnderMouse(float x, float y)
|
|
{
|
|
ScottPlot.CoordinateRect rect = formsPlot1.Plot.GetCoordinateRect(x, y, radius: 10);
|
|
|
|
foreach (var axLine in formsPlot1.Plot.GetPlottables<ScottPlot.Plottables.AxisLine>().Reverse())
|
|
{
|
|
if (axLine.IsUnderMouse(rect))
|
|
return axLine;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
private void FormsPlot1_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
var lineUnderMouse = GetLineUnderMouse(e.X, e.Y);
|
|
if (lineUnderMouse != null)
|
|
{
|
|
PlottableBeingDragged = lineUnderMouse;
|
|
formsPlot1.Interaction.Disable(); // disable panning while dragging
|
|
}
|
|
}
|
|
|
|
private void FormsPlot1_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
PlottableBeingDragged = null;
|
|
formsPlot1.Interaction.Enable(); // enable panning again
|
|
formsPlot1.Refresh();
|
|
this.UpdateCursorValue();
|
|
}
|
|
|
|
private void FormsPlot1_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
// update cross line
|
|
ScottPlot.Pixel mousePixel = new ScottPlot.Pixel(e.X, e.Y);
|
|
ScottPlot.Coordinates mouseCoordinates = formsPlot1.Plot.GetCoordinates(mousePixel);
|
|
//this.Text = $"X={mouseCoordinates.X:N3}, Y={mouseCoordinates.Y:N3}";
|
|
if (CrossHair != null)
|
|
{
|
|
CrossHair.Position = mouseCoordinates;
|
|
var time = DateTime.FromOADate(mouseCoordinates.X);
|
|
|
|
CrossHair.VerticalLine.Text = $"{time:yy-MM-dd\nHH:mm:ss}";
|
|
CrossHair.HorizontalLine.Text = $"{mouseCoordinates.Y:N2}v";
|
|
formsPlot1.Refresh();
|
|
}
|
|
|
|
|
|
|
|
// this rectangle is the area around the mouse in coordinate units
|
|
ScottPlot.CoordinateRect rect = formsPlot1.Plot.GetCoordinateRect(e.X, e.Y, radius: 10);
|
|
|
|
// set cursor based on what's beneath the plottable
|
|
var lineUnderMouse = GetLineUnderMouse(e.X, e.Y);
|
|
|
|
if (PlottableBeingDragged is null)
|
|
{
|
|
if (lineUnderMouse is null) Cursor = Cursors.Default;
|
|
else if (lineUnderMouse.IsDraggable && lineUnderMouse is ScottPlot.Plottables.VerticalLine) Cursor = Cursors.SizeWE;
|
|
else if (lineUnderMouse.IsDraggable && lineUnderMouse is ScottPlot.Plottables.HorizontalLine) Cursor = Cursors.SizeNS;
|
|
}
|
|
else
|
|
{
|
|
// update the position of the plottable being dragged
|
|
if (PlottableBeingDragged is ScottPlot.Plottables.HorizontalLine hl)
|
|
{
|
|
hl.Y = rect.VerticalCenter;
|
|
hl.Text = $"{hl.Y:0.00}v";
|
|
}
|
|
else if (PlottableBeingDragged is ScottPlot.Plottables.VerticalLine vl)
|
|
{
|
|
vl.X = rect.HorizontalCenter;
|
|
var time = DateTime.FromOADate(vl.X);
|
|
//vl.Text = $"{vl.X:0.00}";
|
|
if (lineUnderMouse == CursorLine[0])
|
|
vl.Text = $"[C1] {time:yy-MM-dd\nHH:mm:ss}";
|
|
else if (lineUnderMouse == CursorLine[1])
|
|
vl.Text = $"[C2] {time:yy-MM-dd\nHH:mm:ss}";
|
|
else
|
|
vl.Text = $"{time:yy-MM-dd\nHH:mm:ss}";
|
|
}
|
|
formsPlot1.Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 그룹목록을 추가합니다.
|
|
/// </summary>
|
|
/// <param name="name">표시그룸명(사용자구분추가)</param>
|
|
/// <param name="value">표시채널목록(comma)</param>
|
|
/// <param name="idx">일련번호</param>
|
|
/// <param name="rname">그룹명</param>
|
|
/// <remarks></remarks>
|
|
private void AddGroupRow(string name, int idx, string value, string rname)
|
|
{
|
|
////최대idx를 찾아서 그보다 크게한다.
|
|
var dr = this.dS1.group.NewgroupRow(); // Me.DataSet1.Tables("group").NewRow
|
|
dr.cname = name;
|
|
dr.value = value;
|
|
dr.idx = idx;
|
|
dr.rname = rname;
|
|
dS1.group.AddgroupRow(dr);
|
|
dS1.group.AcceptChanges();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 표시그룹목록을 갱신합니다. 좌측하단
|
|
/// </summary>
|
|
/// <remarks></remarks>
|
|
private void RefreshGroupList()
|
|
{
|
|
|
|
////상단 그룹목록
|
|
//Dim sql As String = "Select * from GRP where USE=1 order by TITLE"
|
|
var DT = PUB.DS.GRP.Where(t => t.USE == 1).OrderBy(t => t.TITLE).ToList();//.CopyToDataTable();//.Select("use=1", "title") as DocumentElement.GRPRow[]; // DBC.GetTable(sql)
|
|
this.cmb_group.DataSource = DT;
|
|
this.cmb_group.DisplayMember = "TITLE";
|
|
this.cmb_group.ValueMember = "IDX";
|
|
if (DT.Count() > 0)
|
|
{
|
|
this.cmb_group.SelectedIndex = 0;
|
|
}
|
|
|
|
////현재의 그룹목록을 삭제
|
|
dS1.group.Clear();
|
|
|
|
////사용자그룹추가
|
|
//sql = "select * from VIEWGROUP order by TITLE,IDx"
|
|
foreach (DocumentElement.VIEWGROUPRow dr in PUB.DS.VIEWGROUP.Select("", "title,idx")) // DBC.GetTable(sql).Rows
|
|
{
|
|
string v = dr.VAL.Trim();
|
|
if (string.IsNullOrEmpty(v))
|
|
{
|
|
continue;
|
|
}
|
|
string newgrpname = "[사용자] " + dr.TITLE;
|
|
AddGroupRow(newgrpname, dr.IDX, v, dr.GNAME);
|
|
}
|
|
|
|
////시스템그룹추가
|
|
// sql = "select * from GRP where USE=1"
|
|
totalChlist.Clear();
|
|
|
|
foreach (DocumentElement.GRPRow Dr in PUB.DS.GRP.Select("use=1")) // DBC.GetTable(sql).Rows
|
|
{
|
|
////이그룹을 사용하는 채널중 사용채널을 찾는다.
|
|
string v = "";
|
|
foreach (DocumentElement.CHANNELRow Dr2 in PUB.DS.CHANNEL.Select("gidx=" + Dr.IDX.ToString() + " and enable=1", "idx")) // DBC.GetTable("select * from CHANNEL where GIDX=" & Dr("IDX") & " AND ENABLE=1 ORDER BY IDX").Rows
|
|
{
|
|
v += System.Convert.ToString((string.IsNullOrEmpty(v) ? "" : ",") + Dr2.IDX.ToString());
|
|
|
|
if (totalChlist.Contains(Dr2.IDX) == false)
|
|
{
|
|
totalChlist.Add(Dr2.IDX);
|
|
}
|
|
}
|
|
if (!string.IsNullOrEmpty(v))
|
|
{
|
|
//AddGroupRow("[시스템]" + Dr.TITLE, Dr.IDX, v, Dr.TITLE);
|
|
}
|
|
}
|
|
|
|
short AllIDX = (short)dS1.group.Rows.Count; ////전체보기메뉴의 인덱스
|
|
AddGroupRow("모든채널표시", -1, "", "전체");
|
|
}
|
|
|
|
#region 우측 채널 목록
|
|
|
|
////우측채널목록을 갱신합니다.
|
|
private void RefreshChannelList()
|
|
{
|
|
this.dS1.channel.Rows.Clear();
|
|
this.dS1.channel.AcceptChanges();
|
|
|
|
//파일목록가져온다
|
|
GrabFilelist();
|
|
|
|
foreach (var charr in this.ChartListData)
|
|
{
|
|
foreach (var ch in charr.ChannelList)
|
|
{
|
|
//전체채널목록에 포함되어있는 대상중 채널목록에 존재하지 않는 경우에만 처리한다.
|
|
if (DTCHLIST.Where(t => t.IDX == ch).Any() && this.dS1.channel.Where(t => t.idx == ch).Any() == false)
|
|
{
|
|
//전체채널정보에서 데이터 수집
|
|
var CHDataRow = DTCHLIST.Rows[ch - 1] as DocumentElement.CHANNELRow;
|
|
if (CHDataRow == null) continue;
|
|
|
|
//채널목록에도 추가한다.
|
|
var activeCount = this.dS1.channel.Where(t => t.use == true).Count();
|
|
var Dr = this.dS1.channel.NewchannelRow();
|
|
Dr.use = activeCount < PUB.CONFIG.MaxChCount;
|
|
Dr.cname = CHDataRow.TITLE;
|
|
Dr.idx = ch;
|
|
Dr.cc = CHDataRow.COLOR;
|
|
Dr.tidx = this.dS1.channel.Rows.Count; //// chinof 의 index번호를 설정해야한다.
|
|
this.dS1.channel.Rows.Add(Dr);
|
|
}
|
|
}
|
|
}
|
|
this.dS1.channel.AcceptChanges();
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
void GrabFilelist()
|
|
{
|
|
/* 주석날짜: 2024-09-20 | 주석내용: VIEWGROUP.xml 파일내용 | 주석자: 이재웅
|
|
*
|
|
* <VIEWGROUUP>
|
|
* <IDX>9</IDX>
|
|
* <GNAME>EL2500A</GNAME>
|
|
* <TITLE>GRP(1...10)</TITLE>
|
|
* <VAL>1,2,3,4,5,6,7,8,9,10</VAL>
|
|
* </VIEWGROUUP>
|
|
*
|
|
**************************************
|
|
*
|
|
* DRGRP.idx = 9
|
|
* DRGRP.mame = "EL2500A"
|
|
* DRGRP.cname = "[사용자] GRP(1...10)"
|
|
* DRGRP.value = "1,2,3,4,5,6,7,8,9,10"
|
|
*/
|
|
|
|
var sttdate = PUB.TREND.graph_time_start;
|
|
var enddate = PUB.TREND.graph_time_end;
|
|
//각 파일 그룹별 대상 파일을 수집합니다.
|
|
/* 작성자: 이재웅, 작성일: 2025-06-23, 내용: 0 ~ this.ChartListData.Length - 1 */
|
|
// Parallel.For(시작값, 종료값, ...) 형태라고 했을 때, 루프 진행은 '시작값 ~ 종료값-1' 실행된다.
|
|
// 'this.ChartListData.Length - 1' -> 'this.ChartListData.Length' 변경
|
|
Parallel.For(0, this.ChartListData.Length, i =>
|
|
{
|
|
var DataFileName = $"DATAB{i + 1}";
|
|
if (ChartListData[i].ChannelList.Any() == true) this.ChartListData[i].FileList = PUB.DB.GetfileS(DataFileName, sttdate, enddate);
|
|
else this.ChartListData[i].FileList = new List<string>();
|
|
|
|
Console.WriteLine($"Processing index {i} on thread {Task.CurrentId}");
|
|
});
|
|
|
|
}
|
|
|
|
private void FormsPlot_Init(ScottPlot.WinForms.FormsPlot plot)
|
|
{
|
|
plot.Plot.Clear();
|
|
|
|
// 초기 데이터 설정
|
|
plot.Plot.Add.Palette = new ScottPlot.Palettes.Penumbra();
|
|
// change figure colors
|
|
plot.Plot.FigureBackground.Color = ScottPlot.Color.FromHex("#181818");
|
|
plot.Plot.DataBackground.Color = ScottPlot.Color.FromHex("#1f1f1f");
|
|
|
|
// change axis and grid colors
|
|
plot.Plot.Axes.Color(ScottPlot.Color.FromHex("#d7d7d7"));
|
|
plot.Plot.Grid.MajorLineColor = ScottPlot.Color.FromHex("#404040");
|
|
|
|
// change legend colors
|
|
plot.Plot.Legend.BackgroundColor = ScottPlot.Color.FromHex("#404040");
|
|
plot.Plot.Legend.FontColor = ScottPlot.Color.FromHex("#d7d7d7");
|
|
plot.Plot.Legend.OutlineColor = ScottPlot.Color.FromHex("#d7d7d7");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 데이터를 조회합니다.
|
|
/// </summary>
|
|
/// <param name="SearchFileList">파일목록을 다시 갱신합니다.</param>
|
|
/// <remarks></remarks>
|
|
private void ShowData(bool AllClear = true)
|
|
{
|
|
//return;
|
|
Console.WriteLine("Enter : ShowData");
|
|
this.Text = $"TrendViewer ({cmb_group.Text})";
|
|
this.lb_selgroup.Text = $"[{cmb_group.Text}]";
|
|
|
|
DateTime SDTotal = DateTime.Now;
|
|
var chcount2 = this.dS1.channel.Rows.Count;
|
|
if (chcount2 == 0)
|
|
{
|
|
UTIL.MsgE($"선택된 채널목록이 없습니다\n채널그룹이 손상되었거나 다시 선택하세요");
|
|
return;
|
|
}
|
|
|
|
GrabFilelist();
|
|
|
|
TimeSpan Tsing = DateTime.Now - SDTotal;
|
|
Console.WriteLine($"[ShowData] Title And Ch : {Tsing.TotalMilliseconds}ms");
|
|
|
|
|
|
DateTime fsd = DateTime.Now;
|
|
var FileCount = ChartListData.Sum(t => t.FileList.Count);
|
|
Tsing = DateTime.Now - SDTotal;
|
|
Console.WriteLine($"[ShowData] {FileCount} Files Checked : {Tsing.TotalMilliseconds}");
|
|
TimeSpan fts = DateTime.Now - fsd;
|
|
lb_filesearchtime.Text = $"[Search : {fts.TotalMilliseconds}ms]";
|
|
|
|
////Record Count Check
|
|
var avdate = PUB.DB.GetAvailableDates(); //변수선언 이동
|
|
if (FileCount == 0)
|
|
{
|
|
Console.WriteLine("ShowData : FileCount 0 Refresh");
|
|
PUB.smsg("");
|
|
|
|
//var avdate = PUB.DB.GetAvailableDates(); //전위치
|
|
if (avdate.Any() == false)
|
|
{
|
|
UTIL.MsgE("지정된 폴더내에 저장된 자료가 없습니다");
|
|
}
|
|
else
|
|
{
|
|
UTIL.MsgI("기간내 조회된 자료가 없습니다\n\n조회 가능 범위\n\n" +
|
|
$"{avdate.First().Year}-{avdate.First().Month}~" +
|
|
$"{avdate.Last().Year}-{avdate.Last().Month}");
|
|
}
|
|
return;
|
|
}
|
|
|
|
Tsing = DateTime.Now - SDTotal;
|
|
Console.WriteLine($"[ShowData] Set Date Area : {Tsing.TotalMilliseconds}");
|
|
|
|
//채널수만큼 데이터버퍼를 확보한다.
|
|
var maxchno = this.dS1.channel.Max(t => t.idx); //idx는 실제 채널번호가 들어있다(1~
|
|
this.myPlotDataX = new List<DateTime>[maxchno];
|
|
this.myPlotDataY = new List<float>[maxchno];
|
|
|
|
// 채널 개수만큼 Scatter 그래프 생성
|
|
this.myPlots = new ScottPlot.Plottables.Scatter[maxchno];
|
|
|
|
//커서삭제
|
|
this.CursorLine[0] = null;
|
|
this.CursorLine[1] = null;
|
|
|
|
////데이터를 모두 초기화하는경우
|
|
if (AllClear)
|
|
{
|
|
LoadFileList.Clear();
|
|
LoadCHList.Clear();
|
|
Tsing = DateTime.Now - SDTotal;
|
|
Console.WriteLine($"[ShowData] Clear Data : {Tsing.TotalMilliseconds}");
|
|
}
|
|
|
|
//커서데이터 삭제
|
|
foreach (DS1.channelRow dr in this.dS1.channel)
|
|
{
|
|
dr.c1 = "";
|
|
dr.c2 = "";
|
|
}
|
|
this.dS1.channel.AcceptChanges();
|
|
|
|
|
|
////추가된채널목록을 가진다.
|
|
DateTime STime = DateTime.Now;
|
|
|
|
int totalchcount = this.dS1.channel.Rows.Count;
|
|
if (totalchcount > PUB.CONFIG.MaxChCount)
|
|
{
|
|
UTIL.MsgE($"채널 수({totalchcount})가 {PUB.CONFIG.MaxChCount}을 초과 합니다. [{PUB.CONFIG.MaxChCount}]이하로 사용자 채널을 생성하여 조회 하세요");
|
|
return;
|
|
}
|
|
|
|
/* 주석날짜: 2024-09-20 | 주석자: 이재웅
|
|
*
|
|
* ParseChDataToChart() : 채널데이터를 차트데이터로 변환
|
|
*/
|
|
|
|
//대상파일의 자료를 읽어와서 차트데이터를 구축한다.
|
|
RecordCount = 0;
|
|
// Parallel.For(시작값, 종료값, ...) 형태라고 했을 때, 루프 진행은 '시작값 ~ 종료값-1' 실행된다.
|
|
Parallel.For(0, this.ChartListData.Length, i =>
|
|
{
|
|
ParseChDataToChart(i, ChartListData[i], DTCHLIST);
|
|
});
|
|
|
|
Tsing = DateTime.Now - SDTotal;
|
|
Console.WriteLine($"[ShowData] Pase Data Complete : {Tsing.TotalMilliseconds}");
|
|
|
|
var sttdate = PUB.TREND.graph_time_start;
|
|
var enddate = PUB.TREND.graph_time_end;
|
|
TimeSpan term = DateTime.Now - STime;
|
|
|
|
////조회영역 및 쿼리시간표시
|
|
this.lb_Area.Text = "[ " + sttdate.ToString("yy-MM-dd HH:mm:ss") + "~" + enddate.ToString("yy-MM-dd HH:mm:ss") + " ]";
|
|
this.lb_querytime.Text = $"[Loading : {term.TotalMilliseconds:#0.0}ms ]";
|
|
|
|
////SET OK
|
|
STime = DateTime.Now;
|
|
Console.WriteLine("ShowData : Chart Refresh");
|
|
|
|
term = DateTime.Now - STime;
|
|
this.lb_charttime.Text = $"[Chart : {term.TotalMilliseconds:#0.0}ms ]";
|
|
|
|
Tsing = DateTime.Now - SDTotal;
|
|
//Console.WriteLine($"[ShowData] Chart Refresh : {Tsing.TotalMilliseconds:#0.0}");
|
|
|
|
TimeSpan Tstotal = DateTime.Now - SDTotal;
|
|
this.lb_totaltime.Text = $"[total : {Tstotal.TotalMilliseconds:#0.0}ms]";
|
|
|
|
Tsing = DateTime.Now - SDTotal;
|
|
//Console.WriteLine($"[ShowData] Complete : {Tsing.TotalMilliseconds:#0.0}");
|
|
|
|
|
|
|
|
// 초기 데이터 생성
|
|
//this.formsPlot1.Plot.Clear();
|
|
FormsPlot_Init(formsPlot1);
|
|
|
|
#region 이전 코드 주석처리
|
|
//formsPlot1.Plot.Add.Palette = new ScottPlot.Palettes.Penumbra();
|
|
//// change figure colors
|
|
//formsPlot1.Plot.FigureBackground.Color = ScottPlot.Color.FromHex("#181818");
|
|
//formsPlot1.Plot.DataBackground.Color = ScottPlot.Color.FromHex("#1f1f1f");
|
|
|
|
//// change axis and grid colors
|
|
//formsPlot1.Plot.Axes.Color(ScottPlot.Color.FromHex("#d7d7d7"));
|
|
//formsPlot1.Plot.Grid.MajorLineColor = ScottPlot.Color.FromHex("#404040");
|
|
|
|
//// change legend colors
|
|
//formsPlot1.Plot.Legend.BackgroundColor = ScottPlot.Color.FromHex("#404040");
|
|
//formsPlot1.Plot.Legend.FontColor = ScottPlot.Color.FromHex("#d7d7d7");
|
|
//formsPlot1.Plot.Legend.OutlineColor = ScottPlot.Color.FromHex("#d7d7d7");
|
|
#endregion
|
|
|
|
/* 주석날짜: 2024-09-20 | 주석내용: 채널별 데이터 그리기 | 주석자: 이재웅
|
|
*
|
|
* xs : x축 List<DateTime>[] 형식의 데이터
|
|
* ys : y축 List<float>[] 형식의 데이터
|
|
* myScatter : x,y 좌표를 가지는 ScottPlot.Plottables.Scatter 형식의 데이터
|
|
*
|
|
* 명시적선언時: List<DateTime>[] myPlotDataX
|
|
* 명시적선언時: List<float>[] myPlotDataY
|
|
* 명시적선언時: ScottPlot.Plottables.Scatter myScatter
|
|
*/
|
|
|
|
for (int i = 0; i < this.myPlots.Length; i++)
|
|
{
|
|
var xs = myPlotDataX[i];
|
|
if (xs == null || xs.Any() == false) continue;
|
|
var ys = myPlotDataY[i];
|
|
var myScatter = formsPlot1.Plot.Add.Scatter(xs, ys);
|
|
|
|
//if (i > 2) break;
|
|
//채널정보에서 색상을 가져온다.
|
|
var chinfo = this.dS1.channel.Where(t => t.idx == i + 1).FirstOrDefault(); //idx에 실제 채널정보가 들어있으니 -1 해야함
|
|
if (chinfo != null)
|
|
{
|
|
var linecolor = Color.FromArgb(chinfo.cc);
|
|
myScatter.LineColor = new ScottPlot.Color(linecolor.R, linecolor.G, linecolor.B);
|
|
}
|
|
else myScatter.LineColor = ScottPlot.Colors.Red;
|
|
myScatter.Color = myScatter.LineColor;
|
|
/* 작성자: 이재웅, 작성일: 2024-11-27, 내용: 변경한 셀이름으로 차트범례 표시 */
|
|
myScatter.LegendText = chinfo.ItemArray[1].ToString(); //$"CH{i + 1}";
|
|
this.myPlots[i] = myScatter; //visible 변경 및 데이터 접근을 위해 변수에 따로 저장해둔다.
|
|
}
|
|
formsPlot1.Plot.Axes.DateTimeTicksBottom();
|
|
|
|
//1번항목을 가져와서 x,y축 range 값 설정
|
|
var plot = this.myPlots.Where(t => t != null).FirstOrDefault();
|
|
if (plot != null)
|
|
{ // 파일이 있더라도 해당하는 데이터가 없을때 Error 발생
|
|
var ymin = plot.Data.GetLimitsY().Min;
|
|
var ymax = plot.Data.GetLimitsY().Max;
|
|
var xmin = plot.Data.GetLimitsX().Min;
|
|
var xmax = plot.Data.GetLimitsX().Max;
|
|
var maxdate = DateTime.FromOADate(xmin);
|
|
var mindate = DateTime.FromOADate(xmax);
|
|
}
|
|
this.lb_datatcnt.Text = $"[ {FileCount} Files / {RecordCount} Records ]";
|
|
CrossHair = formsPlot1.Plot.Add.Crosshair(0, 0);
|
|
CrossHair.TextColor = ScottPlot.Colors.Red;
|
|
CrossHair.TextBackgroundColor = CrossHair.HorizontalLine.Color;
|
|
|
|
//축설정
|
|
formsPlot1.Plot.Axes.AutoScaleX();
|
|
if (PUB.TREND.y_scale_auto)
|
|
formsPlot1.Plot.Axes.AutoScaleY();
|
|
else
|
|
formsPlot1.Plot.Axes.SetLimitsY(PUB.TREND.graph_y_start, PUB.TREND.graph_y_end);
|
|
|
|
formsPlot1.Plot.YLabel("VOLTAGE");
|
|
|
|
|
|
//X축 바닥 여백 변경
|
|
formsPlot1.Plot.Axes.Bottom.MinimumSize = PUB.TREND.graph_bottom_minsize;
|
|
|
|
//십자선추가
|
|
CrossHair = formsPlot1.Plot.Add.Crosshair(0, 0);
|
|
CrossHair.TextColor = ScottPlot.Colors.White;
|
|
CrossHair.TextBackgroundColor = CrossHair.HorizontalLine.Color;
|
|
|
|
//X축 날짜시간형태로 표시
|
|
formsPlot1.Plot.RenderManager.RenderStarting += (s1, e1) =>
|
|
{
|
|
ScottPlot.Tick[] ticks = formsPlot1.Plot.Axes.Bottom.TickGenerator.Ticks;
|
|
for (int i = 0; i < ticks.Length; i++)
|
|
{
|
|
DateTime dt = DateTime.FromOADate(ticks[i].Position);
|
|
string label = $"{dt:yy-MM-dd\nHH:mm:ss}";
|
|
ticks[i] = new ScottPlot.Tick(ticks[i].Position, label);
|
|
}
|
|
};
|
|
|
|
// the Style object contains helper methods to style many items at once
|
|
formsPlot1.Plot.Axes.Color(ScottPlot.Color.FromHex("#a0acb5"));
|
|
formsPlot1.Refresh();
|
|
|
|
if (RecordCount == 0)
|
|
{
|
|
string msg = $"[{sttdate.ToString("yyyy-MM-dd")} ~ {enddate.ToString("yyyy-MM-dd")}] : 데이터가 존재하지 않습니다.";
|
|
PUB.log.Add(msg);
|
|
UTIL.MsgE(msg);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
private void ParseChDataToChart(int idx, ChartListData chlist, DocumentElement.CHANNELDataTable DTCHList)
|
|
{ // 지정한 날짜구간 내의 파일들을 읽음 (채널데이터 → 차트데이터)
|
|
|
|
//데이터가 없는 경우 빠져 나감
|
|
if (chlist.ChannelList.Any() == false || chlist.FileList.Any() == false)
|
|
{
|
|
if (chlist.ChannelList.Any() || chlist.FileList.Any())
|
|
PUB.log.Add($"Load Skip Ch:{chlist.ChannelList.Count},file:{chlist.FileList.Count}");
|
|
return;
|
|
}
|
|
|
|
//지정된 기간의 데이터를 취해야 하므로 날짜 정보를 가져온다.
|
|
long SDtime = PUB.TREND.graph_time_start.ToFileTime();// SttDate.ToFileTime();
|
|
long EDtime = PUB.TREND.graph_time_end.ToFileTime();// EndDate.ToFileTime();
|
|
|
|
//속해있는채널정보를 가져온다.
|
|
|
|
//대상 파일 전체를 확인하여, 지정된 기간내라면 자료를 수집힌다.
|
|
foreach (string file in chlist.FileList)
|
|
{
|
|
////이파일명이 로딩되었다면 불러오지 않는다.
|
|
if (LoadFileList.Contains(file) == true) continue;// TrendCtrl1.Files.IndexOf(file) == -1)
|
|
else LoadFileList.Add(file); //불러온기록에 추가
|
|
|
|
|
|
////해당파일의 지정시간내의 데이터를 모두 읽어온다.
|
|
using (var FS = new System.IO.FileStream(file, System.IO.FileMode.Open))
|
|
using (var SR = new System.IO.StreamReader(FS, System.Text.Encoding.Default))
|
|
while (SR.Peek() != 0)
|
|
{
|
|
string Line = SR.ReadLine();
|
|
if (Line == null) break; //null값이 오는 경우가 있다, 오류 혹은 파일끝에서 발생되었음, 더이상 처리하지 않는다.
|
|
if (Line.Trim() == "") continue; //빈 줄은 무시한다.
|
|
|
|
string[] buf = Line.Split('\t'); //각 데이터는 탭으로 분리 되어있다.
|
|
if (buf.Length < 8 || buf[1].ToUpper() == "TIME") continue; //제목줄 처리하지 않는다.
|
|
|
|
if (long.TryParse(buf[1], out long TIme) == false) continue; //1번항목은 시간이고, 그 이후에 채널 데이터가 들어있다.
|
|
|
|
////속해있는 채널정보를 모두 가져온다.
|
|
foreach (var c in chlist.ChannelList) ////채널목록을 이용하여 데이터를 가져온다.
|
|
{
|
|
//채널정보 확인
|
|
string GRP_NAME, GRP_TITLE;
|
|
Color GRP_COLOR;
|
|
if (LoadCHList.Contains(c) == false)
|
|
{
|
|
var CHDataRow = DTCHList.Where(t => t.IDX == c).FirstOrDefault();
|
|
if (CHDataRow == null) continue; //데이터가없다면 처리하지 않는다.
|
|
else LoadCHList.Add(c);
|
|
GRP_NAME = CHDataRow.GRPNAME;
|
|
GRP_TITLE = CHDataRow.TITLE;
|
|
GRP_COLOR = Color.FromArgb(CHDataRow.COLOR);
|
|
}
|
|
else
|
|
{
|
|
GRP_NAME = "";
|
|
GRP_TITLE = "";
|
|
GRP_COLOR = Color.Black;
|
|
}
|
|
|
|
//각 파일당 160개씩 저장되어있으므로 채널번호를 통해서 파일의 위치를 파악해야한다.
|
|
var chIndexFromFile = PUB.GetChannelIndexFromFileBuffer(c);
|
|
|
|
//숫자값에 문제가 있다면 처리하지 않는다.(0번버퍼는 항상 blank 이기 때문에 인덱스+1해야 데이터인덱스가 맞다)
|
|
if ((chIndexFromFile + 1) >= buf.Length || float.TryParse(buf[chIndexFromFile + 1], out float valueS) == false) continue;
|
|
|
|
//Decpos 데이터가 있다면 적용한다
|
|
if (PUB.CONFIG.datadiv != 0 && PUB.CONFIG.datadiv != 1)
|
|
valueS = (valueS / PUB.CONFIG.datadiv);
|
|
|
|
//데이터추가
|
|
//TrendCtrl1.AddData(TIme, c, valueS, GRP_NAME, GRP_TITLE, GRP_COLOR);
|
|
if (c > myPlotDataX.GetUpperBound(0))
|
|
{
|
|
Array.Resize(ref myPlotDataX, c);
|
|
Array.Resize(ref myPlotDataY, c);
|
|
|
|
}
|
|
if (myPlotDataX[c - 1] == null)
|
|
{
|
|
myPlotDataX[c - 1] = new List<DateTime>();
|
|
myPlotDataY[c - 1] = new List<float>();
|
|
}
|
|
myPlotDataX[c - 1].Add(DateTime.FromFileTime(TIme));
|
|
myPlotDataY[c - 1].Add(valueS);
|
|
|
|
RecordCount += 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public void LinkLabel5_LinkClicked(System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
|
|
{ // 신규그룹생성
|
|
addnewGroup();
|
|
}
|
|
private void SaveViewGroup()
|
|
{
|
|
string[] BackTables = new string[] { "VIEWGROUP" };
|
|
foreach (string tabname in BackTables)
|
|
{
|
|
PUB.DS.Tables[tabname].AcceptChanges();
|
|
var file = System.IO.Path.Combine(PUB.CONFIG.GetDatabasePath(), "Database", "Config", $"{tabname}.xml");
|
|
var fi = new System.IO.FileInfo(file);
|
|
if (fi.Directory.Exists == false) fi.Directory.Create();
|
|
PUB.DS.Tables[tabname].WriteXml(fi.FullName, XmlWriteMode.IgnoreSchema);
|
|
}
|
|
}
|
|
private void addnewGroup()
|
|
{
|
|
////전체채널목록을 생성한다.
|
|
var group_name = this.cmb_group.Text;
|
|
var drGRP = PUB.DS.GRP.Where(t => t.TITLE.Equals(group_name)).FirstOrDefault();
|
|
if (drGRP == null) return;
|
|
|
|
//그룹에 속한 채널 데이터
|
|
var chlist = PUB.DS.CHANNEL.Where(t => t.GIDX == drGRP.IDX).OrderBy(t => t.IDX);
|
|
|
|
//var chlistdata = chlist.Where(t => t.ENABLE == 1).Select(t => t.IDX).ToList();
|
|
//var chinfos = chlistdata.Select(t => new CChinfo { Show = false, TITLE = $"#{t:0000}", Idx = (ushort)t }).ToList();
|
|
/* 작성자: 이재웅, 작성일: 2024-12-04, 작성내용: 채널선택 화면에 변경된 형식의 채널명 표시를 위해 [ex. #A001] */
|
|
var chlistdata = chlist.Where(t => t.ENABLE == 1).Select(t => new { t.IDX, t.TITLE }).ToList();
|
|
var chinfos = chlistdata.Select(t => new CChinfo { Show = false, TITLE = $"{t.TITLE}", Idx = (ushort)t.IDX}).ToList();
|
|
|
|
List<int> chnolist = new List<int>();
|
|
using (var f = new Frm_SelectCH(chinfos.ToArray()))
|
|
if (f.ShowDialog() == DialogResult.OK)
|
|
{
|
|
foreach (ListViewItem item in f.CheckedListBox1.CheckedItems)
|
|
chnolist.Add(int.Parse(item.Tag.ToString()));
|
|
}
|
|
|
|
//선택된 채널 갯수 확인
|
|
if (chnolist.Any() == false || chnolist.Count > PUB.CONFIG.MaxChCount)
|
|
{
|
|
UTIL.MsgE("채널 선택은 (최소 1개 부터 {PUB.CONFIG.MaxChCount}개)까지 가능합니다");
|
|
return;
|
|
}
|
|
|
|
//최종저장 그룹명
|
|
/* 작성자: 이재웅, 작성일: 2024-12-04, 작성내용: 전해조그룹 선택시 채널순으로 Sorting 되도록 형식 변경 [ex. 0001~1600] */
|
|
var name = UTIL.InputBox($"그룹명을 입력하세요\n\n그룹생성({chnolist.Count}건)", $"GRP({chnolist.Min().ToString("D4")}...{chnolist.Max().ToString("D4")})");
|
|
if (name.Item1 == false || name.Item2.isEmpty()) return;
|
|
|
|
group_name = name.Item2.Replace("'", "");
|
|
var value = string.Join(",", chnolist);
|
|
short rlt_maxid = PUBC.AddviewGroup(group_name, value, this.cmb_group.Text);
|
|
if (rlt_maxid != -1)
|
|
{
|
|
AddGroupRow("[사용자] " + group_name, rlt_maxid, value, this.cmb_group.Text);
|
|
SaveViewGroup();
|
|
}
|
|
}
|
|
public void LinkLabel4_LinkClicked(System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
|
|
{ // 선택그룹삭제
|
|
DelteGroup();
|
|
}
|
|
private void DelteGroup()
|
|
{
|
|
var drv = this.bs_grp.Current as DataRowView;
|
|
if (drv == null) return;
|
|
var dr = drv.Row as DS1.groupRow;
|
|
|
|
if (dr.cname.IndexOf("사용자") == -1)
|
|
{
|
|
UTIL.MsgE("사용자 그룹만 삭제가 가능합니다");
|
|
return;
|
|
}
|
|
|
|
if (UTIL.MsgQ($"선택된 사용자 그룹을 삭제하시겠습니까?\n\n{dr.cname}") != DialogResult.Yes) return;
|
|
|
|
if (PUBC.DeleteViewGroup(dr.idx, this.cmb_group.Text))
|
|
{
|
|
UTIL.MsgI("데이터가 삭제되었습니다");
|
|
}
|
|
else
|
|
{
|
|
UTIL.MsgI("삭제가 실패되었습니다");
|
|
}
|
|
drv.Delete();
|
|
dS1.group.AcceptChanges();
|
|
SaveViewGroup();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 우측 그리드뷰에 표시되는 커서 값을 표시한다.
|
|
/// </summary>
|
|
private void UpdateCursorValue()
|
|
{
|
|
//미사용채널데이터의 커서값 삭제
|
|
this.dS1.channel.Where(t => t.IsuseNull() == false && t.use == false).ToList().ForEach(t =>
|
|
{
|
|
t.c1 = t.c2 = "";
|
|
});
|
|
|
|
UpdateCursorValueData(0);
|
|
UpdateCursorValueData(1);
|
|
|
|
dv_chlist.AutoResizeColumns();
|
|
}
|
|
|
|
void UpdateCursorValueData(int idx)
|
|
{
|
|
var c1 = this.CursorLine[idx];
|
|
if (c1 != null)
|
|
{
|
|
var time = DateTime.FromOADate(c1.X);
|
|
foreach (DS1.channelRow dr in this.dS1.channel.Rows)
|
|
{
|
|
var chno = dr.idx;
|
|
|
|
//그래프 플롯이 준비되어있지 않는 경우
|
|
if (chno > myPlotDataX.Length || this.myPlots[chno - 1] == null)
|
|
{
|
|
if (idx == 0) dr.c1 = "ERR";
|
|
else dr.c2 = "ERR";
|
|
continue;
|
|
}
|
|
|
|
var plot = this.myPlots[chno - 1];
|
|
var times = this.myPlotDataX[chno - 1];
|
|
|
|
//해당 데이터 포인트에 값이 있다.
|
|
var samedate = times.Where(t => t == time).FirstOrDefault();
|
|
if (samedate != default(DateTime))
|
|
{
|
|
var index = times.IndexOf(samedate);
|
|
var volt1 = myPlotDataY[chno][index];
|
|
if (idx == 0) dr.c1 = $"{volt1:#0.00}";
|
|
else dr.c2 = $"{volt1:#0.00}"; ;
|
|
continue;
|
|
}
|
|
|
|
//가장자리 데이터 확인
|
|
var leftdata = times.Where(t => t < time).LastOrDefault();
|
|
var rightdata = times.Where(t => t > time).FirstOrDefault();
|
|
if (leftdata == null || rightdata == null)
|
|
{
|
|
if (idx == 0) dr.c1 = "ERR";
|
|
else dr.c2 = "ERR";
|
|
continue;
|
|
}
|
|
|
|
//시간내의 데이터를 찾는다
|
|
var indexL = times.IndexOf(leftdata);
|
|
var indexR = times.IndexOf(rightdata);
|
|
if (indexL == -1 || indexR == -1)
|
|
{
|
|
if (idx == 0) dr.c1 = $"ERR";
|
|
else dr.c2 = $"ERR";
|
|
continue;
|
|
}
|
|
|
|
var voltL = myPlotDataY[chno - 1][indexL];
|
|
var voltR = myPlotDataY[chno - 1][indexR];
|
|
/* 작성자: 이재웅, 작성일: 2024-09-26, 작성내용: volt 차이값 수식 변경 */
|
|
// [이전내용] : var diffvolt = Math.Abs(voltL - voltR);
|
|
var diffvolt = (voltR - voltL);
|
|
if (diffvolt == 0) //Y축 변화가 없다면 그 중간의 데이터는 동일하다
|
|
{
|
|
if (idx == 0) dr.c1 = $"{voltL:#0.00}";
|
|
else dr.c2 = $"{voltL:#0.00}";
|
|
continue;
|
|
}
|
|
|
|
//X축이 변화량의 어디쯤에 있는지 확인
|
|
var XTotal = (rightdata - leftdata).TotalSeconds;
|
|
var xPosition = (time - leftdata).TotalSeconds / XTotal;
|
|
var volt = voltL + (diffvolt * xPosition);
|
|
if (idx == 0) dr.c1 = $"{volt:#0.00}";
|
|
else dr.c2 = $"{volt:#0.00}";
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Ch_AllOrNothing(bool sel)
|
|
{ /* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 데이터그리드 데이터 All/Nothing 함수 */
|
|
|
|
if (this.myPlots == null || this.myPlots.Any() == false) return; //null check
|
|
PUB.smsg("채널정보를 변경하는중");
|
|
|
|
this.myPlots.ToList().ForEach(t => t.IsVisible = sel);
|
|
this.dS1.channel.ToList().ForEach(t =>
|
|
{
|
|
t.use = sel;
|
|
if (sel == false) { t.c1 = t.c2 = ""; }
|
|
});
|
|
this.dS1.channel.AcceptChanges();
|
|
this.formsPlot1.Refresh();
|
|
|
|
PUB.smsg("");
|
|
}
|
|
private void dv_chlist_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
|
{ /* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 데이터그리드 데이터 행 관련 처리 */
|
|
|
|
if (e.RowIndex < 0)
|
|
{ // 데이터그리드 Title 행을 클릭
|
|
if (e.ColumnIndex == 0)
|
|
{ // checkbox 컬럼을 클릭
|
|
selALL = !selALL;
|
|
Ch_AllOrNothing(selALL);
|
|
if (selALL) UpdateCursorValue(); // 전체표시 조건일때 실행
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else // 데이터그리드 Data 행의 checkbox 컬럼을 클릭
|
|
if (e.ColumnIndex != 0) return;
|
|
|
|
|
|
this.dv_chlist.EndEdit();
|
|
this.dv_chlist.SuspendLayout();
|
|
|
|
var Drv = this.bs_Channel.Current as DataRowView;
|
|
var dr = Drv.Row as DS1.channelRow;
|
|
if (Drv == null) return;
|
|
|
|
//해당채널의 표시상태를 반전한다.
|
|
var ch = dr.idx;
|
|
|
|
var plot = this.myPlots[ch - 1];
|
|
dr.use = plot.IsVisible = !plot.IsVisible;
|
|
|
|
if (dr.use == false)
|
|
{
|
|
dr.c1 = dr.c2 = string.Empty;
|
|
}
|
|
dr.EndEdit();
|
|
|
|
this.formsPlot1.Refresh();
|
|
UpdateCursorValue();
|
|
|
|
this.dv_chlist.ResumeLayout();
|
|
}
|
|
private void dv_chlist_MouseLeave(object sender, EventArgs e)
|
|
{ /* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 데이터그리드를 벗어나면 Cursors.Default 처리 */
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
private void dv_chlist_MouseMove(object sender, MouseEventArgs e)
|
|
{ /* 작성자: 이재웅, 작성일: 2024-09-26, 내용: 첫번째 checkbox 컬럼일때 Cursors.Hand 처리 */
|
|
// 마우스 위치의 HitTest 호출
|
|
DataGridView.HitTestInfo hitTestInfo = dv_chlist.HitTest(e.X, e.Y);
|
|
|
|
// 행과 열의 인덱스를 가져옴
|
|
//int rowIndex = hitTestInfo.RowIndex;
|
|
int columnIndex = hitTestInfo.ColumnIndex;
|
|
if (columnIndex == 0)
|
|
this.Cursor = Cursors.Hand;
|
|
else
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
|
|
public void DataGridView1_CellFormatting(object sender, System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
|
|
{
|
|
var dr = this.dS1.channel.Rows[e.RowIndex] as DS1.channelRow;
|
|
if (dr == null) return;
|
|
this.dv_chlist.Rows[e.RowIndex].Cells[1].Style.BackColor = Color.FromArgb(dr.cc);
|
|
if (dr.use) this.dv_chlist.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.White;
|
|
else this.dv_chlist.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.LightGray;
|
|
}
|
|
|
|
public void ToolStripMenuItem1_Click(System.Object sender, System.EventArgs e)
|
|
{
|
|
addnewGroup();
|
|
}
|
|
public void ToolStripMenuItem2_Click(System.Object sender, System.EventArgs e)
|
|
{
|
|
DelteGroup();
|
|
}
|
|
|
|
|
|
public void PrintDocument1_PrintPage(System.Object sender, System.Drawing.Printing.PrintPageEventArgs e)
|
|
{
|
|
Graphics G = e.Graphics;
|
|
|
|
var pagesize = new SizeF(PrintDocument1.DefaultPageSettings.PaperSize.Height, PrintDocument1.DefaultPageSettings.PaperSize.Width);
|
|
|
|
////리포트제목표시
|
|
var title = "Historical Trend Display Report";
|
|
var titlefont = new Font("나눔고딕", 30, FontStyle.Bold);
|
|
var fontsize = G.MeasureString(title, titlefont);
|
|
var Y = 50f;
|
|
var X = (float)((pagesize.Width - fontsize.Width) / 2);
|
|
G.DrawString(title, titlefont, Brushes.Black, X, Y);
|
|
Y = Y + fontsize.Height + 70;
|
|
|
|
int bottommargin = 50;
|
|
var WindowRect = new RectangleF(30, Y, (float)(pagesize.Width * 0.7), pagesize.Height - Y - bottommargin);
|
|
var ChartRect = new RectangleF(WindowRect.Left, WindowRect.Top, WindowRect.Width, WindowRect.Height);
|
|
|
|
var 상호명 = PUB.CONFIG.sangho.Trim();
|
|
var 상호font = new Font("나눔고딕", 18, FontStyle.Bold);
|
|
var 상호size = G.MeasureString(상호명, 상호font);
|
|
G.DrawString(상호명, 상호font, Brushes.Black, WindowRect.Left, WindowRect.Top - 상호size.Height - 10);
|
|
|
|
title = "C1";
|
|
fontsize = G.MeasureString(title, this.Font);
|
|
|
|
var valuestr = "00.025";
|
|
var valuefontsizew = G.MeasureString(valuestr, this.Font).Width * 1.1F;
|
|
var valuefontsizeh = G.MeasureString(valuestr, this.Font).Height;
|
|
|
|
var chtitle = new Rectangle(System.Convert.ToInt32(WindowRect.Left + WindowRect.Width + 20), System.Convert.ToInt32(WindowRect.Top), System.Convert.ToInt32(pagesize.Width - WindowRect.Width - 150), System.Convert.ToInt32(fontsize.Height * 2)); ////제목영역
|
|
var ChRect = new RectangleF(chtitle.Left, chtitle.Top + chtitle.Height, chtitle.Width, pagesize.Height - (chtitle.Top + chtitle.Height) - bottommargin); ////본문영역
|
|
var chinforect = new Rectangle(chtitle.Left, chtitle.Top, chtitle.Width, System.Convert.ToInt32(chtitle.Height + ChRect.Height)); ////총영역
|
|
|
|
var chtitlewidth = ChRect.Width - (valuefontsizew * 2); ////채널정보를 표시하는 영역의 너비
|
|
var chc1valueleft = chinforect.Left + chtitlewidth;
|
|
var chc2valueleft = chc1valueleft + valuefontsizew;
|
|
|
|
//차트영역에 이미지를 그린다.
|
|
G.DrawRectangle(Pens.Black, ChartRect.X, ChartRect.Y, ChartRect.Width, ChartRect.Height);
|
|
|
|
using (var img = this.formsPlot1.Plot.GetImage((int)ChartRect.Width, (int)ChartRect.Height))
|
|
G.DrawImage(img.GetBitmap(), ChartRect.X, ChartRect.Y, ChartRect.Width, ChartRect.Height);
|
|
|
|
|
|
var displayfont = new Font("나눔고딕", 10, FontStyle.Bold);
|
|
var displayfont2 = new Font("나눔고딕", 10);
|
|
////채널정보칸 표시 (바로 이미지를 복제한다?) 제목표시
|
|
G.DrawRectangle(Pens.Gray, chtitle);
|
|
G.DrawString("Cell", displayfont, Brushes.Black, chtitle.Left + 5, (float)(chtitle.Top + (chtitle.Height - valuefontsizeh) / 2));
|
|
G.DrawString("C1", displayfont, Brushes.Black, (float)(chc1valueleft + (valuefontsizew - fontsize.Width) / 2), (float)(chtitle.Top + (chtitle.Height - valuefontsizeh) / 2));
|
|
G.DrawString("C2", displayfont, Brushes.Black, (float)(chc2valueleft + (valuefontsizew - fontsize.Width) / 2), (float)(chtitle.Top + (chtitle.Height - valuefontsizeh) / 2));
|
|
|
|
////세로줄그리기
|
|
G.DrawLine(Pens.Gray, chc1valueleft, chinforect.Top, chc1valueleft, chinforect.Top + chinforect.Height);
|
|
G.DrawLine(Pens.Gray, chc2valueleft, chinforect.Top, chc2valueleft, chinforect.Top + chinforect.Height);
|
|
//G.DrawRectangle(Pens.Red, chinforect)
|
|
|
|
////체크된 채널의 목록값을 보여준다. 그리고 c1,c2값을 보여준다.
|
|
var fsize = G.MeasureString("#11", this.Font);
|
|
var gridheight = ChRect.Height / 25;
|
|
var idx = (short)0;
|
|
Y = ChRect.Top + 10;
|
|
X = ChRect.Left + 10;
|
|
|
|
var gridcnt = (int)(Math.Floor((decimal)(ChRect.Height / gridheight)));
|
|
for (int i = 0; i <= gridcnt + 1; i++)
|
|
{
|
|
float newy = ChRect.Top + (i * gridheight);
|
|
G.DrawLine(Pens.Gray, ChRect.Left, newy, ChRect.Left + ChRect.Width, newy);
|
|
}
|
|
|
|
foreach (var ch in this.dS1.channel.Where(t => t.use))
|
|
{
|
|
fsize = G.MeasureString(ch.cname, displayfont2);
|
|
Y = (float)(ChRect.Top + (idx * gridheight) + ((gridheight - fsize.Height) / 2));
|
|
|
|
////지정역역의 색상을 표시함
|
|
RectangleF ColorRect = new RectangleF(X - 5, Y, chc1valueleft - X - 5, fsize.Height);
|
|
G.FillRectangle(new SolidBrush(Color.FromArgb(ch.cc)), ColorRect);
|
|
G.DrawString(ch.cname, displayfont2, Brushes.Black, X, Y);
|
|
G.DrawString(ch.c1, displayfont2, Brushes.Black, chc1valueleft + 5, Y);
|
|
G.DrawString(ch.c2, displayfont2, Brushes.Black, chc2valueleft + 5, Y);
|
|
idx++;
|
|
}
|
|
ChRect.Height = gridheight * (gridcnt + 0);
|
|
G.DrawRectangle(Pens.Gray, chinforect.Left, chinforect.Top, chinforect.Width, chinforect.Height);
|
|
|
|
displayfont2.Dispose();
|
|
displayfont.Dispose();
|
|
|
|
G.Dispose();
|
|
}
|
|
public void Button1_Click(System.Object sender, System.EventArgs e)
|
|
{
|
|
this.PrintDocument1.DefaultPageSettings.Landscape = true;
|
|
try
|
|
{
|
|
this.PrintPreviewDialog1.ShowDialog();
|
|
}
|
|
catch (Exception ex) { UTIL.MsgE($"인쇄실패\n\n{ex.Message}"); }
|
|
}
|
|
|
|
public void DataGridView2_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
|
|
{ // 불러오기
|
|
if (this.bs_grp.Current == null)
|
|
{
|
|
UTIL.MsgE("선택된 그룹이 없습니다");
|
|
return;
|
|
}
|
|
bt_run.PerformClick();
|
|
}
|
|
public void ToolStripMenuItem3_Click(System.Object sender, System.EventArgs e)
|
|
{
|
|
var drv = this.bs_grp.Current as DataRowView;
|
|
if (drv == null) return;
|
|
var dr = drv.Row as DS1.groupRow;
|
|
MessageBox.Show($"Channel List\n\n{dr.value}");
|
|
}
|
|
public void bt_run_Click(System.Object sender, System.EventArgs e)
|
|
{
|
|
PUB.smsg("Loading");
|
|
var bt = sender as Button;
|
|
bt.Enabled = false;
|
|
|
|
//GroupName = "";
|
|
foreach (var ch in this.ChartListData)
|
|
ch.Clear();
|
|
|
|
//그룹선택확인
|
|
var DRVGRP = this.bs_grp.Current as DataRowView;
|
|
if (DRVGRP == null) return;
|
|
var DRGRP = DRVGRP.Row as DS1.groupRow;
|
|
|
|
/* 주석날짜: 2024-09-20 | 주석내용: VIEWGROUP.xml 파일내용 | 주석자: 이재웅
|
|
*
|
|
* <VIEWGROUUP>
|
|
* <IDX>9</IDX>
|
|
* <GNAME>EL2500A</GNAME>
|
|
* <TITLE>GRP(1...10)</TITLE>
|
|
* <VAL>1,2,3,4,5,6,7,8,9,10</VAL>
|
|
* </VIEWGROUUP>
|
|
*
|
|
**************************************
|
|
*
|
|
* DRGRP.idx = 9
|
|
* DRGRP.mame = "EL2500A"
|
|
* DRGRP.cname = "[사용자] GRP(1...10)"
|
|
* DRGRP.value = "1,2,3,4,5,6,7,8,9,10"
|
|
*/
|
|
|
|
var CHLIST = DRGRP.value.Trim();// ["value"].ToString().Trim(); ////표시채널목록
|
|
var CHArray = CHLIST.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
//채널번호를 채널그룹에 맞게 분리작업을 합니다.
|
|
//채널데이터는 160개를 기준으로 1~10까지 분리되어 저장됩니다.
|
|
//채널번호에 맞는 기준의 번호를 찾아야 합니다.
|
|
foreach (var C in CHArray)
|
|
{
|
|
if (C.isEmpty() || C.IsNumeric() == false) continue;
|
|
|
|
var ch = int.Parse(C);
|
|
var ValueIdx = ch;// int.Parse(C);
|
|
int quotient = ValueIdx / 160; // 몫
|
|
int remainder = ValueIdx % 160; // 나머지
|
|
if (remainder == 0) ValueIdx = quotient - 1;
|
|
else ValueIdx = quotient;
|
|
|
|
if (this.ChartListData[ValueIdx].ChannelList.Contains(ch) == false)
|
|
this.ChartListData[ValueIdx].ChannelList.Add(ch);
|
|
}
|
|
|
|
//우측채널목록갱신(한번만 하면되므로 채널정보가 바뀌었을때만 처리한다.)
|
|
if (CHLIST != SelectedGRPChValue)
|
|
{
|
|
bt_cursor1.BackColor = Color.Empty;
|
|
bt_cursor2.BackColor = Color.Empty;
|
|
|
|
RefreshChannelList();
|
|
SelectedGRPChValue = CHLIST;
|
|
}
|
|
|
|
using (var fd = new Frm_GraphSetup())
|
|
if (fd.ShowDialog() == DialogResult.OK) ShowData();
|
|
|
|
////UpdateChannelInfo()
|
|
bt.Enabled = true;
|
|
PUB.smsg("");
|
|
}
|
|
|
|
public void Button2_Click_2(object sender, EventArgs e)
|
|
{
|
|
using (var f = new Frm_GraphSetup())
|
|
{
|
|
if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
|
ShowData(true);
|
|
}
|
|
}
|
|
|
|
public void cmb_group_SelectedIndexChanged(System.Object sender, System.EventArgs e)
|
|
{
|
|
if (cmb_group.SelectedIndex < 0) return;
|
|
|
|
//기존에 선택된 채널의 활성화를 모두 해제 한다.
|
|
this.dS1.channel.ToList().ForEach(t => t.use = false);
|
|
this.ChartListData.ToList().ForEach(t => t.Clear());
|
|
|
|
SelectedGRPChValue = "";
|
|
RefreshChannelList();
|
|
|
|
string title = cmb_group.Text;
|
|
this.bs_grp.Filter = "rname='" + title + "'";
|
|
this.formsPlot1.Plot.Clear();
|
|
this.formsPlot1.Refresh();
|
|
}
|
|
public void Button1_Click_1(System.Object sender, System.EventArgs e)
|
|
{
|
|
using (var sd = new SaveFileDialog())
|
|
{
|
|
sd.Filter = "png file|*.png";
|
|
sd.FilterIndex = 0;
|
|
sd.FileName = $"GraphCapture_{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.png";
|
|
sd.RestoreDirectory = true;
|
|
if (sd.ShowDialog() == DialogResult.OK)
|
|
{
|
|
var fn = sd.FileName;
|
|
this.formsPlot1.Plot.SavePng(fn, formsPlot1.Width, formsPlot1.Height);
|
|
var dlg = UTIL.MsgQ("생성된 파일을 확인할까요?");
|
|
if (dlg == DialogResult.Yes) UTIL.RunExplorer(fn);
|
|
}
|
|
}
|
|
}
|
|
public void ToolStripButton1_Click_1(object sender, EventArgs e)
|
|
{
|
|
string dir = PUB.CONFIG.GetDatabasePath() + "\\DataBase\\volt";
|
|
UTIL.RunExplorer(dir);//.Shell("explorer " + dir, AppWinStyle.NormalFocus);
|
|
}
|
|
|
|
public void bt_cursor_Click(object sender, EventArgs e)
|
|
{
|
|
var bt = sender as Button;
|
|
var idx = int.Parse(bt.Tag.ToString());
|
|
CreateCursor(idx);
|
|
}
|
|
void CreateCursor(int idx)
|
|
{
|
|
if (this.myPlots == null) return; /* 작성자: 이재웅, 작성일: 2024-09-25, 내용: 'myPlots == null' 조건일때 오류발생 */
|
|
var plot = this.myPlots.Where(t => t != null).FirstOrDefault();
|
|
if (plot == null) return;
|
|
|
|
//화면에 보이는 영역의 데이터를 확인
|
|
var viewx = plot.Axes.XAxis;
|
|
var viewy = plot.Axes.YAxis;
|
|
var xmin = viewx.Min;
|
|
var xmax = viewx.Max;
|
|
var datestep = (xmax - xmin) * 0.33f;
|
|
|
|
//기존에 등록된 것이 있다면 그것을 제거하고 다시 추가한다.
|
|
if (CursorLine[idx] != null) formsPlot1.Plot.Remove(CursorLine[idx]);
|
|
|
|
var timedata = xmin + datestep * (idx + 1);
|
|
var timeval = DateTime.FromOADate(timedata);
|
|
var axLine = formsPlot1.Plot.Add.VerticalLine(timedata);
|
|
axLine.Text = $"[C{idx + 1}] {timeval:yy-MM-dd\nHH:mm:ss}";
|
|
axLine.LabelAlignment = ScottPlot.Alignment.UpperRight;
|
|
axLine.IsDraggable = true;
|
|
this.CursorLine[idx] = axLine;
|
|
|
|
this.formsPlot1.Refresh();
|
|
UpdateCursorValue();
|
|
}
|
|
|
|
// DataGridView의 모든 행의 column 데이터 지우기
|
|
private void ClearColumnData(DataGridView dataGridView, int idx)
|
|
{
|
|
// DataGridView의 모든 행을 반복
|
|
foreach (DataGridViewRow row in dataGridView.Rows)
|
|
{
|
|
// 해당 열의 데이터 지우기
|
|
if (!row.IsNewRow) // 새 행이 아닌 경우에만
|
|
{
|
|
row.Cells[idx + 2].Value = null; // 또는 string.Empty로 설정 가능
|
|
}
|
|
}
|
|
}
|
|
|
|
private void bt_Hide_Click(object sender, EventArgs e)
|
|
{
|
|
//기존에 등록된 것이 있다면 그것을 제거한다.
|
|
if (CursorLine[0] != null)
|
|
{
|
|
formsPlot1.Plot.Remove(CursorLine[0]);
|
|
CursorLine[0] = null;
|
|
ClearColumnData(dv_chlist, 0);
|
|
}
|
|
if (CursorLine[1] != null)
|
|
{
|
|
formsPlot1.Plot.Remove(CursorLine[1]);
|
|
CursorLine[1] = null;
|
|
ClearColumnData(dv_chlist, 1);
|
|
}
|
|
this.formsPlot1.Refresh();
|
|
}
|
|
|
|
private void btConfig_Click(object sender, EventArgs e)
|
|
{
|
|
if (UTIL.ShowPropertyDialog(PUB.TREND) == DialogResult.OK)
|
|
{
|
|
PUB.TREND.Save();
|
|
|
|
formsPlot1.Plot.Axes.Bottom.MinimumSize = PUB.TREND.graph_bottom_minsize;
|
|
if (PUB.TREND.y_scale_auto)
|
|
formsPlot1.Plot.Axes.AutoScaleY();
|
|
else
|
|
formsPlot1.Plot.Axes.SetLimitsY(PUB.TREND.graph_y_start, PUB.TREND.graph_y_end);
|
|
|
|
formsPlot1.Refresh();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|