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 static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskBand;
using System.Linq;
using System.Data.SqlTypes;
using System.Diagnostics;
using vmsnet.Attributes;
using vmsnet.Configures;
using AR;
using ScottPlot.TickGenerators.TimeUnits;

namespace vmsnet.HMI
{
    public enum RelayControl
    {
        [Coil(address: 10)]
        mKA
    }

    public enum ESTATUS
    {
        가동,
        이상,
        대기,
        기타,
        미사용
    }

    public enum EDRAGTYPE
    {
        GROUP
    }

    public enum EBUTTONTYPE
    {
        CELL,
        GROUP,
        NBZERO,
        NBRESET,
        PRINT,
        SELGROUP,
    }

    public partial class DispCtrl : UserControl
    {
        public bool MonitorOn { get; set; }
        public CMouseinfo Mouseinfo;    //마우스위치정보
     
        //그룹데이터
        public CGROUP[] GROUPS = new CGROUP[0];
        public CWINDOW WINDOW = new CWINDOW();

        object grplock = new object();

        public void ResizeGroup(int newsize)
        {
            lock (grplock)
            {
                if (newsize > this.GROUPS.Length)
                {
                    Array.Resize(ref GROUPS, newsize);
                }
            }
        }
        public void SetItemDisable()
        {
            lock (grplock)
            {
                foreach (var g in GROUPS)
                {
                    foreach (var gi in g.Items)
                    {
                        gi.활성화 = false;
                        gi._onalamh = false;
                        gi._onalaml = false;
                        gi._onalamover = false;
                        gi._p_onalamh = false;
                        gi._p_onalaml = false;
                        gi._p_onalamover = false;
                        gi.mtime = "";
                    }
                }
            }
        }

        public int GroupCount
        {
            get
            {
                if (GROUPS == null) return 0;
                if (GROUPS.Any() == false) return 0;
                return GROUPS.Length;
            }
        }
        public int GroupItemCount
        {
            get
            {
                if (GROUPS == null) return 0;
                if (GROUPS.Any() == false) return 0;
                return GROUPS[0].Items.Length;
            }
        }
        //public void SetGroupItem(CGROUP[] grp)
        //{
        //    lock (grplock)
        //    {
        //        GROUPS = grp;
        //    }
        //}
        //public void SetGroupItem(int idx, CGROUP item)
        //{
        //    lock (grplock)
        //    {
        //        GROUPS[idx] = item;
        //    }
        //}
        //public CGROUP[] GetGroupItem()
        //{
        //    lock (grplock)
        //    {
        //        return this.GROUPS;
        //    }
        //}

        private int[,,] values;
        private String[,,] times;

        private Boolean _init = false;    //그리드초기화완료여부
        private String _initmsg = "initializing...";
        public int initpercent = 0;

        //미정리
        public int ZoneMarginX = 0;
        public int ZoneMarginY = 50;

        public Font Font_Nav = new Font("arial", 9);
        private Font FONT_NANUM_20_BOLD = new Font("나눔고딕", 20, FontStyle.Bold);
        private Font FONT_TAHOMA_80_BOLD = new Font("tahoma", 80, FontStyle.Bold);


        /// <summary>
        /// 데이터가 지정된 시간이 넘어가면 이벤트를 이용해서 알림을함(외부의 트렌드뷰에서 사용하려고함)
        /// </summary>
        public int NotifyDataSecond = 2;
        private DateTime LastNotifyTime = DateTime.Now.AddSeconds(-60);

        //alam1 blick
        private short blink1 = 0;
        private short blink2 = 0;
        private Boolean A1 = false;
        private Boolean A2 = false;

        public int SmallSize = 300;

        //140829 데이터분주 추가 (arin)
        public int datadiv = 1;

        public Font FONT_NB = new Font("Tahoma", 30, FontStyle.Bold);
        public Font FONT_CN = new Font("Tahoma", 8, FontStyle.Bold);
        public Font FONT_CV = new Font("Tahoma", 9, FontStyle.Bold);

        public Font FONT_CT = new Font("Tahoma", 8, FontStyle.Bold);
        public Font FONT_AV = new Font("Tahoma", 7, FontStyle.Bold);

        public Font FONT_KA = new Font("Arial", 40, FontStyle.Bold);    //ka-value 
        public Font FONT_KU = new Font("Arial", 10, FontStyle.Bold);    //ka-unit
        public bool EnableKA { get; set; } = false;

        /// <summary>
        /// 개별보기일때 현재 표시해야할 아이템의 값이 변한경우
        /// </summary>
        public event OnChangedShowIndexHandler OnChangedShowIndex;
        public delegate void OnChangedShowIndexHandler(int index);

        public event OnNotifyDataHandler OnNotifyData;
        public delegate void OnNotifyDataHandler(DateTime datetCurrenttime);

        //MY EVENT

        public event OnClickPrintHandler OnClickPrint;  //click print
        public delegate void OnClickPrintHandler(CGROUP idx);

        public event OnClickProbeSensorHandler OnClickCell; //셀클릭
        public delegate void OnClickProbeSensorHandler(CITEM idx, MouseEventArgs e);

        public event OnClickGroupHandler OnClickGroup;  //그룹클릭
        public delegate void OnClickGroupHandler(CGROUP idx, MouseEventArgs e);

        public event OnSetNBHandler OnSetNullbalance;  //그룹클릭
        public delegate void OnSetNBHandler(CGROUP grp, Single value);

        public event OnAlamChangeHHandler OnAlamChange;  //알람이 변경되었을경우(활성/비활성)
        public delegate void OnAlamChangeHHandler(int ch, COMM.EALAMRAISETYPE raisetype, COMM.EALAMTYPE atype, String AM, Single value, Single maxvalue, Single minvalue);

        public event OnRaiseAlamChangeHHandler RaiseAlarm;  //알람이 변경되었을경우(활성/비활성)
        public delegate void OnRaiseAlamChangeHHandler(Boolean On);

        public Boolean _raise_alarm = false;         //알람발생여부
        public Boolean _raise_alarm_pre = false;     //알람발생여부(이전)

        private short _raise_alarm_count = 0;           //셀알람발생카운트(모든그룹의합)
        private short _raise_alarm_nbcount = 0;         //셀알람발생카운트(모든그룹의합)

        private short _pre_raise_alarm_count = 0;       //이전알람발생시 만들어진 카운터
        private short _pre_raise_alarm_nbcount = 0;     //이전알람발생시 만들어진 널밸런트 카운터
        public Boolean NullbalanceRealValue = true;
        public Boolean Sumab = false; //150320 

        //private CTrendData TrendData = new CTrendData();            

        //화면디자인관련
        private Color bordercolor;  //테두리 색상
        private Cursor cursor = Cursors.Default;    //현재커서의모양
        private SolidBrush SB_White_200 = new SolidBrush(Color.FromArgb(200, Color.White));
        private SolidBrush SB_WhiteSmoke_200 = new SolidBrush(Color.FromArgb(200, Color.WhiteSmoke));
        private SolidBrush SB_Black_150 = new SolidBrush(Color.FromArgb(200, Color.Black));

        public Color design_backcolor_start;
        public Color design_backcolor_normal;

        private string Warn_msg = "";   //경고메세지(화면 최상단중앙에 표시됨)
        private Font Warn_Font;

        private Font alamfont = new Font("Arial", 8, FontStyle.Bold); //ALAMICON_01

        //기타설정'
        // public Rectangle[] GroupRect; //그룹머릿글
        //  public Rectangle headRect;  //머릿부분
        //  public Rectangle ItemRect;  //아이템표시영역(전체)
        //  public Rectangle[] ItemsRect; //세부아이템표시영역(MATRIX 값에따라서 갯수가 변경된다)
        //  public Rectangle[] NBRects; //nullbalance Area (그룹갯수별로 잇기때문에 여러개가된다)

        public RectangleF WindowRect;   //윈도우데이터가표시되는 영역
                                        // public RectangleF BarRect;      //바그래프가 표시되는 영역
                                        // public RectangleF TrendRect;    //트렌드뷰가 표시되는 영역
                                        //  public RectangleF NavRect;      //네비게이션이 표시되는 영역

        private Font font_grouptitle = new Font("나눔고딕", 20, FontStyle.Bold);
        private Font font_header = new Font("나눔고딕", 10, FontStyle.Bold);

        private Color font_grouptitle_color = Color.Red;
        private Color font_header_color = Color.Yellow;

        //private List<RectangleF> Buttons = new List<RectangleF>(0);
        //private List<Object> Buttonstag = new List<Object>(0);
        //private List<EBUTTONTYPE> Buttonstype = new List<EBUTTONTYPE>(0);

        private List<CButton> Buttons = new List<CButton>();

        public Boolean bMakeRect = true;

        /// <summary>
        /// 개별보기? 전체보기?
        /// </summary>
        private Boolean _FullScreen;
        public Boolean FullScreen
        {
            get
            {
                return _FullScreen;
            }
            set
            {
                _FullScreen = value;
                if (!_FullScreen)
                {
                    ShowIndex = 0;
                }
                else ShowIndex = -1;
            }
        }

        private int _showindex = -1;

        /// <summary>
        /// 개별보기 모드일때 현재 화면에 표시할 그룹의 인덱스
        /// </summary>            
        public int ShowIndex
        {
            get
            {
                return _showindex;
            }
            set
            {
                if (FullScreen)
                {
                    //전체홤녀일경우에는 사용하지 않는다.
                    _showindex = -1;
                }
                else
                {
                    if (value != -1 && value != _showindex)
                    {
                        _showindex = value;
                        if (OnChangedShowIndex != null) OnChangedShowIndex(_showindex);
                    }
                }

            }
        }

        #region *** Constructors ***

        public int[,,] Values
        {
            get { return this.values; }
            set { this.values = value; }
        }

        public String[,,] Times
        {
            get { return this.times; }
            set { this.times = value; }
        }
        public Color Font_Grouptitle_Color
        {
            get { return this.font_grouptitle_color; }
            set { this.font_grouptitle_color = value; }
        }

        public Color Font_Header_Color
        {
            get { return this.font_header_color; }
            set { this.font_header_color = value; }
        }

