This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
App.xaml
2
App.xaml
@@ -1,7 +1,7 @@
|
||||
<Application x:Class="MembranePoreTester.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
StartupUri="Views/TestView.xaml">
|
||||
<Application.Resources>
|
||||
<!-- 全局样式定义 -->
|
||||
<ResourceDictionary>
|
||||
|
||||
@@ -17,39 +17,50 @@
|
||||
public ushort PressureRegisterStation1 { get; set; }
|
||||
public ushort PressureRegisterStation2 { get; set; }
|
||||
public ushort PressureRegisterStation3 { get; set; }
|
||||
public ushort PressureModeRegister { get; set; }
|
||||
public ushort PressCoil { get; set; }
|
||||
public ushort StartCoil { get; set; }
|
||||
public ushort EnableCoil { get; set; }
|
||||
public ushort StopCoil { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public ushort PressureUpperLimit { get; set; } = 300;
|
||||
public ushort PressureRate { get; set; } = 280;
|
||||
public ushort PressureCoeff { get; set; } = 282;
|
||||
public ushort HPCoeff1 { get; set; } = 3120;
|
||||
public ushort LPCoeff1 { get; set; } = 3122;
|
||||
public ushort HPCoeff2 { get; set; } = 3124;
|
||||
public ushort LPCoeff2 { get; set; } = 3126;
|
||||
public ushort HPCoeff3 { get; set; } = 3128;
|
||||
public ushort LPCoeff3 { get; set; } = 3130;
|
||||
public ushort LargeFlowCoeff1 { get; set; } = 3048;
|
||||
public ushort SmallFlowCoeff1 { get; set; } = 380;
|
||||
public ushort LargeFlowCoeff2 { get; set; } = 1218;
|
||||
public ushort SmallFlowCoeff2 { get; set; } = 1318;
|
||||
public ushort LargeFlowCoeff3 { get; set; } = 1418;
|
||||
public ushort SmallFlowCoeff3 { get; set; } = 1468;
|
||||
public ushort FlowModeRegister1 { get; set; } = 5; // 工位1流量模式 (0=大,1=小)
|
||||
public ushort FlowModeRegister2 { get; set; } = 6;
|
||||
public ushort FlowModeRegister3 { get; set; } = 7;
|
||||
// 校准系数地址(需与PLC约定)
|
||||
public ushort PressureCalibZero { get; set; } = 3200;
|
||||
public ushort PressureCalibSpan { get; set; } = 3202;
|
||||
public ushort FlowCalibZero { get; set; } = 3204;
|
||||
public ushort FlowCalibSpan { get; set; } = 3206;
|
||||
|
||||
|
||||
// 设备参数地址(示例,请根据实际PLC地址修改)
|
||||
public ushort UpperLampData1 { get; set; } = 350;
|
||||
public ushort UpperLampData2 { get; set; } = 351;
|
||||
public ushort UpperLampData3 { get; set; } = 352;
|
||||
public ushort UpperLampData4 { get; set; } = 353;
|
||||
public ushort UpperLampData5 { get; set; } = 354;
|
||||
public ushort UpperLampData6 { get; set; } = 355;
|
||||
|
||||
public ushort LowerLampData1 { get; set; } = 356;
|
||||
public ushort LowerLampData2 { get; set; } = 357;
|
||||
public ushort LowerLampData3 { get; set; } = 358;
|
||||
public ushort LowerLampData4 { get; set; } = 359;
|
||||
public ushort LowerLampData5 { get; set; } = 360;
|
||||
public ushort LowerLampData6 { get; set; } = 361;
|
||||
|
||||
public ushort LeftEyeAreaCoeff { get; set; } = 212;
|
||||
public ushort RightEyeAreaCoeff { get; set; } = 214;
|
||||
public ushort SaveRateCorrectionCoeff { get; set; } = 428;
|
||||
|
||||
public ushort MiddleLamp1 { get; set; } = 362;
|
||||
public ushort MiddleLamp2 { get; set; } = 363;
|
||||
public ushort MiddleLamp3 { get; set; } = 364;
|
||||
public ushort MiddleLamp4 { get; set; } = 365;
|
||||
public ushort MiddleLamp5 { get; set; } = 366;
|
||||
public ushort MiddleLamp6 { get; set; } = 367;
|
||||
public ushort MiddleLamp7 { get; set; } = 368;
|
||||
|
||||
public ushort MotorLimit { get; set; } = 330;
|
||||
public ushort ResetCompensation { get; set; } = 340; // 新增复位补偿
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,7 +69,11 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum ParameterDataType
|
||||
{
|
||||
Int16, // 16位整数
|
||||
Float32 // 32位浮点数
|
||||
}
|
||||
|
||||
|
||||
public interface IPlcService
|
||||
@@ -75,6 +90,10 @@
|
||||
Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count);
|
||||
|
||||
Task WriteSingleRegisterAsync(ushort registerAddress, ushort value);
|
||||
|
||||
float UshortToFloat(ushort P1, ushort P2);
|
||||
|
||||
Task WriteMultipleRegistersAsync(ushort registerAddress, float value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -104,6 +104,51 @@ namespace MembranePoreTester.Communication
|
||||
return await ReadFloatAsync(startAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ushort转为float类型
|
||||
/// </summary>
|
||||
/// <param name="P1"></param>
|
||||
/// <param name="P2"></param>
|
||||
/// <returns>float型数据</returns>
|
||||
public float UshortToFloat(ushort P1, ushort P2)
|
||||
{
|
||||
int intSign, intSignRest, intExponent, intExponentRest;
|
||||
float faResult, faDigit;
|
||||
intSign = P1 / 32768;
|
||||
intSignRest = P1 % 32768;
|
||||
intExponent = intSignRest / 128;
|
||||
intExponentRest = intSignRest % 128;
|
||||
faDigit = (float)(intExponentRest * 65536 + P2) / 8388608;
|
||||
faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1);
|
||||
return faResult;
|
||||
}
|
||||
|
||||
public async Task WriteMultipleRegistersAsync(ushort registerAddress, float value)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
await Task.Delay(100);
|
||||
await _master.WriteMultipleRegistersAsync(_config.SlaveId, registerAddress, SplitFloatToUShortArray((float)value));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Float转为Ushort数组发送
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns>返回ushort数组</returns>
|
||||
public ushort[] SplitFloatToUShortArray(float value)
|
||||
{
|
||||
byte[] floatBytes = BitConverter.GetBytes(value);
|
||||
ushort[] ushortArray = new ushort[floatBytes.Length / 2];
|
||||
|
||||
for (int i = 0, j = 0; i < floatBytes.Length; i += 2, j++)
|
||||
{
|
||||
ushortArray[j] = BitConverter.ToUInt16(floatBytes, i);
|
||||
}
|
||||
|
||||
return ushortArray;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
263
ViewModels/TestViewModel.cs
Normal file
263
ViewModels/TestViewModel.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace MembranePoreTester
|
||||
{
|
||||
public partial class TestViewModel : ObservableObject
|
||||
{
|
||||
private readonly DispatcherTimer _angleUpdateTimer;
|
||||
private bool _isTestRunning;
|
||||
|
||||
public TestViewModel()
|
||||
{
|
||||
_angleUpdateTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(100)
|
||||
};
|
||||
_angleUpdateTimer.Tick += AngleUpdateTimer_Tick;
|
||||
|
||||
// 默认值
|
||||
ResolutionAngle = 0.1;
|
||||
RotationSpeed = 30;
|
||||
LeftEyeCoeff = 1.0;
|
||||
RightEyeCoeff = 1.0;
|
||||
RetentionCorrectionCoeff = 1.0;
|
||||
IsSampleMode = true;
|
||||
}
|
||||
|
||||
// 当前模式显示文本
|
||||
[ObservableProperty]
|
||||
private string _currentMode = "试样测试";
|
||||
|
||||
// 模式切换
|
||||
private bool _isBlankMode;
|
||||
public bool IsBlankMode
|
||||
{
|
||||
get => _isBlankMode;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isBlankMode, value) && value)
|
||||
{
|
||||
CurrentMode = "空白模式";
|
||||
IsSampleMode = false;
|
||||
OnModeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isSampleMode;
|
||||
public bool IsSampleMode
|
||||
{
|
||||
get => _isSampleMode;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isSampleMode, value) && value)
|
||||
{
|
||||
CurrentMode = "试样测试";
|
||||
IsBlankMode = false;
|
||||
OnModeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 参数设置
|
||||
[ObservableProperty]
|
||||
private double _resolutionAngle;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _rotationSpeed;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _currentAngle;
|
||||
|
||||
// 视野面积数据
|
||||
[ObservableProperty]
|
||||
private double _leftEyeArea;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _rightEyeArea;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _binocularArea;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _lowerField;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _blankArea;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _fieldRetentionRate;
|
||||
|
||||
// 灯条数据
|
||||
[ObservableProperty]
|
||||
private double _upperLampData;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _middleLampData;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _lowerLampData;
|
||||
|
||||
// 面积系数
|
||||
[ObservableProperty]
|
||||
private double _leftEyeCoeff;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _rightEyeCoeff;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _retentionCorrectionCoeff;
|
||||
|
||||
// 命令
|
||||
[RelayCommand]
|
||||
private void Reset()
|
||||
{
|
||||
CurrentAngle = 0;
|
||||
// 用户在此添加硬件复位代码
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenLeftEye()
|
||||
{
|
||||
// 用户实现左眼开逻辑
|
||||
UpdateLeftEyeArea();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenRightEye()
|
||||
{
|
||||
// 用户实现右眼开逻辑
|
||||
UpdateRightEyeArea();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Reverse()
|
||||
{
|
||||
// 反转方向
|
||||
RotationSpeed = -Math.Abs(RotationSpeed);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Forward()
|
||||
{
|
||||
// 正转方向
|
||||
RotationSpeed = Math.Abs(RotationSpeed);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void StartTest()
|
||||
{
|
||||
if (_isTestRunning) return;
|
||||
_isTestRunning = true;
|
||||
_angleUpdateTimer.Start();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void StopTest()
|
||||
{
|
||||
_isTestRunning = false;
|
||||
_angleUpdateTimer.Stop();
|
||||
}
|
||||
|
||||
// 导航命令(供用户跳转页面)
|
||||
[RelayCommand]
|
||||
private void NavigateHome()
|
||||
{
|
||||
// 用户实现:关闭当前窗口或切换主窗口内容
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateTest()
|
||||
{
|
||||
// 已在测试界面,可忽略
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateData()
|
||||
{
|
||||
// 跳转到数据记录界面
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateRecord()
|
||||
{
|
||||
// 跳转到记录画面
|
||||
}
|
||||
|
||||
// 内部辅助方法
|
||||
private void OnModeChanged()
|
||||
{
|
||||
if (IsBlankMode)
|
||||
{
|
||||
BlankArea = 0;
|
||||
FieldRetentionRate = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateFieldAreas();
|
||||
}
|
||||
}
|
||||
|
||||
private void AngleUpdateTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (!_isTestRunning) return;
|
||||
|
||||
// 模拟角度变化
|
||||
CurrentAngle += RotationSpeed * 0.1;
|
||||
if (CurrentAngle >= 360) CurrentAngle -= 360;
|
||||
if (CurrentAngle < 0) CurrentAngle += 360;
|
||||
|
||||
CalculateFieldAreas();
|
||||
UpdateLampData();
|
||||
}
|
||||
|
||||
private void CalculateFieldAreas()
|
||||
{
|
||||
double angle = CurrentAngle;
|
||||
LeftEyeArea = Math.Round(50 + 30 * Math.Sin(angle * Math.PI / 180) * LeftEyeCoeff, 3);
|
||||
RightEyeArea = Math.Round(50 + 30 * Math.Cos(angle * Math.PI / 180) * RightEyeCoeff, 3);
|
||||
BinocularArea = Math.Round(LeftEyeArea + RightEyeArea - 20, 3);
|
||||
LowerField = Math.Round(30 + 20 * Math.Sin(angle * Math.PI / 180), 1);
|
||||
|
||||
if (IsBlankMode)
|
||||
{
|
||||
BlankArea = Math.Round(10 + 5 * Math.Sin(angle * Math.PI / 180), 3);
|
||||
FieldRetentionRate = BinocularArea > 0
|
||||
? Math.Round((BinocularArea - BlankArea) / BinocularArea * 100, 2)
|
||||
: 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldRetentionRate = Math.Round(BinocularArea / 100 * 100 * RetentionCorrectionCoeff, 2);
|
||||
if (FieldRetentionRate > 100) FieldRetentionRate = 100;
|
||||
if (FieldRetentionRate < 0) FieldRetentionRate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLeftEyeArea()
|
||||
{
|
||||
LeftEyeArea = Math.Round(LeftEyeArea + 5, 3);
|
||||
if (LeftEyeArea > 100) LeftEyeArea = 100;
|
||||
CalculateFieldAreas();
|
||||
}
|
||||
|
||||
private void UpdateRightEyeArea()
|
||||
{
|
||||
RightEyeArea = Math.Round(RightEyeArea + 5, 3);
|
||||
if (RightEyeArea > 100) RightEyeArea = 100;
|
||||
CalculateFieldAreas();
|
||||
}
|
||||
|
||||
private void UpdateLampData()
|
||||
{
|
||||
Random rand = new Random();
|
||||
UpperLampData = Math.Round(rand.NextDouble() * 100, 2);
|
||||
MiddleLampData = Math.Round(rand.NextDouble() * 100, 2);
|
||||
LowerLampData = Math.Round(rand.NextDouble() * 100, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,87 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:MembranePoreTester"
|
||||
Title="设备控制系统"
|
||||
Width="1024" Height="768"
|
||||
Title="智能设备控制平台"
|
||||
Width="1024" MinHeight="768" WindowState="Maximized"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="NoResize"
|
||||
Background="{StaticResource WindowBackground}">
|
||||
ResizeMode="CanResize"
|
||||
Background="#F5F7FA" FontFamily="Segoe UI">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 统一按钮样式 -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#2196F3"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1976D2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#0D47A1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 统一文本框样式 -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5,2"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一标签样式 -->
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="#2C3E50"/>
|
||||
<Setter Property="Margin" Value="5,5,5,5"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一GroupBox样式 -->
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#E9ECF0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||
<Setter Property="Padding" Value="12"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GroupBox">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{TemplateBinding Header}" FontWeight="SemiBold" FontSize="14" Margin="0,0,0,12"/>
|
||||
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -14,25 +90,31 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextBlock Grid.Row="0" Text="智能设备控制平台" FontSize="24" FontWeight="Bold"
|
||||
Foreground="White" HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
||||
<!-- 标题栏 -->
|
||||
<Border Grid.Row="0" Background="#2196F3" CornerRadius="6" Padding="12" Margin="0,0,0,12">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<TextBlock Text="智能设备控制平台" FontSize="24" FontWeight="Bold" Foreground="White"/>
|
||||
<TextBlock Text="v1.0" FontSize="14" Foreground="#E3F2FD" VerticalAlignment="Bottom" Margin="10,0,0,5"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 主要内容区 -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧参数区 -->
|
||||
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="0,0,12,0">
|
||||
<!-- 灯条数据组 -->
|
||||
<GroupBox Header="灯条数据">
|
||||
<GroupBox Header="📊 灯条数据">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -44,46 +126,46 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 第1行 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="上灯条数据1"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.UpperLampData1, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="下灯条数据1"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DeviceData.LowerLampData1, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="上灯条数据2"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.UpperLampData2, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="下灯条数据2"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DeviceData.LowerLampData2, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="上灯条数据3"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding DeviceData.UpperLampData3, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="下灯条数据3"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding DeviceData.LowerLampData3, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData1" Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.UpperLampData1, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="0" Grid.Column="2" Content="上灯条数据4"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding DeviceData.UpperLampData4, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData4" Grid.Row="0" Grid.Column="3" Text="{Binding DeviceData.UpperLampData4, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第2行 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="下灯条数据1"/>
|
||||
<TextBox IsReadOnly="True" Grid.Row="1" Name="txtLowerLampData1" Grid.Column="1" Text="{Binding DeviceData.LowerLampData1, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="1" Grid.Column="2" Content="下灯条数据4"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="3" Text="{Binding DeviceData.LowerLampData4, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Grid.Row="1" Name="txtLowerLampData4" Grid.Column="3" Text="{Binding DeviceData.LowerLampData4, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第3行 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="上灯条数据2"/>
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData2" Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.UpperLampData2, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="2" Grid.Column="2" Content="上灯条数据5"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding DeviceData.UpperLampData5, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData5" Grid.Row="2" Grid.Column="3" Text="{Binding DeviceData.UpperLampData5, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第4行 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="下灯条数据2"/>
|
||||
<TextBox IsReadOnly="True" Grid.Row="3" Name="txtLowerLampData2" Grid.Column="1" Text="{Binding DeviceData.LowerLampData2, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="3" Grid.Column="2" Content="下灯条数据5"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="3" Text="{Binding DeviceData.LowerLampData5, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtLowerLampData5" Grid.Row="3" Grid.Column="3" Text="{Binding DeviceData.LowerLampData5, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第5行 -->
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="上灯条数据3"/>
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData3" Grid.Row="4" Grid.Column="1" Text="{Binding DeviceData.UpperLampData3, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="4" Grid.Column="2" Content="上灯条数据6"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="3" Text="{Binding DeviceData.UpperLampData6, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtUpperLampData6" Grid.Row="4" Grid.Column="3" Text="{Binding DeviceData.UpperLampData6, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第6行 -->
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="下灯条数据3"/>
|
||||
<TextBox IsReadOnly="True" Grid.Row="5" Name="txtLowerLampData3" Grid.Column="1" Text="{Binding DeviceData.LowerLampData3, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="5" Grid.Column="2" Content="下灯条数据6"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="3" Text="{Binding DeviceData.LowerLampData6, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtLowerLampData6" Grid.Row="5" Grid.Column="3" Text="{Binding DeviceData.LowerLampData6, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 面积系数组 -->
|
||||
<GroupBox Header="面积系数">
|
||||
<!-- 面积系数与保存率矫正系数 -->
|
||||
<GroupBox Header="📐 面积系数">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -96,53 +178,53 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="左眼面积系数"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.LeftEyeAreaCoeff, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox x:Name="txtLeftEyeAreaCoeff" Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.LeftEyeAreaCoeff, UpdateSourceTrigger=PropertyChanged}" Width="120"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="右眼面积系数"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DeviceData.RightEyeAreaCoeff, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox x:Name="txtRightEyeAreaCoeff" Grid.Row="1" Grid.Column="1" Text="{Binding DeviceData.RightEyeAreaCoeff, UpdateSourceTrigger=PropertyChanged}" Width="120"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="保存率矫正系数"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.SaveRateCorrectionCoeff, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox x:Name="txtSaveRateCorrectionCoeff" Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.SaveRateCorrectionCoeff, UpdateSourceTrigger=PropertyChanged}" Width="120"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 中灯数据组 -->
|
||||
<GroupBox Header="中灯数据">
|
||||
<GroupBox Header="💡 中灯数据">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 第1行 -->
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="中灯1"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp1, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp1" Grid.Row="0" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp1, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="0" Grid.Column="2" Content="中灯5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp5" Grid.Row="0" Grid.Column="3" Text="{Binding DeviceData.MiddleLamp5, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第2行 -->
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="中灯2"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp2, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp2" Grid.Row="1" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp2, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="1" Grid.Column="2" Content="中灯6"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp6" Grid.Row="1" Grid.Column="3" Text="{Binding DeviceData.MiddleLamp6, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第3行 -->
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="中灯3"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp3, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp3" Grid.Row="2" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp3, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Grid.Row="2" Grid.Column="2" Content="中灯7"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp7" Grid.Row="2" Grid.Column="3" Text="{Binding DeviceData.MiddleLamp7, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
|
||||
<!-- 第4行 -->
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="中灯4"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp4, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="中灯5"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp5, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="中灯6"/>
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp6, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="中灯7"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp7, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
|
||||
<TextBox IsReadOnly="True" Name="txtMiddleLamp4" Grid.Row="3" Grid.Column="1" Text="{Binding DeviceData.MiddleLamp4, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
@@ -150,40 +232,49 @@
|
||||
|
||||
<!-- 右侧控制区 -->
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<!-- 电机限位 -->
|
||||
<GroupBox Header="电机状态">
|
||||
<!-- 电机状态 -->
|
||||
<GroupBox Header="⚙️ 电机状态">
|
||||
<StackPanel Margin="5">
|
||||
<Label Content="电机限位"/>
|
||||
<TextBox Text="{Binding DeviceData.MotorLimit, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,12"/>
|
||||
<Button Content="保存参数" Command="{Binding SaveParametersCommand}" Margin="0,5"/>
|
||||
<Button Content="读取参数" Command="{Binding LoadParametersCommand}" Margin="0,5"/>
|
||||
<Label Content="电机限位" FontWeight="SemiBold"/>
|
||||
<TextBox x:Name="txtMotorLimit" Text="{Binding DeviceData.MotorLimit, UpdateSourceTrigger=PropertyChanged}" Height="28" Margin="0,0,0,12"/>
|
||||
<Label Content="复位补偿" FontWeight="SemiBold"/>
|
||||
<TextBox x:Name="txtResetCompensation" Text="{Binding DeviceData.MotorLimit, UpdateSourceTrigger=PropertyChanged}" Height="28" Margin="0,0,0,12"/>
|
||||
<!--<Button Content="💾 保存参数" Command="{Binding SaveParametersCommand}" Margin="0,5" Background="#4CAF50"/>
|
||||
<Button Content="🔄 读取参数" Command="{Binding LoadParametersCommand}" Margin="0,5" Background="#FF9800"/>-->
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 控制按钮组 -->
|
||||
<GroupBox Header="设备控制">
|
||||
<GroupBox Header="🎮 设备控制">
|
||||
<WrapPanel Margin="5">
|
||||
<Button Content="复位" Command="{Binding ResetCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="左眼开" Command="{Binding OpenLeftEyeCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="反转" Command="{Binding ReverseCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="右眼开" Command="{Binding OpenRightEyeCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="正转" Command="{Binding ForwardCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="🔄 复位" Command="{Binding ResetCommand}" Width="80" Background="#607D8B"/>
|
||||
<Button Content="👁️ 左眼开" Command="{Binding OpenLeftEyeCommand}" Width="80" Background="#4CAF50"/>
|
||||
<Button Content="↩️ 反转" Command="{Binding ReverseCommand}" Width="80" Background="#FF9800"/>
|
||||
<Button Content="👁️ 右眼开" Command="{Binding OpenRightEyeCommand}" Width="80" Background="#4CAF50"/>
|
||||
<Button Content="↪️ 正转" Command="{Binding ForwardCommand}" Width="80" Background="#FF9800"/>
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 通信状态(可选) -->
|
||||
<GroupBox Header="通信状态">
|
||||
<TextBlock Text="{Binding CommunicationStatus}" Foreground="{Binding StatusColor}" FontWeight="Bold" Margin="5"/>
|
||||
<!-- 通信状态 -->
|
||||
<GroupBox Header="📡 通信状态">
|
||||
<Border Background="#F8F9FA" CornerRadius="4" Padding="10" Margin="5">
|
||||
<StackPanel>
|
||||
<Ellipse Width="12" Height="12" Fill="{Binding StatusColor}" Margin="0,0,0,5" HorizontalAlignment="Left"/>
|
||||
<TextBlock Text="{Binding CommunicationStatus}" FontWeight="Bold" Foreground="{Binding StatusColor}" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 底部导航栏 -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,12,0,0">
|
||||
<Button Content="主页" Command="{Binding NavigateHomeCommand}" Width="100" Margin="5"/>
|
||||
<Button Content="测试界面" Command="{Binding NavigateTestCommand}" Width="100" Margin="5"/>
|
||||
<Button Content="数据记录" Command="{Binding NavigateDataRecordCommand}" Width="100" Margin="5"/>
|
||||
<Button Content="记录画面" Command="{Binding NavigateRecordScreenCommand}" Width="100" Margin="5"/>
|
||||
</StackPanel>
|
||||
<Border Grid.Row="2" Background="White" CornerRadius="6" Padding="10" Margin="0,12,0,0" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<WrapPanel HorizontalAlignment="Center">
|
||||
<Button Content="🏠 主页" Command="{Binding NavigateHomeCommand}" Width="100" Background="#607D8B"/>
|
||||
<Button Content="🧪 测试界面" Command="{Binding NavigateTestCommand}" Width="100" Background="#2196F3"/>
|
||||
<Button Content="📊 数据记录" Command="{Binding NavigateDataRecordCommand}" Width="100" Background="#4CAF50"/>
|
||||
<Button Content="📸 记录画面" Command="{Binding NavigateRecordScreenCommand}" Width="100" Background="#FF9800"/>
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,14 +1,189 @@
|
||||
using System.Windows;
|
||||
using MembranePoreTester.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
using MembranePoreTester.Communication;
|
||||
|
||||
namespace MembranePoreTester
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly PlcConfiguration _config;
|
||||
private DispatcherTimer _autoRefreshTimer;
|
||||
|
||||
// 文本框映射:(文本框, 地址, 参数名, 是否浮点数)
|
||||
private readonly List<(TextBox textBox, ushort address, string name, bool isFloat)> _textBoxMapping = new();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new MainViewModel();
|
||||
_plcService = App.PlcService;
|
||||
_config = App.PlcConfig;
|
||||
|
||||
// 16位整数(灯条、中灯、电机)
|
||||
AddMapping(txtUpperLampData1, _config.UpperLampData1, "上灯条数据1", false);
|
||||
AddMapping(txtLowerLampData1, _config.LowerLampData1, "下灯条数据1", false);
|
||||
AddMapping(txtUpperLampData2, _config.UpperLampData2, "上灯条数据2", false);
|
||||
AddMapping(txtLowerLampData2, _config.LowerLampData2, "下灯条数据2", false);
|
||||
AddMapping(txtUpperLampData3, _config.UpperLampData3, "上灯条数据3", false);
|
||||
AddMapping(txtLowerLampData3, _config.LowerLampData3, "下灯条数据3", false);
|
||||
AddMapping(txtUpperLampData4, _config.UpperLampData4, "上灯条数据4", false);
|
||||
AddMapping(txtLowerLampData4, _config.LowerLampData4, "下灯条数据4", false);
|
||||
AddMapping(txtUpperLampData5, _config.UpperLampData5, "上灯条数据5", false);
|
||||
AddMapping(txtLowerLampData5, _config.LowerLampData5, "下灯条数据5", false);
|
||||
AddMapping(txtUpperLampData6, _config.UpperLampData6, "上灯条数据6", false);
|
||||
AddMapping(txtLowerLampData6, _config.LowerLampData6, "下灯条数据6", false);
|
||||
|
||||
AddMapping(txtMiddleLamp1, _config.MiddleLamp1, "中灯1", false);
|
||||
AddMapping(txtMiddleLamp2, _config.MiddleLamp2, "中灯2", false);
|
||||
AddMapping(txtMiddleLamp3, _config.MiddleLamp3, "中灯3", false);
|
||||
AddMapping(txtMiddleLamp4, _config.MiddleLamp4, "中灯4", false);
|
||||
AddMapping(txtMiddleLamp5, _config.MiddleLamp5, "中灯5", false);
|
||||
AddMapping(txtMiddleLamp6, _config.MiddleLamp6, "中灯6", false);
|
||||
AddMapping(txtMiddleLamp7, _config.MiddleLamp7, "中灯7", false);
|
||||
|
||||
AddMapping(txtMotorLimit, _config.MotorLimit, "电机限位", true);
|
||||
AddMapping(txtResetCompensation, _config.ResetCompensation, "复位补偿", true);
|
||||
|
||||
|
||||
AddMapping(txtLeftEyeAreaCoeff, _config.LeftEyeAreaCoeff, "左眼面积系数", true);
|
||||
AddMapping(txtRightEyeAreaCoeff, _config.RightEyeAreaCoeff, "右眼面积系数", true);
|
||||
AddMapping(txtSaveRateCorrectionCoeff, _config.SaveRateCorrectionCoeff, "保存率矫正系数", true);
|
||||
|
||||
RegisterTextBoxEvents();
|
||||
|
||||
Loaded += async (s, e) =>
|
||||
{
|
||||
_autoRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||
_autoRefreshTimer.Tick += AutoRefreshTimer_Tick;
|
||||
_autoRefreshTimer.Start();
|
||||
await LoadAllParameters();
|
||||
};
|
||||
|
||||
Closed += (s, e) => _autoRefreshTimer?.Stop();
|
||||
}
|
||||
|
||||
private void AddMapping(TextBox textBox, ushort address, string name, bool isFloat)
|
||||
{
|
||||
if (textBox != null)
|
||||
{
|
||||
_textBoxMapping.Add((textBox, address, name, isFloat));
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterTextBoxEvents()
|
||||
{
|
||||
foreach (var (tb, addr, name, isFloat) in _textBoxMapping)
|
||||
{
|
||||
tb.LostFocus += async (s, e) =>
|
||||
{
|
||||
if (float.TryParse(tb.Text, out float value))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isFloat)
|
||||
await WriteFloatAsync(addr, value);
|
||||
else
|
||||
await WriteInt16Async(addr, (ushort)value);
|
||||
System.Diagnostics.Debug.WriteLine($"{name} 已写入: {value}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"写入 {name} 失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show($"请输入有效的数值", "输入错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
await UpdateSingleParameter(tb, addr, isFloat);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async void AutoRefreshTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
foreach (var (tb, addr, name, isFloat) in _textBoxMapping)
|
||||
{
|
||||
if (!tb.IsFocused)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isFloat)
|
||||
{
|
||||
float val = await ReadFloatAsync(addr);
|
||||
tb.Text = val.ToString("F3");
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort val = await ReadInt16Async(addr);
|
||||
tb.Text = val.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"自动刷新 {name} 失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task UpdateSingleParameter(TextBox tb, ushort addr, bool isFloat)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isFloat)
|
||||
{
|
||||
float val = await ReadFloatAsync(addr);
|
||||
tb.Text = val.ToString("F3");
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort val = await ReadInt16Async(addr);
|
||||
tb.Text = val.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"更新失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAllParameters()
|
||||
{
|
||||
foreach (var (tb, addr, name, isFloat) in _textBoxMapping)
|
||||
{
|
||||
await UpdateSingleParameter(tb, addr, isFloat);
|
||||
}
|
||||
}
|
||||
|
||||
// 16位整数读写
|
||||
private async Task<ushort> ReadInt16Async(ushort address)
|
||||
{
|
||||
var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 1);
|
||||
return registers[0];
|
||||
}
|
||||
|
||||
private async Task WriteInt16Async(ushort address, ushort value)
|
||||
{
|
||||
await ((ModbusTcpPlcService)_plcService).WriteSingleRegisterAsync(address, value);
|
||||
}
|
||||
|
||||
// 32位浮点数读写
|
||||
private async Task<float> ReadFloatAsync(ushort address)
|
||||
{
|
||||
var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 2);
|
||||
return ((ModbusTcpPlcService)_plcService).UshortToFloat(registers[1], registers[0]);
|
||||
}
|
||||
|
||||
private async Task WriteFloatAsync(ushort address, float value)
|
||||
{
|
||||
await ((ModbusTcpPlcService)_plcService).WriteMultipleRegistersAsync(address, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
245
Views/TestView.xaml
Normal file
245
Views/TestView.xaml
Normal file
@@ -0,0 +1,245 @@
|
||||
<Window x:Class="MembranePoreTester.TestView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="测试界面"
|
||||
Width="1024" MinHeight="768" WindowState="Maximized"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Background="#F5F7FA" FontFamily="Segoe UI" KeyDown="Window_KeyDown">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 统一按钮样式(与 MainWindow 一致) -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#2196F3"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1976D2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#0D47A1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 统一文本框样式 -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5,2"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 只读文本框样式 -->
|
||||
<Style x:Key="ReadOnlyTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="Background" Value="#F5F5F5"/>
|
||||
<Setter Property="Foreground" Value="#2196F3"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一标签样式 -->
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="#2C3E50"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一GroupBox样式 -->
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#E9ECF0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||
<Setter Property="Padding" Value="12"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GroupBox">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{TemplateBinding Header}" FontWeight="SemiBold" FontSize="14" Margin="0,0,0,12"/>
|
||||
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 统一 RadioButton 样式 -->
|
||||
<Style TargetType="RadioButton">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏(蓝底卡片) -->
|
||||
<Border Grid.Row="0" Background="#2196F3" CornerRadius="6" Padding="12" Margin="0,0,0,12">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="测试界面" FontSize="20" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<TextBlock Text="当前模式:" Foreground="White" FontSize="14" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<Border Background="White" CornerRadius="20" Padding="12,4">
|
||||
<TextBlock Text="{Binding CurrentMode}" Foreground="#2196F3" FontSize="14" FontWeight="Bold"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" >
|
||||
<RadioButton Content="空白模式" IsChecked="{Binding IsBlankMode}" GroupName="TestMode" Foreground="White" FontSize="13"/>
|
||||
<RadioButton Content="试样测试" IsChecked="{Binding IsSampleMode}" GroupName="TestMode" Foreground="White" FontSize="13" Margin="10,5,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 参数设置区域 -->
|
||||
<GroupBox Grid.Row="1" Header="⚙️ 参数设置" Margin="0,0,0,12">
|
||||
<Grid Margin="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Label Content="分辨角度:" Margin="0,0,5,0"/>
|
||||
<TextBox Text="{Binding ResolutionAngle, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Content="°" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Label Content="转动速度:" Margin="0,0,5,0"/>
|
||||
<TextBox Text="{Binding RotationSpeed, UpdateSourceTrigger=PropertyChanged}" Width="80"/>
|
||||
<Label Content="°/s" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Label Content="当前角度:" Margin="0,0,5,0"/>
|
||||
<TextBox Text="{Binding CurrentAngle, UpdateSourceTrigger=PropertyChanged}" Width="80" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="°" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 视野面积数据区域 -->
|
||||
<GroupBox Grid.Row="2" Header="👁️ 视野面积数据" Margin="0,0,0,12">
|
||||
<Grid Margin="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="左目视野面积:" Width="110"/>
|
||||
<TextBox Text="{Binding LeftEyeArea}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="cm²" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="右目视野面积:" Width="110"/>
|
||||
<TextBox Text="{Binding RightEyeArea}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="cm²" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="双目视野面积:" Width="110"/>
|
||||
<TextBox Text="{Binding BinocularArea}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="cm²" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="下方视野:" Width="110"/>
|
||||
<TextBox Text="{Binding LowerField}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="°" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Grid.Column="0" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="空白视野面积:" Width="110"/>
|
||||
<TextBox Text="{Binding BlankArea}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="cm²" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" Margin="5">
|
||||
<Label Content="视野保存率:" Width="110"/>
|
||||
<TextBox Text="{Binding FieldRetentionRate}" Width="100" IsReadOnly="True" Background="#F5F5F5" Foreground="#2196F3" FontWeight="Bold"/>
|
||||
<Label Content="%" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 控制按钮区域 -->
|
||||
<GroupBox Grid.Row="3" Header="🎮 设备控制" Margin="0,0,0,12">
|
||||
<WrapPanel HorizontalAlignment="Center" Margin="5">
|
||||
<Button Content="🔄 复位" Command="{Binding ResetCommand}" Background="#607D8B"/>
|
||||
<Button Content="👁️ 左眼开" Command="{Binding OpenLeftEyeCommand}" Background="#4CAF50"/>
|
||||
<Button Content="↩️ 反转" Command="{Binding ReverseCommand}" Background="#FF9800"/>
|
||||
<Button Content="👁️ 右眼开" Command="{Binding OpenRightEyeCommand}" Background="#4CAF50"/>
|
||||
<Button Content="↪️ 正转" Command="{Binding ForwardCommand}" Background="#FF9800"/>
|
||||
<Button Content="▶️ 测试" Command="{Binding StartTestCommand}" Background="#2196F3"/>
|
||||
<Button Content="⏹️ 停止" Command="{Binding StopTestCommand}" Background="#F44336"/>
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 底部导航栏 -->
|
||||
<Border Grid.Row="4" Background="White" CornerRadius="6" Padding="10" Margin="0,12,0,0" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<WrapPanel HorizontalAlignment="Center">
|
||||
<Button Content="🏠 主页" Command="{Binding NavigateHomeCommand}" Width="100" Background="#607D8B"/>
|
||||
<Button Content="🧪 测试界面" Command="{Binding NavigateTestCommand}" Width="100" Background="#2196F3"/>
|
||||
<Button Content="📊 数据记录" Command="{Binding NavigateDataRecordCommand}" Width="100" Background="#4CAF50"/>
|
||||
<Button Content="📸 记录画面" Command="{Binding NavigateRecordScreenCommand}" Width="100" Background="#FF9800"/>
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Window>
|
||||
29
Views/TestView.xaml.cs
Normal file
29
Views/TestView.xaml.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using MembranePoreTester.ViewModels;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MembranePoreTester
|
||||
{
|
||||
/// <summary>
|
||||
/// TestView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class TestView : Window
|
||||
{
|
||||
public TestView()
|
||||
{
|
||||
InitializeComponent();
|
||||
// 如果需要在打开时自动开始模拟,可以在此设置 DataContext
|
||||
DataContext = new TestViewModel();
|
||||
}
|
||||
|
||||
private void Window_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.P)
|
||||
{
|
||||
var win = new MainWindow();
|
||||
win.Owner = this;
|
||||
win.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,55 +3,6 @@
|
||||
"IpAddress": "192.168.1.10",
|
||||
"Port": 502,
|
||||
"SlaveId": 1,
|
||||
"PressureRegisterStation1": 6,
|
||||
"PressureRegisterStation2": 8,
|
||||
"PressureRegisterStation3": 10,
|
||||
"PressureModeRegister": 200, // 高压/低压选择寄存器(0=低压,1=高压)
|
||||
"PressCoil": 100, // 加压线圈(M100)
|
||||
"StartCoil": 20, // 启动线圈(M20)
|
||||
"EnableCoil": 21, // 使能线圈(M21)
|
||||
"StopCoil": 7, // 停止线圈(M7)
|
||||
"PressureFactor": 1.0,
|
||||
"WetFlowRegister": 2,
|
||||
"DryFlowRegister": 4,
|
||||
"WetFlowFactor": 1.0,
|
||||
"DryFlowFactor": 1.0,
|
||||
|
||||
|
||||
|
||||
|
||||
"PressureUpperLimit": 300,
|
||||
"PressureRate": 280,
|
||||
"PressureCoeff": 282,
|
||||
"HPCoeff1": 3120,
|
||||
"LPCoeff1": 3122,
|
||||
"HPCoeff2": 3124,
|
||||
"LPCoeff2": 3126,
|
||||
"HPCoeff3": 3128,
|
||||
"LPCoeff3": 3130,
|
||||
"LargeFlowCoeff1": 3048,
|
||||
"SmallFlowCoeff1": 380,
|
||||
"LargeFlowCoeff2": 1218,
|
||||
"SmallFlowCoeff2": 1318,
|
||||
"LargeFlowCoeff3": 1418,
|
||||
"SmallFlowCoeff3": 1468,
|
||||
"FlowModeRegister1": 5,
|
||||
"FlowModeRegister2": 6,
|
||||
"FlowModeRegister3": 7,
|
||||
"PressureCalibZero": 3200,
|
||||
"PressureCalibSpan": 3202,
|
||||
"FlowCalibZero": 3204,
|
||||
"FlowCalibSpan": 3206
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user