diff --git a/SmartAquaViewer/Classes/Converter.cs b/SmartAquaViewer/Classes/Converter.cs
index 19c258c..a955903 100644
--- a/SmartAquaViewer/Classes/Converter.cs
+++ b/SmartAquaViewer/Classes/Converter.cs
@@ -31,4 +31,45 @@ namespace SmartAquaViewer.Classes
return value as string;
}
}
+
+ public class InverseBoolConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ => !(value is bool b && b);
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ => !(value is bool b && b);
+ }
+
+ public class EnergyToGreenHouseGasConverter : IValueConverter
+ {
+ // 2024년 배출계수 (kgCO₂/kWh)
+ private const double EmissionFactor = 0.4747;
+
+ ///
+ /// kWh → tCO₂ 변환
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is double kWh)
+ {
+ double tonCO2 = (kWh * EmissionFactor) / 1000.0;
+ return tonCO2.ToString("F2"); // 소수점 3자리
+ }
+ return null;
+ }
+
+ ///
+ /// tCO₂ → kWh 역변환 (Binding TwoWay 대응)
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (double.TryParse(value?.ToString(), out double tonCO2))
+ {
+ double kWh = (tonCO2 * 1000.0) / EmissionFactor;
+ return kWh;
+ }
+ return null;
+ }
+ }
}
diff --git a/SmartAquaViewer/View/EnegyView.xaml b/SmartAquaViewer/View/EnegyView.xaml
index 74850c4..bc56c08 100644
--- a/SmartAquaViewer/View/EnegyView.xaml
+++ b/SmartAquaViewer/View/EnegyView.xaml
@@ -6,8 +6,14 @@
xmlns:local="clr-namespace:SmartAquaViewer.View"
xmlns:control="clr-namespace:SmartAquaViewer.Controls"
xmlns:helper="clr-namespace:SmartAquaViewer.Helper"
+ xmlns:classes="clr-namespace:SmartAquaViewer.Classes"
mc:Ignorable="d"
d:DesignHeight="940" d:DesignWidth="1650">
+
+
+
+
+
@@ -85,7 +91,7 @@
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Center">
+ RowStyle="{StaticResource DataGridRowStyle}" ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
+ Style="{StaticResource MaterialDesignUserForegroundRadioButton}"
+ IsChecked="{Binding UseAverage, Converter={StaticResource InverseBoolConverter}, Mode=TwoWay}"/>
+ Style="{StaticResource MaterialDesignUserForegroundRadioButton}"
+ IsChecked="{Binding UseAverage, Mode=TwoWay}"/>
@@ -279,9 +287,22 @@
-
+
+
+
+
+
diff --git a/SmartAquaViewer/View/EnegyView.xaml.cs b/SmartAquaViewer/View/EnegyView.xaml.cs
index b279bff..4da873d 100644
--- a/SmartAquaViewer/View/EnegyView.xaml.cs
+++ b/SmartAquaViewer/View/EnegyView.xaml.cs
@@ -24,10 +24,5 @@ namespace SmartAquaViewer.View
{
InitializeComponent();
}
-
- private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
-
- }
}
}
diff --git a/SmartAquaViewer/View/GreenHouseView.xaml b/SmartAquaViewer/View/GreenHouseView.xaml
index c7368f7..150ba6e 100644
--- a/SmartAquaViewer/View/GreenHouseView.xaml
+++ b/SmartAquaViewer/View/GreenHouseView.xaml
@@ -4,25 +4,90 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SmartAquaViewer.View"
+ xmlns:helper="clr-namespace:SmartAquaViewer.Helper"
+ xmlns:classes="clr-namespace:SmartAquaViewer.Classes"
mc:Ignorable="d"
d:DesignHeight="940" d:DesignWidth="1650">
+
+
+
+
+
+
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
_useAverage; set { _useAverage = value; OnPropertyChanged(); } }
+
+ private bool _showMarkers;
public bool ShowMarkers { get => _showMarkers; set { _showMarkers = value; OnPropertyChanged(); } }
private bool _showLegends;
public bool ShowLegends { get => _showLegends; set { _showLegends = value; OnPropertyChanged(); } }
+ private bool _isDonut;
+ public bool IsDonut { get => _isDonut; set { _isDonut = value; OnPropertyChanged(); } }
+
public ICommand DrawGraphCommand { get; }
@@ -277,7 +283,7 @@ namespace SmartAquaViewer.ViewModel
GraphControlVM.SetStackAreaPlot(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends);
break;
case GraphType.PIE:
- GraphControlVM.SetPieChart(WaterQualityList, SelectedYFields);
+ GraphControlVM.SetPieChart(WaterQualityList, SelectedYFields, UseAverage, IsDonut);
break;
default:
break;
diff --git a/SmartAquaViewer/ViewModel/GraphControlViewModel.cs b/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
index 9249a95..e4ab0ed 100644
--- a/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
+++ b/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
@@ -594,6 +594,7 @@ namespace SmartAquaViewer.ViewModel
AngleSpan = 360,
StartAngle = 0,
StrokeThickness = 0.5,
+ InsideLabelFormat = "{1}\n {0:F2}",
InsideLabelPosition = 0.8,
OutsideLabelFormat = null // 라벨은 내부만
};
@@ -609,8 +610,6 @@ namespace SmartAquaViewer.ViewModel
Model.Title = $"설비별 {(useAverage ? "평균" : "합계")} 소비 비중";
Model.Series.Add(ps);
- Model.IsLegendVisible = true;
-
Model.InvalidatePlot(true);
}
diff --git a/SmartAquaViewer/ViewModel/GreenHouseGasViewModel.cs b/SmartAquaViewer/ViewModel/GreenHouseGasViewModel.cs
index 8b25718..ec84244 100644
--- a/SmartAquaViewer/ViewModel/GreenHouseGasViewModel.cs
+++ b/SmartAquaViewer/ViewModel/GreenHouseGasViewModel.cs
@@ -1,18 +1,218 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Input;
+using SmartAquaViewer.Controls;
+using SmartAquaViewer.DataAnalysis;
+using SmartAquaViewer.Model;
namespace SmartAquaViewer.ViewModel
{
public class GreenHouseGasViewModel : INotifyPropertyChanged
{
+ public GraphControlViewModel GraphControlVM { get; } = new GraphControlViewModel();
+ public ObservableCollection GraphTypes { get; }
+
+ public List WaterQualityList { get; set; }
+
+ private int _selectedGraphIndex = -1;
+ public int SelectedGraphIndex
+ {
+ get => _selectedGraphIndex;
+ set
+ {
+ if (_selectedGraphIndex != value)
+ {
+ _selectedGraphIndex = value;
+ OnPropertyChanged();
+ // 인덱스가 바뀌면 enum도 맞춰준다
+ if (value >= 0 && value < GraphTypes.Count)
+ SelectedGraphType = GraphTypes[value];
+ }
+ }
+ }
+
+ private GraphType _selectedGraphType;
+ public GraphType SelectedGraphType
+ {
+ get => _selectedGraphType;
+ set
+ {
+ if (_selectedGraphType != value)
+ {
+ _selectedGraphType = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(ShowXSelector));
+ RebuildFieldCandidates();
+
+ var idx = GraphTypes.IndexOf(value);
+ if (idx != -1 && idx != _selectedGraphIndex)
+ {
+ _selectedGraphIndex = idx;
+ OnPropertyChanged(nameof(SelectedGraphIndex));
+ }
+ }
+ }
+ }
+
+ private StepFieldKind _selectedKind = StepFieldKind.Sensor; // 기본값은 센서
+ public StepFieldKind SelectedKind
+ {
+ get => _selectedKind;
+ set
+ {
+ if (_selectedKind != value)
+ {
+ _selectedKind = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public bool ShowXSelector => SelectedGraphType == GraphType.SCATTER;
+
+ // [필드 후보 목록] 탭/시스템에 따라 달라짐
+ public ObservableCollection AvailableFields { get; } = new();
+
+ // [X축 후보/선택]
+ public ObservableCollection XFieldCandidates { get; } = new();
+ private FieldItem? _selectedXField;
+ public FieldItem? SelectedXField
+ {
+ get => _selectedXField;
+ set { if (_selectedXField != value) { _selectedXField = value; OnPropertyChanged(); } }
+ }
+
+ // [Y축 후보/선택] — Line/Step: 다중, Scatter/Box: 단일
+ public ObservableCollection YFieldCandidates { get; } = new();
+
+ // 다중 선택(Y)용
+ public ObservableCollection SelectedYFields { get; } = new();
+
+ // 단일 선택(Y)용
+ private FieldItem? _selectedYField;
+ public FieldItem? SelectedYField
+ {
+ get => _selectedYField;
+ set { if (_selectedYField != value) { _selectedYField = value; OnPropertyChanged(); } }
+ }
+
+ private bool _showMarkers; // Line
+ public bool ShowMarkers { get => _showMarkers; set { _showMarkers = value; OnPropertyChanged(); } }
+
+ private bool _showLegends;
+ public bool ShowLegends { get => _showLegends; set { _showLegends = value; OnPropertyChanged(); } }
+
+ public ICommand DrawGraphCommand { get; }
+
public GreenHouseGasViewModel()
{
- // Initialization logic for GreenHouseGasViewModel can go here
+ WaterQualityList = Datas.GetWaterQualityVO();
+ GraphTypes = new ObservableCollection
+ {
+ GraphType.LINE,
+ GraphType.STACKAREA,
+ GraphType.PIE
+ };
+
+ SelectedKind = StepFieldKind.Energy; // 기본적으로 에너지 관련 필드만 표시
+
+ DrawGraphCommand = new RelayCommand(DrawGraph);
+
+ RebuildAvailableFields();
+ RebuildFieldCandidates();
+ }
+
+ private void DrawGraph(object obj)
+ {
+ switch (SelectedGraphType)
+ {
+ case GraphType.LINE:
+ GraphControlVM.SetMultiLineGraph(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends);
+ break;
+ case GraphType.STACKAREA:
+ GraphControlVM.SetStackAreaPlot(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends);
+ break;
+ case GraphType.PIE:
+ GraphControlVM.SetPieChart(WaterQualityList, SelectedYFields);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void RebuildAvailableFields()
+ {
+ AvailableFields.Clear();
+
+ // 공통 시간
+ AvailableFields.Add(new FieldItem { Name = "RecordedTime", Display = "시간", DataType = typeof(DateTime) });
+
+ AvailableFields.Add(new FieldItem { Name = "TotalEnergy", Display = "총 전력", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Filtering.SandFilterEnergy", Display = "모래여과기", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Filtering.CirculationPumpEnergy", Display = "순환펌프", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Filtering.HeatPumpEnergy", Display = "히트펌프", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Filtering.AirBlowerEnergy", Display = "에어브로와", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Sterilizing.OzoneGeneratorEnergy", Display = "오존발생기", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Sterilizing.UVSterilizerEnergy", Display = "자외선 살균기", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Sterilizing.OzoneDissolverEnergy", Display = "오존용해장치", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ AvailableFields.Add(new FieldItem { Name = "Sterilizing.ExcessOzoneDestroyerEnergy", Display = "배오존장치", DataType = typeof(double), Kind = StepFieldKind.Energy });
+ }
+
+ private void RebuildFieldCandidates()
+ {
+ // 후보 초기화
+ XFieldCandidates.Clear();
+ YFieldCandidates.Clear();
+
+ // X축: 시간 우선
+ foreach (var f in AvailableFields)
+ {
+ XFieldCandidates.Add(f);
+ if (SelectedGraphType == GraphType.LINE || SelectedGraphType == GraphType.STACKAREA) break;
+ }
+ SelectedXField = AvailableFields.FirstOrDefault(f => f.DataType == typeof(DateTime))
+ ?? AvailableFields.FirstOrDefault();
+
+ IEnumerable src = AvailableFields.Where(f => f.Kind == SelectedKind);
+
+ if (SelectedGraphType is GraphType.LINE or GraphType.STACKAREA or GraphType.PIE)
+ {
+ // 수치형만 (LINE/STACKAREA/PIE 연속값 위주)
+ src = src.Where(f => f.DataType == typeof(double));
+ }
+
+ // Y축 후보: 수치형
+ foreach (var f in src)
+ {
+ if (SelectedGraphType is GraphType.STACKAREA or GraphType.PIE && f.Name.Equals("TotalEnergy")) continue;
+ YFieldCandidates.Add(f);
+ }
+
+ // 기본 선택 세팅 (타입별)
+ SelectedYFields.Clear();
+ SelectedYField = null;
+
+ switch (SelectedGraphType)
+ {
+ case GraphType.LINE:
+ //var def = YFieldCandidates.FirstOrDefault();
+ //if (def != null) SelectedYFields.Add(def);
+ break;
+ case GraphType.STACKAREA:
+ SelectedYField = YFieldCandidates.FirstOrDefault();
+ break;
+
+ case GraphType.PIE:
+ //SelectedYField = YFieldCandidates.FirstOrDefault();
+ break;
+ }
+ OnPropertyChanged(nameof(SelectedYFields));
}
public event PropertyChangedEventHandler? PropertyChanged;