        public Font Font_Grouptitle
        {
            get { return this.font_grouptitle; }
            set { this.font_grouptitle = value; }
        }

        public Font Font_Header
        {
            get { return this.font_header; }
            set { this.font_header = value; }
        }

        public DispCtrl()
        {
            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);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            // Redraw when resized
            this.SetStyle(ControlStyles.ResizeRedraw, true);

            this.Font = SystemInformation.MenuFont;
            this.Warn_Font = new Font("나눔고딕", 20, FontStyle.Bold);

            this.Font_Grouptitle = new Font("나눔고딕", 25, FontStyle.Bold);
            this.Font_Header = new Font("나눔고딕", 20, FontStyle.Bold);

            this.bordercolor = Color.LightGray;
            this.Warn_msg += ("메세지가 없습니다");

            this.design_backcolor_normal = Color.DarkBlue;
            this.design_backcolor_start = Color.Black;
            this.FullScreen = false;
        }


        private void InitializeComponent()
        {
            this.Name = "DispCtrl";
            this.Size = new System.Drawing.Size(287, 321);
            this.Load += new System.EventHandler(this.DispCtrl_Load);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DispCtrl_MouseDown);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DispCtrl_MouseMove);
            this.Resize += arLabel_Resize;
        }

        void arLabel_Resize(object sender, EventArgs e)
        {
            Invalidate();
        }

        public Boolean init
        {
            get { return this._init; }
            set { this._init = value; }
        }
        private void DispCtrl_Load(object sender, EventArgs e)
        {

        }

        #endregion

        #region *** Properties ***


        /// <summary>
        /// 경고메세지를 반환합니다.
        /// </summary>
        public string Message_Warning
        {
            get { return this.Warn_msg.ToString(); }
            set
            {
                if (value == null || string.IsNullOrEmpty(value)) Warn_msg = "";
                else Warn_msg += (value) + "\r\n";
            }
        }

        /// <summary>
        /// 테두리색상
        /// </summary>
        public Color BorderColor
        {
            get { return this.bordercolor; }
            set
            {
                bordercolor = value;
                Update();
            }
        }
        #endregion

        private RectangleF[] GetRectangleTitle(Graphics g, RectangleF ItemRect, CGROUP grp, Boolean isprint)
        {
            Font applyfont = this.FONT_CT;// grp.셀글꼴;
            if (isprint) applyfont = new Font(applyfont.FontFamily, 6, FontStyle.Bold);

            String Cellno = grp.maxcellname;
            SizeF CellNoSize = g.MeasureString(Cellno, applyfont);
            SizeF CellAlSize = g.MeasureString("88/88", applyfont);

            //-------------  | -----------  |  ------------
            //    cell             pv              alarm
            //-------------  | -----------  |  ------------

            Single H = (Single)(CellNoSize.Height * 2.5);   //높이값
            Single NameSize = (Single)(CellNoSize.Width * 1.05); //살짝더크게
            Single PvSize = ItemRect.Width - (Single)(CellNoSize.Width * 1.05) - (Single)(CellAlSize.Width);
            Single AlamSize = ItemRect.Width - NameSize - PvSize;

            RectangleF CellNoRect = new RectangleF(ItemRect.Left, ItemRect.Top, NameSize, H);
            RectangleF CellPvRect = new RectangleF(CellNoRect.Left + CellNoRect.Width, ItemRect.Top, PvSize, H);
            RectangleF CellALRect = new RectangleF(CellPvRect.Left + CellPvRect.Width, ItemRect.Top, AlamSize, H);

            return new RectangleF[3] { CellNoRect, CellPvRect, CellALRect };
        }

        private void DrawItem(Graphics g, CGROUP grp, RectangleF R, short CellGroupidx, Boolean isprint, Boolean isVisible)
        {
            RectangleF[] CellTitleRect = DrawCellTitle(g, R, grp, isprint);   //0번은 title label ,1 번은 value label

            grp._cell_title_width = CellTitleRect[0].Width;  //셀의 제목표시줄(번호)의 너비값
            grp._cell_value_width = CellTitleRect[1].Width;  //셀의 제목표시줄(값)의 너비값
            grp._cell_alam_width = CellTitleRect[2].Width;   //셀의 알람표시줄의 너비값

            Single height = R.Height - CellTitleRect[0].Height; //채널표시영역의 옾이값

            //실제채널이 표시되는 데이터 표시
            RectangleF channelarea = new RectangleF(R.Left, R.Top + CellTitleRect[0].Height, R.Width, height);
            if (isVisible) DrawCellMatrix(g, channelarea, grp, CellGroupidx, isprint);

            //vertical line
            Single lx = R.Left + grp._cell_title_width;
            if (isVisible) g.DrawLine(Pens.Silver, lx, R.Top, lx, R.Top + R.Height);
            lx += grp._cell_value_width;
            if (isVisible) g.DrawLine(Pens.Silver, lx, R.Top, lx, R.Top + R.Height);
            if (isVisible) g.DrawLine(Pens.Silver, lx, R.Top + (CellTitleRect[2].Height / 2), lx + grp._cell_alam_width, R.Top + (CellTitleRect[2].Height / 2));
            //lx += grp._cell_alam_width / 2;
            //if (isVisible) g.DrawLine(Pens.Silver, lx, R.Top + (CellTitleRect[2].Height / 2), lx, R.Top + R.Height);
        }

        private RectangleF[] DrawCellTitle(Graphics g, RectangleF ItemRect, CGROUP grp, Boolean isprint)
        {
            Font applyfont = this.FONT_CT;// grp.셀글꼴;
            if (isprint) applyfont = new Font(applyfont.FontFamily, 6, FontStyle.Bold);

            String Cellno = grp.maxcellname;
            SizeF CellNoSize = g.MeasureString(Cellno, applyfont);
            SizeF CellAlSize = g.MeasureString("88/88", applyfont);

            //-------------  | -----------  |  ------------
            //    cell             pv              alarm
            //-------------  | -----------  |  ------------

            Single H = (Single)(CellNoSize.Height * 2.5);   //높이값
            Single NameSize = (Single)(CellNoSize.Width * 1.05); //살짝더크게
            Single PvSize = ItemRect.Width - (Single)(CellNoSize.Width * 1.05) - (Single)(CellAlSize.Width);
            Single AlamSize = ItemRect.Width - NameSize - PvSize;

            RectangleF CellNoRect = new RectangleF(ItemRect.Left, ItemRect.Top, NameSize, H);
            RectangleF CellPvRect = new RectangleF(CellNoRect.Left + CellNoRect.Width, ItemRect.Top, PvSize, H);
            RectangleF CellALRect = new RectangleF(CellPvRect.Left + CellPvRect.Width, ItemRect.Top, AlamSize, H);

            Color gbclor;

            if (grp.AlarmType == "AUTO") gbclor = Color.SkyBlue;
            else if (grp.AlarmType == "MANUAL") gbclor = Color.LightGray;
            else gbclor = Color.Pink;   //stop


            RectangleF TITLERECT = new RectangleF(ItemRect.Left, ItemRect.Top, ItemRect.Width, H - 2);
            g.FillRectangle(new SolidBrush(gbclor), TITLERECT);

            //DRAW UNDERLINE
            g.DrawLine(Pens.DimGray, TITLERECT.Left, TITLERECT.Top + TITLERECT.Height, TITLERECT.Left + TITLERECT.Width, TITLERECT.Top + TITLERECT.Height);
            //g.DrawRectangle(Pens.Red, TITLERECT.Left, TITLERECT.Top, TITLERECT.Width, TITLERECT.Height);

            g.DrawString("Cell", applyfont, Brushes.Black, CellNoRect.Left + CellNoRect.Width / 2 - CellNoSize.Width / 2, CellNoRect.Top + CellNoRect.Height / 2 - CellNoSize.Height / 2);

            Cellno = "PV";
            CellNoSize = g.MeasureString(Cellno, applyfont);
            g.DrawString(Cellno, applyfont, Brushes.Black, CellPvRect.Left + CellPvRect.Width / 2 - CellNoSize.Width / 2, CellPvRect.Top + CellPvRect.Height / 2 - CellNoSize.Height / 2);

            Cellno = "Alarm";
            CellNoSize = g.MeasureString(Cellno, applyfont);

            String SH = "H", SL = "L";
            if (grp.AlarmType == "AUTO")
            {
                SH = "+";
                SL = "-";
            }

            SizeF a, h, l;
            a = g.MeasureString("ALM", applyfont);
            h = g.MeasureString(SH, applyfont);
            l = g.MeasureString(SL, applyfont);

            //Single y = 0, x = 0;
            g.DrawString("ALM", applyfont, Brushes.Black, CellALRect.Left + CellALRect.Width / 2 - a.Width / 2, CellALRect.Top + CellALRect.Height / 4 - a.Height / 2);


            var alrecthl = new RectangleF(CellALRect.X, CellALRect.Y + CellALRect.Height / 2f, CellALRect.Width, CellALRect.Height / 2f);
            g.DrawString($"{SH}/{SL}", applyfont, Brushes.Black, alrecthl, new StringFormat
            {
                LineAlignment = StringAlignment.Center,
                Alignment = StringAlignment.Center
            });


            return new RectangleF[3] { CellNoRect, CellPvRect, CellALRect };
        }

        public String initmsg
        {
            get { return this._initmsg; }
            set
            {
                this._initmsg = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 화면에 표시되는 내용중 실제 셀의 내용이다. 세부내역은 이곳에서 작성됨
        /// </summary>
        /// <param name="g"></param>
        /// <param name="srcrect"></param>
        /// <param name="grp"></param>
        /// <param name="cidx"></param>
        private void DrawCellMatrix(Graphics g, RectangleF srcrect, CGROUP grp, short cidx, Boolean isprint)
        {
            try
            {
                Single height = (Single)srcrect.Height / grp.RowCount;    //지정한 갯수를 나눈다.            
                Single newx = srcrect.Left;

                for (short j = 0; j < grp.RowCount; j++)
                {
                    int idx = cidx * grp.RowCount + j;
                    Single newy = j * height;

                    RectangleF R =
                        new RectangleF(newx + 1, newy + srcrect.Top, grp.Cell_Title_Width + grp.Cell_Value_Width + grp.Cell_Alam_Width - 1, height - 2);

                    //번호와 영역을 재계산한다.
                    if (grp.Items.GetUpperBound(0) < idx)
                        Array.Resize(ref grp.itemarray, idx + 1);

                    //항목초기화
                    if (grp.Items[idx] == null)
                    {
                        grp.Items[idx] = new CITEM()
                        {
                            이름 = "--",
                            알람형태 = COMM.EALAMTYPE.사용안함,
                            alamh = -999,
                            alaml = -999,
                            aalamh = -999,
                            aalaml = -999,
                            alamv = -999,
                            Onset = false,
                            //firstsave = true,
                        };
                    }

                    CITEM Item = grp.Items[idx];

                    //Single Alamh=0,AlamL=0;
                    switch (Item.알람형태)
                    {
                        case COMM.EALAMTYPE.그룹설정을따름:
                            Item.alamh = grp.HIGH;
                            Item.alaml = grp.LOW;
                            Item.aalamh = grp.UP;
                            Item.aalaml = grp.UP;
                            //Item.alamv = -999;
                            break;
                        case COMM.EALAMTYPE.사용안함:
                            Item.alamh = -999;
                            Item.alaml = -999;
                            Item.aalamh = -999;
                            Item.aalaml = -999;
                            Item.alamv = -999;
                            break;

                        case COMM.EALAMTYPE.개별알람자동: //원본값을 그대로 사용한다.
                        case COMM.EALAMTYPE.개별알람: //원본값을 그대로 사용한다.
                            break;
                    }

                    Item.row = j;
                    Item.column = cidx;
                    Item.rect = R;

                    //기본배경색상(그라디언트)
                    Color BackColor = Color.WhiteSmoke;
                    var ApplyFont_CN = this.FONT_CN;// grp.셀글꼴;
                    var ApplyFont_CV = this.FONT_CV;
                    var ApplyFont_AV = this.FONT_AV;

                    if (isprint)
                    {
                        //출력상태에서는 작게 출력한다.
                        ApplyFont_CN = new Font(ApplyFont_CN.FontFamily, 6, FontStyle.Bold);
                        ApplyFont_CV = new Font(ApplyFont_CV.FontFamily, 6, FontStyle.Bold);
                        ApplyFont_AV = new Font(ApplyFont_AV.FontFamily, 6, FontStyle.Bold);
                    }

                    SizeF Fontsize_CN = g.MeasureString(Item.이름, ApplyFont_CN);
                    Single fnewx = R.Left + grp.Cell_Title_Width / 2 - Fontsize_CN.Width / 2;
                    Single fnewy = R.Top + R.Height / 2 - Fontsize_CN.Height / 2;

                    //알람발생여부초기화
                    Item._p_onalamh = Item.onalamh;     //이전값기록
                    Item._p_onalaml = Item.onalaml;     //이전값기록
                    Item.onalamh = false;               //현재알람초기화
                    Item.onalaml = false;               //현재알람초기화

                    //데이터표시
                    String ValueStr = "";
                    Item.CurValue = 0;
                    Item.CurValue1 = 0;   //140829 
                    String TimeStr = "";

                    //글자표시색상
                    Color BrColor = grp.셀글꼴색상;

                    //값의 저장소가 아에없다면?
                    //Boolean OverDATA = false;
                    if (this.Values == null)
                    {
                        Item.value2 = 0;
                        Item.CurValue = 0f;
                        Item.CurValue1 = 0f;
                        TimeStr = "Null Error";
                        BrColor = Color.LightGray;
                    }
                    else
                    {
                        Single Offset = 0;  //수동으로입력된 옾셋                 

                        if (!Item.사용)   //사용여부자체를 설정한다.
                        {
                            Item.value2 = 0;
                            Item.alamv = -999;
                            Item.CurValue = 0f; //미설정상태라면 값을 표시하지 않는다.
                            Item.CurValue1 = 0f;
                            TimeStr = "Not USE";
                            grp._unusedcount += 1;
                            BackColor = Color.LightGray;
                            BrColor = Color.Gray;
                        }
                        else //정상상태이다.
                        {
                            //Br = new SolidBrush(Color.Black);
                            Offset = Item.Offset;  //수동으로입력된 옾셋                      

                            var u1 = this.Values.GetUpperBound(0);
                            var u2 = this.Values.GetUpperBound(1);
                            var u3 = this.Values.GetUpperBound(2);
                            if (Item.idx_dev >= 0 && Item.idx_unit >= 0 && Item.idx_ch >= 0 &&
                                Item.idx_dev <= u1 && Item.idx_unit <= u2 && Item.idx_ch <= u3)
                            {
                                Item.value2 = this.Values[Item.idx_dev, Item.idx_unit, Item.idx_ch];    //측정값
                                TimeStr = this.Times[Item.idx_dev, Item.idx_unit, Item.idx_ch];         //측정시간
                            }
                            else
                            {
                                Item.value2 = 0;
                                TimeStr = "";
                            }

                            if (!Item.활성화) grp._disccount += 1;
                        }

                        //backgroun Color
                        if (Offset != 0) BrColor = Color.Blue;

                        //OVER DATA
                        if (Item.value2 >= 32000 || Item.value2 >= 65000)
                        {
                            Item._onalamover = true;
                            //CurValue = (Single)(Item.value2 / (Math.Pow(10, Item.decpos))) + Offset;

                            //Over aalam
                            if (TimeStr != "" && TimeStr != "Not USE" && Item._p_onalamover == false)
                            {
                                String alamtimes = DateTime.Parse(TimeStr).ToString("yy-MM-dd HH:mm:ss");
                                OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.OVER_ON, Item.알람형태, "--", Item.CurValue, Item.alamh, Item.alaml);
                                Item._p_onalamover = true;
                            }
                            Item.CurValue = 0f;
                            Item.CurValue1 = 0f;
                        }
                        else
                        {
                            Item._onalamover = false;

                            //if(Item.value2 != 9999)                               

                            //Over aalam
                            if (TimeStr != "" && TimeStr != "Not USE" && Item._p_onalamover == true)
                            {
                                String alamtimes = DateTime.Parse(TimeStr).ToString("yy-MM-dd HH:mm:ss");
                                OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.OVER_OFF, Item.알람형태, "--", Item.CurValue, Item.alamh, Item.alaml);
                                Item._p_onalamover = false;
                            }
                        }

                        //현재값
                        Item.CurValue = (Single)(Item.value2 / (Math.Pow(10, Item.decpos))) + Offset;
                        Item.CurValue1 = Item.CurValue; //데이터분주가 들어있지않은 값이다.

                        //140828 데이터분주값입력
                        if (datadiv != 0 && datadiv != 1)
                            Item.CurValue = (Single)(Item.CurValue / (Single)datadiv);


                        //최대,최소,합계VOLT 누적
                        if (Item.사용 && Item.활성화)
                        {
                            //if (Item.CurValue > 0) Debugger.Break();

                            if (Item.CurValue >= grp.Maxvolt)
                            {
                                grp._maxvolt = Item.CurValue;
                                grp._maxitem = Item;
                            }
                            if (Item.CurValue <= grp.Minvolt)
                            {
                                grp._minvolt = Item.CurValue;
                                grp._minitem = Item;
                            }
                            grp._sumvolt += Item.CurValue1; //분주되기전의 sum값 140829 
                            grp._sumvolt1 += Item.CurValue; //분주된후의 sum값 140829 
                        }

                        //알람활성화여부(오버데이터는 알람설정하지 않는다)
                        if (!Item._onalamover && Item.알람형태 != COMM.EALAMTYPE.사용안함 && Item.사용)
                        {
                            switch (Item.알람형태)
                            {
                                case COMM.EALAMTYPE.그룹설정을따름:
                                    if (grp.AlarmType == "AUTO")
                                    {

                                        if (Item.alamv == -999) Item.alamv = Item.CurValue;

                                        Item.적용형태 = "AUTO";
                                        if (Item.CurValue > Item.alamv)  //큰값이 있을경우 상한선을 확인한다.
                                        {
                                            Single gap = Math.Abs(Item.CurValue - Item.alamv);
                                            if (gap >= Math.Abs(Item.aalamh))
                                            {
                                                Item.onalamh = true;
                                                Item.onalaml = false;
                                                BackColor = Color.Orange; //BackColor0 = Color.LightPink;
                                            }
                                        }
                                        else if (Item.CurValue < Item.alamv)
                                        {
                                            Single gap = Math.Abs(Item.CurValue - Item.alamv);
                                            if (gap >= Math.Abs(Item.aalaml))
                                            {
                                                Item.onalamh = false;
                                                Item.onalaml = true;
                                                BackColor = Color.SkyBlue; //BackColor0 = Color.LightBlue;
                                            }
                                        }

                                    }
                                    else if (grp.AlarmType == "MANUAL")
                                    {
                                        Item.적용형태 = "MANUAL";
                                        if (Item.CurValue >= Item.alamh)
                                        {
                                            Item.onalamh = true;
                                            Item.onalaml = false;
                                            BackColor = Color.Orange;// BackColor0 = Color.LightPink;
                                        }
                                        else if (Item.CurValue < Item.alaml)
                                        {
                                            Item.onalamh = false;
                                            Item.onalaml = true;
                                            BackColor = Color.SkyBlue; //BackColor0 = Color.LightBlue;
                                        }
                                    }
                                    else
                                    {
                                        Item.적용형태 = "STOP";
                                        Item.onalamh = false;
                                        Item.onalaml = false;
                                    }
                                    break;
                                case COMM.EALAMTYPE.개별알람:
                                    Item.적용형태 = "MANUAL";
                                    if (Item.CurValue >= Item.alamh)
                                    {
                                        Item.onalamh = true;
                                        Item.onalaml = false;
                                        BackColor = Color.Orange; //BackColor0 = Color.LightPink;
                                    }
                                    else if (Item.CurValue < Item.alaml)
                                    {
                                        Item.onalamh = false;
                                        Item.onalaml = true;
                                        BackColor = Color.SkyBlue;// BackColor0 = Color.LightBlue;
                                    }

                                    break;
                                case COMM.EALAMTYPE.개별알람자동:
                                    Item.적용형태 = "AUTO";
                                    if (Item.alamv == -999) Item.alamv = Item.CurValue;

                                    if (Item.CurValue > Item.alamv)  //큰값이 있을경우 상한선을 확인한다.
                                    {
                                        Single gap = Math.Abs(Item.CurValue - Item.alamv);
                                        if (gap >= Math.Abs(Item.aalamh))
                                        {
                                            Item.onalamh = true;
                                            Item.onalaml = false;
                                            BackColor = Color.Orange; //BackColor0 = Color.LightPink;
                                        }
                                    }
                                    else if (Item.CurValue < Item.alamv)
                                    {
                                        Single gap = Math.Abs(Item.CurValue - Item.alamv);
                                        if (gap >= Math.Abs(Item.aalaml))
                                        {
                                            Item.onalamh = false;
                                            Item.onalaml = true;
                                            BackColor = Color.SkyBlue; //BackColor0 = Color.LightBlue;
                                        }
                                    }
                                    break;
                            }
                        }
                        //CurValue = (Single)Math.Round(CurValue, 3);
                    }


                    if (Item.활성화 && init == true && (Item.onalaml != Item._p_onalaml || Item._p_onalamh != Item.onalamh))  //이전과 알람상태가 변경된경우다.
                    {
                        String alamtimes;

                        if (TimeStr != "" && TimeStr != "Not USE") alamtimes = DateTime.Parse(TimeStr).ToString("yy-MM-dd HH:mm:ss");
                        else alamtimes = "";

                        Single AH, AL;
                        if (Item.적용형태 == "AUTO")
                        {
                            AH = Item.aalamh;
                            AL = Item.aalaml;
                        }
                        else
                        {
                            AH = Item.alamh;
                            AL = Item.alaml;
                        }
                        if (Item.onalamh && !Item._p_onalamh)
                        {

                            OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.HIGH_ON, Item.알람형태, Item.적용형태, Item.CurValue, AH, AL);
                            Item._p_onalamh = true;
                        }
                        else if (Item.onalaml && !Item._p_onalaml)
                        {
                            OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.LOW_ON, Item.알람형태, Item.적용형태, Item.CurValue, AH, AL);
                            Item._p_onalaml = true;
                        }
                        else if (!Item.onalamh && Item._p_onalamh)
                        {
                            OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.HIGH_OFF, Item.알람형태, Item.적용형태, Item.CurValue, AH, AL);
                            Item._p_onalamh = false;
                        }
                        else if (!Item.onalaml && Item._p_onalaml)
                        {
                            OnAlamChange(Item.idx, COMM.EALAMRAISETYPE.LOW_OFF, Item.알람형태, Item.적용형태, Item.CurValue, AH, AL);
                            Item._p_onalamh = false;
                        }
                    }

                    if (!Item.활성화 && Item.사용)
                    {
                        Item._onalamover = false;
                        Item._onalamh = false;
                        Item._onalaml = false;
                        Item._p_onalamh = false;
                        Item._p_onalaml = false;
                        Item._p_onalamover = false;

                        BackColor = Color.WhiteSmoke;
                        //BackColor0 = Color.LightGray;
                        BrColor = (Color.Gray);
                    }

                    if (Item._onalamover)
                    {
                        ValueStr = $"{Item.CurValue:0.000}";
                        BrColor = (Color.DarkMagenta);
                        grp._errorcount += 1;
                    }
                    else if (!Item.활성화)
                    {
                        ValueStr = "--";
                    }
                    else ValueStr = $"{Item.CurValue:0.000}";

                    //초기화전상태라면 초기화된걸로 확인시킨다.
                    if (!Item.Onset) Item.Onset = true;

                    //Draw Cell Background
                    using (var br = new SolidBrush(BackColor))
                        g.FillRectangle(br, R);

                    //Border
                    using (var pen = new Pen(Color.LightGray))
                    {
                        g.DrawLine(pen, R.Left + grp.Cell_Title_Width, R.Top, R.Left + grp.Cell_Title_Width, R.Top + R.Height);
                        g.DrawLine(pen, R.Left + grp.Cell_Title_Width + grp.Cell_Value_Width, R.Top, R.Left + grp.Cell_Title_Width + grp.Cell_Value_Width, R.Top + R.Height);
                    }

                    //Cell 제목줄 표시
                    if (Item.사용)
                    {
                        Color cc;
                        if (Item.ismin) cc = Color.Blue; //Color.DarkBlue;
                        else if (Item.ismax) cc = Color.Red;
                        else cc = BrColor;

                        using (var br = new SolidBrush(cc))
                            g.DrawString(Item.이름, ApplyFont_CN, br, fnewx, fnewy);
                    }

                    Item.Value = ValueStr;
                    Item.mtime = TimeStr;


                    if (idx < grp._null_itemseq)
                    {
                        if (Item.사용 && Item.활성화)
                        {
                            grp._sumvoltA += Item.CurValue1;
                            if (Item.알람형태 != COMM.EALAMTYPE.사용안함)
                            {
                                if (NullbalanceRealValue) grp._null_valueL += Item.CurValue1;  //알람이 오프된경우라면 처리하지않게한다.
                                else grp._null_valueL += Item.CurValue;  //알람이 오프된경우라면 처리하지않게한다.

                            }
                        }
                    }
                    else
                    {
                        if (Item.사용 && Item.활성화)
                        {
                            grp._sumvoltB += Item.CurValue1;
                            if (Item.알람형태 != COMM.EALAMTYPE.사용안함)
                            {
                                if (NullbalanceRealValue) grp._null_valueR += Item.CurValue1;
                                else grp._null_valueR += Item.CurValue;
                            }
                        }
                    }

                    Fontsize_CN = g.MeasureString(ValueStr, ApplyFont_CV);
                    fnewx = R.Left + grp.Cell_Title_Width + grp.Cell_Value_Width / 2 - Fontsize_CN.Width / 2;
                    fnewy = R.Top + R.Height / 2 - Fontsize_CN.Height / 2;

                    if (this.WINDOW.Debug)
                        ValueStr += cidx.ToString();

                    //display value
                    if (Item.사용)
                    {
                        using (var Br = new SolidBrush(BrColor))
                            g.DrawString(ValueStr, ApplyFont_CV, Br, fnewx, fnewy);
                    }

                    //display alam                    
                    String AlamstrH = "";
                    String AlamstrL = "";
                    switch (Item.알람형태)
                    {
                        case COMM.EALAMTYPE.사용안함:    //직접사용안함
                            BrColor = Color.Gray;
                            AlamstrH = "OFF";
                            AlamstrL = "OFF";
                            break;

                        case COMM.EALAMTYPE.그룹설정을따름:
                            BrColor = Color.Black;

                            if (grp.AlarmType == "AUTO")
                            {
                                AlamstrH = Item.aalamh.ToString();
                                AlamstrL = Item.aalaml.ToString();
                            }
                            else if (grp.AlarmType == "MANUAL")
                            {
                                AlamstrH = Item.alamh.ToString();
                                AlamstrL = Item.alaml.ToString();
                            }
                            else   //그룹설정이 OFF된경우
                            {
                                AlamstrH = "OFF";
                                AlamstrL = "OFF";
                            }
                            break;

                        case COMM.EALAMTYPE.개별알람:
                            BrColor = (Color.Blue);
                            AlamstrH = "↑" + Item.alamh.ToString();
                            AlamstrL = "↓" + Item.alaml.ToString();
                            break;

                        case COMM.EALAMTYPE.개별알람자동:
                            BrColor = (Color.Magenta);
                            AlamstrH = "+" + Item.aalamh.ToString();
                            AlamstrL = "-" + Item.aalaml.ToString();
                            break;
                    }

                    Fontsize_CN = g.MeasureString(AlamstrH, ApplyFont_AV);
                    SizeF fontsize2 = g.MeasureString(AlamstrL, ApplyFont_AV);


                    fnewy = R.Top + R.Height / 2 - Fontsize_CN.Height / 2;

                    if (Item.사용)
                    {

                        if (Item.활성화)
                        {
                            if (Item._onalamh || Item._onalaml) grp._alamcount += 1;

                        }

                        Color SSBColor = BrColor;
                        if (!Item.활성화) SSBColor = Color.Gray;

                        using (var SSB = new SolidBrush(SSBColor))
                        {
                            fnewx = R.Left + grp.Cell_Title_Width + grp.Cell_Value_Width + (grp.Cell_Alam_Width / 2 - Fontsize_CN.Width) / 2;
                            g.DrawString($"{AlamstrH}/{AlamstrL}", ApplyFont_AV, SSB, fnewx, fnewy);
                        }
                    }

                    //Cell 테두리
                    ////g.DrawRectangle(new Pen(Color.Gray), R.Left, R.Top, R.Width - 1, R.Height);

                    //Cell Underbar 
                    g.DrawLine(Pens.Gray, R.Left, (R.Top + R.Height), R.Left + R.Width, (R.Top + R.Height));


                    //버튼할당을위한 값할당
                    this.Buttons.Add(new CButton(R, Item, EBUTTONTYPE.CELL));
                }
            }
            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;
            }
        }

        private void DrawDebugInfo(Graphics g)
        {

        }

        public void DrawGroup(Graphics g, CGROUP grp, Point margin, SizeF ItemSize, RectangleF WinRect, int 열번호, int 줄번호, Boolean isprint, bool IsVisible)
        {
            SizeF Fontsize;
            grp._minitem = null;
            grp._maxitem = null;

            try
            {
                //그룹내의정보를 설정
                Single newx = WinRect.Left + 열번호 * ItemSize.Width;
                Single newy = WinRect.Top + 줄번호 * ItemSize.Height;
                grp.R = new RectangleF(newx + 4, newy + 4, ItemSize.Width * grp.열병합 - 8, ItemSize.Height * grp.줄병합 - 8);

                //그룹타이틀
                newy = grp.R.Top;
                SizeF FontHeight = g.MeasureString(grp.이름, this.Font_Grouptitle);
                Single newheigth = (FontHeight.Height * 4);
                grp.Rect_Title = new RectangleF(grp.R.Left, newy, grp.R.Width, newheigth);
                newy += newheigth;

                //전체아이템너비
                newy += 0;
                newheigth = grp.R.Height + grp.R.Top - newy;
                grp.Rect_body = new RectangleF(grp.R.Left, newy, grp.R.Width, newheigth);

                //각 셀그룹의 영역을 설정함
                Single LastX = 0;
                int Gap = 6;
                grp.CellGroup_Width = (grp.Rect_body.Width / grp.ColumnCount) - ((Gap * (grp.ColumnCount - 1) / grp.ColumnCount));
                grp.CellGroup_Height = grp.Rect_body.Height;
                grp.CellGroup = new RectangleF[grp.ColumnCount];
                for (int ii = 1; ii <= grp.ColumnCount; ii++)
                {
                    if (ii == 1)
                    {
                        //처음개체
                        grp.CellGroup[ii - 1] = new RectangleF(grp.Rect_body.Left, grp.Rect_body.Top, grp.CellGroup_Width, grp.CellGroup_Height);
                        LastX = grp.CellGroup[ii - 1].Left + grp.CellGroup[ii - 1].Width + Gap;
                    }
                    else
                    {
                        //이후개체
                        grp.CellGroup[ii - 1] = new RectangleF(LastX, grp.Rect_body.Top, grp.CellGroup_Width, grp.CellGroup_Height);
                        LastX += grp.CellGroup_Width + Gap;
                    }
                }
                //
                RectangleF GrpRect = new RectangleF(grp.R.Left + margin.X, grp.R.Top + margin.Y, grp.R.Width, grp.R.Height);

                //그룹제목표시줄(title)
                Single MaxTitleWidth = 0;

                Color GroupBackColor = Color.DarkMagenta;
                if (this.WINDOW.IDX > 1) GroupBackColor = Color.DarkGreen;

                RectangleF newbackgrounrect = new RectangleF(grp.Rect_Title.Left + margin.X,
                        grp.Rect_Title.Top + margin.Y,
                        grp.Rect_Title.Width,
                        grp.Rect_Title.Height);

                if (IsVisible)
                {
                    using (var lgb = new LinearGradientBrush(newbackgrounrect, Color.Black, GroupBackColor, LinearGradientMode.BackwardDiagonal))
                        g.FillRectangle(lgb, newbackgrounrect);
                }


                Fontsize = g.MeasureString(grp.이름, this.font_grouptitle);
                newx = grp.Rect_Title.Left + 10 + margin.X;
                newy = grp.Rect_Title.Top + 7 + margin.Y;// -Fontsize.Height / 2 + grp.Rect_Title.Height / 2;
                                                         //g.DrawString(grp.이름 + this.Buttons.Count.ToString(), this.font_grouptitle, Brushes.Black, newx + 2, newy + 2);
                if (IsVisible)
                    g.DrawString(grp.이름, this.font_grouptitle, Brushes.Yellow, newx, newy);
                if (Fontsize.Width > MaxTitleWidth) MaxTitleWidth = Fontsize.Width;

                //그룹명클릭시에 그룹설정을 오픈하기위해서 버튼을 할당한다.
                RectangleF GrpTITLE = new RectangleF(newx, newy, Fontsize.Width, Fontsize.Height);
                if (IsVisible) this.Buttons.Add(new CButton(GrpTITLE, grp, EBUTTONTYPE.GROUP));
                //this.Buttonstag.Add(grp);
                //this.Buttonstype.Add(EBUTTONTYPE.GROUP);

                //셀의 갯수를 표시
                newy += Fontsize.Height + 1f;
                int usechcount = grp.Items.Length - grp._unusedcount;

                String Cellcount = "NO ITEMS";
                Cellcount = (grp.Items.Length - grp._unusedcount).ToString() + " Cell\n" +
                  grp._disccount.ToString() + " Disc.\n" +
                  (grp._errorcount).ToString() + " Over";


                using (Font CellCountfont = new Font(this.font_grouptitle.Name, this.font_grouptitle.Size - 5))
                {
                    Fontsize = g.MeasureString(Cellcount, CellCountfont);
                    //g.DrawString(Cellcount, CellCountfont, Brushes.Black, newx + 2, newy + 2);    //shadow
                    if (IsVisible) g.DrawString(Cellcount, CellCountfont, Brushes.Yellow, newx, newy);

                    newy += Fontsize.Height + 1.5f;
                    Fontsize = g.MeasureString("Print", CellCountfont);

                    //프린터버튼추가
                    RectangleF printbutrect = new RectangleF(newx, newy, (Single)(Fontsize.Width * 1.5), Fontsize.Height);
                    if (IsVisible) g.DrawString("Print", CellCountfont, Brushes.White, printbutrect.Left + (printbutrect.Width - Fontsize.Width) / 2, printbutrect.Top + (printbutrect.Height - Fontsize.Height) / 2);
                    if (IsVisible) g.DrawRectangle(Pens.White, new Rectangle((int)newx, (int)newy, (int)(Fontsize.Width * 1.5), (int)Fontsize.Height));
                    if (IsVisible) Buttons.Add(new CButton(printbutrect, grp, EBUTTONTYPE.PRINT));
                }

                if (Fontsize.Width > MaxTitleWidth) MaxTitleWidth = Fontsize.Width;

                //그룹의정보(header)
                Single HeaderWidth = (Single)(grp.Rect_Title.Width - MaxTitleWidth - 20);
                Single HeaderHeigth = (Single)(grp.Rect_Title.Height * 0.8);

                //헤더정보를 표시
                newx = grp.Rect_Title.Left + margin.X + (grp.Rect_Title.Width - HeaderWidth) - 5;
                newy = grp.Rect_Title.Top + margin.Y + (grp.Rect_Title.Height - HeaderHeigth) / 2;

                grp.Rect_Header = new RectangleF(newx, newy, HeaderWidth, HeaderHeigth);
                //그림자//g.FillRectangle(new SolidBrush(Color.FromArgb(50,Color.Black)), grp.Rect_Header.Left+2,grp.Rect_Header.Top+2,grp.Rect_Header.Width,grp.Rect_Header.Height);
                if (IsVisible) g.FillRectangle(new SolidBrush(Color.FromArgb(50, Color.Black)), grp.Rect_Header);
                if (IsVisible) g.DrawRectangle(Pens.Black, grp.Rect_Header.Left, grp.Rect_Header.Top, grp.Rect_Header.Width, grp.Rect_Header.Height);

                //표시영역(body)                
                //셀아이템하위 그룹을 표시한다.
                short gidx = 0;
                grp._maxvolt = -999;
                grp._minvolt = 999;
                grp._avgvolt = 0;
                grp._sumvolt = 0;
                grp._sumvolt1 = 0;
                grp._sumvoltA = 0;
                grp._sumvoltB = 0;
                grp._unusedcount = 0;   //미사용채널수
                grp._null_valueL = 0;
                grp._null_valueR = 0;
                grp._errorcount = 0;
                grp._disccount = 0;
                grp._alamcount = 0; //알람발생셀수
                grp._alamcountlb = 0;

                foreach (RectangleF R in grp.CellGroup)
                {
                    //해당그룹의 셀을 표시
                    DrawItem(g, grp, new RectangleF(R.Left + margin.X, R.Top + margin.Y, R.Width - 1, R.Height), gidx, isprint, IsVisible);

                    //border
                    if (IsVisible) g.DrawRectangle(Pens.DarkGray, (int)R.Left + margin.X, (int)R.Top + margin.Y, (int)R.Width - 1, (int)R.Height);
                    gidx += 1;

                    //g.DrawString(this.window.disconnecttime.ToString(), this.Font, Brushes.Red, R.Left,R.Top);
                }
                
                grp._amp = 0;
                if (grp._ampidx != "" && grp._ampidx != null)
                {
                    var buff = grp._ampidx.Split(new char[] { ',' });
                    int di1 = int.Parse(buff[0]);
                    int di2 = int.Parse(buff[1]);
                    int di3 = int.Parse(buff[2]);

                    try
                    {
                        grp._ampdecpos = 3;
                        grp._amp = (float)(this.Values[di1, di2, di3 - 1] / (Math.Pow(10, grp._ampdecpos)));
                    }
                    catch
                    {
                        grp._amp = -1f;
                    }
                }

                if (usechcount == 0) grp._avgvolt = 0;
                else grp._avgvolt = grp.Sumvolt1 / usechcount;

                Single nullvalue = (grp._null_valueR - grp._null_valueL) + grp.NullBalanceOffset;
                String HeaderStr = $"▣ SUM : {grp.Sumvolt:#0.0}";
                if (Sumab)
                {
                    HeaderStr += $"\n▣ SUMA : {grp._sumvoltA:#0.000}";
                    HeaderStr += $"\n▣ SUMB : {grp._sumvoltB:#0.000}";
                }
                HeaderStr += $"\n▣ AVG : {grp.Avgvolt:#0.000}";
                if (!Sumab)
                {
                    HeaderStr += $"\n▣ MAX : {grp.Maxvolt:#0.000}";
                    HeaderStr += $"\n▣ MIN : {grp.Minvolt:#0.000}";
                }

                //집계데이터출력                                                        
                Fontsize = g.MeasureString(HeaderStr, this.font_header);
                newy = grp.Rect_Header.Top + 5; // -Fontsize.Height / 2 + grp.Rect_Header.Height / 2;
                if (IsVisible) g.DrawString(HeaderStr, this.font_header, Brushes.White, grp.Rect_Header.Left + 5, newy);

                //NB데이터와의 SPLITER
                newx = grp.Rect_Header.Left + 5 + Fontsize.Width + 5;
                Single SpliiterY = newy;
                Single SplliterH = newy + grp.Rect_Header.Height - (newy - grp.Rect_Header.Top) * 2;
                if (IsVisible) g.DrawLine(Pens.Black, newx, newy, newx, SplliterH);

                //NB데이터출력
                newx += 3;

                //NB용 알람표시
                Single imgwidth = 30;
                Single imgheight = 30;
                if (!(grp.nbh == 0 && grp.nbl == 0))
                {
                    if (Math.Abs(nullvalue) > grp.nbl)
                    {
                        grp._nbalam_l = true;   //a2 on
                        if (!grp._pre_nbalam_l)
                        {
                            if (grp.AlarmType != "STOP") OnAlamChange(grp.IDX, COMM.EALAMRAISETYPE.A2_ON, COMM.EALAMTYPE.사용안함, "NB", (Single)nullvalue, (Single)grp.nbl, (Single)grp.nbl);
                            grp._pre_nbalam_l = true;
                            A2 = false;
                            blink2 = 0;
                        }
                        else   //게속발생중이다.
                        {
                            if (A2 == false)
                            {
                                if (blink2 >= 1)
                                {
                                    A2 = true;
                                    blink2 = 0;
                                }
                                else blink2 += 1;
                            }
                            else
                            {
                                if (blink2 >= 1)
                                {
                                    A2 = false;
                                    blink2 = 0;
                                }
                                else blink2 += 1;
                            }
                        }
                    }
                    else // a2영역보다는 낮으므로 a2를 해제한다.
                    {
                        grp._nbalam_l = false;  //a2 off

                        if (grp._pre_nbalam_l)  //이전에 a2알람이 켜져잇엇다면 알람을 끈다.
                        {
                            OnAlamChange(grp.IDX, COMM.EALAMRAISETYPE.A2_OFF, COMM.EALAMTYPE.사용안함, "NB", (Single)nullvalue, (Single)grp.nbl, (Single)grp.nbl);
                            grp._pre_nbalam_l = false;
                            blink1 = 0;
                        }


                        if (Math.Abs(nullvalue) > grp.nbh) //a1영역보다 크다면
                        {
                            grp._nbalam_h = true;
                            if (!grp._pre_nbalam_h) //이전에 발생안햇다면
                            {
                                if (grp.AlarmType != "STOP") OnAlamChange(grp.IDX, COMM.EALAMRAISETYPE.A1_ON, COMM.EALAMTYPE.사용안함, "NB", (Single)nullvalue, (Single)grp.nbh, (Single)grp.nbh);
                                grp._pre_nbalam_h = true;
                                blink1 = 0;
                            }
                            else
                            {
                                if (A1 == false)
                                {
                                    if (blink1 >= 1)
                                    {
                                        A1 = true;
                                        blink1 = 0;
                                    }
                                    else blink1 += 1;
                                }
                                else
                                {
                                    if (blink1 >= 1)
                                    {
                                        A1 = false;
                                        blink1 = 0;
                                    }
                                    else blink1 += 1;
                                }
                            }
                        }
                        else   //둘다적용안되는구역이다.
                        {
                            grp._nbalam_h = false;  //a1 off
                            if (grp._pre_nbalam_h)  //이전에 a1알람이 켜져잇엇다면 알람을 끈다.
                            {
                                OnAlamChange(grp.IDX, COMM.EALAMRAISETYPE.A1_OFF, COMM.EALAMTYPE.사용안함, "NB", (Single)nullvalue, (Single)grp.nbl, (Single)grp.nbl);
                                grp._pre_nbalam_h = false;
                            }
                            blink1 = 0;
                            blink2 = 0;
                        }
                    }
                }

                if (grp.AlarmType == "STOP")    //그룹설정이 OFF인경우에는 모두 끈다.
                {
                    grp._nbalam_h = false;
                    grp._nbalam_l = false;
                }

                Bitmap B1;
                if (grp._nbalam_h)
                {
                    grp._alamcountlb += 1;
                    if (A1) B1 = vmsnet.Properties.Resources.Orange_Ball;
                    else B1 = vmsnet.Properties.Resources.Red_Ball;
                }
                else B1 = vmsnet.Properties.Resources.Blue_Ball;

                Bitmap B2;
                if (grp._nbalam_l)
                {
                    grp._alamcountlb += 1;
                    if (A2) B2 = vmsnet.Properties.Resources.Orange_Ball;
                    else B2 = vmsnet.Properties.Resources.Red_Ball;
                }
                else B2 = vmsnet.Properties.Resources.Blue_Ball;

                if (!isprint || WINDOW.CoulumnCount <= 2)
                {
                    if (IsVisible) g.DrawImage(B1, newx + 25 * 0, newy, imgwidth, imgheight);
                    Fontsize = g.MeasureString("A1", alamfont);
                    if (IsVisible) g.DrawString("A1", alamfont, Brushes.Black, (newx + 25 * 0) + imgwidth / 2 - Fontsize.Width / 2, newy + imgheight / 2 - Fontsize.Height / 2);

                    if (IsVisible) g.DrawImage(B2, newx + 25 * 1, newy, imgwidth, imgheight);
                    Fontsize = g.MeasureString("A2", alamfont);
                    if (IsVisible) g.DrawString("A2", alamfont, Brushes.Black, (newx + 25 * 1) + imgwidth / 2 - Fontsize.Width / 2, newy + imgheight / 2 - Fontsize.Height / 2);

                    //ADJEST
                    if (IsVisible) g.DrawImage(vmsnet.Properties.Resources.Clear_Green_Button, newx + 25 * 0, newy + 2 + imgheight, imgwidth, imgheight);
                    if (IsVisible) Buttons.Add(new CButton(new RectangleF(newx + 25 * 0, newy + 2 + imgheight, imgwidth, imgheight), grp, EBUTTONTYPE.NBZERO));

                    //RESET
                    if (IsVisible) g.DrawImage(vmsnet.Properties.Resources.Backup_Green_Button, newx + 25 * 1, newy + imgheight, imgwidth, imgheight);
                    if (IsVisible) Buttons.Add(new CButton(new RectangleF(newx + 25 * 1, newy + imgheight, imgwidth, imgheight), grp, EBUTTONTYPE.NBRESET));
                }

                //끝부분에 KA 를 표시한다.
                String Kaunit = grp._ampunit;// +grp._ampidx.ToString() + "/" + grp._ampdecpos.ToString();
                
                /* 작성자: 이재웅, 작성일: 2024-12-04, 작성내용: {전류량[KA] × 100} 변경  */
                String MaxKA = (grp._amp * 10f).ToString("#0.00");

                //Console.WriteLine($"{grp.이름} : {MaxKA} {grp._ampunit}");
                //if (grp.이름 == "EL2500E") MaxKA = "0.01";


                /**************************************************************************************/
                /* 작성자: 이재웅, 작성일: 2024-11-19, 작성내용: 8개 전해조의 합산 전류량[KA] Modbus 전송 부분 */
                /**************************************************************************************/

                //using (var client = new JdModbus("COM3", new RtuConfigure() { BaudRate = 115200 }))
                //{
                //    client.Connect();

                //    ushort[] grpKA = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 전력량 배열 (예시)
                //    byte slaveaddr = 3;
                //    client.WriteHoldingRegisters(RelayControl.mKA, grpKA, slaveaddr);
                //}

                /**************************************************************************************/

                Fontsize = g.MeasureString(MaxKA, FONT_KA);
                SizeF FontSizeu = g.MeasureString(Kaunit, FONT_KU);
                RectangleF KARECT = new RectangleF(grp.Rect_Header.Left + grp.Rect_Header.Width - Fontsize.Width, grp.Rect_Header.Top + (grp.Rect_Header.Height - Fontsize.Height), Fontsize.Width, Fontsize.Height);
                if (EnableKA == false)
                    KARECT = new RectangleF(grp.Rect_Header.Right, KARECT.Top, 0, 0);
                //g.FillRectangle(Brushes.Black, KARECT);
                if (IsVisible && KARECT.IsEmpty == false)
                {
                    g.DrawString(MaxKA.ToString(), FONT_KA, Brushes.Black, KARECT.Left + 2, KARECT.Top + 2);
                    g.DrawString(MaxKA.ToString(), FONT_KA, Brushes.Yellow, KARECT.Left, KARECT.Top);
                    g.DrawString(Kaunit, FONT_KU, Brushes.Yellow, KARECT.Left + KARECT.Width - FontSizeu.Width - 10, KARECT.Top - 10);
                }

                //NullBalance Display
                if (!isprint || WINDOW.CoulumnCount <= 2)
                {
                    newx = newx + 30 * 2;
                    String nullstring = nullvalue.ToString("#0.00");
                    RectangleF rect_nullb = new RectangleF(newx, newy, KARECT.Left - newx, grp.Rect_Header.Height - 10);
                    Fontsize = g.MeasureString(nullstring, FONT_NB);
                    Single center = rect_nullb.Left + rect_nullb.Width / 2;
                    Single px = ((rect_nullb.Width / 2) * Math.Abs(nullvalue) / 5);
                    if (nullvalue < -5)
                    {
                        px = (rect_nullb.Width * (0) / (5 + 5)) - 1;
                    }
                    else if (nullvalue > 5)
                    {
                        px = (rect_nullb.Width * (5 + 5) / (5 + 5)) - 1;
                    }
                    else px = (rect_nullb.Width * (nullvalue + 5) / (5 + 5)) - 1;

                    Color nbcolor = Color.White;
                    if (grp._nbalam_h) nbcolor = Color.Orange;
                    else if (grp._nbalam_l) nbcolor = Color.Red;
                    else nbcolor = Color.White;

                    if (IsVisible) g.DrawString(nullstring, FONT_NB, new SolidBrush(Color.FromArgb(230, nbcolor)), newx + rect_nullb.Width / 2 - Fontsize.Width / 2, rect_nullb.Top + rect_nullb.Height / 2 - Fontsize.Height / 2);


                    Color NewSBColor;
                    if (grp.NullBalanceOffset != 0) NewSBColor = Color.FromArgb(180, Color.DarkBlue);
                    else NewSBColor = Color.FromArgb(180, Color.Red);
                    using (SolidBrush NewSB = new SolidBrush(NewSBColor))
                    {
                        if (IsVisible) g.DrawLine(Pens.Red, px + rect_nullb.Left, rect_nullb.Top, px + rect_nullb.Left, rect_nullb.Top + rect_nullb.Height);
                        if (IsVisible) g.DrawLine(Pens.Red, px + rect_nullb.Left + 1, rect_nullb.Top, px + rect_nullb.Left + 1, rect_nullb.Top + rect_nullb.Height);
                        PointF[] sqp = new PointF[3];
                        sqp[0] = new PointF(px + rect_nullb.Left - 10, rect_nullb.Top);
                        sqp[1] = new PointF(px + rect_nullb.Left, rect_nullb.Top + 10);
                        sqp[2] = new PointF(px + rect_nullb.Left + 10, rect_nullb.Top);
                        if (IsVisible) g.FillPolygon(Brushes.Red, sqp);
                        NewSB.Dispose();
                    }

                    //null balance 테두리 그림
                    if (IsVisible) g.DrawRectangle(Pens.Orange, rect_nullb.Left, rect_nullb.Top, rect_nullb.Width, rect_nullb.Height);
                    var term = rect_nullb.Width / 10f;
                    for (int i = 1; i < 10; i++)
                    {
                        var x = rect_nullb.Left + (i * term);
                        var y = (rect_nullb.Top + (rect_nullb.Height * 0.8f));
                        var y2 = rect_nullb.Top + rect_nullb.Height;

                        if (IsVisible) g.DrawLine(Pens.Orange, x - 1, y, x - 1, y2);
                        //String str = (i - 5).ToString();
                    }

                    newx = newx + rect_nullb.Width + 3;
                    if (IsVisible) g.DrawLine(Pens.Black, newx, SpliiterY, newx, SplliterH);
                }
                else
                {
                    if (IsVisible)
                    {
                        String nullstring = "NB=" + nullvalue.ToString("#0.00");
                        using (var nullbfont = new Font(FONT_NB.FontFamily, 10))
                        {
                            g.DrawString(nullstring, nullbfont, SB_White_200, KARECT.Left, KARECT.Top - 10);
                        }
                    }
                }

                //현그룹의 테두리 그림                    
                //g.DrawRectangle(Pens.Black, (int)grp.R.Left, (int)grp.R.Top, (int)grp.R.Width+1, (int)grp.R.Height);
                //grp.Showinfo = true;

                //추가헤더정보
                if (!isprint && grp.Showinfo && IsVisible)
                {
                    using (var ifont = new Font(this.font_header, FontStyle.Bold))
                    {
                        String nbstr = "▣ NULL BALANCE ▣";
                        nbstr += "\n\n▶L:" + grp._null_valueL.ToString("#0.00") + "v";
                        nbstr += ",R:" + grp._null_valueR.ToString("#0.00") + "v";
                        nbstr += "    ▶DEV(R-L):" + (grp._null_valueR - grp._null_valueL).ToString("#0.00") + "v";
                        nbstr += "    ▶Adj. Offset:" + (grp.NullBalanceOffset).ToString("#0.00") + "v";
                        nbstr += "\n\n▶Null Balance : " + (grp._null_valueR - grp._null_valueL + grp.NullBalanceOffset).ToString("#0.00") + "v";
                        nbstr += "\n▶Null Balance Alarm: A1(" + grp.nbh.ToString("#0.00") + "),A2(" + grp.nbl.ToString("#0.00") + ")";
                        nbstr += "\n\n * 막대그래프의 구간은 -5v ~ 5v 입니다.";
                        nbstr += "\n * 막대그래프의 색상이 하늘색일경우에는 Adj.Offset 이 적용된 값입니다.";

                        SizeF fs = g.MeasureString(nbstr, ifont);
                        Single tx, ty, tw, th, Margin = 0;

                        tx = grp.Rect_Header.Left + 10;
                        ty = grp.Rect_Header.Top + grp.Rect_Header.Height - 6;
                        tw = (fs.Width * 1.1f);
                        th = (fs.Height * 1.1f + 60);

                        RectangleF infoboxb = new RectangleF(tx + 1, ty + 1, tw, th);
                        RectangleF infoboxf = new RectangleF(tx, ty, tw, th);
                        RectangleF intoboxs = new RectangleF(tx + Margin + 3, ty + Margin, tw - (Margin * 2), th - (Margin * 2));

                        //SolidBrush BRB = new SolidBrush(Color.FromArgb(150, Color.Black));
                        //SolidBrush BRF = new SolidBrush(Color.FromArgb(200, Color.White));

                        g.FillRectangle(SB_Black_150, infoboxb);
                        g.FillRectangle(SB_White_200, infoboxf);

                        g.DrawString(nbstr, ifont, Brushes.Black, intoboxs.Left + 1, intoboxs.Top + 3);
                        // g.DrawString(nbstr, this.font_header, Brushes.Orange, intoboxs.Left, intoboxs.Top + intoboxs.Height / 2 - fs.Height / 2);

                        Single Imagey = intoboxs.Top + fs.Height + 10;

                        g.DrawImage(vmsnet.Properties.Resources.Clear_Green_Button, intoboxs.Left, Imagey, imgwidth, imgheight);
                        g.DrawString("현재 Null Balance 값을 Adj.Offset 으로 설정하여 '0'이 되게 합니다.", ifont, Brushes.Black, intoboxs.Left + 35, Imagey + 5);

                        Imagey += 25;
                        g.DrawImage(vmsnet.Properties.Resources.Backup_Green_Button, intoboxs.Left, Imagey, imgwidth, imgheight);
                        g.DrawString("입력된 Adj.Offset 을 제거하여 원래의 Null Balance가 보이게 합니다.", ifont, Brushes.Black, intoboxs.Left + 35, Imagey + 5);

                        //BRF.Dispose();
                    }
                }

                _raise_alarm_count += grp._alamcount;
                _raise_alarm_nbcount += grp._alamcountlb;

                if (grp._maxitemp != null)
                {
                    grp._maxitemp.ismax = false;
                }
                if (grp._minitemp != null) grp._minitemp.ismin = false;
                if (grp._maxitem != null)
                {
                    grp._maxitem.ismax = true;
                    grp._maxitemp = grp._maxitem;
                }
                if (grp._minitem != null)
                {
                    grp._minitem.ismin = true;
                    grp._minitemp = grp._minitem;
                }
            }
            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;
            }
        }

        private void DrawGroups(Graphics g)
        {
            _raise_alarm_count = 0; //알람카운트 
            _raise_alarm_nbcount = 0;


            //윈도우크기를 설정해준다(개체의크기와 같다)
            if (!FullScreen)
            {
                //개별표시면모드 150313
                //현재화면에 1개의 그룹만 표시한다. 
                this.WINDOW._itemwidth = this.WindowRect.Width; // / this.window.CoulumnCount;
                this.WINDOW._itemheight = WindowRect.Height; // / this.window.RowCount;
            }
            else
            {
                //전체화면 모드이므로 지정된 갯수만큼 사이즈를 조절한다.
                this.WINDOW._itemwidth = WindowRect.Width / this.WINDOW.CoulumnCount;
                this.WINDOW._itemheight = WindowRect.Height / this.WINDOW.RowCount;
            }

          
            if (this.GROUPS.Length < 1)
            {
                g.DrawString("NO Group", this.Font, Brushes.Red, WindowRect.Left, WindowRect.Top);
            }
            else
            {
                //150313
                //if (FullScreen)
                //{
                var idx = 0;
                CGROUP infogrp = null;
                //if (GROUPS[0].이름 == "EL2500E") Debugger.Break();
                foreach (CGROUP grp in this.GROUPS)
                {
                    var visible = true;
                    var 열번호 = grp.열번호;
                    var 줄번호 = grp.줄번호;

                    if (FullScreen == false)
                    {
                        if (ShowIndex < 0 || ShowIndex > this.GROUPS.GetUpperBound(0)) visible = false;
                        else visible = (idx == ShowIndex);
                        열번호 = 0;
                        줄번호 = 0;
                    }

                    if (grp.Showinfo) infogrp = grp;
                    //else
                    //{
                        DrawGroup(g, grp, new Point(0, 0), new SizeF(WINDOW.ITEMWIDTH, WINDOW.ITEMHEIGHT), WindowRect, 열번호, 줄번호, false, visible);
                    //}

                    idx += 1;
                }

                if (infogrp != null) DrawGroup(g, infogrp, new Point(0, 0), new SizeF(WINDOW.ITEMWIDTH, WINDOW.ITEMHEIGHT), WindowRect, infogrp.열번호, infogrp.줄번호, false, true);

                //end group
                //}
                //else
                //{
                //    //개별표시모드이므로 지정된 index 그룹만 표시한다.
                //    if (ShowIndex < 0 || ShowIndex > this.GROUPS.GetUpperBound(0))
                //    {
                //        //인덱스값이 잘못된경우이므로 처리할 수 없다
                //        g.DrawString("개별그룹보기 인덱스 오류 index=" + ShowIndex.ToString(), this.Font, Brushes.Red, 100, 200);
                //    }
                //    else
                //    {
                //        CGROUP grp = this.GROUPS[ShowIndex];
                //        DrawGroup(g, grp, new Point(0, 0), new SizeF(WINDOW.ITEMWIDTH, WINDOW.ITEMHEIGHT), WindowRect, 0, 0, false);
                //    }

                //}
            }


            if (_raise_alarm_count > 0 || _raise_alarm_nbcount > 0) _raise_alarm = true;    //현재알람이 켜져야한다.
            else _raise_alarm = false;

            //g.DrawString(_raise_alarm_count.ToString(), this.font_grouptitle, Brushes.White, 100, 100);
            //g.DrawString(_raise_alarm_nbcount.ToString(), this.font_grouptitle, Brushes.White, 100, 200);

            //알람의강제오프기능이 활성화되었다면?
            //현재 알람이 활성화되어있다면 알람을 해제한다.
            //이전알람과비교해서 달라졌다면 알람을 발생시켜준다.
            //단 상태가 같은데 현재 알람카운터가 이전알람카운터보다 클경우에는  알람을 다시 발생한다.

            if (_raise_alarm && !_raise_alarm_pre) { RaiseAlarm(true); _raise_alarm_pre = true; }   //이전에는 발생안했다가 새로 발생한경우
            if (!_raise_alarm && _raise_alarm_pre) { RaiseAlarm(false); _raise_alarm_pre = false; } //이전에 발생했다가 끄는경우
            if ((_raise_alarm_count > _pre_raise_alarm_count || _pre_raise_alarm_nbcount > _raise_alarm_nbcount) && _raise_alarm)    //알람이켜져잇는상태에서 알람카운터가 증가했다면 재 알람
            {
                RaiseAlarm(true);
                _raise_alarm_pre = true;
            }

            _pre_raise_alarm_count = _raise_alarm_count;
            _pre_raise_alarm_nbcount = _raise_alarm_nbcount;

        }



        /// Override OnPaint method
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            this.SuspendLayout();

            try
            {
                ///각영역을 새로 만들어야하는경우이다.
                if (bMakeRect)
                {
                    if (FullScreen)
                    {
                        WindowRect = new RectangleF(Padding.Left, Padding.Top, this.Width - Padding.Left - Padding.Right, this.Height - Padding.Top - Padding.Bottom);
                        //TrendRect = new RectangleF(0,0,0,0);
                        //  BarRect = new RectangleF(0, 0, 0, 0);
                        // NavRect = new RectangleF(0, 0, 0, 0);
                    }
                    else
                    {

                        WindowRect = new RectangleF(Padding.Left, Padding.Top, this.Width - Padding.Left - Padding.Right, this.Height - Padding.Top - Padding.Bottom);

                        //개별화면일경우
                        // int WindowWidth = SmallSize; // (int)((this.Width - Padding.Left - Padding.Right) * 0.4);
                        // int halfHeight = (int)((this.Height- Padding.Top - Padding.Bottom - 50-10) * 0.5);
                        // WindowRect = new RectangleF(Padding.Left, Padding.Top, WindowWidth, this.Height - Padding.Top - Padding.Bottom); 

                        //  NavRect = new RectangleF(WindowRect.Right + 10, WindowRect.Top, this.Width - WindowRect.Right - 10 - 1, 50);
                        //  TrendRect = new RectangleF(NavRect.Left, WindowRect.Top + 50, this.Width - WindowRect.Right - 10 - 1, halfHeight);
                        // BarRect = new RectangleF(NavRect.Left, TrendRect.Bottom + 10, TrendRect.Width, halfHeight);
                    }
                    bMakeRect = false;
                }

                Buttons = new List<CButton>(0);
                //Buttonstag = new List<Object>(0);
                //Buttonstype = new List<EBUTTONTYPE>(0);

                Boolean isnull = false;
                if (!init) isnull = true;
                else
                {
                    if (this.WINDOW == null || this.GROUPS == null) isnull = true;
                    if (isnull && this.GROUPS.Length == 0) isnull = true;
                }

                //표시아이템이없다면 오류를 낸다.
                if (isnull)
                {
                    //progress
                    int bw = (int)(this.Width * 0.8);
                    int bh = 30;

                    Rectangle rr = new Rectangle((this.Width - bw) / 2, (this.Height - bh) / 2, bw, bh);
                    Rectangle FullRect = new Rectangle(0, 0, this.Width, this.Height);
                    using (var gradientBrush = new LinearGradientBrush(FullRect, Color.White, Color.White, LinearGradientMode.Vertical))
                    {
                        e.Graphics.FillRectangle(gradientBrush, FullRect);
                        SizeF initsize = e.Graphics.MeasureString(initmsg, FONT_NANUM_20_BOLD);
                        e.Graphics.DrawString(initmsg, FONT_NANUM_20_BOLD, Brushes.Black, this.Width / 2 - initsize.Width / 2, this.Height / 2 - initsize.Height / 2 - bh - 20);
                    }

                    using (var lb = new LinearGradientBrush(rr, Color.LightBlue, Color.SkyBlue, LinearGradientMode.Vertical))
                    {
                        if (initpercent > 100) initpercent = 100;
                        int perc = (int)(rr.Width * initpercent / 100);
                        e.Graphics.FillRectangle(lb, new Rectangle(rr.Left, rr.Top, perc, rr.Height));
                        e.Graphics.DrawRectangle(Pens.DarkGray, rr);
                        e.Graphics.DrawRectangle(Pens.DarkGoldenrod, FullRect.Left, FullRect.Top, FullRect.Width - 1, FullRect.Height - 1);
                    }

                    this.ResumeLayout();
                    return;
                }

                // AntiAliasing
                e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                //그룹목록
                DrawGroups(e.Graphics);     /* GROUPS 화면 Display !!! */

                //monitor off display
                if (MonitorOn == false)
                {
                    e.Graphics.FillRectangle(SB_WhiteSmoke_200, DisplayRectangle);
                    e.Graphics.DrawString("MONITOR PAUSE", FONT_TAHOMA_80_BOLD, Brushes.Gray, DisplayRectangle, new StringFormat
                    {
                        Alignment = StringAlignment.Center,
                        LineAlignment = StringAlignment.Center,
                    });
                }

                //if (!FullScreen && ShowIndex >= 0)
                {
                    //트렌드뷰용채널데이터를 새로 만드는경우
                    //if (bMakeTrendViewChData) MakeTrendViewChData();

                    //마지막데이터와 2초의 차이가 있다면 데이터를 생성해준다.
                    DateTime NOwdate = DateTime.Now;
                    TimeSpan ts = NOwdate - LastNotifyTime;
                    if (ts.TotalSeconds >= NotifyDataSecond)
                    {
                        //알림
                        if (OnNotifyData != null) OnNotifyData(NOwdate);
                        LastNotifyTime = NOwdate;
                    }
                }
            }
            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;
            }
        }


        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            bMakeRect = true;
            //bMakeBarRect = true;
            Invalidate();
        }

        private int Check_GroupHeader(MouseEventArgs e, Boolean resetshowinfo = false)
        {
            if (this.GROUPS == null) return -1;
            for (int i = 0; i < this.GROUPS.Length; i++)
            {
                RectangleF rect = this.GROUPS[i].Rect_Header;
                if (resetshowinfo) this.GROUPS[i].Showinfo = false;

                //현재위치가 해당 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 int Check_Group(MouseEventArgs e)
        {
            if (this.GROUPS == null) return -1;
            for (int i = 0; i < this.GROUPS.Length; i++)
            {
                RectangleF rect = this.GROUPS[i].R;

                //현재위치가 해당 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 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].Rect;

                //현재위치가 해당 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 DispCtrl_MouseDown(object sender, MouseEventArgs e)
        {
            //현재마우스위치의 아이템을 찾ㄴ은다.
            int idx = check_buttons(e);
            if (idx != -1)
            {
                CButton But = Buttons[idx];
                //Object tt = this.Buttonstag[idx];
                //EBUTTONTYPE BT = this.Buttonstype[idx];

                switch (But.ButtonType)
                {
                    case EBUTTONTYPE.SELGROUP:
                        ShowIndex = ((CGROUP)But.Tag).IDX - 1;
                        //bMakeTrendViewChData = true;
                        //bMakeBarRect = true;
                        // MakeTrendViewChData();
                        break;
                    case EBUTTONTYPE.CELL:
                        OnClickCell((CITEM)But.Tag, e);
                        break;
                    case EBUTTONTYPE.GROUP:
                        OnClickGroup((CGROUP)But.Tag, e);
                        break;
                    case EBUTTONTYPE.NBRESET:
                        //MessageBox.Show("NBRESET" + idx.ToString()  );
                        ((CGROUP)But.Tag).NullBalanceOffset = 0;
                        this.OnSetNullbalance((CGROUP)(But.Tag), 0f);
                        this.Refresh();
                        break;
                    case EBUTTONTYPE.NBZERO:
                        //MessageBox.Show("NBZERO" + idx.ToString());
                        Single Offset = ((CGROUP)But.Tag)._null_valueR - ((CGROUP)But.Tag)._null_valueL;
                        ((CGROUP)But.Tag).NullBalanceOffset = -1 * Offset;
                        this.OnSetNullbalance((CGROUP)(But.Tag), (Single)(-1 * Offset));
                        this.Refresh();
                        break;

                    case EBUTTONTYPE.PRINT:
                        OnClickPrint(((CGROUP)But.Tag));
                        break;


                }
            }
        }

        private void DispCtrl_MouseMove(object sender, MouseEventArgs e)
        {
            //마우스위치를 저장한다.
            if (this.Mouseinfo == null) this.Mouseinfo = new CMouseinfo(new PointF(e.X - WindowRect.Left, e.Y - WindowRect.Top));
            else this.Mouseinfo.Position = new PointF(e.X - WindowRect.Left, e.Y - WindowRect.Top);

            //스피드체크영역이라면 마우스를 핸드로 변경한다.
            Mouseinfo.Hand = false;
            Mouseinfo.Cross = false;
            Mouseinfo.Move = false;

            //이동중인데 마우스가 눌려있다면 DRAG다
            if (e.Button == MouseButtons.Left) Mouseinfo.Drag = true;
            else Mouseinfo.Drag = false;

            if (Mouseinfo.Drag) //드래그중이다
            {
                if (Mouseinfo.DragType != EDRAGTYPE.GROUP) Mouseinfo.Move = true; //화면이동으로
                else Mouseinfo.Hand = true;   //잡은형태
            }

            if (this.FullScreen)
            { /* 작성자: 이재웅, 작성일: 2024-12-04, 작성내용: '전체보기' 상태일 때만 그룹헤더의 [Null Balance] 팝업 정보가 표시되도록 변경  */

                //마우스가 현재 그룹헤더에 존재하는지확인 (헤더에 올경우 특정 정보를 표시)
                if (!Mouseinfo.Hand && !Mouseinfo.Move)
                {
                    int tpidx = Check_GroupHeader(e, true);
                    if (tpidx != -1)
                    {
                        Mouseinfo.Hand = true;
                        this.GROUPS[tpidx].Showinfo = true;
                        //this.showHeaderInfo = true;
                    }

                }
                //마우스가 현재 그룹에 존재하는지?
                if (!Mouseinfo.Hand && !Mouseinfo.Move)
                {
                    int tpidx = Check_Group(e);
                    if (tpidx != -1)
                    {
                        Mouseinfo.Hand = true;
                    }
                }
            }

            if (Mouseinfo.Hand) this.Cursor = Cursors.Hand;
            if (Mouseinfo.Cross) this.Cursor = Cursors.Cross;

            this.Refresh();
        }



    }       //end class

}   //end namespace