객체1,2의 데이터 통신에 대해서 질문드리고 싶습니다.

안녕하세요. 간단하게 풀어볼려고 해도 안 풀려서 질문드리게 되었습니다…ㅠ

public async Task NextLoadtodasmmisalarmDB()
{
    if (setotime != null)
    {
        try
        {
            connString = "host=localhost;Port=5432;Username=postgres;Password=dkenc;Database=mmis;";
            string query = $@"Select * From to_das_mmi_alarm where otime > @Setotime Order by otime desc";
            using (var conn = new NpgsqlConnection(connString))
            {
                await conn.OpenAsync();
                using (var cmd = new NpgsqlCommand(query, conn))
                {
                    cmd.Parameters.AddWithValue("@Setotime", setotime);


                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            var otime = reader.GetDateTime(0);

                            var alarm = new to_das_mmi_alarm()
                            {
                                Otime = otime,
                                Name = reader.GetString(1),
                                Value = reader.GetDouble(2),
                                A_tag_no = reader.GetString(3),
                                State = reader.GetBoolean(4),
                                Description = reader.GetString(5),
                                Source_sys_name = reader.GetString(6),
                            };

                            
                            setotime = otime;
                            updatedAlarms.Insert(0, alarm);

                            // 버튼용 구조체 만들기 아래는!
                            // 알람을 추가하거나 업데이트하기 전에 A_tag_no 기준으로 검색
                            var existingAlarm = updatedAlarms2.FirstOrDefault(a => a.A_tag_no == alarm.A_tag_no);

                            if (existingAlarm != null)
                            {
                                // 이미 존재하는 알람의 otime이 새 알람의 otime보다 이전인 경우 업데이트
                                if (existingAlarm.Otime < alarm.Otime)
                                {
                                    var updatedAlarm = new to_das_mmi_alarm()
                                    {
                                        Otime = alarm.Otime,
                                        Name = alarm.Name,
                                        Value = alarm.Value,
                                        A_tag_no = alarm.A_tag_no,
                                        State = alarm.State,
                                        Description = alarm.Description,
                                        Source_sys_name = alarm.Source_sys_name,
                                       
                                        Isview = alarm.Isview,

                                    };
                                    var index = updatedAlarms2.IndexOf(existingAlarm);
                                    if (index != -1)
                                    {
                                        updatedAlarms2[index] = updatedAlarm;
                                    }
                                }
                            }
                            else
                            {
                                // 새 A_tag_no의 알람이면 그냥 추가
                                updatedAlarms2.Add(alarm);
                            }
                        }
                    }
                }
            }
            Dispatcher.Invoke(() =>
            {

                qwe.ItemsSource = updatedAlarms2;

            });
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

}

public void SetupTimer()
{
    _timer = new DispatcherTimer();
    _timer.Interval = TimeSpan.FromSeconds(1);
    _timer.Tick += Timer_Tick;

    _timer.Start();
}
       
private void Timer_Tick(object sender, EventArgs e)
{
    foreach (var alarm in updatedAlarms)
    {
        alarm.Isview = !alarm.Isview; // 현재 값의 반대로 설정
    }

}

저의 질문에 관련된 핵심 코드라고 생각되는 것을 올립니다.

to_das_mmi_alarm 클래스는 ObservableCollection타입이며, INotifyPropertyChanged가 구현되어있는 상태입니다.

isview라는 칼럼은 to_das_mmi_alarm 클래스에서 설정해둔 bool타입 변수입니다.
의도한 것은 1초마다 타이머에 의해 true,false로 값이 변동이 되고, 디버깅하여 잘 작동하는 것을 확인하였습니다.

DB에서 받아온 값들을 전체 조회하고 꾸준히 누적시켜서 쌓는 구조체가 UpdatedAlarms입니다.
이 UpdatedAlarms에서 a_tag_no별 최신시간별 한개의 행만을 가지고 있는 것이 UpdatedAlarms2입니다.

코드를 실행하여 외부기기로부터 값이 꾸준히 DB에 쌓이게 되고, 이 DB의 데이터는 UpdatedAlarms에 전체 누적으로 쌓입니다.
UpdatedAlarms2에는 a_tag_no별로 최신시간에 해당하는 값이 들어오면 존재하던 행이 대체되어 매핑이 됩니다.

