This commit is contained in:
xyy
2026-03-28 16:48:41 +08:00
parent 2f1cacd097
commit 8bd9226955
9 changed files with 403 additions and 231 deletions

View File

@@ -17,8 +17,7 @@ namespace MembranePoreTester.Communication
public ushort WetFlowRegister { get; set; } // 湿膜流量寄存器起始地址
public ushort DryFlowRegister { get; set; } // 干膜流量寄存器起始地址
public double PressureFactor { get; set; } = 1.0; // 压力系数(单位换算)
public double WetFlowFactor { get; set; } = 1.0; // 湿膜流量系数
public double DryFlowFactor { get; set; } = 1.0; // 干膜流量系数
// ========== 工位专用寄存器 ==========
public ushort PressureRegisterStation1 { get; set; } // 工位1 压力寄存器起始地址
@@ -35,7 +34,6 @@ namespace MembranePoreTester.Communication
// ========== 运维参数(用户可设置) ==========
public ushort PressureUpperLimit { get; set; } = 300; // 加压上限 D300
public ushort PressureRate { get; set; } = 280; // 加压速率 D280
public ushort PressureCoeff { get; set; } = 282; // 加压系数 D282
// 高压/低压系数(每个工位独立)
public ushort HPCoeff1 { get; set; } = 3120; // 工位1 高压系数
@@ -56,15 +54,29 @@ namespace MembranePoreTester.Communication
// 流量模式选择寄存器0=大流量1=小流量)
public ushort FlowModeRegister { get; set; } = 4; // 工位1 流量模式
public ushort FlowModeRegister1 { get; set; } = 5; // 工位1 流量模式
public ushort FlowModeRegister2 { get; set; } = 6;
public ushort FlowModeRegister3 { get; set; } = 7;
// 校准系数(零点/量程)寄存器地址
public ushort PressureCalibZero { get; set; } = 3200; // 压力零点系数
public ushort PressureCalibSpan { get; set; } = 3202; // 压力量程系数
public ushort FlowCalibZero { get; set; } = 3204; // 流量零点系数
public ushort FlowCalibSpan { get; set; } = 3206; // 流量量程系数
public ushort High1 { get; set; } // 工位1
public ushort High2 { get; set; } // 工位1
public ushort High3 { get; set; } // 工位1
public ushort Low1 { get; set; } // 工位1
public ushort Low2 { get; set; } // 工位1
public ushort Low3 { get; set; } // 工位1
public ushort SmallFlow1 { get; set; } // 工位1
public ushort SmallFlow2 { get; set; } // 工位1
public ushort SmallFlow3 { get; set; } // 工位1
public ushort BigFlow1 { get; set; } // 工位1
public ushort BigFlow2 { get; set; } // 工位1
public ushort BigFlow3 { get; set; } // 工位1
}
/// <summary>

View File

@@ -55,6 +55,7 @@ namespace MembranePoreTester.Communication
public async Task WriteRegisterAsync(ushort registerAddress, ushort value)
{
await EnsureConnectedAsync();
await Task.Delay(100);
await _master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value);
}
@@ -62,6 +63,7 @@ namespace MembranePoreTester.Communication
public async Task<bool> ReadCoilAsync(ushort coilAddress)
{
await EnsureConnectedAsync();
await Task.Delay(100);
bool[] result = await _master?.ReadCoilsAsync(_config.SlaveId, coilAddress, 1);
return result[0];
}
@@ -70,6 +72,7 @@ namespace MembranePoreTester.Communication
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
{
await EnsureConnectedAsync();
await Task.Delay(100);
return await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count);
}
@@ -77,12 +80,14 @@ namespace MembranePoreTester.Communication
{
await EnsureConnectedAsync();
int val = (int)value;
await Task.Delay(100);
await _master.WriteMultipleRegistersAsync(1, registerAddress, intToushorts(val));
}
public async Task WriteMultipleRegistersAsync(ushort registerAddress, float value)
{
await EnsureConnectedAsync();
await Task.Delay(100);
await _master.WriteMultipleRegistersAsync(_config.SlaveId, registerAddress, SplitFloatToUShortArray((float)value));
}

View File

