feat: 에너지 -> 온실가스 변환식 및 데이터 출력

hhsung_work
HyungJune Kim 10 months ago
parent 6bc2fc30fb
commit 2e56475725

@ -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;
/// <summary>
/// kWh → tCO₂ 변환
/// </summary>
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;
}
/// <summary>
/// tCO₂ → kWh 역변환 (Binding TwoWay 대응)
/// </summary>
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;
}
}
}

@ -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">
<UserControl.Resources>
<classes:InverseBoolConverter x:Key="InverseBoolConverter"/>
</UserControl.Resources>
<Grid Background="#243851">
<Grid.RowDefinitions>
<RowDefinition Height="160"/>
@ -85,7 +91,7 @@
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Center">
<DataGrid Style="{StaticResource DataGridStyle}" ItemsSource="{Binding WaterQualityList}" Background="Transparent"
RowStyle="{StaticResource DataGridRowStyle}" ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}" SelectionChanged="DataGrid_SelectionChanged">
RowStyle="{StaticResource DataGridRowStyle}" ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
<DataGrid.Columns>
<DataGridTextColumn
Header="시간"
@ -246,11 +252,13 @@
<RadioButton x:Name="rbStatus" Content="합계"
GroupName="pie" Margin="0 0 30 0"
Foreground="White" FontSize="20"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"/>
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"
IsChecked="{Binding UseAverage, Converter={StaticResource InverseBoolConverter}, Mode=TwoWay}"/>
<RadioButton x:Name="pie" Content="평균"
GroupName="strpPlot" Grid.Column="1"
GroupName="pie" Grid.Column="1"
Foreground="White" FontSize="20"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"/>
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"
IsChecked="{Binding UseAverage, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
@ -279,9 +287,22 @@
</CheckBox>
<CheckBox Content="범례 표시" IsChecked="{Binding ShowLegends}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/>
<CheckBox Content="도넛 모드" IsChecked="{Binding IsDonutMode}" Margin="0 0 15 0"
VerticalContentAlignment="Center">
<CheckBox.Style>
<Style TargetType="CheckBox" BasedOn="{StaticResource MaterialDesignUserForegroundCheckBox}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedGraphType}" Value="LINE">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedGraphType}" Value="STACKAREA">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Content="도넛 모드" IsChecked="{Binding IsDonut}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center">
<CheckBox.Style>

@ -24,10 +24,5 @@ namespace SmartAquaViewer.View
{
InitializeComponent();
}
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
}

@ -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">
<UserControl.Resources>
<classes:EnergyToGreenHouseGasConverter x:Key="EnergyToGreenHouseGasConverter"/>
</UserControl.Resources>
<Grid Background="#243851">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="650"/>
</Grid.ColumnDefinitions>
<Grid Grid.RowSpan="2" Margin="20">
<ScrollViewer Grid.RowSpan="2" Margin="20"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Center">
<DataGrid Style="{StaticResource DataGridStyle}" Background="Transparent" ItemsSource="{Binding WaterQualityList}"
RowStyle="{StaticResource DataGridRowStyle}" ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}">
<DataGrid.Columns>
<DataGridTextColumn
Header="시간"
Binding="{Binding RecordedTime, StringFormat=\{0:HH:mm:ss\}}"
ElementStyle="{StaticResource DataGridElmenetStyle}"/>
</Grid>
<DataGridTextColumn Header="모래여과기" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Filtering.SandFilterEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="순환펌프" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Filtering.CirculationPumpEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="히트펌프" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Filtering.HeatPumpEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="에어브로와" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Filtering.AirBlowerEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="오존발생기" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Sterilizing.OzoneGeneratorEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Sterilizing.UVSterilizerEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="자외선"/>
<TextBlock Text="살균기"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Header="오존용해장치" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Sterilizing.OzoneDissolverEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="배오존장치" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding Sterilizing.ExcessOzoneDestroyerEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
<DataGridTextColumn Header="총 온실가스" ElementStyle="{StaticResource DataGridElmenetStyle}"
Binding="{Binding TotalEnergy, Converter={StaticResource EnergyToGreenHouseGasConverter}}"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
<Border Grid.Row="0" Grid.Column="1" Margin="0 20 20 20" CornerRadius="10"
Background="#384659" BorderBrush="#404F63" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel>
<Grid Margin="15 15 15 15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="그래프" VerticalAlignment="Center"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
<ComboBox Margin="15 0 0 0" Height="40" Grid.Column="1"
Style="{StaticResource ComboBoxStyle}"
ItemsSource="{Binding GraphTypes}"
SelectedIndex="{Binding SelectedGraphIndex, Mode=TwoWay}"
helper:ComboBoxHelper.SelectFirstOnItemsChange="True"
IsEditable="False" IsTextSearchEnabled="False"/>
</Grid>
</StackPanel>
</Grid>
</Border>
<Border Grid.Row="1" Grid.Column="1" Margin="0 0 20 20" CornerRadius="10"

@ -229,12 +229,18 @@ namespace SmartAquaViewer.ViewModel
set { if (_selectedYField != value) { _selectedYField = value; OnPropertyChanged(); } }
}
private bool _showMarkers; // Line
private bool _useAverage;
public bool UseAverage { get => _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;

@ -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);
}

@ -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<GraphType> GraphTypes { get; }
public List<WaterQualityVO> 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<FieldItem> AvailableFields { get; } = new();
// [X축 후보/선택]
public ObservableCollection<FieldItem> 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<FieldItem> YFieldCandidates { get; } = new();
// 다중 선택(Y)용
public ObservableCollection<FieldItem> 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>
{
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<FieldItem> 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;

Loading…
Cancel
Save