여기서 오류가 발생합니다.
초기 실행시에는 무리 없이 updatedAlarms2에서 updatedAlarms의 isview칼럼의 값을 가져옵니다. true,false로 꾸준히 변동되는 값을 매핑해서 가져옵니다.
그러나 새로운 값이 들어와 값이 대체되게 되면, 그때 당시에 해당하는 값을 가져올 뿐, 주기성으로 바뀌는 값에 대해서는 반응을 하지 못합니다.

저는 꾸준히 주기성으로 바뀌는 isview의 값(true,false)을 가져오고 싶습니다.

제가 시도해본 작업으론

  1. isview = updatedAlarms[1].Isview; 로 바꾸어 직접참조 (?)도 해보았고,
  2. 타이머 메서드에서 foreach문을 하나 더 만들어 사용도 해보았습니다.

위와 같이 변경해볼려고 하였는데 잘 안되었습니다. 혹시 좋은 방법이 있는지 알려주시면 너무 너무 감사하겠습니다 ㅜ!


private void Timer_Tick(object sender, EventArgs e)
{
foreach (var alarm in updatedAlarms2)
{
alarm.Isview = !alarm.Isview; // 현재 값의 반대로 설정
}

}
요로케 바꾸면 제가 원하는 기능은 제대로 작동하지만,저는 근본적으로 하나의 전역 값(?)을 여러 구조체에서 공통적으로 참조하고 싶습니다.

알려주시면 열심히 찾아보고 배우겠습니다 ㅜ