@@ -86,6 +86,7 @@ namespace MembranePoreTester.ViewModels
public ICommand GenerateReportCommand { get; }
public ICommand OpenPressureCalibCommand { get; }
public ICommand OpenPressureCalibCommand2 { get; }
@@ -107,21 +108,28 @@ namespace MembranePoreTester.ViewModels
SaveCommand = new RelayCommand(SaveToDatabase);
ExportCommand = new RelayCommand(ExportToExcel);
OpenPressureCalibCommand = new RelayCommand(OpenPressureCalibration);
OpenPressureCalibCommand2 = new RelayCommand(OpenPressureCalibration2);
// 启动定时器,每秒读取一次
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1);
_timer.Tick += async (s, e) => await ReadCurrentPlcAsync();
_timer.Tick += async (s, e) =>
{
if (StationId > 0 && !IsDisposed)
{
await ReadCurrentPlcAsync();
}
};
_timer.Start();
// 立即读取一次
Task.Run(async () =>
{
await ReadCurrentPlcAsync();
}
);
//// 立即读取一次
//Task.Run(async () =>
//{
// await Task.Delay(1000); // 等待2秒确保PLC连接稳定
// await ReadCurrentPlcAsync();
//}
//);
}
public override void Dispose()
@@ -137,32 +145,29 @@ namespace MembranePoreTester.ViewModels
private async Task ReadCurrentPlcAsync()
{
try
await SafeExecuteAsync("ReadCurrentPressure", async () =>
{
if (IsDisposed || _plcService == null) return;
await Task.Delay(100); // 小延迟避免过快读取
float rawPressure = await _plcService.ReadPressureAsync(StationId);
Record.BubbleCurrentPressure =Math.Round( rawPressure,2);
Record.BubbleCurrentPressure = Math.Round(rawPressure, 2);
OnPropertyChanged(nameof(Record.BubbleCurrentPressure));
}
catch (Exception ex)
{
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
private async Task ReadPlcAsync()
{
if (IsDisposed) return; // 改3添加这行检查
try
{
Record.BubblePointPressure = Record.BubbleCurrentPressure;
if (IsDisposed) return;
Record.BubblePointPressure = Record.BubbleCurrentPressure;
OnPropertyChanged(nameof(Record.BubblePointPressure));
OnPropertyChanged(nameof(Record.BubblePointPressure));
}
catch (Exception ex)
{
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -248,18 +253,58 @@ namespace MembranePoreTester.ViewModels
private void OpenPressureCalibration()
{
// 使用简单的输入框获取新零点系数和量程系数
string zeroStr = Microsoft.VisualBasic.Interaction.InputBox("请输入压力零点系数", "压力校准", "0");
string spanStr = Microsoft.VisualBasic.Interaction.InputBox("请输入压力量程系数", "压力校准", "1");
if (float.TryParse(zeroStr, out float zero) && float.TryParse(spanStr, out float span))
ushort address = new ushort();
switch (StationId)
{
Task.Run(async () =>
{
await WriteFloatAsync(_plcConfig.PressureCalibZero, zero);
await WriteFloatAsync(_plcConfig.PressureCalibSpan, span);
MessageBox.Show("压力校准系数已写入", "完成");
});
case 1:
{
address = _plcConfig.High1;
break;
}
case 2:
{
address = _plcConfig.High2;
break;
}
case 3:
{
address = _plcConfig.High3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
private void OpenPressureCalibration2()
{
ushort address = new ushort();
switch (StationId)
{
case 1:
{
address = _plcConfig.Low1;
break;
}
case 2:
{
address = _plcConfig.Low2;
break;
}
case 3:
{
address = _plcConfig.Low3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
private void ExportToExcel()

View File

@@ -165,22 +165,25 @@ namespace MembranePoreTester.ViewModels
}
private async Task ReadPressureModeAsync()
{
try
await SafeExecuteAsync($"ReadPressureModeAsync{StationId}", async () =>
{
ushort[] values = await _plcService.ReadHoldingRegistersAsync(_plcConfig.PressureModeRegister, 1);
ushort val = values[0];
string newValue = val == 0 ? "高压" : "低压";
Application.Current.Dispatcher.Invoke(() =>
try
{
// 更新选中项
HighLowPressure = newValue;
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取压力模式失败: {ex.Message}");
}
ushort[] values = await _plcService.ReadHoldingRegistersAsync(_plcConfig.PressureModeRegister, 1);
ushort val = values[0];
string newValue = val == 0 ? "高压" : "低压";
Application.Current.Dispatcher.Invoke(() =>
{
// 更新选中项
HighLowPressure = newValue;
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取压力模式失败: {ex.Message}");
}
});
}
private async Task TogglePressAsync()
@@ -204,33 +207,38 @@ namespace MembranePoreTester.ViewModels
MessageBox.Show($"读取加压状态失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// 在 StationItem 类中添加字段
private bool _lastEnableReadFailed = false;
private DateTime _lastEnableErrorTime = DateTime.MinValue;
private async Task ReadEnableStatusAsync()
{
try
await SafeExecuteAsync($"ReadEnableStatus_Station{StationId}", async () =>
{
bool status = await _plcService.ReadCoilAsync(_plcConfig.EnableCoil); // 读取 M21
EnableStatus = status;
bool pressStatus = await _plcService.ReadCoilAsync(_plcConfig.PressCoil);//这里也要更新加压的按钮状态
_isPressing = pressStatus;
// 在UI线程更新按钮文字
Application.Current.Dispatcher.Invoke(() =>
try
{
PressButtonText = pressStatus ? "停止加压" : "加压";
});
bool status = await _plcService.ReadCoilAsync(_plcConfig.EnableCoil); // 读取 M21
EnableStatus = status;
bool pressStatus = await _plcService.ReadCoilAsync(_plcConfig.PressCoil);//这里也要更新加压的按钮状态
_isPressing = pressStatus;
// 在UI线程更新按钮文字
Application.Current.Dispatcher.Invoke(() =>
{
PressButtonText = pressStatus ? "停止加压" : "加压";
});
}
catch (Exception ex)
{
// 读取出错时保持原状态或显示错误
System.Diagnostics.Debug.WriteLine($"读取使能状态失败: {ex.Message}");
}
}
catch (Exception ex)
{
// 读取出错时保持原状态或显示错误
System.Diagnostics.Debug.WriteLine($"读取使能状态失败: {ex.Message}");
}
});
}
private async Task WriteCoilAsync(ushort coil, bool value)
@@ -283,7 +291,7 @@ namespace MembranePoreTester.ViewModels
public MainViewModel()
{
for (int i = 1; i <= 1; i++)
for (int i = 1; i <= 3; i++)
{
var station = new StationItem
{

View File

@@ -92,53 +92,56 @@ namespace MembranePoreTester.ViewModels
private async void AutoCollectTimer_Tick(object sender, EventArgs e)
{
if (!IsActive) return; // 不在当前标签页,跳过采集
try
await SafeExecuteAsync($"AutoCollect_Station{StationId}", async () =>
{
// 1. 读取当前压力
float rawPressure = await _plcService.ReadPressureAsync(StationId);
double pressure = Math.Round(rawPressure,2);
try
{
// 1. 读取当前压力
float rawPressure = await _plcService.ReadPressureAsync(StationId);
double pressure = Math.Round(rawPressure, 2);
double flow = 0;
if (TestMode == "湿膜")
{
float rawFlow = await _plcService.ReadWetFlowAsync();
flow = rawFlow;
}
else
{
float rawFlow = await _plcService.ReadDryFlowAsync();
flow = rawFlow;
}
flow = Math.Round(flow,2);
// 3. 在 DataPoints 中查找是否存在相同压力的行(允许微小误差)
var existing = Record.DataPoints.FirstOrDefault(p => Math.Abs(p.Pressure - pressure) < 0.001);
if (existing != null)
{
// 更新对应列
double flow = 0;
if (TestMode == "湿膜")
existing.WetFlow = flow;
{
float rawFlow = await _plcService.ReadWetFlowAsync();
flow = rawFlow;
}
else
existing.DryFlow = flow;
}
else
{
// 新增一行
var newPoint = new Models.DataPoint { Pressure = pressure };
if (TestMode == "湿膜")
newPoint.WetFlow = flow;
else
newPoint.DryFlow = flow;
Record.DataPoints.Add(newPoint);
}
{
float rawFlow = await _plcService.ReadDryFlowAsync();
flow = rawFlow;
}
// 4. 刷新曲线
UpdatePlot();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"自动采集失败: {ex.Message}");
}
flow = Math.Round(flow, 2);
// 3. 在 DataPoints 中查找是否存在相同压力的行(允许微小误差)
var existing = Record.DataPoints.FirstOrDefault(p => Math.Abs(p.Pressure - pressure) < 0.001);
if (existing != null)
{
// 更新对应列
if (TestMode == "湿膜")
existing.WetFlow = flow;
else
existing.DryFlow = flow;
}
else
{
// 新增一行
var newPoint = new Models.DataPoint { Pressure = pressure };
if (TestMode == "湿膜")
newPoint.WetFlow = flow;
else
newPoint.DryFlow = flow;
Record.DataPoints.Add(newPoint);
}
// 4. 刷新曲线
UpdatePlot();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"自动采集失败: {ex.Message}");
}
});
}
/// <summary>
@@ -278,7 +281,7 @@ namespace MembranePoreTester.ViewModels
SaveCommand = new RelayCommand(SaveToDatabase);
ExportCommand = new RelayCommand(ExportToExcel);
OpenFlowCalibCommand = new RelayCommand(OpenFlowCalibration);
OpenFlowCalibCommand2 = new RelayCommand(OpenFlowCalibration2);
Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot();
@@ -294,22 +297,25 @@ namespace MembranePoreTester.ViewModels
private async Task ReadPressureModeAsync()
{
try
await SafeExecuteAsync($"ReadPressureModeAsync{StationId}", async () =>
{
ushort[] values = await _plcService.ReadHoldingRegistersAsync(_plcConfig.FlowModeRegister, 1);
ushort val = values[0];
string newValue = val == 0 ? "大流量" : "小流量";
Application.Current.Dispatcher.Invoke(() =>
try
{
// 更新选中项
HighLowPressure = newValue;
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取流量模式失败: {ex.Message}");
}
ushort[] values = await _plcService.ReadHoldingRegistersAsync(_plcConfig.FlowModeRegister, 1);
ushort val = values[0];
string newValue = val == 0 ? "大流量" : "小流量";
Application.Current.Dispatcher.Invoke(() =>
{
// 更新选中项
HighLowPressure = newValue;
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取流量模式失败: {ex.Message}");
}
});
}
public string HighLowPressure
@@ -327,11 +333,12 @@ namespace MembranePoreTester.ViewModels
private async Task ReadPlcAsync()
{
try
await SafeExecuteAsync($"ManualRead_Station{StationId}", async () =>
{
// 始终读取压力
float rawPressure = await _plcService.ReadPressureAsync(StationId);
double pressure = rawPressure * _plcConfig.PressureFactor;
double pressure = rawPressure;
if (SelectedDataPoint != null)
{
@@ -340,12 +347,12 @@ namespace MembranePoreTester.ViewModels
if (TestMode == "湿膜")
{
float rawWet = await _plcService.ReadWetFlowAsync();
SelectedDataPoint.WetFlow = rawWet * _plcConfig.WetFlowFactor;
SelectedDataPoint.WetFlow = rawWet;
}
else
{
float rawDry = await _plcService.ReadDryFlowAsync();
SelectedDataPoint.DryFlow = rawDry * _plcConfig.DryFlowFactor;
SelectedDataPoint.DryFlow = rawDry;
}
}
else
@@ -355,20 +362,19 @@ namespace MembranePoreTester.ViewModels
if (TestMode == "湿膜")
{
float rawWet = await _plcService.ReadWetFlowAsync();
newPoint.WetFlow = rawWet * _plcConfig.WetFlowFactor;
newPoint.WetFlow = rawWet;
}
else
{
float rawDry = await _plcService.ReadDryFlowAsync();
newPoint.DryFlow = rawDry * _plcConfig.DryFlowFactor;
newPoint.DryFlow = rawDry;
}
Record.DataPoints.Add(newPoint);
}
}
catch (Exception ex)
{
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
private void AddDataPoint()
@@ -422,17 +428,58 @@ namespace MembranePoreTester.ViewModels
private void OpenFlowCalibration()
{
string zeroStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量零点系数", "流量校准", "0");
string spanStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量量程系数", "流量校准", "1");
if (float.TryParse(zeroStr, out float zero) && float.TryParse(spanStr, out float span))
ushort address = new ushort();
switch (StationId)
{
Task.Run(async () =>
{
await WriteFloatAsync(_plcConfig.FlowCalibZero, zero);
await WriteFloatAsync(_plcConfig.FlowCalibSpan, span);
MessageBox.Show("流量校准系数已写入", "完成");
});
case 1:
{
address = _plcConfig.BigFlow1;
break;
}
case 2:
{
address = _plcConfig.BigFlow2;
break;
}
case 3:
{
address = _plcConfig.BigFlow3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
private void OpenFlowCalibration2()
{
ushort address = new ushort();
switch (StationId)
{
case 1:
{
address = _plcConfig.SmallFlow1;
break;
}
case 2:
{
address = _plcConfig.SmallFlow2;
break;
}
case 3:
{
address = _plcConfig.SmallFlow3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
private int _stationId;
@@ -514,6 +561,8 @@ namespace MembranePoreTester.ViewModels
public ICommand OpenFlowCalibCommand { get; }
public ICommand OpenFlowCalibCommand2 { get; }
public ICommand ExportCommand { get; }

View File

@@ -1,5 +1,6 @@
using MembranePoreTester.Communication;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace MembranePoreTester.ViewModels
@@ -58,6 +59,75 @@ namespace MembranePoreTester.ViewModels
new System.Windows.DependencyObject());
}
// ========== 错误抑制机制 ==========
private class ErrorRecord
{
public bool HasError { get; set; }
public DateTime LastErrorTime { get; set; }
}
private readonly Dictionary<string, ErrorRecord> _errorRecords = new();
/// <summary>
/// 安全执行异步操作,自动抑制重复错误
/// </summary>
protected async Task<bool> SafeExecuteAsync(string operationKey, Func<Task> action, int suppressSeconds = 3)
{
// 检查是否需要抑制
if (_errorRecords.TryGetValue(operationKey, out var record) && record.HasError)
{
if ((DateTime.Now - record.LastErrorTime).TotalSeconds < suppressSeconds)
{
return false;
}
}
try
{
await action();
// 成功,清除错误记录
if (_errorRecords.TryGetValue(operationKey, out var successRecord))
{
successRecord.HasError = false;
}
return true;
}
catch (Exception ex)
{
// 记录错误
if (!_errorRecords.TryGetValue(operationKey, out var errorRecord))
{
errorRecord = new ErrorRecord();
_errorRecords[operationKey] = errorRecord;
}
errorRecord.HasError = true;
errorRecord.LastErrorTime = DateTime.Now;
// 静默记录到输出窗口
Debug.WriteLine($"[{DateTime.Now:HH:mm:ss}] {operationKey} 失败: {ex.Message}");
return false;
}
}
// 新增:检查是否已释放
protected bool IsDisposed => _disposed;

View File

@@ -94,7 +94,8 @@
<!-- 底部按钮 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="⚙ 压校准" Command="{Binding OpenPressureCalibCommand}" Padding="15,8" Background="#607D8B"/>
<Button Content="⚙ 压校准" Command="{Binding OpenPressureCalibCommand}" Padding="15,8" Background="#607D8B"/>
<Button Content="⚙ 低压校准" Command="{Binding OpenPressureCalibCommand2}" Padding="15,8" Background="#607D8B"/>
<Button Content="💾 保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Background="#4CAF50"/>
<Button Content="📄 生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Background="#2196F3"/>
<Button Content="📊 导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Background="#FF9800"/>

View File

@@ -147,6 +147,14 @@
<Label Grid.Row="3" Grid.Column="0" Content="压力单位:"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding PressureUnits}"
SelectedItem="{Binding Record.PressureUnit}"/>
<Label Grid.Row="3" Grid.Column="2" Content="(干/湿膜)/(大/小流量):"/>
<ComboBox HorizontalAlignment="Left" Grid.Row="3" Grid.Column="3" SelectedItem="{Binding TestMode}" Width="80">
<ComboBoxItem Content="湿膜" IsSelected="True"/>
<ComboBoxItem Content="干膜"/>
</ComboBox>
<ComboBox Grid.Row="3" HorizontalAlignment="Right" Grid.Column="3" ItemsSource="{Binding PressureModeList}"
SelectedItem="{Binding SelectedPressureMode}" Width="100"/>
</Grid>
</GroupBox>
@@ -158,91 +166,54 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧数据表格 --><!--
<GroupBox Grid.Column="0" Header="📊 压力-流量数据" Margin="0,0,5,0">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content=" 添加行" Command="{Binding AddDataPointCommand}" Background="#4CAF50"/>
<Button Content=" 删除行" Command="{Binding RemoveDataPointCommand}" Background="#F44336"/>
<ComboBox SelectedItem="{Binding TestMode}" Width="80">
<ComboBoxItem Content="湿膜" IsSelected="True"/>
<ComboBoxItem Content="干膜"/>
</ComboBox>
<ComboBox ItemsSource="{Binding PressureModeList}"
SelectedItem="{Binding SelectedPressureMode}" Width="100"/>
--><!--<Button Content="📡 读取PLC" Command="{Binding ReadPlcCommand}" Background="#FF9800"/>--><!--
</StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
<DataGridTextColumn Header="湿膜流量(L/min)"
Binding="{Binding WetFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
<DataGridTextColumn Header="干膜流量(L/min)"
Binding="{Binding DryFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</DockPanel>
</GroupBox>-->
<!-- 左侧数据表格区域,改为两列 Grid -->
<Grid Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 湿膜表格 -->
<GroupBox Grid.Column="0" Header="💧 湿膜数据" Margin="0,0,5,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400">
<Grid Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 湿膜表格 -->
<GroupBox Grid.Column="0" Header="💧 湿膜数据" Margin="0,0,5,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
<DataGridTextColumn Header="湿膜流量(L/min)"
<DataGridTextColumn Header="湿膜流量(L/min)"
Binding="{Binding WetFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<!-- 干膜表格 -->
<GroupBox Grid.Column="1" Header="🔥 干膜数据" Margin="5,0,0,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
<!-- 干膜表格 -->
<GroupBox Grid.Column="1" Header="🔥 干膜数据" Margin="5,0,0,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
<DataGridTextColumn Header="干膜流量(L/min)"
<DataGridTextColumn Header="干膜流量(L/min)"
Binding="{Binding DryFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</ScrollViewer>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" Background="#E9ECF0"/>
<!-- 右侧曲线图 -->
@@ -280,7 +251,8 @@
</GroupBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="10,0"/>
<StackPanel Orientation="Horizontal">
<Button Content="🔧 流量校准" Command="{Binding OpenFlowCalibCommand}" Background="#607D8B"/>
<Button Content="🔧 流量校准" Command="{Binding OpenFlowCalibCommand}" Background="#607D8B"/>
<Button Content="🔧 小流量校准" Command="{Binding OpenFlowCalibCommand2}" Background="#607D8B"/>
<Button Content="📄 生成报告" Command="{Binding GenerateReportCommand}" Background="#2196F3"/>
<Button Content="💾 保存到历史" Command="{Binding SaveCommand}" Background="#4CAF50"/>
<Button Content="📊 导出Excel" Command="{Binding ExportCommand}" Background="#FF9800"/>

View File

@@ -24,7 +24,7 @@
// 流量寄存器地址(每个工位共用,但需配合流量模式切换)
"WetFlowRegister": 12, // 湿膜流量寄存器起始地址D2D3
"DryFlowRegister": 4, // 干膜流量寄存器起始地址D4D5
"DryFlowRegister": 12, // 干膜流量寄存器起始地址D4D5
"WetFlowFactor": 1.0, // 湿膜流量系数
"DryFlowFactor": 1.0, // 干膜流量系数
@@ -56,10 +56,20 @@
"FlowModeRegister2": 6, // 工位2 流量模式
"FlowModeRegister3": 7, // 工位3 流量模式
// 校准系数(零点/量程寄存器地址约定若PLC未分配可自定义
"PressureCalibZero": 3200, // 压力零点系数D3200D3201
"PressureCalibSpan": 3202, // 压力量程系数D3202D3203
"FlowCalibZero": 3204, // 流量零点系数D3204D3205
"FlowCalibSpan": 3206 // 流量量程系数D3206D3207
"High1": 60, // M60
"High2": 62, // M62
"High3": 64, // M64
"Low1": 61, //
"Low2": 63, //
"Low3": 65, //
"SmallFlow1": 40, // M60
"SmallFlow2": 44, // M62
"SmallFlow3": 47, // M64
"BigFlow1": 49, //
"BigFlow2": 42, //
"BigFlow3": 46 //
}
}