Files
vms2016_kadisp/cVMS.NET_CS/Dialog/TrendView/Frm_trend_real.cs

616 lines
25 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.Reflection;
using System.Runtime.InteropServices;
using TrendCtrlII;
using AR;
using System.Data.SqlTypes;
using System.Diagnostics.Eventing.Reader;
using System.Data.Linq;
namespace vmsnet
{
public partial class Frm_trend_Real
{
private string lasttime = "";
private DateTime EndDate = DateTime.Now;
private bool init = false;
private HMI.DispCtrl DispCtrl1;
readonly ScottPlot.WinForms.FormsPlot formsPlot1;
ScottPlot.Plottables.Scatter[] Streamer1;
List<float>[] dataVolt = null;
List<double>[] dataTime = null;
short voltlimit = 0;
short timelimit = 10;
public Frm_trend_Real(HMI.DispCtrl dispctrl)
{
// 디자이너에서 이 호출이 필요합니다.
InitializeComponent();
// InitializeComponent() 호출 뒤에 초기화 코드를 추가하세요.
//현재데이터베이스를 복제한다.
MakeTempDatabase();
formsPlot1 = new ScottPlot.WinForms.FormsPlot() { Dock = DockStyle.Fill };
formsPlot1.MouseDown += FormsPlot1_MouseDown;
formsPlot1.MouseUp += FormsPlot1_MouseUp;
formsPlot1.MouseMove += FormsPlot1_MouseMove;
CrossHair = formsPlot1.Plot.Add.Crosshair(0, 0);
CrossHair.TextColor = ScottPlot.Colors.White;
CrossHair.TextBackgroundColor = CrossHair.HorizontalLine.Color;
formsPlot1.Plot.YLabel("VOLTAGE");
formsPlot1.Plot.XLabel("COUNT");
formsPlot1.Plot.Axes.Bottom.MinimumSize = PUB.TREND.graph_bottom_minsize;
this.formsPlot1.Plot.ShowLegend();
formsPlot1.Plot.Axes.DateTimeTicksBottom();
this.formsPlot1.Plot.Axes.ContinuouslyAutoscale = true;
this.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);
}
};
Panel2.Controls.Clear();
this.Panel2.Controls.Add(this.formsPlot1);
////그룹데이터목록
this.cmb_tanks.Items.Clear();
for (int i = 0; i <= dispctrl.GROUPS.Length - 1; i++)
{
var grp = dispctrl.GROUPS[i];
this.cmb_tanks.Items.Add(grp.);
}
DispCtrl1 = dispctrl;
PUB.RemoteCommandEvent += Pub_RemoteCommandEvent;
Refresh_전해조목록();
if (this.cmb_tanks.Items.Count > 0)
this.cmb_tanks.SelectedIndex = 0;
Streamer1 = new ScottPlot.Plottables.Scatter[0];
dataVolt = new List<float>[0];
dataTime = new List<double>[0];
}
public void Frm_trend_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
saveviewSetting();
PUB.RemoteCommandEvent -= Pub_RemoteCommandEvent;
formsPlot1.MouseDown -= FormsPlot1_MouseDown;
formsPlot1.MouseUp -= FormsPlot1_MouseUp;
formsPlot1.MouseMove -= FormsPlot1_MouseMove;
Timer1.Stop();
}
void MakeTempDatabase()
{
//현재데이터베이스를 복제한다.
this.documentElement1.Clear();
this.documentElement1.GRP.Merge(PUB.DS.GRP);
this.documentElement1.CHANNEL.Merge(PUB.DS.CHANNEL);
this.documentElement1.AcceptChanges();
this.ds1.Clear();
this.ds1.AcceptChanges();
}
private void Refresh_전해조목록()
{
this.cmb_tanks.Items.Clear();
var grplist = this.documentElement1.GRP.Where(t => t.USE == 1);
if (grplist.Any() == false)
{
UTIL.MsgE("활성화된 그룹이 없습니다");
return;
}
var namelist = grplist.Select(t => $"[{t.IDX:00}] {t.TITLE}").ToArray();
this.cmb_tanks.Items.AddRange(namelist);
}
public void Frm_trend_Load(System.Object sender, System.EventArgs e)
{
EndDate = DateTime.Now;
////마지막으로 선택한 그룹을 선택해준다.
var selidx0 = (PUB.CONFIG.tvr_selectgroup0); // XMl.Data("trendview", "selectgroup0", "0")
this.cmb_tanks.SelectedIndex = selidx0;
loadviewSetting();
init = true;
Timer1.Start();
}
private void Pub_RemoteCommandEvent(object sender, RemoteCommand e)
{
try
{
if (e.Command == rCommand.ValueUpdate)
{
//스트리머,데이터,선택된 채널 정보가 있어야 업데이트 가능하다
if (e.Data != null && this.Streamer1 != null && this.selectchlist.Any())
{
var data = e.Data as List<NotifyData>;
//선택된 채널의 정보만 사용
var recvdatas = data.Where(t => selectchlist.Contains(t.chno)).Select(t => t);
if (recvdatas.Any() == false) return; //대상채널데이터가 없다.
//받은데이터를 화면에 추가한다.
foreach (var newdata in recvdatas)
{
var ch = newdata.chno - 1;
var val = newdata.value;
var time = newdata.time;
//자료가없거나 스트리머가 없는 경우
if (ch >= this.Streamer1.Length || this.Streamer1[ch] == null) continue;
float value = 0;
if (PUB.CONFIG.datadiv != 0 && PUB.CONFIG.datadiv != 1)
value = (newdata.value) / PUB.CONFIG.datadiv;
else
value = (newdata.value);
//채널정보를 통해서 소수점위치와 옾셋값을 가져온다
if (newdata.decpos > 0) value = (float)(value / Math.Pow(10, newdata.decpos));
//최종옵셋
value += newdata.offset;
//데이터 추가
if (this.Streamer1[ch].IsVisible)
{
var v_time = DateTime.Parse(time);
this.dataTime[ch].Add(v_time.ToOADate());
this.dataVolt[ch].Add(value);
var mintime = DateTime.FromOADate(dataTime[ch].First());
var maxtime = DateTime.FromOADate(dataTime[ch].Last());
var ts = (maxtime - mintime);
if (ts.TotalMinutes >= this.timelimit)
{
//10개지운다
if (dataTime[ch].Count > 10)
{
dataTime[ch].RemoveRange(0, 10);
dataVolt[ch].RemoveRange(0, 10);
}
}
}
}
this.BeginInvoke(new Action(() =>
{
if (voltlimit != 0) formsPlot1.Plot.Axes.AutoScaleX();
this.formsPlot1.Refresh();
}));
}
}
}
catch (NullReferenceException ex1)
{
// 예외 메시지, 파일명, 행 번호를 로그에 기록
var stackTrace = new System.Diagnostics.StackTrace(ex1, true);
var frame = stackTrace.GetFrame(0);
string fileName = frame.GetFileName();
int lineNumber = frame.GetFileLineNumber();
// UI 디스플레이 중, 예외발생 문제
PUB.log.AddE($"NullReferenceException Message={ex1.Message}, 파일명={fileName}, 행번호={lineNumber}");
return;
}
catch (Exception ex2)
{
// 예외 메시지, 파일명, 행 번호를 로그에 기록
var stackTrace = new System.Diagnostics.StackTrace(ex2, true);
var frame = stackTrace.GetFrame(0);
string fileName = frame.GetFileName();
int lineNumber = frame.GetFileLineNumber();
// Else Error !!!
PUB.log.AddE($"Exception Message={ex2.Message}, 파일명={fileName}, 행번호={lineNumber}");
return;
}
}
#region "scott plot mouse event"
ScottPlot.Plottables.Crosshair CrossHair;
ScottPlot.Plottables.VerticalLine[] CursorLine;
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();
}
ScottPlot.Plottables.AxisLine PlottableBeingDragged = null;
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);
if (mouseCoordinates.X is double.NaN || mouseCoordinates.Y is double.NaN ||
mouseCoordinates.X is double.PositiveInfinity || mouseCoordinates.Y is double.PositiveInfinity ||
mouseCoordinates.X is double.NegativeInfinity || mouseCoordinates.Y is double.NegativeInfinity)
{
return;
}
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);
if (PlottableBeingDragged is null)
{
// set cursor based on what's beneath the plottable
var lineUnderMouse = GetLineUnderMouse(e.X, e.Y);
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}";
vl.Text = $"{time:yy-MM-dd\nHH:mm:ss}";
}
formsPlot1.Refresh();
}
}
#endregion
//그룹이 선택된 경우
public void cmb_group_SelectedIndexChanged(System.Object sender, System.EventArgs e)
{
if (cmb_tanks.SelectedIndex < 0) return;
if (System.Diagnostics.Debugger.IsAttached) Console.WriteLine($"grp selectedindex val={cmb_tanks.SelectedIndex}");
/* 작성자: 이재웅, 작성일: 2024-11-22, 내용: 차트/Scatter/Volt/Time 초기화 */
this.formsPlot1.Plot.Clear();
this.Streamer1 = new ScottPlot.Plottables.Scatter[0];
this.dataVolt = new List<float>[0];
this.dataTime = new List<double>[0];
/**********************************************************************/
//현재등록된 목록을 모두 삭제
this.ds1.channel.Clear();
this.formsPlot1.Plot.Remove<ScottPlot.Plottables.DataStreamer>(); //데이터스트림삭제
this.formsPlot1.Refresh();
var grpName = cmb_tanks.Text.Substring(4).Trim();
var grpNo = int.Parse(cmb_tanks.Text.Substring(1, 2));
var grpdr = this.documentElement1.GRP.Where(t => t.TITLE.Equals(grpName)).FirstOrDefault();
if (grpdr == null) return;
var chlist = this.documentElement1.CHANNEL.Where(t => t.GIDX == grpdr.IDX);
if (chlist.Any() == false) return;
// 미리저장된 목록을 확인한다.
var chstr = "";
if (grpNo == 0) chstr = PUB.CONFIG.grp0chlist;
else if (grpNo == 1) chstr = PUB.CONFIG.grp1chlist;
else if (grpNo == 2) chstr = PUB.CONFIG.grp2chlist;
else if (grpNo == 3) chstr = PUB.CONFIG.grp3chlist;
else if (grpNo == 4) chstr = PUB.CONFIG.grp4chlist;
else if (grpNo == 5) chstr = PUB.CONFIG.grp5chlist;
else if (grpNo == 6) chstr = PUB.CONFIG.grp6chlist;
else if (grpNo == 7) chstr = PUB.CONFIG.grp7chlist;
else if (grpNo == 8) chstr = PUB.CONFIG.grp8chlist;
else if (grpNo == 9) chstr = PUB.CONFIG.grp9chlist;
var chbuf = chstr.Split((char[])(new[] { ',' }), StringSplitOptions.RemoveEmptyEntries);
//목록은 추가하지만 스트립을 추가하지 않으니 최초 선택시에는 데이터가 나오지 않는다.
//사용자는 채널 선택을 사용해야 데이터가 보인다.
this.ds1.channel.Clear();
this.selectchlist.Clear();
foreach (var dr in chlist)
{
var newdr = ds1.channel.NewchannelRow();
newdr.idx = dr.IDX;
newdr.use = chbuf.Contains(dr.IDX.ToString()) ? true : false;
newdr.c1 = "";
newdr.c2 = "";
newdr.cname = dr.TITLE;
newdr.cc = dr.COLOR;
/* 작성자: 이재웅, 작성일: 2024-12-09, 내용: '개별보기' 상태에서 전해조 그룹 변경 時 자동으로 선택된 전해조 채널 보이기 */
if (newdr.use)
{
int idx = dr.IDX;
//지정된 플롯의 표시여부를 변경한다.
if (this.Streamer1.Length < idx)
{
Array.Resize(ref Streamer1, idx);
Array.Resize(ref dataVolt, idx);
Array.Resize(ref dataTime, idx);
}
if (dataTime[idx - 1] == null) dataTime[idx - 1] = new List<double>();
if (dataVolt[idx - 1] == null) dataVolt[idx - 1] = new List<float>();
if (Streamer1[idx - 1] == null) Streamer1[idx - 1] = formsPlot1.Plot.Add.ScatterPoints(dataTime[idx - 1], dataVolt[idx - 1]);
Streamer1[idx - 1].LineWidth = 1;
Streamer1[idx - 1].MarkerSize = 0;
/* 작성자: 이재웅, 작성일: 2024-11-27, 내용: 변경한 셀이름으로 차트범례 표시 */
Streamer1[idx - 1].LegendText = newdr.cname; //$"CH{idx}";
Streamer1[idx - 1].IsVisible = true;
selectchlist.Add(idx); //선택채널에 추가한다.
}
/************************************************************************/
ds1.channel.AddchannelRow(newdr);
}
ds1.channel.AcceptChanges();
formsPlot1.Refresh();
}
public void ToolStripButton1_Click_1(object sender, EventArgs e)
{
string dir = System.IO.Path.Combine(PUB.CONFIG.GetDatabasePath(), "DataBase", "volt");
UTIL.RunExplorer(dir);
}
private void loadviewSetting()
{
cmb_volt.SelectedIndex = (PUB.CONFIG.cell_voltindex); // Xml.Data("rtlview", "voltindex", "1")
cmb_time.SelectedIndex = (PUB.CONFIG.cell_timeindex); // XMl.Data("rtlview", "timeindex", "0")
}
private void saveviewSetting()
{
PUB.CONFIG.cell_voltindex = (cmb_volt.SelectedIndex);
PUB.CONFIG.cell_timeindex = (cmb_time.SelectedIndex);
PUB.CONFIG.Save();
}
public void bs_grp_CurrentChanged(object sender, EventArgs e)
{
////사용자가 그룹을 선택하면 해당 그룹에 연결된 채널목록을 가지고 사용 데이터 그룹을 결정함
}
/// <summary>
/// 1부터시작하는 채널의 번호입니다.(인덱스가 아님)
/// 채널선택화면에서 선택된 채널 번호만 들어있다.
/// docuementElement1 에는 모든 그룹/채널 정보가 들어있다
/// </summary>
List<int> selectchlist = new List<int>();
public void btSelectCH_Click(object sender, EventArgs e)
{
var grpName = cmb_tanks.Text.Substring(4).Trim();
var grpNo = int.Parse(cmb_tanks.Text.Substring(1, 2));
//현재선택된 그룹의 채널목록을 추출해야한다.
if (grpName.isEmpty())
{
UTIL.MsgE("채널 그룹을 선택하세요");
return;
}
var drGrp = documentElement1.GRP.Where(t => t.TITLE == grpName).FirstOrDefault();
if (drGrp == null)
{
UTIL.MsgE("선택된 채널 그룹 정보가 없습니다");
return;
}
//채널목록을 확인한다.
var chrows = documentElement1.CHANNEL.Where(t => t.GIDX == drGrp.IDX);
if (chrows.Any() == false)
{
UTIL.MsgE("해당 그룹에 등록된 채널정보가 없습니다");
return;
}
//현재 등록된 채널정보를 토대로 데이터를 선택한다
using (var f = new Frm_SelectCH(ds1.channel))
{
if (f.ShowDialog() == DialogResult.OK)
{
//현재등록된 목록을 모두 삭제
this.formsPlot1.Plot.Remove<ScottPlot.Plottables.Scatter>(); //데이터스트림삭제
this.formsPlot1.Refresh();
this.selectchlist.Clear();
//기존에 선택된 자료 선택하제
foreach (var item in ds1.channel.Where(t => t.use))
{
item.use = false;
}
ds1.channel.AcceptChanges();
foreach (ListViewItem item in f.CheckedListBox1.Items) //선택된 목록만 가져온다
{
int idx = int.Parse(item.Tag.ToString()); //.SubItems(0).Text)
//지정된 플롯의 표시여부를 변경한다.
if (this.Streamer1.Length < idx)
{
Array.Resize(ref Streamer1, idx);
Array.Resize(ref dataVolt, idx);
Array.Resize(ref dataTime, idx);
}
if (dataTime[idx - 1] == null) dataTime[idx - 1] = new List<double>();
if (dataVolt[idx - 1] == null) dataVolt[idx - 1] = new List<float>();
if (Streamer1[idx - 1] != null) this.Streamer1[idx - 1] = null;
Streamer1[idx - 1] = this.formsPlot1.Plot.Add.ScatterPoints(dataTime[idx - 1], dataVolt[idx - 1]);
Streamer1[idx - 1].LineWidth = 1;
Streamer1[idx - 1].MarkerSize = 0;
/* 작성자: 이재웅, 작성일: 2024-11-27, 내용: 변경한 셀이름으로 차트범례 표시 */
Streamer1[idx - 1].LegendText = item.Text; //$"CH{idx}";
Streamer1[idx - 1].IsVisible = item.Checked;
var dr = ds1.channel.Where(t => t.idx == idx).FirstOrDefault();
if (dr != null)
{
dr.use = item.Checked;
dr.EndEdit();
}
if (item.Checked) selectchlist.Add(idx); //선택채널에 추가한다.
}
ds1.channel.AcceptChanges();
//사용자목록에 저장한다
string chstr = string.Join(",", selectchlist.ToArray());
if (grpNo == 0) PUB.CONFIG.grp0chlist = chstr;
else if (grpNo == 1) PUB.CONFIG.grp1chlist = chstr;
else if (grpNo == 2) PUB.CONFIG.grp2chlist = chstr;
else if (grpNo == 3) PUB.CONFIG.grp3chlist = chstr;
else if (grpNo == 4) PUB.CONFIG.grp4chlist = chstr;
else if (grpNo == 5) PUB.CONFIG.grp5chlist = chstr;
else if (grpNo == 6) PUB.CONFIG.grp6chlist = chstr;
else if (grpNo == 7) PUB.CONFIG.grp7chlist = chstr;
else if (grpNo == 8) PUB.CONFIG.grp8chlist = chstr;
else if (grpNo == 9) PUB.CONFIG.grp9chlist = chstr;
PUB.CONFIG.Save();
if (CrossHair == null)
{
CrossHair = formsPlot1.Plot.Add.Crosshair(0, 0);
CrossHair.TextColor = ScottPlot.Colors.White;
CrossHair.TextBackgroundColor = CrossHair.HorizontalLine.Color;
}
formsPlot1.Plot.Axes.ContinuouslyAutoscale = true;
this.formsPlot1.Refresh();
}
}
}
public void Timer1_Tick(object sender, EventArgs e)
{
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
}
private void cmb_volt_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmb_volt.SelectedIndex < 0) return;
if (cmb_volt.SelectedIndex == 0) voltlimit = 0;
else voltlimit = short.Parse(this.cmb_volt.Text.Replace("v", ""));
if (this.formsPlot1 != null)
{
if (voltlimit == 0)
this.formsPlot1.Plot.Axes.ContinuouslyAutoscale = true;
else
{
this.formsPlot1.Plot.Axes.ContinuouslyAutoscale = false;
this.formsPlot1.Plot.Axes.SetLimitsY(0, voltlimit);
}
}
}
private void cmb_time_SelectedIndexChanged_1(object sender, EventArgs e)
{
if (cmb_time.SelectedIndex < 0) return;
timelimit = short.Parse(this.cmb_time.Text.Replace("분", ""));
}
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();
}
}
}
}