to_das_mmi_alarm 도 올려주실수있으실까요?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace button_binding_practice
{

public class to_das_mmi_alarm : INotifyPropertyChanged
    {

        private DateTime _Otime;
        private string _Name;
        private double _Value;
        private string _A_tag_no;
        private bool _State;
        private string _Description;
        private string _Source_sys_name;
        private int _Priority;
        private string _Responsibility;
        private double _Setpoint;
        private string _Setpoint_unit;
        private string _Dcs_md;
        private string _Dcs_ms;
        private bool _Ack;


        private Int64 _No;
        private int _Light;
        private bool _IsBlinking; // 깜박임 상태 관리
        private bool _Reset;
        private bool _IsYellowBlinking; // 노란색 깜박임 상태 관리
        private bool _Isview; //reset버튼 관련
        private bool _Isview_2; //알람에 떠오르냐 마냐의 최종 검증
        private string _Now_state;

    public DateTime Otime
        {
            get => _Otime;
            set => SetProperty(ref _Otime, value);
        }
        public string Name
        {
            get => _Name;
            set => SetProperty(ref _Name, value);
        }
        public double Value
        {
            get => _Value;
            set => SetProperty(ref _Value, value);
        }
        public string A_tag_no
        {
            get => _A_tag_no;
            set => SetProperty(ref _A_tag_no, value);
        }
        public bool State
        {
            get => _State;
            set
            {
                if (SetProperty(ref _State, value))
                {
                    OnPropertyChanged(nameof(Light)); // Light에 대한 변경 알림
                }
            }
        }
        public string Description
        {
            get => _Description;
            set => SetProperty(ref _Description, value);
        }
        public string Source_sys_name
        {
            get => _Source_sys_name;
            set => SetProperty(ref _Source_sys_name, value);
        }
        public int Priority
        {
            get => _Priority;
            set => SetProperty(ref _Priority, value);
        }
        public string Responsibility
        {
            get => _Responsibility;
            set => SetProperty(ref _Responsibility, value);
        }
        public double Setpoint
        {
            get => _Setpoint;
            set => SetProperty(ref _Setpoint, value);
        }
        public string Setpoint_unit
        {
            get => _Setpoint_unit;
            set => SetProperty(ref _Setpoint_unit, value);
        }
        public string Dcs_md
        {
        get => _Dcs_md;
        set => SetProperty(ref _Dcs_md, value);
        }
        public string Dcs_ms
        {
            get => _Dcs_ms;
            set => SetProperty(ref _Dcs_ms, value);
        }
        public bool Ack
        {
            get => _Ack;
        set
        {
            if (SetProperty(ref _Ack, value))
            {
                OnPropertyChanged(nameof(Light));
            }
        }
    }
        public Int64 No
        {
            get => _No;
            set => SetProperty(ref _No, value);
        }
        public bool Isview
        {
            get => _Isview;
            set => SetProperty(ref _Isview, value);
        }
        public bool Isview_2
        {
            get => _Isview_2;
            set => SetProperty(ref _Isview_2, value);
        }
    public string Now_state
    {
        get => _Now_state;
        set => SetProperty(ref _Now_state, value);
    }
    
        public int Light
        {
        get
        {
            // 기존의 Light 계산 로직을 유지합니다.
            int calculatedLight = 0;
            if (Now_state == "N") calculatedLight = 3;
            else if (Now_state == "C")  calculatedLight = 0;

            else if (Now_state == "A" && !State) calculatedLight = 2;
            else if (Now_state == "A" && State) calculatedLight = 1;

            // Light 값이 3일 때 Isview의 값에 따라 Light 값을 조정합니다.
            // Isview가 true면 Light를 1로, false면 0으로 설정할 수 있습니다.
            // 이 부분은 Isview에 따른 Light 값의 조정 예시입니다.
            if (calculatedLight == 3)
            {
                return Isview ? 4 : 5; // Isview가 true면 1, false면 0을 반환합니다.
            }
            else
            {
                return calculatedLight; // 그 외의 경우에는 계산된 Light 값을 그대로 사용합니다.
            }
        }
        set => SetProperty(ref _Light, value);



        

    }


    // 기존 속성들...

    public void ToggleBlinkState()
        {
            _IsBlinking = !_IsBlinking;
            OnPropertyChanged(nameof(IsBlinking));
        }

        public bool IsBlinking
        {
            get => _IsBlinking;
            set
            {
                if (_IsBlinking != value)
                {
                    _IsBlinking = value;
                    OnPropertyChanged(nameof(IsBlinking));
                }
            }
        }
        public bool Reset
        {
            get => _Reset;
        set
        {
            if (SetProperty(ref _Reset, value))
            {
                OnPropertyChanged(nameof(Light)); // Light에 대한 변경 알림
            }
        }
    }
        public void ToggleYellowBlinkState()
        {
            _IsYellowBlinking = !_IsYellowBlinking;
            OnPropertyChanged(nameof(IsYellowBlinking));
        }

        public bool IsYellowBlinking
        {
            get { return _IsYellowBlinking; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Equals(storage, value))
            {
                return false;
            }

            storage = value;
            OnPropertyChanged(propertyName); 
            if (propertyName == nameof(Isview))
            {
                OnPropertyChanged(nameof(Light));
            }
        return true;
        }
       
    }

}```

본문에 올렸던 건 안 쓰는 변수명을 짤라서 했는데, 요기엔 전체 다 올리겠습니다.!

update2에 데이터 갱신시 데이터를 new하시는데, 기존 객체에 값만 넣어보셨나요?

앜 ㅋ 코드 이해했습니다 ㅋㅋㅋ

update1, update2에 데이터가 동일하게 안움직인다는 말씀이신거죠?
당연히 그렇죠ㅎㅎ
위에 말씀드린대로 객체 생성(new)하지말고 'existingAlarm’찾고 데이터 갱신하는 쪽으로 해보세요 ㅎㅎ

어느 부분인지 좀 더 길게 말씀해 주실 수 있을까요 ?

                                var updatedAlarm = new to_das_mmi_alarm()

요기를 말씀하시는건가요 …?
new를 빼고
var updatedAlarm = to_das_mmi_alarm() 라면; 오류가 떠서 실행이 안됩니다. ㅠ

아 그렇게 해서 하면 이전의 누적데이터가 같이 갱신이 되어버려서요
이전의 누적 데이터인 updatedAlarms에는 이전의 값들이 쭈욱 쌓이길 바라는데;

updatedAlarms2에 해당하는 값이 바꾸어서 들어가면 그 바뀌면서 들어간 값이
updatedAlarms에도 영향을 미치더라구요 ㅜ

var updatedAlarm = new to_das_mmi_alarm()
{
    Otime = alarm.Otime,
    Name = alarm.Name,
    Value = alarm.Value,
    A_tag_no = alarm.A_tag_no,
    State = alarm.State,
    Description = alarm.Description,
    Source_sys_name = alarm.Source_sys_name,       
    Isview = alarm.Isview,
};
var index = updatedAlarms2.IndexOf(existingAlarm);
if (index != -1)
{
    updatedAlarms2[index] = updatedAlarm;
}

이부분을

existingAlarm.Otime  = alarm.Otime;
existingAlarm.Name  = alarm.Name;

이렇게요 ㅎㅎ
하지만 그냥 이렇게쓰면, UI Thread가 update2를 사용하는것같으니

         Dispatcher.Invoke(() =>
            {
existingAlarm.Otime  = alarm.Otime;
existingAlarm.Name  = alarm.Name;
...
            });

아ㅎㅎ
그러면 updatealarms.insert(0, alarm)하잖아요.

updatedAlarms2[index] = updatedAlarm;
대신
updatedAlarms2[index] = alarm;

고고싱!

말씀해주신대로 해보고, 저에 맞게 가공을 해서 시도해본다고 늦었습니다.
UI상의 표현으로 되는거라면 이렇게도 되는군요 ! (잘 배웠습니다! )
근데 제가 지금 하고 있는 게… 구조체라는 모양을 (?) 유지해야 하는거라서요

private async Task Practice()
{
    // 기존 스타일을 지웁니다

    // 각 a_tag_no에 대한 가장 최신 otime을 가져옵니다
    var queryResult = updatedAlarms2
        .GroupBy(alarm => alarm.A_tag_no)
        .Select(group => group.OrderByDescending(alarm => alarm.Otime).First())
        .ToList();




    QueryResult.Clear();
    foreach (var alarm in queryResult)
    {
        QueryResult.Add(alarm);
    }
}

public void SetButtonsDataContext()
{
    foreach (var alarm in mainWindow.QueryResult)
    {
        //MessageBox.Show(alarm.Name + "와" + alarm.Ack + "와" + alarm.State);
        var button = FindButtonByName(alarm.A_tag_no);
        if (button != null)
        {
            button.DataContext = alarm;
        }
    }
}

public Button FindButtonByName(string name)
{
    foreach (var child in bubu.Children)
    {
        if (child is Button button && button.Content.ToString() == name)
        {
            return button;
        }
        else if (child is Panel panel)
        {
            // 자식이 Panel (예: StackPanel, Grid 등)인 경우, 그 내부를 순회
            foreach (var innerChild in panel.Children)
            {
                if (innerChild is Button innerButton && innerButton.Content.ToString() == name)
                {
                    // 중첩된 컨테이너 내의 버튼을 찾았을 경우
                    return innerButton;
                }
            }
        }
    }
    return null; // 일치하는 버튼이 없으면 null 반환
}

위의 코드와 같이 구조체에서의 한 행이 button의 dataContext로 매핑 시키고 있습니다.

그렇다보니 구조체의 형태를 유지하면서 가져가야됩니다…ㅠ
혹시 이와 관련된 방법으론 없을까요 ?

구조체가 갑자기 어디서 나오는건지 모르겠네요 ㅎㅎ
그리고 질문의 의도를 모르겠습니다^^

제 코드를 전체 올리는 것보다… 제가 막히는 부분에 대한 것만 해결을 하면 될 것이라고 생각을 해서 좀 짧게 올렸습니다.

제가 python으로 시작해서 gpt한테 물어보고 할 때 리스트나 클래스등으로 만들어진 것을 구조체라는 표현을 쓰기에 저렇게 썼는데 혼동을 드려서 죄송합니다.

맨 처음 글을 올렸을 시에는 앞서 답변해주신 것처럼 UI상에 나타나는 값이 제대로 바뀌어 표현이 되면 제가 하고자 했던 것에 적용이 쉽게 끝날 줄 알고, 일 부분의 코드를 올렸습니다.

그후 친절하게 답변을 달아주셔서 UI에 표현되는 방식은 해결이 되었으나, 원래 하고자 하였던 (바로 윗 댓글이 새롭게 3개정도의 메서드) 작업에는 적용이 되지 않았습니다.

원래 하고자 하였던 작업은 updatedAlarms2를 쿼리문을 사용하여 리스트화 시킨 후, 이 리스트의 각 행을 조건에 맞는 Button에 매핑 시키는 작업을 통해 버튼에 데이터 트리거를 작동시키는 것이였습니다. (이때의 데이터 트리거에 isview가 1초마다 true,false가 됨에 따라 다른 속성을 적용하고자 합니다.)

  1. 새로운 데이터가 추가되어 updatedAlarms2에 대체되어 적용되면서 updatedAlarms 객체에는 영향이 끼치지 않는다.
  2. updatedAlarms2를 리스트화 시켜 updatedAlarms2의 각 행의 a_tag_no의 값과 Button태그의 Content의 값이 같을 때 Button.DataContext로 매핑 시킨다.

위에 요구사항에 맞는 작업이 있을까요 ㅜㅜ?
부탁드리겠습니다.