Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df36b65b30 | ||
|
|
b2da230e24 | ||
|
|
f9554447ec | ||
|
|
9a6adab2a3 | ||
|
|
4030c18dc8 | ||
|
|
7c7339d72c | ||
|
|
9991a82d19 | ||
|
|
164e4db95b | ||
|
|
49f1cc0cf7 | ||
|
|
65815607bd | ||
|
|
b79c34bba7 | ||
|
|
0cba5198f2 | ||
|
|
263fffbcf5 | ||
|
|
df5c7566fb | ||
|
|
17d9904898 | ||
|
|
43893e5da6 | ||
|
|
a4a95e6cf3 | ||
|
|
9da775aa37 | ||
|
|
954afaaf39 | ||
|
|
61420da42e | ||
|
|
070463ae8e | ||
| 8f625a7a41 | |||
| 81b257cbdc | |||
| 73bcebf598 | |||
| a9587a90b3 | |||
|
|
18d317623d | ||
|
|
ce62b19960 | ||
|
|
527169bffb | ||
|
|
7b25ef07cc | ||
|
|
1b6624c262 | ||
|
|
69557bc108 | ||
| c850099e2c | |||
|
|
eb5fa4a948 | ||
|
|
0966f1f4f6 | ||
|
|
7cd88de040 | ||
|
|
4af7a825db | ||
|
|
b2286cd514 | ||
| dc4562e9ae | |||
|
|
1e1f1e930e | ||
|
|
375399607c | ||
| 4a435db5c3 | |||
|
|
8ed011f91e | ||
|
|
2f4388723c | ||
|
|
00c224ceff | ||
|
|
8d504d91e5 | ||
|
|
72fac0e41b | ||
|
|
b80edaea78 | ||
| 57ccef3f5b |
17
App.xaml
17
App.xaml
@@ -1,6 +1,19 @@
|
||||
<Application x:Class="TabletTester2025.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
<Application.Resources>
|
||||
<sys:Double x:Key="SmallTextFontSize">14</sys:Double>
|
||||
<sys:Double x:Key="TableFontSize">14</sys:Double>
|
||||
<sys:Double x:Key="BaseFontSize">16</sys:Double>
|
||||
<sys:Double x:Key="ButtonFontSize">16</sys:Double>
|
||||
<sys:Double x:Key="TabFontSize">17</sys:Double>
|
||||
<sys:Double x:Key="ParamValueFontSize">19</sys:Double>
|
||||
<sys:Double x:Key="LargeInputFontSize">24</sys:Double>
|
||||
<sys:Double x:Key="TitleFontSize">24</sys:Double>
|
||||
<sys:Double x:Key="MetricValueFontSize">28</sys:Double>
|
||||
<sys:Double x:Key="KeypadButtonFontSize">22</sys:Double>
|
||||
<sys:Double x:Key="KeypadCommandFontSize">18</sys:Double>
|
||||
<sys:Double x:Key="KeypadValueFontSize">28</sys:Double>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
</Application>
|
||||
|
||||
16
App.xaml.cs
16
App.xaml.cs
@@ -56,6 +56,7 @@ namespace TabletTester2025
|
||||
|
||||
-- 硬度
|
||||
HardnessAvg REAL,
|
||||
HardnessAverageDeviation REAL,
|
||||
HardnessRSD REAL,
|
||||
HardnessMax REAL,
|
||||
HardnessMin REAL,
|
||||
@@ -106,6 +107,20 @@ namespace TabletTester2025
|
||||
";
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
var hardnessSampleCmd = connection.CreateCommand();
|
||||
hardnessSampleCmd.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS HardnessSamplePoints (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
TestBatchId INTEGER NOT NULL,
|
||||
SequenceNo INTEGER NOT NULL,
|
||||
Value REAL NOT NULL,
|
||||
DeviationFromAverage REAL NOT NULL,
|
||||
RecordedAt TEXT NOT NULL,
|
||||
FOREIGN KEY(TestBatchId) REFERENCES TestBatches(Id) ON DELETE CASCADE
|
||||
);
|
||||
";
|
||||
hardnessSampleCmd.ExecuteNonQuery();
|
||||
|
||||
var sampleCmd = connection.CreateCommand();
|
||||
sampleCmd.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS DissolutionSamplePoints (
|
||||
@@ -180,6 +195,7 @@ CREATE TABLE IF NOT EXISTS DissolutionSamplePoints (
|
||||
{
|
||||
("TestBatches", "HardnessMax", "REAL", "0"),
|
||||
("TestBatches", "HardnessMin", "REAL", "0"),
|
||||
("TestBatches", "HardnessAverageDeviation", "REAL", "0"),
|
||||
("TestBatches", "HardnessTestCount", "INTEGER", "6"),
|
||||
("TestBatches", "HardnessInternalMin", "REAL", "40"),
|
||||
("TestBatches", "HardnessInternalMax", "REAL", "60"),
|
||||
|
||||
@@ -18,12 +18,20 @@ namespace TabletTester2025.Data
|
||||
}
|
||||
|
||||
public DbSet<TestBatch> TestBatches { get; set; }
|
||||
public DbSet<HardnessSamplePoint> HardnessSamplePoints { get; set; }
|
||||
public DbSet<DissolutionSamplePoint> DissolutionSamplePoints { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<TestBatch>().ToTable("TestBatches");
|
||||
modelBuilder.Entity<TestBatch>().HasKey(t => t.Id);
|
||||
modelBuilder.Entity<HardnessSamplePoint>().ToTable("HardnessSamplePoints");
|
||||
modelBuilder.Entity<HardnessSamplePoint>().HasKey(t => t.Id);
|
||||
modelBuilder.Entity<HardnessSamplePoint>()
|
||||
.HasOne<TestBatch>()
|
||||
.WithMany(t => t.HardnessSamples)
|
||||
.HasForeignKey(t => t.TestBatchId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<DissolutionSamplePoint>().ToTable("DissolutionSamplePoints");
|
||||
modelBuilder.Entity<DissolutionSamplePoint>().HasKey(t => t.Id);
|
||||
modelBuilder.Entity<DissolutionSamplePoint>()
|
||||
|
||||
@@ -23,12 +23,31 @@ namespace TabletTester2025.Services
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public void InsertBatch(TestBatch batch, IEnumerable<DissolutionSamplePoint> dissolutionSamples)
|
||||
public void InsertBatch(
|
||||
TestBatch batch,
|
||||
IEnumerable<DissolutionSamplePoint> dissolutionSamples,
|
||||
IEnumerable<HardnessSamplePoint>? hardnessSamples = null)
|
||||
{
|
||||
using var db = new AppDbContext(_connectionString);
|
||||
db.TestBatches.Add(batch);
|
||||
db.SaveChanges();
|
||||
|
||||
var hardnessDetails = (hardnessSamples ?? Enumerable.Empty<HardnessSamplePoint>())
|
||||
.Where(s => double.IsFinite(s.Value) && s.Value > 0)
|
||||
.OrderBy(s => s.SequenceNo)
|
||||
.Select(s => new HardnessSamplePoint
|
||||
{
|
||||
TestBatchId = batch.Id,
|
||||
SequenceNo = s.SequenceNo,
|
||||
Value = s.Value,
|
||||
DeviationFromAverage = s.DeviationFromAverage,
|
||||
RecordedAt = s.RecordedAt
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (hardnessDetails.Count > 0)
|
||||
db.HardnessSamplePoints.AddRange(hardnessDetails);
|
||||
|
||||
var samples = dissolutionSamples
|
||||
.Where(s => s.Percent.HasValue)
|
||||
.Select(s => new DissolutionSamplePoint
|
||||
@@ -42,22 +61,39 @@ namespace TabletTester2025.Services
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (samples.Count == 0)
|
||||
if (samples.Count > 0)
|
||||
db.DissolutionSamplePoints.AddRange(samples);
|
||||
|
||||
if (hardnessDetails.Count == 0 && samples.Count == 0)
|
||||
return;
|
||||
|
||||
db.DissolutionSamplePoints.AddRange(samples);
|
||||
db.SaveChanges();
|
||||
batch.HardnessSamples = hardnessDetails;
|
||||
batch.DissolutionSamples = samples;
|
||||
}
|
||||
|
||||
public List<TestBatch> GetBatches(int? stationId = null, int limit = 100)
|
||||
{
|
||||
using var db = new AppDbContext(_connectionString);
|
||||
var query = db.TestBatches.Include(b => b.DissolutionSamples).AsQueryable();
|
||||
var query = db.TestBatches
|
||||
.Include(b => b.HardnessSamples)
|
||||
.Include(b => b.DissolutionSamples)
|
||||
.AsQueryable();
|
||||
if (stationId.HasValue)
|
||||
query = query.Where(b => b.StationId == stationId.Value);
|
||||
query = query.OrderByDescending(b => b.TestTime).Take(limit);
|
||||
return query.ToList();
|
||||
}
|
||||
|
||||
public void DeleteBatch(int id)
|
||||
{
|
||||
using var db = new AppDbContext(_connectionString);
|
||||
var batch = db.TestBatches.Find(id);
|
||||
if (batch != null)
|
||||
{
|
||||
db.TestBatches.Remove(batch);
|
||||
db.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
Models/HardnessSamplePoint.cs
Normal file
51
Models/HardnessSamplePoint.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
|
||||
namespace TabletTester2025.Models
|
||||
{
|
||||
public class HardnessSamplePoint : ObservableObject
|
||||
{
|
||||
private int _id;
|
||||
private int _testBatchId;
|
||||
private int _sequenceNo;
|
||||
private double _value;
|
||||
private double _deviationFromAverage;
|
||||
private DateTime _recordedAt;
|
||||
|
||||
public int Id
|
||||
{
|
||||
get => _id;
|
||||
set => SetProperty(ref _id, value);
|
||||
}
|
||||
|
||||
public int TestBatchId
|
||||
{
|
||||
get => _testBatchId;
|
||||
set => SetProperty(ref _testBatchId, value);
|
||||
}
|
||||
|
||||
public int SequenceNo
|
||||
{
|
||||
get => _sequenceNo;
|
||||
set => SetProperty(ref _sequenceNo, value);
|
||||
}
|
||||
|
||||
public double Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetProperty(ref _value, value);
|
||||
}
|
||||
|
||||
public double DeviationFromAverage
|
||||
{
|
||||
get => _deviationFromAverage;
|
||||
set => SetProperty(ref _deviationFromAverage, value);
|
||||
}
|
||||
|
||||
public DateTime RecordedAt
|
||||
{
|
||||
get => _recordedAt;
|
||||
set => SetProperty(ref _recordedAt, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
public string IpAddress { get; set; }
|
||||
public int Port { get; set; }
|
||||
public byte SlaveId { get; set; }
|
||||
public PlcFloatWordOrder FloatWordOrder { get; set; } = PlcFloatWordOrder.LowWordFirst;
|
||||
|
||||
// 硬度
|
||||
public ushort HardnessMax { get; set; }
|
||||
@@ -14,7 +15,8 @@
|
||||
|
||||
public ushort HardnessSudu { get; set; }
|
||||
public ushort HardnessWeiyi { get; set; }
|
||||
|
||||
public ushort HardnessLimit { get; set; }
|
||||
|
||||
public ushort HardnessForward { get; set; }
|
||||
public ushort HardnessBack { get; set; }
|
||||
|
||||
@@ -22,12 +24,19 @@
|
||||
// 兼容旧代码:硬度完成线圈与溶出或硬度实时值寄存器地址
|
||||
public ushort HardnessOver { get; set; }
|
||||
public ushort HardnessCompleteCoil { get; set; }
|
||||
public ushort HardnessStartOver { get; set; }
|
||||
public ushort HardnessPoSun { get; set; }
|
||||
public ushort HardnessPressure { get; set; }
|
||||
// 脆碎度
|
||||
public ushort FriabilityStartCoil { get; set; }
|
||||
public ushort FriabilityRpm { get; set; }
|
||||
public ushort FriabilityRounds { get; set; }
|
||||
public ushort FriabilityRoundsBox { get; set; } // 兼容旧配置字段
|
||||
public ushort FriabilityRealtimeRounds { get; set; }
|
||||
public ushort FriabilityTestTime { get; set; }
|
||||
public ushort FriabilityWeightBefore { get; set; }
|
||||
public ushort FriabilityWeightAfter { get; set; }
|
||||
public ushort FriabilityLossPercent { get; set; }
|
||||
public ushort WeightBefore { get; set; } // 天平重量寄存器(可选)
|
||||
public ushort WeightAfter { get; set; }
|
||||
public ushort FriabilityStartCoil2 { get; set; }
|
||||
@@ -49,6 +58,8 @@
|
||||
|
||||
// 溶出
|
||||
public ushort DissolutionRpm { get; set; }
|
||||
public ushort Dissolution1Speed { get; set; }
|
||||
public ushort Dissolution2Speed { get; set; }
|
||||
public ushort DissolutionPercent { get; set; }
|
||||
public ushort Dissolution1Percent { get; set; }
|
||||
public ushort Dissolution2Percent { get; set; }
|
||||
@@ -66,4 +77,10 @@
|
||||
public ushort Dissolution1SampleInterval { get; set; }
|
||||
public ushort Dissolution2SampleInterval { get; set; }
|
||||
}
|
||||
|
||||
public enum PlcFloatWordOrder
|
||||
{
|
||||
LowWordFirst,
|
||||
HighWordFirst
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,21 @@ namespace TabletTester2025.Models
|
||||
|
||||
// 硬度
|
||||
public double HardnessAvg { get; set; }
|
||||
public double HardnessAverageDeviation { get; set; }
|
||||
public double HardnessRSD { get; set; }
|
||||
public double HardnessMax { get; set; }
|
||||
public double HardnessMin { get; set; }
|
||||
public int HardnessTestCount { get; set; }
|
||||
public double HardnessInternalMin { get; set; }
|
||||
public double HardnessInternalMax { get; set; }
|
||||
public List<HardnessSamplePoint> HardnessSamples { get; set; } = new();
|
||||
|
||||
[NotMapped]
|
||||
public string HardnessSampleSummary => HardnessSamples == null || HardnessSamples.Count == 0
|
||||
? ""
|
||||
: string.Join(";", HardnessSamples
|
||||
.OrderBy(s => s.SequenceNo)
|
||||
.Select(s => $"{s.SequenceNo}:{s.Value:0.0}"));
|
||||
|
||||
// 脆碎度
|
||||
public double FriabilityLoss { get; set; }
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace TabletTester2025.Services
|
||||
{
|
||||
var data = batches.ToList();
|
||||
var sheet = package.Workbook.Worksheets.Add("硬度报表");
|
||||
WriteHeader(sheet, "检测时间", "样品名称", "平均值(N)", "RSD(%)", "最大值(N)", "最小值(N)", "测试次数", "内控下限(N)", "内控上限(N)", "判定");
|
||||
WriteHeader(sheet, "检测时间", "样品名称", "平均值(N)", "平均偏差(N)", "RSD", "最大值(N)", "测试次数", "单次数据(N)", "判定");
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
@@ -75,13 +75,37 @@ namespace TabletTester2025.Services
|
||||
sheet.Cells[row, 1].Value = b.TestTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
sheet.Cells[row, 2].Value = b.SampleName;
|
||||
sheet.Cells[row, 3].Value = b.HardnessAvg;
|
||||
sheet.Cells[row, 4].Value = b.HardnessRSD;
|
||||
sheet.Cells[row, 5].Value = b.HardnessMax;
|
||||
sheet.Cells[row, 6].Value = b.HardnessMin;
|
||||
sheet.Cells[row, 4].Value = b.HardnessAverageDeviation;
|
||||
sheet.Cells[row, 5].Value = b.HardnessRSD;
|
||||
sheet.Cells[row, 6].Value = b.HardnessMax;
|
||||
sheet.Cells[row, 7].Value = b.HardnessTestCount;
|
||||
sheet.Cells[row, 8].Value = b.HardnessInternalMin;
|
||||
sheet.Cells[row, 9].Value = b.HardnessInternalMax;
|
||||
sheet.Cells[row, 10].Value = b.HardnessPassText;
|
||||
sheet.Cells[row, 8].Value = b.HardnessSampleSummary;
|
||||
sheet.Cells[row, 9].Value = b.HardnessPassText;
|
||||
row++;
|
||||
}
|
||||
|
||||
sheet.Cells.AutoFitColumns();
|
||||
AddHardnessSamplesSheet(package, data);
|
||||
}
|
||||
|
||||
private static void AddHardnessSamplesSheet(ExcelPackage package, IEnumerable<TestBatch> batches)
|
||||
{
|
||||
var samples = GetHardnessSampleRows(batches).ToList();
|
||||
if (samples.Count == 0)
|
||||
return;
|
||||
|
||||
var sheet = package.Workbook.Worksheets.Add("硬度单次明细");
|
||||
WriteHeader(sheet, "检测时间", "样品名称", "序号", "硬度值(N)", "与平均值偏差(N)", "记录时间");
|
||||
|
||||
int row = 2;
|
||||
foreach (var item in samples)
|
||||
{
|
||||
sheet.Cells[row, 1].Value = item.Batch.TestTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
sheet.Cells[row, 2].Value = item.Batch.SampleName;
|
||||
sheet.Cells[row, 3].Value = item.Sample.SequenceNo;
|
||||
sheet.Cells[row, 4].Value = item.Sample.Value;
|
||||
sheet.Cells[row, 5].Value = item.Sample.DeviationFromAverage;
|
||||
sheet.Cells[row, 6].Value = item.Sample.RecordedAt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
row++;
|
||||
}
|
||||
|
||||
@@ -123,7 +147,7 @@ namespace TabletTester2025.Services
|
||||
{
|
||||
var data = batches.ToList();
|
||||
var sheet = package.Workbook.Worksheets.Add("崩解报表");
|
||||
WriteHeader(sheet, "检测时间", "样品名称", "剂型规格", "时限(秒)", "崩解时间(秒)", "剩余未崩解管", "水浴温度(℃)", "判定");
|
||||
WriteHeader(sheet, "检测时间", "样品名称", "崩解时间(秒)", "水浴温度(℃)", "判定");
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
@@ -137,12 +161,9 @@ namespace TabletTester2025.Services
|
||||
{
|
||||
sheet.Cells[row, 1].Value = b.TestTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
sheet.Cells[row, 2].Value = b.SampleName;
|
||||
sheet.Cells[row, 3].Value = b.DisintegrationDosageForm;
|
||||
sheet.Cells[row, 4].Value = b.DisintegrationLimitSeconds;
|
||||
sheet.Cells[row, 5].Value = b.DisintegrationTimeSec;
|
||||
sheet.Cells[row, 6].Value = b.RemainingTubesAtEnd;
|
||||
sheet.Cells[row, 7].Value = b.DisintegrationTemp;
|
||||
sheet.Cells[row, 8].Value = b.DisintegrationPassText;
|
||||
sheet.Cells[row, 3].Value = b.DisintegrationTimeSec;
|
||||
sheet.Cells[row, 4].Value = b.DisintegrationTemp;
|
||||
sheet.Cells[row, 5].Value = b.DisintegrationPassText;
|
||||
row++;
|
||||
}
|
||||
|
||||
@@ -297,6 +318,15 @@ namespace TabletTester2025.Services
|
||||
.ThenBy(x => x.Sample.ScheduledTimeMin);
|
||||
}
|
||||
|
||||
private static IEnumerable<(TestBatch Batch, HardnessSamplePoint Sample)> GetHardnessSampleRows(IEnumerable<TestBatch> batches)
|
||||
{
|
||||
return batches
|
||||
.SelectMany(batch => (batch.HardnessSamples ?? Enumerable.Empty<HardnessSamplePoint>())
|
||||
.Select(sample => (Batch: batch, Sample: sample)))
|
||||
.OrderBy(x => x.Batch.TestTime)
|
||||
.ThenBy(x => x.Sample.SequenceNo);
|
||||
}
|
||||
|
||||
private static void WriteHeader(ExcelWorksheet sheet, params string[] headers)
|
||||
{
|
||||
for (int i = 0; i < headers.Length; i++)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace TabletTester2025.Services
|
||||
Task<bool> CheckConnectionAsync();
|
||||
//从 PLC 的指定起始地址,读取 1 个 32 位浮点型(float)数据。
|
||||
Task<float> ReadFloatAsync(ushort startAddress);
|
||||
//从 PLC 的指定起始地址,读取 1 个 32 位整型(int)数据。
|
||||
//从 PLC 的指定地址,读取 1 个 16 位整型数据。
|
||||
Task<int> ReadIntAsync(ushort startAddress);
|
||||
//向 PLC 的指定线圈地址,写入一个布尔值(开关量)。
|
||||
Task WriteCoilAsync(ushort coilAddress, bool value);
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace TabletTester2025.Services
|
||||
|
||||
private readonly PlcConfiguration _config;
|
||||
private readonly SemaphoreSlim _connectLock = new(1, 1);
|
||||
private readonly SemaphoreSlim _ioLock = new(1, 1);
|
||||
private TcpClient? _tcpClient;
|
||||
private IModbusMaster? _master;
|
||||
|
||||
@@ -95,7 +96,7 @@ namespace TabletTester2025.Services
|
||||
public async Task<float> ReadFloatAsync(ushort startAddress)
|
||||
{
|
||||
var registers = await ReadHoldingRegistersAsync(startAddress, 2);
|
||||
return UshortToFloat(registers[1], registers[0]);
|
||||
return RegistersToFloat(registers[0], registers[1]);
|
||||
}
|
||||
|
||||
public async Task<int> ReadIntAsync(ushort startAddress)
|
||||
@@ -122,13 +123,10 @@ namespace TabletTester2025.Services
|
||||
|
||||
public Task WriteFloatAsync(ushort startAddress, float value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
ushort[] registers =
|
||||
{
|
||||
(ushort)((bytes[2] << 8) | bytes[3]),
|
||||
(ushort)((bytes[0] << 8) | bytes[1])
|
||||
};
|
||||
if (!float.IsFinite(value))
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "PLC浮点写入值不能是NaN或Infinity。");
|
||||
|
||||
ushort[] registers = FloatToRegisters(value);
|
||||
return ExecuteAsync(master => master.WriteMultipleRegistersAsync(_config.SlaveId, startAddress, registers));
|
||||
}
|
||||
|
||||
@@ -152,10 +150,11 @@ namespace TabletTester2025.Services
|
||||
|
||||
private async Task<T> ExecuteAsync<T>(Func<IModbusMaster, Task<T>> action)
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
|
||||
await _ioLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
await EnsureConnectedAsync();
|
||||
|
||||
if (_master == null)
|
||||
throw new InvalidOperationException("PLC连接未初始化");
|
||||
|
||||
@@ -166,18 +165,42 @@ namespace TabletTester2025.Services
|
||||
CloseConnection();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ioLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static float UshortToFloat(ushort high, ushort low)
|
||||
private float RegistersToFloat(ushort firstRegister, ushort secondRegister)
|
||||
{
|
||||
byte[] bytes = new byte[4];
|
||||
bytes[0] = (byte)(high >> 8);
|
||||
bytes[1] = (byte)(high & 0xFF);
|
||||
bytes[2] = (byte)(low >> 8);
|
||||
bytes[3] = (byte)(low & 0xFF);
|
||||
return _config.FloatWordOrder == PlcFloatWordOrder.HighWordFirst
|
||||
? WordsToFloat(firstRegister, secondRegister)
|
||||
: WordsToFloat(secondRegister, firstRegister);
|
||||
}
|
||||
|
||||
private static float WordsToFloat(ushort highWord, ushort lowWord)
|
||||
{
|
||||
byte[] bytes =
|
||||
{
|
||||
(byte)(lowWord & 0xFF),
|
||||
(byte)(lowWord >> 8),
|
||||
(byte)(highWord & 0xFF),
|
||||
(byte)(highWord >> 8)
|
||||
};
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
private ushort[] FloatToRegisters(float value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
ushort highWord = (ushort)((bytes[3] << 8) | bytes[2]);
|
||||
ushort lowWord = (ushort)((bytes[1] << 8) | bytes[0]);
|
||||
|
||||
return _config.FloatWordOrder == PlcFloatWordOrder.HighWordFirst
|
||||
? new[] { highWord, lowWord }
|
||||
: new[] { lowWord, highWord };
|
||||
}
|
||||
|
||||
private void CloseConnection()
|
||||
{
|
||||
try { _master?.Dispose(); } catch { }
|
||||
@@ -191,6 +214,7 @@ namespace TabletTester2025.Services
|
||||
{
|
||||
CloseConnection();
|
||||
_connectLock.Dispose();
|
||||
_ioLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,18 @@ namespace TabletTester2025.Services
|
||||
// 模拟不同地址的数据
|
||||
float value = startAddress switch
|
||||
{
|
||||
72 => 40 + (float)_rand.NextDouble() * 20, // 硬度最大采集力
|
||||
100 => 40 + (float)_rand.NextDouble() * 20, // 硬度 40~60N
|
||||
410 => 4.0f, // 脆碎试验时间(min)
|
||||
1314 => 40 + (float)_rand.NextDouble() * 20, // 硬度实时力显示
|
||||
82 => _rand.Next(0, 101), // 脆碎实时圈数
|
||||
410 => 100f, // 脆碎圈数
|
||||
412 => 5.0f + (float)_rand.NextDouble() * 2, // 脆碎度前重
|
||||
414 => 4.9f + (float)_rand.NextDouble() * 2, // 后重
|
||||
300 => 37.0f, // 温度
|
||||
416 => 1.0f, // 失重率%
|
||||
300 => 100.0f, // 硬度加压速度(mm/min)
|
||||
330 => 31.0f, // 崩解升降频次(次/min)
|
||||
340 => 50f, // 溶出速度1(r/min)
|
||||
350 => 50f, // 溶出速度2(r/min)
|
||||
400 => 50 + (float)_rand.NextDouble() * 30, // 转速
|
||||
402 => 70 + (float)_rand.NextDouble() * 30, // 溶出度%
|
||||
404 => 70 + (float)_rand.NextDouble() * 30, // 溶出2溶出度%
|
||||
@@ -34,8 +41,14 @@ namespace TabletTester2025.Services
|
||||
// 👇 新增 ReadIntAsync 的模拟实现
|
||||
public Task<int> ReadIntAsync(ushort startAddress)
|
||||
{
|
||||
// 模拟整数返回,比如返回0-1000之间的随机数
|
||||
return Task.FromResult(_rand.Next(0, 1000));
|
||||
int value = startAddress switch
|
||||
{
|
||||
410 => 100, // 脆碎圈数
|
||||
430 => 30, // 溶出1时间(min)
|
||||
440 => 30, // 溶出2时间(min)
|
||||
_ => _rand.Next(0, 1000)
|
||||
};
|
||||
return Task.FromResult(value);
|
||||
}
|
||||
|
||||
public Task WriteCoilAsync(ushort coilAddress, bool value) => Task.CompletedTask;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace TabletTester2025.Services
|
||||
{
|
||||
public readonly record struct HardnessStatistics(
|
||||
double Average,
|
||||
double AverageDeviation,
|
||||
double RsdPercent,
|
||||
double Maximum,
|
||||
double Minimum,
|
||||
@@ -26,9 +27,10 @@ namespace TabletTester2025.Services
|
||||
.ToList();
|
||||
|
||||
if (validValues.Count == 0)
|
||||
return new HardnessStatistics(0, 0, 0, 0, 0, false);
|
||||
return new HardnessStatistics(0, 0, 0, 0, 0, 0, false);
|
||||
|
||||
double average = validValues.Average();
|
||||
double averageDeviation = validValues.Average(value => Math.Abs(value - average));
|
||||
double standardDeviation = CalculateSampleStandardDeviation(validValues, average);
|
||||
double rsd = average == 0 ? 0 : standardDeviation / average * 100;
|
||||
bool countMet = validValues.Count >= Math.Max(1, requiredCount);
|
||||
@@ -36,6 +38,7 @@ namespace TabletTester2025.Services
|
||||
|
||||
return new HardnessStatistics(
|
||||
average,
|
||||
averageDeviation,
|
||||
rsd,
|
||||
validValues.Max(),
|
||||
validValues.Min(),
|
||||
|
||||
79
ViewModels/HardnessDisplaySamplePoint.cs
Normal file
79
ViewModels/HardnessDisplaySamplePoint.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
|
||||
namespace TabletTester2025.ViewModels
|
||||
{
|
||||
public class HardnessDisplaySamplePoint : ObservableObject
|
||||
{
|
||||
private int _groupNo;
|
||||
private int _cumulativeNo;
|
||||
private int _sequenceNo;
|
||||
private double _value;
|
||||
private double _deviationFromAverage;
|
||||
private double _groupAverage;
|
||||
private double _groupAverageDeviation;
|
||||
private double _groupRSD;
|
||||
private DateTime _recordedAt;
|
||||
private bool _isSummaryRow;
|
||||
|
||||
public bool IsSummaryRow
|
||||
{
|
||||
get => _isSummaryRow;
|
||||
set => SetProperty(ref _isSummaryRow, value);
|
||||
}
|
||||
|
||||
public int GroupNo
|
||||
{
|
||||
get => _groupNo;
|
||||
set => SetProperty(ref _groupNo, value);
|
||||
}
|
||||
|
||||
public int CumulativeNo
|
||||
{
|
||||
get => _cumulativeNo;
|
||||
set => SetProperty(ref _cumulativeNo, value);
|
||||
}
|
||||
|
||||
public int SequenceNo
|
||||
{
|
||||
get => _sequenceNo;
|
||||
set => SetProperty(ref _sequenceNo, value);
|
||||
}
|
||||
|
||||
public double Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetProperty(ref _value, value);
|
||||
}
|
||||
|
||||
public double DeviationFromAverage
|
||||
{
|
||||
get => _deviationFromAverage;
|
||||
set => SetProperty(ref _deviationFromAverage, value);
|
||||
}
|
||||
|
||||
public double GroupAverage
|
||||
{
|
||||
get => _groupAverage;
|
||||
set => SetProperty(ref _groupAverage, value);
|
||||
}
|
||||
|
||||
public double GroupAverageDeviation
|
||||
{
|
||||
get => _groupAverageDeviation;
|
||||
set => SetProperty(ref _groupAverageDeviation, value);
|
||||
}
|
||||
|
||||
public double GroupRSD
|
||||
{
|
||||
get => _groupRSD;
|
||||
set => SetProperty(ref _groupRSD, value);
|
||||
}
|
||||
|
||||
public DateTime RecordedAt
|
||||
{
|
||||
get => _recordedAt;
|
||||
set => SetProperty(ref _recordedAt, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using TabletTester2025.Models;
|
||||
using TabletTester2025.Services;
|
||||
using 片剂四用仪.Views;
|
||||
|
||||
namespace TabletTester2025.ViewModels
|
||||
{
|
||||
@@ -34,7 +33,6 @@ namespace TabletTester2025.ViewModels
|
||||
public IAsyncRelayCommand OpenSettingsCommand { get; }
|
||||
public IAsyncRelayCommand OpenHistoryCommand { get; }
|
||||
public IAsyncRelayCommand OpenCalibrationCommand { get; }
|
||||
public IAsyncRelayCommand ShowDataCommand { get; }
|
||||
|
||||
public MainViewModel(IPlcService plc, DatabaseService db, ExcelExportService excel, AlarmService alarm, PlcConfiguration plcConfig)
|
||||
{
|
||||
@@ -61,20 +59,12 @@ namespace TabletTester2025.ViewModels
|
||||
OpenSettingsCommand = new AsyncRelayCommand(() =>
|
||||
{
|
||||
var window = new SettingsWindow();
|
||||
window.Owner = Application.Current.MainWindow;
|
||||
if (window.ShowDialog() == true)
|
||||
Tester.ApplyPharmaDefaults();
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
OpenHistoryCommand = new AsyncRelayCommand(() => { new HistoryWindow().ShowDialog(); return Task.CompletedTask; });
|
||||
|
||||
// 跳转到 PlcDataPage 页面
|
||||
|
||||
ShowDataCommand = new AsyncRelayCommand(async () =>
|
||||
{
|
||||
// 用你项目里已有的PLC实例(假设叫 _plcClient)
|
||||
var window = new ShowData(_plc);
|
||||
window.ShowDialog();
|
||||
});
|
||||
OpenHistoryCommand = new AsyncRelayCommand(() => { new HistoryWindow(_db).ShowDialog(); return Task.CompletedTask; });
|
||||
}
|
||||
|
||||
private async Task ConnectToPlc()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,15 +15,17 @@
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="RowBackground" Value="White"/>
|
||||
<Setter Property="AlternatingRowBackground" Value="#F8FAFC"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource TableFontSize}"/>
|
||||
<Setter Property="HeadersVisibility" Value="Column"/>
|
||||
<Setter Property="AutoGenerateColumns" Value="False"/>
|
||||
<Setter Property="CanUserResizeColumns" Value="True"/>
|
||||
<Setter Property="CanUserSortColumns" Value="True"/>
|
||||
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
|
||||
<Setter Property="RowHeight" Value="32"/>
|
||||
<Setter Property="RowHeight" Value="38"/>
|
||||
<Setter Property="SelectionMode" Value="Single"/>
|
||||
<Setter Property="SelectionUnit" Value="FullRow"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
</Style>
|
||||
|
||||
<!-- 表头样式 -->
|
||||
@@ -31,8 +33,8 @@
|
||||
<Setter Property="Background" Value="#EFF3F6"/>
|
||||
<Setter Property="Foreground" Value="#1E293B"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Height" Value="36"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource TableFontSize}"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="BorderBrush" Value="#E2E8F0"/>
|
||||
<Setter Property="BorderThickness" Value="0,0,1,1"/>
|
||||
@@ -41,7 +43,7 @@
|
||||
<!-- 按钮样式 -->
|
||||
<Style TargetType="Button" x:Key="ActionButton">
|
||||
<Setter Property="MinWidth" Value="80"/>
|
||||
<Setter Property="Height" Value="34"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="Margin" Value="6,0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Background" Value="#FFFFFF"/>
|
||||
@@ -49,7 +51,7 @@
|
||||
<Setter Property="BorderBrush" Value="#CBD5E1"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource TableFontSize}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
@@ -79,7 +81,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6"
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6"
|
||||
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1"
|
||||
Padding="12,0">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
@@ -94,15 +96,37 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="DangerButton" BasedOn="{StaticResource ActionButton}">
|
||||
<Setter Property="Background" Value="#DC2626"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#DC2626"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="6"
|
||||
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1"
|
||||
Padding="12,0">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#B91C1C"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TabControl 样式 -->
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Padding" Value="16,8"/>
|
||||
<Setter Property="Padding" Value="18,10"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="#475569"/>
|
||||
<Setter Property="Template">
|
||||
@@ -143,7 +167,7 @@
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Border Grid.Row="0" Background="#F8FAFC" CornerRadius="16,16,0,0" Padding="20,16">
|
||||
<TextBlock Text="📋 历史检测记录" FontSize="20" FontWeight="Bold"
|
||||
<TextBlock Text="📋 历史检测记录" FontSize="{StaticResource TitleFontSize}" FontWeight="Bold"
|
||||
Foreground="#0F172A" HorizontalAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
@@ -163,19 +187,22 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Button Grid.Row="0" Content="📎 导出硬度报表" Style="{StaticResource ExportButton}"
|
||||
Width="140" HorizontalAlignment="Right" Margin="0,0,0,12" Click="ExportHardness_Click"/>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,12">
|
||||
<Button Content="🗑️ 删除选中" Style="{StaticResource DangerButton}"
|
||||
Width="120" Margin="0,0,8,0" Click="DeleteHardness_Click"/>
|
||||
<Button Content="📎 导出硬度报表" Style="{StaticResource ExportButton}"
|
||||
Width="140" Click="ExportHardness_Click"/>
|
||||
</StackPanel>
|
||||
<DataGrid x:Name="HardnessGrid" Grid.Row="1">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="检测时间" Binding="{Binding TestTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Width="160"/>
|
||||
<DataGridTextColumn Header="样品名称" Binding="{Binding SampleName}" Width="120"/>
|
||||
<DataGridTextColumn Header="平均值(N)" Binding="{Binding HardnessAvg, StringFormat=F1}" Width="90"/>
|
||||
<DataGridTextColumn Header="RSD(%)" Binding="{Binding HardnessRSD, StringFormat=F1}" Width="80"/>
|
||||
<DataGridTextColumn Header="平均偏差(N)" Binding="{Binding HardnessAverageDeviation, StringFormat=F2}" Width="110"/>
|
||||
<DataGridTextColumn Header="RSD" Binding="{Binding HardnessRSD, StringFormat=F1}" Width="80"/>
|
||||
<DataGridTextColumn Header="最大值(N)" Binding="{Binding HardnessMax, StringFormat=F1}" Width="90"/>
|
||||
<DataGridTextColumn Header="最小值(N)" Binding="{Binding HardnessMin, StringFormat=F1}" Width="90"/>
|
||||
<DataGridTextColumn Header="测试次数" Binding="{Binding HardnessTestCount}" Width="70"/>
|
||||
<DataGridTextColumn Header="内控下限(N)" Binding="{Binding HardnessInternalMin, StringFormat=F1}" Width="100"/>
|
||||
<DataGridTextColumn Header="内控上限(N)" Binding="{Binding HardnessInternalMax, StringFormat=F1}" Width="100"/>
|
||||
<DataGridTextColumn Header="单次数据(N)" Binding="{Binding HardnessSampleSummary}" Width="220"/>
|
||||
<DataGridTextColumn Header="判定" Binding="{Binding HardnessPassText}" Width="70"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
@@ -189,8 +216,12 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Button Grid.Row="0" Content="📎 导出脆碎度报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" HorizontalAlignment="Right" Margin="0,0,0,12" Click="ExportFriability_Click"/>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,12">
|
||||
<Button Content="🗑️ 删除选中" Style="{StaticResource DangerButton}"
|
||||
Width="120" Margin="0,0,8,0" Click="DeleteFriability_Click"/>
|
||||
<Button Content="📎 导出脆碎度报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" Click="ExportFriability_Click"/>
|
||||
</StackPanel>
|
||||
<DataGrid x:Name="FriabilityGrid" Grid.Row="1">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="检测时间" Binding="{Binding TestTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Width="160"/>
|
||||
@@ -214,16 +245,17 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Button Grid.Row="0" Content="📎 导出崩解报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" HorizontalAlignment="Right" Margin="0,0,0,12" Click="ExportDisintegration_Click"/>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,12">
|
||||
<Button Content="🗑️ 删除选中" Style="{StaticResource DangerButton}"
|
||||
Width="120" Margin="0,0,8,0" Click="DeleteDisintegration_Click"/>
|
||||
<Button Content="📎 导出崩解报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" Click="ExportDisintegration_Click"/>
|
||||
</StackPanel>
|
||||
<DataGrid x:Name="DisintegrationGrid" Grid.Row="1">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="检测时间" Binding="{Binding TestTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Width="160"/>
|
||||
<DataGridTextColumn Header="样品名称" Binding="{Binding SampleName}" Width="120"/>
|
||||
<DataGridTextColumn Header="剂型规格" Binding="{Binding DisintegrationDosageForm}" Width="100"/>
|
||||
<DataGridTextColumn Header="时限(秒)" Binding="{Binding DisintegrationLimitSeconds}" Width="80"/>
|
||||
<DataGridTextColumn Header="崩解时间(秒)" Binding="{Binding DisintegrationTimeSec}" Width="100"/>
|
||||
<DataGridTextColumn Header="剩余未崩解管" Binding="{Binding RemainingTubesAtEnd}" Width="110"/>
|
||||
<DataGridTextColumn Header="水浴温度(℃)" Binding="{Binding DisintegrationTemp, StringFormat=F1}" Width="110"/>
|
||||
<DataGridTextColumn Header="判定" Binding="{Binding DisintegrationPassText}" Width="70"/>
|
||||
</DataGrid.Columns>
|
||||
@@ -238,8 +270,12 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Button Grid.Row="0" Content="📎 导出溶出报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" HorizontalAlignment="Right" Margin="0,0,0,12" Click="ExportDissolution_Click"/>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,12">
|
||||
<Button Content="🗑️ 删除选中" Style="{StaticResource DangerButton}"
|
||||
Width="120" Margin="0,0,8,0" Click="DeleteDissolution_Click"/>
|
||||
<Button Content="📎 导出溶出报表" Style="{StaticResource ExportButton}"
|
||||
Width="150" Click="ExportDissolution_Click"/>
|
||||
</StackPanel>
|
||||
<DataGrid x:Name="DissolutionGrid" Grid.Row="1">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="检测时间" Binding="{Binding TestTime, StringFormat=yyyy-MM-dd HH:mm:ss}" Width="160"/>
|
||||
|
||||
@@ -13,11 +13,10 @@ namespace TabletTester2025
|
||||
private readonly DatabaseService _dbService;
|
||||
private List<TestBatch> _allData;
|
||||
|
||||
public HistoryWindow()
|
||||
public HistoryWindow(DatabaseService dbService)
|
||||
{
|
||||
InitializeComponent();
|
||||
var connectionString = "Data Source=TabletTests.db";
|
||||
_dbService = new DatabaseService(connectionString);
|
||||
_dbService = dbService ?? throw new ArgumentNullException(nameof(dbService));
|
||||
LoadData();
|
||||
}
|
||||
|
||||
@@ -43,6 +42,66 @@ namespace TabletTester2025
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void DeleteHardness_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (HardnessGrid.SelectedItem is not TestBatch batch)
|
||||
{
|
||||
MessageBox.Show("请先选择一条记录", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
var result = MessageBox.Show($"确定要删除该记录吗?\n检测时间:{batch.TestTime:yyyy-MM-dd HH:mm:ss}",
|
||||
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
_dbService.DeleteBatch(batch.Id);
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void DeleteFriability_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (FriabilityGrid.SelectedItem is not TestBatch batch)
|
||||
{
|
||||
MessageBox.Show("请先选择一条记录", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
var result = MessageBox.Show($"确定要删除该记录吗?\n检测时间:{batch.TestTime:yyyy-MM-dd HH:mm:ss}",
|
||||
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
_dbService.DeleteBatch(batch.Id);
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void DeleteDisintegration_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DisintegrationGrid.SelectedItem is not TestBatch batch)
|
||||
{
|
||||
MessageBox.Show("请先选择一条记录", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
var result = MessageBox.Show($"确定要删除该记录吗?\n检测时间:{batch.TestTime:yyyy-MM-dd HH:mm:ss}",
|
||||
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
_dbService.DeleteBatch(batch.Id);
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void DeleteDissolution_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DissolutionGrid.SelectedItem is not TestBatch batch)
|
||||
{
|
||||
MessageBox.Show("请先选择一条记录", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
var result = MessageBox.Show($"确定要删除该记录吗?\n检测时间:{batch.TestTime:yyyy-MM-dd HH:mm:ss}",
|
||||
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
_dbService.DeleteBatch(batch.Id);
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void ExportHardness_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var data = HardnessGrid.ItemsSource as IEnumerable<TestBatch>;
|
||||
|
||||
@@ -23,15 +23,15 @@
|
||||
<SolidColorBrush x:Key="ValueBrush" Color="#102A43"/>
|
||||
|
||||
<Style TargetType="Button" x:Key="ActionButton">
|
||||
<Setter Property="MinWidth" Value="112"/>
|
||||
<Setter Property="Height" Value="42"/>
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
<Setter Property="Padding" Value="14,0"/>
|
||||
<Setter Property="MinWidth" Value="116"/>
|
||||
<Setter Property="Height" Value="44"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="Padding" Value="16,0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource ButtonFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
@@ -78,9 +78,9 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource TabFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Padding" Value="24,12"/>
|
||||
<Setter Property="Padding" Value="24,13"/>
|
||||
<Setter Property="Foreground" Value="#465A6E"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
@@ -109,44 +109,56 @@
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource PanelBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="14"/>
|
||||
<Setter Property="Padding" Value="16"/>
|
||||
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Background" Value="{StaticResource PanelBackgroundBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Width" Value="130"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="Height" Value="42"/>
|
||||
<Setter Property="Padding" Value="8,2"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="BorderBrush" Value="#B7C4D2"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="helpers:NumericInput.IsEnabled" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DataGrid">
|
||||
<Setter Property="FontSize" Value="{StaticResource TableFontSize}"/>
|
||||
<Setter Property="RowHeight" Value="36"/>
|
||||
<Setter Property="ColumnHeaderHeight" Value="38"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="FontSize" Value="{StaticResource TableFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="RadioButton">
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="Margin" Value="0,0,22,0"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="ParamLabel">
|
||||
<Setter Property="Width" Value="250"/>
|
||||
<Setter Property="Width" Value="260"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource LabelBrush}"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Normal"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="StackPanel" x:Key="ParamRow">
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="0,6,26,6"/>
|
||||
<Setter Property="Margin" Value="0,7,26,7"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="MinWidth" Value="400"/>
|
||||
<Setter Property="MinWidth" Value="430"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Border" x:Key="MetricCard">
|
||||
@@ -154,14 +166,14 @@
|
||||
<Setter Property="BorderBrush" Value="#E0E7EF"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="6"/>
|
||||
<Setter Property="Padding" Value="12,10"/>
|
||||
<Setter Property="Padding" Value="14,12"/>
|
||||
<Setter Property="Margin" Value="6"/>
|
||||
<Setter Property="MinHeight" Value="86"/>
|
||||
<Setter Property="MinHeight" Value="94"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="MetricLabel">
|
||||
<Setter Property="Foreground" Value="{StaticResource LabelBrush}"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Normal"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="TextAlignment" Value="Center"/>
|
||||
@@ -170,7 +182,7 @@
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="MetricValue">
|
||||
<Setter Property="Foreground" Value="{StaticResource ValueBrush}"/>
|
||||
<Setter Property="FontSize" Value="26"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource MetricValueFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="TextAlignment" Value="Center"/>
|
||||
@@ -194,14 +206,14 @@
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="FooterLabel">
|
||||
<Setter Property="Foreground" Value="#647487"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource SmallTextFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="FooterValue">
|
||||
<Setter Property="Foreground" Value="#102A43"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
@@ -220,7 +232,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="{Binding LocalAlarm}"
|
||||
FontSize="15"
|
||||
FontSize="{StaticResource BaseFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Center">
|
||||
@@ -235,22 +247,22 @@
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="硬度测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="硬度测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="脆碎度测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="崩解测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出1测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试完成">
|
||||
<DataTrigger Binding="{Binding LocalAlarm}" Value="溶出2测试完成,已保存">
|
||||
<Setter Property="Foreground" Value="#1565C0"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@@ -258,8 +270,10 @@
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<TabControl Grid.Row="1" FontSize="13" BorderThickness="0">
|
||||
<TabControl Grid.Row="1" FontSize="{StaticResource BaseFontSize}" BorderThickness="0">
|
||||
<TabItem Header="硬度">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid Margin="4,14,4,4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -269,39 +283,39 @@
|
||||
|
||||
<GroupBox Header="当前参数" Grid.Row="0" Margin="0,5">
|
||||
<UniformGrid Columns="3" Margin="10">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,18,0">
|
||||
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,18,0">
|
||||
<TextBlock Text="内控下限(N):" Width="130" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HardnessInternalMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HardnessInternalMin, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,18,0">
|
||||
<TextBlock Text="内控上限(N):" Width="130" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HardnessInternalMax, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding HardnessInternalMax, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>-->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0">
|
||||
<TextBlock Text="测试次数:" Width="110" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HardnessTestCount}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HardnessTestCount}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<GroupBox Header="测试结果" Grid.Row="1">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GroupBox Header="测试结果" Grid.Row="0">
|
||||
<UniformGrid Columns="5">
|
||||
<UniformGrid Grid.Row="0" Columns="4">
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="最大力值(N)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessMax, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
<TextBlock Text="实时力(N)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessShishilizhi, StringFormat=F1}" Foreground="#C62828" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="最小力值(N)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessMin, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||
<TextBlock Text="最大力值(N)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessMax, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
@@ -312,26 +326,154 @@
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="RSD(%)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="平均偏差(N)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessAverageDeviation, StringFormat=F2}" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="RSD" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessRSD, StringFormat=F2}" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="测试次数" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="本组次数" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessCurrentCount}" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="累计次数" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding HardnessTotalCount}" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
<DataGrid Grid.Row="1"
|
||||
ItemsSource="{Binding HardnessDisplaySamplePoints}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column"
|
||||
Margin="10,8,10,10"
|
||||
MinHeight="160"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Background" Value="#DBEAFE"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Header="累计次数" Width="85">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding CumulativeNo}" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Text" Value=""/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="组号" Binding="{Binding GroupNo}" Width="65"/>
|
||||
<DataGridTemplateColumn Header="组内序号" Width="80">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding SequenceNo}" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Text" Value=""/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="硬度值(N)" Width="95">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Value, StringFormat=F1}" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Text" Value=""/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="与平均值偏差(N)" Width="125">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DeviationFromAverage, StringFormat=F2}" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Text" Value=""/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="组平均值(N)" Binding="{Binding GroupAverage, StringFormat=F1}" Width="105"/>
|
||||
<!--<DataGridTextColumn Header="组平均偏差(N)" Binding="{Binding GroupAverageDeviation, StringFormat=F2}" Width="120"/>-->
|
||||
<DataGridTextColumn Header="组RSD" Binding="{Binding GroupRSD, StringFormat=F2}" Width="95"/>
|
||||
<DataGridTemplateColumn Header="记录时间" Width="105">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding RecordedAt, StringFormat=HH:mm:ss}" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSummaryRow}" Value="True">
|
||||
<Setter Property="Text" Value=""/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||
|
||||
<Button Command="{Binding StartHardnessCommand}" Content="开始" Style="{StaticResource StartButton}"/>
|
||||
<Button Command="{Binding StopHardnessCommand}" Content="停止" Style="{StaticResource StopButton}"/>
|
||||
<Button Command="{Binding HardnessResetCommand}" Content="复位" Style="{StaticResource ResetButton}"/>
|
||||
<Button Command="{Binding HardnessResetCommand}" Content="{Binding HardnessResetButtonText}" Style="{StaticResource ResetButton}"/>
|
||||
<Button Command="{Binding ClearHardnessRecordsCommand}" Content="清空记录" Style="{StaticResource SecondaryButton}"/>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="脆碎度">
|
||||
@@ -344,33 +486,35 @@
|
||||
|
||||
<GroupBox Header="当前参数" Grid.Row="0">
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="试验时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding FriabilityTargetTimeMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="转速设置(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding FriabilityTargetRpm, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding FriabilityTargetRpm, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="试验转数(转):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding FriabilityTargetRounds}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding FriabilityTargetRounds}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="失重率限度(%):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding FriabilityMaxLossPercent, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding FriabilityMaxLossPercent, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="测试结果" Grid.Row="1">
|
||||
<UniformGrid Columns="3">
|
||||
<UniformGrid Columns="4">
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="实时圈数" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding FriabilityRealtimeRounds, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="脆碎前质量(g)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBox Text="{Binding WeightBefore, Mode=TwoWay, UpdateSourceTrigger=LostFocus, StringFormat=F3}"
|
||||
Width="150"
|
||||
FontSize="22"
|
||||
FontSize="{StaticResource LargeInputFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalContentAlignment="Center"
|
||||
Margin="0,8,0,0"/>
|
||||
@@ -381,7 +525,7 @@
|
||||
<TextBlock Text="脆碎后质量(g)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBox Text="{Binding WeightAfter, Mode=TwoWay, UpdateSourceTrigger=LostFocus, StringFormat=F3}"
|
||||
Width="150"
|
||||
FontSize="22"
|
||||
FontSize="{StaticResource LargeInputFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalContentAlignment="Center"
|
||||
Margin="0,8,0,0"/>
|
||||
@@ -389,7 +533,7 @@
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="脆碎度(%)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="失重率(%)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding LossPercent, StringFormat=F2}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -399,6 +543,7 @@
|
||||
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||
<Button Command="{Binding StartFriabilityCommand}" Content="开始" Style="{StaticResource StartButton}"/>
|
||||
<Button Command="{Binding StopFriabilityCommand}" Content="停止" Style="{StaticResource StopButton}"/>
|
||||
<Button Command="{Binding SaveFriabilityResultCommand}" Content="保存记录" Style="{StaticResource SecondaryButton}" IsEnabled="{Binding CanSaveFriabilityResult}"/>
|
||||
<!--<Button Command="{Binding ResetFriabilityCommand}" Content="复位" Style="{StaticResource ResetButton}"/>-->
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
@@ -418,34 +563,64 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GroupBox Header="当前参数" Grid.Row="0">
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出1时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution1TimeMin}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Dissolution1TimeMin}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出2时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution2TimeMin}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Dissolution2TimeMin}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出1间隔取样时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution1SampleIntervalMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Dissolution1SampleIntervalMin, StringFormat=F0}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出2间隔取样时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution2SampleIntervalMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Dissolution2SampleIntervalMin, StringFormat=F0}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="30min最低溶出度Q(%):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DissolutionMinPercentAt30Min, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="介质温度(℃):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="测试结果" Grid.Row="1">
|
||||
<GroupBox Header="运行计时" Grid.Row="1">
|
||||
<UniformGrid Columns="4">
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="溶出1累计(min)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution1ElapsedTime, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="溶出1倒计时(min)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution1Countdown, StringFormat=F1}" Foreground="#2E7D32" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="溶出2累计(min)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution2ElapsedTime, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="溶出2倒计时(min)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding Dissolution2Countdown, StringFormat=F1}" Foreground="#1565C0" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="测试结果" Grid.Row="2">
|
||||
<UniformGrid Columns="4">
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
@@ -474,7 +649,7 @@
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="溶出双曲线和R²值" Grid.Row="2">
|
||||
<GroupBox Header="溶出双曲线和R²值" Grid.Row="3">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="240"/>
|
||||
@@ -484,12 +659,12 @@
|
||||
<oxy:PlotView Grid.Row="0" Model="{Binding DissolutionPlotModel}" Margin="4"/>
|
||||
<WrapPanel Grid.Row="1" Margin="4,8,4,2">
|
||||
<TextBlock Text="{Binding Dissolution1RSquared, StringFormat='溶出1 R² = {0:F4}'}"
|
||||
FontSize="15"
|
||||
FontSize="{StaticResource BaseFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="#2E7D32"
|
||||
Margin="0,0,28,0"/>
|
||||
<TextBlock Text="{Binding Dissolution2RSquared, StringFormat='溶出2 R² = {0:F4}'}"
|
||||
FontSize="15"
|
||||
FontSize="{StaticResource BaseFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="#1565C0"/>
|
||||
</WrapPanel>
|
||||
@@ -502,7 +677,7 @@
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="取样记录" Grid.Row="3">
|
||||
<GroupBox Header="取样记录" Grid.Row="4">
|
||||
<DataGrid ItemsSource="{Binding DissolutionSamplePoints}"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
@@ -511,8 +686,8 @@
|
||||
IsReadOnly="True"
|
||||
MinHeight="150"
|
||||
MaxHeight="240"
|
||||
RowHeight="34"
|
||||
FontSize="14"
|
||||
RowHeight="36"
|
||||
FontSize="{StaticResource TableFontSize}"
|
||||
GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="通道" Binding="{Binding ChannelName}" Width="90"/>
|
||||
@@ -548,47 +723,47 @@
|
||||
|
||||
<GroupBox Header="当前参数" Grid.Row="0">
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
|
||||
<TextBlock Text="剂型规格:" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationDosageForm}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="崩解速度(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationSpeedRpm, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="崩解时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationTimeMin, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding DisintegrationTimeMin, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="介质温度(℃):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding DisintegrationTemp, StringFormat=F1}" FontSize="{StaticResource ParamValueFontSize}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="测试结果" Grid.Row="1">
|
||||
<UniformGrid Columns="2">
|
||||
<UniformGrid Columns="1">
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="崩解时间(秒)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding DisintegrationSeconds}" Style="{StaticResource MetricValue}"/>
|
||||
<TextBlock Text="实际崩解时间(秒)" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBox Text="{Binding DisintegrationActualSecondsText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Width="160"
|
||||
Height="50"
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
IsEnabled="{Binding CanSaveDisintegrationResult}"
|
||||
TextAlignment="Center"
|
||||
FontSize="{StaticResource MetricValueFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{StaticResource ValueBrush}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource MetricCard}">
|
||||
<!--<Border Style="{StaticResource MetricCard}">
|
||||
<StackPanel>
|
||||
<TextBlock Text="剩余未崩解管数" Style="{StaticResource MetricLabel}"/>
|
||||
<TextBlock Text="{Binding RemainingTubes}" Foreground="#C62828" Style="{StaticResource MetricValue}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>-->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<WrapPanel Grid.Row="2" Style="{StaticResource CommandBar}">
|
||||
<Button Command="{Binding StartDisintegrationCommand}" Content="开始" Style="{StaticResource StartButton}"/>
|
||||
<Button Command="{Binding StopDisintegrationCommand}" Content="停止" Style="{StaticResource StopButton}"/>
|
||||
<Button Command="{Binding SaveDisintegrationResultCommand}" Content="保存记录" Style="{StaticResource SecondaryButton}" />
|
||||
<!--<Button Command="{Binding ResetDisintegrationCommand}" Content="复位" Style="{StaticResource ResetButton}"/>-->
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
@@ -608,12 +783,18 @@
|
||||
|
||||
<Border Background="#0F3D68" CornerRadius="6" Margin="0,0,0,8" Padding="12,10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="片剂四用仪 硬度 · 脆碎度 · 溶出 · 崩解"
|
||||
FontSize="22"
|
||||
FontSize="{StaticResource TitleFontSize}"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Margin="0,0,12,0"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Command="{Binding OpenSettingsCommand}" Content="参数设置" Style="{StaticResource ActionButton}"/>
|
||||
<Button Command="{Binding OpenHistoryCommand}" Content="历史记录" Style="{StaticResource ActionButton}"/>
|
||||
<Button Command="{Binding ExportAllCommand}" Content="导出报告" Style="{StaticResource ActionButton}"/>
|
||||
@@ -673,21 +854,12 @@
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding GlobalAlarm}"
|
||||
Foreground="#C62828"
|
||||
FontSize="14"
|
||||
FontSize="{StaticResource SmallTextFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Button Grid.Column="3"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Opacity="0"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Margin="8,0,0,0"
|
||||
Command="{Binding ShowDataCommand}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
<Setter Property="MinWidth" Value="72"/>
|
||||
<Setter Property="MinHeight" Value="58"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="22"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource KeypadButtonFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#C7D2DE"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
<Style TargetType="Button" x:Key="CommandButton" BasedOn="{StaticResource KeyButton}">
|
||||
<Setter Property="FontSize" Value="18"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource KeypadCommandFontSize}"/>
|
||||
<Setter Property="Background" Value="#E9F1F8"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
@@ -36,7 +36,7 @@
|
||||
Grid.Row="0"
|
||||
Height="58"
|
||||
Padding="12,0"
|
||||
FontSize="28"
|
||||
FontSize="{StaticResource KeypadValueFontSize}"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalContentAlignment="Center"
|
||||
@@ -74,7 +74,7 @@
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="点击数字输入,确定后写入当前输入框"
|
||||
Foreground="#526273"
|
||||
FontSize="13"
|
||||
FontSize="{StaticResource SmallTextFontSize}"
|
||||
HorizontalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
xmlns:helpers="clr-namespace:TabletTester2025.Helpers"
|
||||
Title="参数设置 - 中国药典2025"
|
||||
Width="1024"
|
||||
Height="768"
|
||||
MinWidth="900"
|
||||
MinHeight="768"
|
||||
WindowState="Maximized"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="CanResize"
|
||||
Background="#EDF1F5">
|
||||
<Window.Resources>
|
||||
@@ -15,40 +16,47 @@
|
||||
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="Margin" Value="0,0,0,14"/>
|
||||
<Setter Property="Padding" Value="14"/>
|
||||
<Setter Property="Padding" Value="16"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource PanelBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="ParamLabel">
|
||||
<Setter Property="Width" Value="220"/>
|
||||
<Setter Property="Width" Value="230"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Foreground" Value="#526273"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Normal"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Width" Value="180"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="Height" Value="42"/>
|
||||
<Setter Property="Padding" Value="8,2"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BaseFontSize}"/>
|
||||
<Setter Property="helpers:NumericInput.IsEnabled" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="{StaticResource ButtonFontSize}"/>
|
||||
<Setter Property="MinHeight" Value="44"/>
|
||||
<Setter Property="Padding" Value="16,0"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="StackPanel" x:Key="ParamRow">
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="0,6,32,6"/>
|
||||
<Setter Property="Margin" Value="0,7,32,7"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="StandardNote">
|
||||
<Setter Property="Foreground" Value="#6A7888"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource SmallTextFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Normal"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="Margin" Value="0,10,0,0"/>
|
||||
@@ -63,29 +71,33 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Background="#0F3D68" CornerRadius="6" Padding="14,10" Margin="0,0,0,14">
|
||||
<TextBlock Text="药典与内控参数设置"
|
||||
<TextBlock Text="参数设置"
|
||||
Foreground="White"
|
||||
FontSize="22"
|
||||
FontSize="{StaticResource TitleFontSize}"
|
||||
FontWeight="Bold"/>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<GroupBox Header="硬度内控">
|
||||
<GroupBox Header="硬度">
|
||||
<StackPanel>
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="内控下限(N):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessMinBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="内控上限(N):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessMaxBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="测试次数:" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessCountBox" helpers:NumericInput.AllowDecimal="False"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="加压压力:" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessPressureBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="加压速度(mm/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessSpeedBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="硬度破损判定(N):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="HardnessDamageThresholdBox" helpers:NumericInput.AllowDecimal="True"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
<TextBlock Text="硬度没有统一药典数值限度,此处作为企业内控范围和本机测试次数。"
|
||||
Style="{StaticResource StandardNote}"/>
|
||||
@@ -97,27 +109,23 @@
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="转速(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="FriabilityRpmBox" TextChanged="FriabilityCalculationBox_TextChanged"/>
|
||||
<TextBox x:Name="FriabilityRpmBox"/>
|
||||
</StackPanel>
|
||||
<!--<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="试验时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="FriabilityTimeBox"/>
|
||||
</StackPanel>-->
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="试验次数(次):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="FriabilityTimeBox" TextChanged="FriabilityCalculationBox_TextChanged"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="试验转数(自动计算):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="脆碎圈数:" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="FriabilityRoundsBox"
|
||||
helpers:NumericInput.AllowDecimal="False"
|
||||
helpers:NumericInput.IsEnabled="False"
|
||||
IsReadOnly="True"
|
||||
Background="#F3F7FA"
|
||||
Foreground="#526273"/>
|
||||
helpers:NumericInput.AllowDecimal="False"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="最大失重率(%):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="FriabilityMaxLossBox"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
<TextBlock Text="默认:转速25±1 r/min,试验时间4min,自动计算100转;减失重量不得过1.0%,且不得检出断裂、龟裂或粉碎的片。"
|
||||
<TextBlock Text="默认:转速25±1 r/min,试验时间4min,脆碎圈数100转;减失重量不得过1.0%,且不得检出断裂、龟裂或粉碎的片。"
|
||||
Style="{StaticResource StandardNote}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -126,32 +134,15 @@
|
||||
<StackPanel>
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="剂型规格:" Style="{StaticResource ParamLabel}"/>
|
||||
<ComboBox x:Name="DisintegrationDosageFormBox"
|
||||
Width="180"
|
||||
Height="40"
|
||||
FontSize="15"
|
||||
SelectionChanged="DisintegrationDosageFormBox_SelectionChanged">
|
||||
<ComboBoxItem Content="普通片" Tag="900"/>
|
||||
<ComboBoxItem Content="薄膜衣片" Tag="1800"/>
|
||||
<ComboBoxItem Content="糖衣片" Tag="3600"/>
|
||||
<ComboBoxItem Content="胶囊" Tag="1800"/>
|
||||
</ComboBox>
|
||||
<TextBlock Text="崩解时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DisintegrationTimeMinBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="最长崩解时间(秒):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DisintegrationMaxSecBox" helpers:NumericInput.AllowDecimal="False"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="升降频率(次/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBlock Text="升降频次(次/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DisintegrationSpeedBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="介质温度(℃):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DisintegrationTempBox"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
<TextBlock Text="默认:升降频率30-32次/min,介质温度37±1℃。不同剂型按药典或品种正文规定时限执行。"
|
||||
<TextBlock Text="崩解时间按照样品测试设置,保存后立即写入设备。"
|
||||
Style="{StaticResource StandardNote}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -159,38 +150,38 @@
|
||||
<GroupBox Header="溶出度">
|
||||
<StackPanel>
|
||||
<WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="30分钟最低溶出度Q(%):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DissolutionMinPercentBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<!--<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出介质温度(℃):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="DissolutionTempBox"/>
|
||||
</StackPanel>-->
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出速度1(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution1SpeedBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出1运行时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution1TimeBox" helpers:NumericInput.AllowDecimal="False"/>
|
||||
<TextBlock Text="溶出速度2(r/min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution2SpeedBox"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出2运行时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution2TimeBox" helpers:NumericInput.AllowDecimal="False"/>
|
||||
<TextBlock Text="溶出1时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution1TimeBox"
|
||||
helpers:NumericInput.AllowDecimal="False"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出2时间(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution2TimeBox"
|
||||
helpers:NumericInput.AllowDecimal="False"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出1取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution1IntervalBox"/>
|
||||
<TextBox x:Name="Dissolution1IntervalBox" helpers:NumericInput.AllowDecimal="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}">
|
||||
<TextBlock Text="溶出2取样间隔(min):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="Dissolution2IntervalBox"/>
|
||||
<TextBox x:Name="Dissolution2IntervalBox" helpers:NumericInput.AllowDecimal="True"/>
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
<StackPanel Style="{StaticResource ParamRow}" Margin="0,8,0,0">
|
||||
<TextBlock Text="取样时间点(分钟,逗号分隔):" Style="{StaticResource ParamLabel}"/>
|
||||
<TextBox x:Name="SampleTimesBox"
|
||||
Width="430"
|
||||
helpers:NumericInput.IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="默认:普通制剂通常取6片,溶出介质温度37±0.5℃;Q值、介质、转速和取样点应按具体品种正文或企业批准标准录入。"
|
||||
<TextBlock Text="默认:普通制剂通常取6片,溶出介质温度37±0.5℃;转速和取样间隔应按样品种类录入。"
|
||||
Style="{StaticResource StandardNote}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -200,8 +191,8 @@
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,16,0,0">
|
||||
<Button x:Name="SaveButton"
|
||||
Content="保存"
|
||||
Width="110"
|
||||
Height="40"
|
||||
Width="116"
|
||||
Height="44"
|
||||
Margin="6"
|
||||
Click="SaveButton_Click"
|
||||
Background="{StaticResource PrimaryBrush}"
|
||||
@@ -210,8 +201,8 @@
|
||||
FontWeight="SemiBold"/>
|
||||
<Button x:Name="CancelButton"
|
||||
Content="取消"
|
||||
Width="110"
|
||||
Height="40"
|
||||
Width="116"
|
||||
Height="44"
|
||||
Margin="6"
|
||||
Click="CancelButton_Click"
|
||||
Background="#687789"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using TabletTester2025.Models;
|
||||
@@ -12,61 +13,105 @@ namespace TabletTester2025
|
||||
{
|
||||
InitializeComponent();
|
||||
LoadSettings();
|
||||
Loaded += SettingsWindow_Loaded;
|
||||
}
|
||||
|
||||
private async void SettingsWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadHardnessPressureAsync();
|
||||
await LoadHardnessSpeedAsync();
|
||||
await LoadHardnessDamageThresholdAsync();
|
||||
|
||||
// 脆碎度
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveFriabilityRpmRegister(), FriabilityRpmBox);
|
||||
await LoadPlcIntToTextBoxAsync(ResolveFriabilityRoundsRegister(), FriabilityRoundsBox);
|
||||
|
||||
// 崩解
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveDisintegrationSpeedRegister(), DisintegrationSpeedBox);
|
||||
|
||||
// 溶出度
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution1SpeedRegister(), Dissolution1SpeedBox);
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution2SpeedRegister(), Dissolution2SpeedBox);
|
||||
await LoadPlcIntToTextBoxAsync(ResolveDissolution1TimeRegister(), Dissolution1TimeBox);
|
||||
await LoadPlcIntToTextBoxAsync(ResolveDissolution2TimeRegister(), Dissolution2TimeBox);
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution1IntervalRegister(), Dissolution1IntervalBox);
|
||||
await LoadPlcFloatToTextBoxAsync(ResolveDissolution2IntervalRegister(), Dissolution2IntervalBox);
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
var p = App.CurrentPharmaParams;
|
||||
HardnessMinBox.Text = p.HardnessMin_N.ToString();
|
||||
HardnessMaxBox.Text = p.HardnessMax_N.ToString();
|
||||
HardnessCountBox.Text = p.HardnessTestCount.ToString();
|
||||
FriabilityRpmBox.Text = p.FriabilityTargetRpm.ToString();
|
||||
FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###");
|
||||
FriabilityRoundsBox.Text = CalculateFriabilityRounds(
|
||||
ResolveFriabilityTargetTimeMin(p),
|
||||
p.FriabilityTargetRpm > 0 ? p.FriabilityTargetRpm : 25).ToString();
|
||||
//FriabilityTimeBox.Text = ResolveFriabilityTargetTimeMin(p).ToString("0.###");
|
||||
FriabilityRoundsBox.Text = ResolveFriabilityTargetRounds(p).ToString();
|
||||
FriabilityMaxLossBox.Text = p.FriabilityMaxLossPercent.ToString();
|
||||
SelectDisintegrationDosageForm(p.DisintegrationDosageForm);
|
||||
DisintegrationMaxSecBox.Text = p.DisintegrationMaxSeconds.ToString();
|
||||
DisintegrationSpeedBox.Text = p.DisintegrationSpeedRpm.ToString();
|
||||
DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString();
|
||||
DissolutionMinPercentBox.Text = p.DissolutionMinPercentAt30min.ToString();
|
||||
DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString();
|
||||
DisintegrationTimeMinBox.Text = ResolveDisintegrationTimeMin(p).ToString("0.###");
|
||||
DisintegrationSpeedBox.Text = p.DisintegrationSpeedRpm.ToString("0.###");
|
||||
//DisintegrationTempBox.Text = p.DisintegrationTemperatureC.ToString();
|
||||
//DissolutionTempBox.Text = p.DissolutionTemperatureC.ToString();
|
||||
Dissolution1TimeBox.Text = p.Dissolution1TimeMin.ToString();
|
||||
Dissolution2TimeBox.Text = p.Dissolution2TimeMin.ToString();
|
||||
Dissolution1IntervalBox.Text = p.Dissolution1SampleIntervalMin.ToString();
|
||||
Dissolution2IntervalBox.Text = p.Dissolution2SampleIntervalMin.ToString();
|
||||
SampleTimesBox.Text = string.Join(",", p.DissolutionSampleTimes);
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
private async void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = App.CurrentPharmaParams;
|
||||
p.HardnessMin_N = double.Parse(HardnessMinBox.Text);
|
||||
p.HardnessMax_N = double.Parse(HardnessMaxBox.Text);
|
||||
var current = App.CurrentPharmaParams;
|
||||
var p = new PharmaParameters
|
||||
{
|
||||
StandardVersion = current.StandardVersion,
|
||||
HardnessMin_N = current.HardnessMin_N,
|
||||
HardnessMax_N = current.HardnessMax_N,
|
||||
FriabilityTargetTimeMin = current.FriabilityTargetTimeMin,
|
||||
DissolutionMinPercentAt30min = current.DissolutionMinPercentAt30min,
|
||||
DisintegrationDosageForm = current.DisintegrationDosageForm,
|
||||
DisintegrationSpeedRpm = current.DisintegrationSpeedRpm,
|
||||
DisintegrationTemperatureC = current.DisintegrationTemperatureC,
|
||||
DissolutionTemperatureC = current.DissolutionTemperatureC,
|
||||
Dissolution1TimeMin = current.Dissolution1TimeMin,
|
||||
Dissolution2TimeMin = current.Dissolution2TimeMin,
|
||||
DissolutionSampleTimes = current.DissolutionSampleTimes?.ToArray() ?? Array.Empty<int>()
|
||||
};
|
||||
p.HardnessTestCount = int.Parse(HardnessCountBox.Text);
|
||||
p.FriabilityTargetRpm = double.Parse(FriabilityRpmBox.Text);
|
||||
p.FriabilityTargetTimeMin = int.Parse(FriabilityTimeBox.Text);
|
||||
p.FriabilityTargetRounds = CalculateFriabilityRounds(p.FriabilityTargetTimeMin, p.FriabilityTargetRpm);
|
||||
p.FriabilityMaxLossPercent = double.Parse(FriabilityMaxLossBox.Text);
|
||||
p.DisintegrationDosageForm = GetSelectedDisintegrationDosageForm();
|
||||
p.DisintegrationMaxSeconds = int.Parse(DisintegrationMaxSecBox.Text);
|
||||
p.DisintegrationSpeedRpm = double.Parse(DisintegrationSpeedBox.Text);
|
||||
p.DisintegrationTemperatureC = double.Parse(DisintegrationTempBox.Text);
|
||||
p.DissolutionMinPercentAt30min = double.Parse(DissolutionMinPercentBox.Text);
|
||||
p.DissolutionTemperatureC = double.Parse(DissolutionTempBox.Text);
|
||||
p.Dissolution1TimeMin = int.Parse(Dissolution1TimeBox.Text);
|
||||
p.Dissolution2TimeMin = int.Parse(Dissolution2TimeBox.Text);
|
||||
p.Dissolution1SampleIntervalMin = double.Parse(Dissolution1IntervalBox.Text);
|
||||
p.Dissolution2SampleIntervalMin = double.Parse(Dissolution2IntervalBox.Text);
|
||||
p.DissolutionSampleTimes = SampleTimesBox.Text
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => int.Parse(s.Trim()))
|
||||
.ToArray();
|
||||
double hardnessPressure = ParseFiniteDouble(HardnessPressureBox.Text, "加压压力");
|
||||
double hardnessSpeed = ParsePositiveDouble(HardnessSpeedBox.Text, "加压速度");
|
||||
double hardnessDamageThreshold = ParsePositiveDouble(HardnessDamageThresholdBox.Text, "硬度破损判定");
|
||||
double friabilityRpm = ParseFiniteDouble(FriabilityRpmBox.Text, "脆碎度转速");
|
||||
p.FriabilityTargetRpm = friabilityRpm;
|
||||
//p.FriabilityTargetTimeMin = ParseFiniteDouble(FriabilityTimeBox.Text, "脆碎度试验时间");
|
||||
p.FriabilityTargetRounds = ParsePositiveInt(FriabilityRoundsBox.Text, "脆碎圈数");
|
||||
p.FriabilityMaxLossPercent = ParseFiniteDouble(FriabilityMaxLossBox.Text, "最大失重率");
|
||||
double disintegrationTimeMin = ParsePositiveDouble(DisintegrationTimeMinBox.Text, "崩解时间");
|
||||
double disintegrationSpeed = ParsePositiveDouble(DisintegrationSpeedBox.Text, "升降频次");
|
||||
p.DisintegrationSpeedRpm = disintegrationSpeed;
|
||||
p.DisintegrationMaxSeconds = ToDisintegrationSeconds(disintegrationTimeMin);
|
||||
//p.DisintegrationTemperatureC = ParseFiniteDouble(DisintegrationTempBox.Text, "崩解介质温度");
|
||||
//p.DissolutionTemperatureC = ParseFiniteDouble(DissolutionTempBox.Text, "溶出介质温度");
|
||||
double dissolution1Speed = ParsePositiveDouble(Dissolution1SpeedBox.Text, "溶出速度1");
|
||||
double dissolution2Speed = ParsePositiveDouble(Dissolution2SpeedBox.Text, "溶出速度2");
|
||||
p.Dissolution1TimeMin = ParsePositiveInt(Dissolution1TimeBox.Text, "溶出1时间");
|
||||
p.Dissolution2TimeMin = ParsePositiveInt(Dissolution2TimeBox.Text, "溶出2时间");
|
||||
p.Dissolution1SampleIntervalMin = ParsePositiveDouble(Dissolution1IntervalBox.Text, "溶出1取样间隔");
|
||||
p.Dissolution2SampleIntervalMin = ParsePositiveDouble(Dissolution2IntervalBox.Text, "溶出2取样间隔");
|
||||
|
||||
ValidateParameters(p);
|
||||
await WriteHardnessPressureAsync(hardnessPressure);
|
||||
await WriteHardnessSpeedAsync(hardnessSpeed);
|
||||
await WriteHardnessDamageThresholdAsync(hardnessDamageThreshold);
|
||||
await WriteFriabilityRpmAsync(friabilityRpm);
|
||||
await WriteDisintegrationTimeAsync(disintegrationTimeMin);
|
||||
await WriteDisintegrationSpeedAsync(disintegrationSpeed);
|
||||
await WriteDissolution1SpeedAsync(dissolution1Speed);
|
||||
await WriteDissolution2SpeedAsync(dissolution2Speed);
|
||||
await WriteDissolution1TimeAsync(p.Dissolution1TimeMin);
|
||||
await WriteDissolution2TimeAsync(p.Dissolution2TimeMin);
|
||||
await WriteDissolution1IntervalAsync(p.Dissolution1SampleIntervalMin);
|
||||
await WriteDissolution2IntervalAsync(p.Dissolution2SampleIntervalMin);
|
||||
App.CurrentPharmaParams = p;
|
||||
App.SaveCurrentPharmaParameters();
|
||||
|
||||
MessageBox.Show("参数已保存并立即生效", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
@@ -85,61 +130,25 @@ namespace TabletTester2025
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DisintegrationDosageFormBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (DisintegrationDosageFormBox.SelectedItem is ComboBoxItem item && item.Tag is string seconds)
|
||||
DisintegrationMaxSecBox.Text = seconds;
|
||||
}
|
||||
|
||||
private void FriabilityCalculationBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (FriabilityRoundsBox == null)
|
||||
return;
|
||||
|
||||
if (int.TryParse(FriabilityTimeBox?.Text, out int timeMin)
|
||||
&& double.TryParse(FriabilityRpmBox?.Text, out double rpm)
|
||||
&& timeMin > 0
|
||||
&& rpm > 0)
|
||||
{
|
||||
FriabilityRoundsBox.Text = CalculateFriabilityRounds(timeMin, rpm).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
FriabilityRoundsBox.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectDisintegrationDosageForm(string dosageForm)
|
||||
{
|
||||
foreach (ComboBoxItem item in DisintegrationDosageFormBox.Items)
|
||||
{
|
||||
if (string.Equals(item.Content?.ToString(), dosageForm, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
DisintegrationDosageFormBox.SelectedItem = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DisintegrationDosageFormBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private string GetSelectedDisintegrationDosageForm()
|
||||
{
|
||||
return DisintegrationDosageFormBox.SelectedItem is ComboBoxItem item
|
||||
? item.Content?.ToString() ?? "普通片"
|
||||
: "普通片";
|
||||
}
|
||||
|
||||
private static void ValidateParameters(PharmaParameters p)
|
||||
{
|
||||
if (!double.IsFinite(p.HardnessMin_N) || !double.IsFinite(p.HardnessMax_N))
|
||||
throw new InvalidOperationException("硬度参数必须为有效数字。");
|
||||
if (p.HardnessMin_N < 0 || p.HardnessMax_N <= p.HardnessMin_N)
|
||||
throw new InvalidOperationException("硬度内控上限必须大于下限。");
|
||||
throw new InvalidOperationException("硬度上限必须大于下限。");
|
||||
if (p.HardnessTestCount <= 0)
|
||||
throw new InvalidOperationException("硬度测试次数必须大于0。");
|
||||
if (!double.IsFinite(p.FriabilityTargetRpm) || !double.IsFinite(p.FriabilityTargetTimeMin) || !double.IsFinite(p.FriabilityMaxLossPercent))
|
||||
throw new InvalidOperationException("脆碎度参数必须为有效数字。");
|
||||
if (p.FriabilityTargetRpm <= 0 || p.FriabilityTargetTimeMin <= 0 || p.FriabilityTargetRounds <= 0 || p.FriabilityMaxLossPercent <= 0)
|
||||
throw new InvalidOperationException("脆碎度参数必须大于0。");
|
||||
if (!double.IsFinite(p.DisintegrationSpeedRpm) || !double.IsFinite(p.DisintegrationTemperatureC))
|
||||
throw new InvalidOperationException("崩解参数必须为有效数字。");
|
||||
if (p.DisintegrationMaxSeconds <= 0 || p.DisintegrationSpeedRpm <= 0 || p.DisintegrationTemperatureC <= 0)
|
||||
throw new InvalidOperationException("崩解参数必须大于0。");
|
||||
if (!double.IsFinite(p.DissolutionMinPercentAt30min) || !double.IsFinite(p.DissolutionTemperatureC)
|
||||
|| !double.IsFinite(p.Dissolution1SampleIntervalMin) || !double.IsFinite(p.Dissolution2SampleIntervalMin))
|
||||
throw new InvalidOperationException("溶出度参数必须为有效数字。");
|
||||
if (p.DissolutionMinPercentAt30min < 0 || p.DissolutionMinPercentAt30min > 150)
|
||||
throw new InvalidOperationException("溶出度Q值应在0到150之间。");
|
||||
if (p.DissolutionTemperatureC <= 0 || p.Dissolution1TimeMin <= 0 || p.Dissolution2TimeMin <= 0)
|
||||
@@ -147,7 +156,294 @@ namespace TabletTester2025
|
||||
if (p.Dissolution1SampleIntervalMin <= 0 || p.Dissolution2SampleIntervalMin <= 0)
|
||||
throw new InvalidOperationException("溶出取样间隔必须大于0。");
|
||||
if (p.DissolutionSampleTimes == null || p.DissolutionSampleTimes.Length == 0 || p.DissolutionSampleTimes.Any(t => t <= 0))
|
||||
throw new InvalidOperationException("溶出取样时间点必须为大于0的分钟数。");
|
||||
throw new InvalidOperationException("溶出取样配置必须为有效的正数分钟值。");
|
||||
}
|
||||
|
||||
private static double ParseFiniteDouble(string text, string fieldName)
|
||||
{
|
||||
if (!double.TryParse(text, out double value) || !double.IsFinite(value))
|
||||
throw new InvalidOperationException($"{fieldName}必须为有效数字。");
|
||||
if (value < 0)
|
||||
throw new InvalidOperationException($"{fieldName}不能小于0。");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int ParsePositiveInt(string text, string fieldName)
|
||||
{
|
||||
if (!int.TryParse(text, out int value))
|
||||
throw new InvalidOperationException($"{fieldName}必须为整数。");
|
||||
if (value <= 0)
|
||||
throw new InvalidOperationException($"{fieldName}必须大于0。");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static double ParsePositiveDouble(string text, string fieldName)
|
||||
{
|
||||
double value = ParseFiniteDouble(text, fieldName);
|
||||
if (value <= 0)
|
||||
throw new InvalidOperationException($"{fieldName}必须大于0。");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task LoadHardnessPressureAsync()
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessPressureRegister();
|
||||
if (registerAddress == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
float value = await App.PlcService.ReadFloatAsync(registerAddress);
|
||||
if (float.IsFinite(value))
|
||||
HardnessPressureBox.Text = value.ToString("0.###");
|
||||
}
|
||||
catch
|
||||
{
|
||||
HardnessPressureBox.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteHardnessPressureAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessPressureRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("加压压力PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static ushort ResolveHardnessPressureRegister()
|
||||
{
|
||||
return App.PlcConfig.HardnessPressure != 0 ? App.PlcConfig.HardnessPressure : (ushort)1480;
|
||||
}
|
||||
|
||||
private async Task LoadHardnessSpeedAsync()
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessSpeedRegister();
|
||||
if (registerAddress == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
float value = await App.PlcService.ReadFloatAsync(registerAddress);
|
||||
if (float.IsFinite(value) && value > 0)
|
||||
HardnessSpeedBox.Text = value.ToString("0.###");
|
||||
}
|
||||
catch
|
||||
{
|
||||
HardnessSpeedBox.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteHardnessSpeedAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessSpeedRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("加压速度PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static ushort ResolveHardnessSpeedRegister()
|
||||
{
|
||||
return App.PlcConfig.HardnessSudu != 0 ? App.PlcConfig.HardnessSudu : (ushort)300;
|
||||
}
|
||||
|
||||
private async Task LoadHardnessDamageThresholdAsync()
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessDamageThresholdRegister();
|
||||
if (registerAddress == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
float value = await App.PlcService.ReadFloatAsync(registerAddress);
|
||||
if (float.IsFinite(value) && value >= 0)
|
||||
HardnessDamageThresholdBox.Text = value.ToString("0.###");
|
||||
}
|
||||
catch
|
||||
{
|
||||
HardnessDamageThresholdBox.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteHardnessDamageThresholdAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveHardnessDamageThresholdRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("硬度破损判定PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static ushort ResolveHardnessDamageThresholdRegister()
|
||||
{
|
||||
return App.PlcConfig.HardnessPoSun != 0 ? App.PlcConfig.HardnessPoSun : (ushort)400;
|
||||
}
|
||||
|
||||
private async Task LoadPlcFloatToTextBoxAsync(ushort address, TextBox textBox)
|
||||
{
|
||||
if (address == 0) return;
|
||||
try
|
||||
{
|
||||
float value = await App.PlcService.ReadFloatAsync(address);
|
||||
if (float.IsFinite(value))
|
||||
textBox.Text = value.ToString("0.###");
|
||||
}
|
||||
catch { textBox.Text = ""; }
|
||||
}
|
||||
|
||||
private async Task LoadPlcIntToTextBoxAsync(ushort address, TextBox textBox)
|
||||
{
|
||||
if (address == 0) return;
|
||||
try
|
||||
{
|
||||
int value = await App.PlcService.ReadIntAsync(address);
|
||||
if (value >= 0)
|
||||
textBox.Text = value.ToString();
|
||||
}
|
||||
catch { textBox.Text = ""; }
|
||||
}
|
||||
|
||||
private static async Task WriteFriabilityRpmAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveFriabilityRpmRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("脆碎度转速PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static ushort ResolveFriabilityRpmRegister()
|
||||
{
|
||||
return App.PlcConfig.FriabilityRpm != 0 ? App.PlcConfig.FriabilityRpm : (ushort)320;
|
||||
}
|
||||
|
||||
private static ushort ResolveFriabilityRoundsRegister()
|
||||
{
|
||||
if (App.PlcConfig.FriabilityRounds != 0)
|
||||
return App.PlcConfig.FriabilityRounds;
|
||||
if (App.PlcConfig.FriabilityRoundsBox != 0)
|
||||
return App.PlcConfig.FriabilityRoundsBox;
|
||||
return App.PlcConfig.FriabilityTestTime != 0 ? App.PlcConfig.FriabilityTestTime : (ushort)410;
|
||||
}
|
||||
|
||||
private static ushort ResolveDisintegrationTimeRegister()
|
||||
{
|
||||
return App.PlcConfig.DisintegrationTime != 0 ? App.PlcConfig.DisintegrationTime : (ushort)420;
|
||||
}
|
||||
|
||||
private static ushort ResolveDisintegrationSpeedRegister()
|
||||
{
|
||||
return App.PlcConfig.DisintegrationSpeed != 0 ? App.PlcConfig.DisintegrationSpeed : (ushort)330;
|
||||
}
|
||||
|
||||
private static async Task WriteDisintegrationTimeAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDisintegrationTimeRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("崩解时间PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(
|
||||
(int)Math.Round(value, MidpointRounding.AwayFromZero),
|
||||
1,
|
||||
ushort.MaxValue));
|
||||
}
|
||||
|
||||
private static async Task WriteDisintegrationSpeedAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDisintegrationSpeedRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("崩解升降频次PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution1SpeedAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution1SpeedRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出速度1 PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution2SpeedAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution2SpeedRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出速度2 PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution1TimeAsync(int value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution1TimeRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出1时间PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(value, 1, ushort.MaxValue));
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution2TimeAsync(int value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution2TimeRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出2时间PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteRegisterAsync(registerAddress, (ushort)Math.Clamp(value, 1, ushort.MaxValue));
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution1IntervalAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution1IntervalRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出1取样间隔PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static async Task WriteDissolution2IntervalAsync(double value)
|
||||
{
|
||||
ushort registerAddress = ResolveDissolution2IntervalRegister();
|
||||
if (registerAddress == 0)
|
||||
throw new InvalidOperationException("溶出2取样间隔PLC寄存器地址未配置。");
|
||||
|
||||
await App.PlcService.WriteFloatAsync(registerAddress, (float)value);
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution1SpeedRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution1Speed != 0 ? App.PlcConfig.Dissolution1Speed : (ushort)340;
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution2SpeedRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution2Speed != 0 ? App.PlcConfig.Dissolution2Speed : (ushort)350;
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution1TimeRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution1Time != 0 ? App.PlcConfig.Dissolution1Time : (ushort)430;
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution2TimeRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution2Time != 0 ? App.PlcConfig.Dissolution2Time : (ushort)440;
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution1IntervalRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution1SampleInterval != 0 ? App.PlcConfig.Dissolution1SampleInterval : (ushort)432;
|
||||
}
|
||||
|
||||
private static ushort ResolveDissolution2IntervalRegister()
|
||||
{
|
||||
return App.PlcConfig.Dissolution2SampleInterval != 0 ? App.PlcConfig.Dissolution2SampleInterval : (ushort)442;
|
||||
}
|
||||
|
||||
private static double ResolveFriabilityTargetTimeMin(PharmaParameters p)
|
||||
@@ -162,12 +458,24 @@ namespace TabletTester2025
|
||||
return 4.0;
|
||||
}
|
||||
|
||||
private static int CalculateFriabilityRounds(double timeMin, double rpm)
|
||||
private static int ResolveFriabilityTargetRounds(PharmaParameters p)
|
||||
{
|
||||
if (timeMin <= 0 || rpm <= 0)
|
||||
return 0;
|
||||
if (p.FriabilityTargetRounds > 0)
|
||||
return p.FriabilityTargetRounds;
|
||||
|
||||
return Math.Max(1, (int)Math.Round(timeMin * rpm, MidpointRounding.AwayFromZero));
|
||||
return 100;
|
||||
}
|
||||
|
||||
private static double ResolveDisintegrationTimeMin(PharmaParameters p)
|
||||
{
|
||||
return p.DisintegrationMaxSeconds > 0
|
||||
? p.DisintegrationMaxSeconds / 60.0
|
||||
: 15.0;
|
||||
}
|
||||
|
||||
private static int ToDisintegrationSeconds(double minutes)
|
||||
{
|
||||
return Math.Max(1, (int)Math.Round(minutes * 60, MidpointRounding.AwayFromZero));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
<Window x:Class="片剂四用仪.Views.ShowData"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="药典参数设置" Height="768" Width="1024"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Background="#F0F2F5">
|
||||
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 卡片样式 -->
|
||||
<Style x:Key="CardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="CornerRadius" Value="10"/>
|
||||
<Setter Property="Margin" Value="15,15,80,15"/>
|
||||
<Setter Property="Padding" Value="15"/>
|
||||
<Setter Property="MinWidth" Value="390"/>
|
||||
<Setter Property="Effect">
|
||||
<Setter.Value>
|
||||
<DropShadowEffect Color="#D0D7DE" BlurRadius="8" ShadowDepth="1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 分组标题样式 -->
|
||||
<Style x:Key="GroupTitle" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Foreground" Value="#1976D2"/>
|
||||
<Setter Property="Margin" Value="0 0 0 10"/>
|
||||
</Style>
|
||||
|
||||
<!-- 标签样式 -->
|
||||
<Style x:Key="SettingLabel" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="Width" Value="190"/>
|
||||
<Setter Property="Foreground" Value="#333333"/>
|
||||
<Setter Property="Margin" Value="0 6 0 6"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
</Style>
|
||||
|
||||
<!-- 输入框样式 -->
|
||||
<Style x:Key="SettingTextBox" TargetType="TextBox">
|
||||
<Setter Property="Width" Value="150"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="Margin" Value="0 6 0 6"/>
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
<Setter Property="Background" Value="#F8F9FA"/>
|
||||
<Setter Property="BorderBrush" Value="#D1D5DB"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="10">
|
||||
<WrapPanel Orientation="Horizontal">
|
||||
|
||||
<!-- 模块1:硬度测试参数 -->
|
||||
<Border Style="{StaticResource CardStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource GroupTitle}" Text="⚙ 硬度测试参数"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="速度输入(mm/min)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_HardnessSpeed"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="位移输入(mm)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_HardnessDisplacement"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="电机极限输入"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_HardnessMotorLimit"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="破损判定输入(N)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_HardnessDamageThreshold"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="最大力采集"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_MaxForceCollect" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 模块2:脆碎度测试参数 -->
|
||||
<Border Style="{StaticResource CardStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource GroupTitle}" Text="⚙ 脆碎度测试参数"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="试验时间设置"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_BrittlenessTestTime"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="脆碎前质量输入"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_PreBrittlenessMass"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="脆碎后质量输入"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_PostBrittlenessMass"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="失重率(%)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_WeightLossRate"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 模块3:崩解测试参数 -->
|
||||
<Border Style="{StaticResource CardStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource GroupTitle}" Text="⚙ 崩解测试参数"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="崩解速度(r/min)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_DisintegrationSpeed"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="崩解时间设置(min)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_DisintegrationTime"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 模块4:溶出测试参数 -->
|
||||
<Border Style="{StaticResource CardStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource GroupTitle}" Text="⚙ 溶出测试参数"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出时间设置(min)"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_DissolutionTime"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出1间隔取样时间设置"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_Dissolution1SamplingInterval"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="溶出2间隔取样时间设置"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_Dissolution2SamplingInterval"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 模块5:力值与温度校准 -->
|
||||
<Border Style="{StaticResource CardStyle}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource GroupTitle}" Text="⚙ 力值与温度校准"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="力显示"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_ForceDisplay" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="力系数"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_ForceCoefficient"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="力保护"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_ForceProtection"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="温度系数"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_TemperatureCoefficient" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingLabel}" Text="温度显示"/>
|
||||
<TextBox Style="{StaticResource SettingTextBox}" Name="txt_TemperatureDisplay"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</WrapPanel>
|
||||
</ScrollViewer>
|
||||
</Window>
|
||||
@@ -1,179 +0,0 @@
|
||||
using Microsoft.Win32;
|
||||
using Sunny.UI;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using TabletTester2025.Helpers;
|
||||
using TabletTester2025.Services;
|
||||
using static OfficeOpenXml.ExcelErrorValue;
|
||||
|
||||
namespace 片剂四用仪.Views
|
||||
{
|
||||
public partial class ShowData : Window
|
||||
{
|
||||
private readonly IPlcService _plc;
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
public ShowData(IPlcService plc)
|
||||
{
|
||||
InitializeComponent();
|
||||
_plc = plc;
|
||||
Loaded += ShowData_Loaded;
|
||||
Closing += ShowData_Closing;
|
||||
}
|
||||
|
||||
private async void ShowData_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
BindAllTextBoxWriteEvents();
|
||||
await StartPlcReadLoop(_cts.Token);
|
||||
}
|
||||
|
||||
private void ShowData_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
}
|
||||
|
||||
// ====================== 读取 PLC ======================
|
||||
private async Task StartPlcReadLoop(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var m in _paramMappings)
|
||||
{
|
||||
var tb = FindName(m.TextBoxName) as TextBox;
|
||||
if (tb == null) continue;
|
||||
if (NumericInput.GetIsKeypadOpen(tb) || tb.IsKeyboardFocusWithin)
|
||||
continue;
|
||||
|
||||
if (m.Type == PlcParamType.Int)
|
||||
{
|
||||
int value = await _plc.ReadIntAsync((ushort)m.Address);
|
||||
|
||||
Dispatcher.Invoke(() => tb.Text = value.ToString());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
float value = await _plc.ReadFloatAsync((ushort)m.Address);
|
||||
|
||||
Dispatcher.Invoke(() => tb.Text = value.ToString("F2"));
|
||||
|
||||
}
|
||||
}
|
||||
// 正常循环的Delay,同样处理取消异常
|
||||
try
|
||||
{
|
||||
await Task.Delay(1000, token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 窗口关闭时的取消异常,正常退出循环
|
||||
break;
|
||||
}
|
||||
|
||||
//await Task.Delay(1000, token);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 写入 PLC ======================
|
||||
private void BindAllTextBoxWriteEvents()
|
||||
{
|
||||
foreach (var m in _paramMappings)
|
||||
{
|
||||
var tb = FindName(m.TextBoxName) as TextBox;
|
||||
if (tb == null) continue;
|
||||
|
||||
if (tb.IsReadOnly)
|
||||
continue;
|
||||
|
||||
NumericInput.SetIsEnabled(tb, true);
|
||||
NumericInput.SetAllowDecimal(tb, m.Type != PlcParamType.Int);
|
||||
NumericInput.SetAllowNegative(tb, false);
|
||||
NumericInput.AddValueCommittedHandler(tb, async (_, _) => await WritePlcValueAsync(m, tb));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WritePlcValueAsync(PlcParamMapping mapping, TextBox textBox)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (mapping.Type == PlcParamType.Int)
|
||||
{
|
||||
if (int.TryParse(textBox.Text, out int val))
|
||||
await _plc.WriteRegisterAsync((ushort)mapping.Address, (ushort)Math.Clamp(val, 0, ushort.MaxValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (double.TryParse(textBox.Text, out double val))
|
||||
{
|
||||
textBox.Text = val.ToString("F3");
|
||||
await _plc.WriteFloatAsync((ushort)mapping.Address, (float)val);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// ====================== 地址映射表 ======================
|
||||
private readonly PlcParamMapping[] _paramMappings = new[]
|
||||
{
|
||||
new PlcParamMapping("txt_HardnessSpeed", 300, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_HardnessDisplacement", 310, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_HardnessMotorLimit", 298, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_HardnessDamageThreshold", 400, PlcParamType.Float),
|
||||
|
||||
|
||||
new PlcParamMapping("txt_BrittlenessTestTime", 410, PlcParamType.Int),
|
||||
new PlcParamMapping("txt_PreBrittlenessMass", 412, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_PostBrittlenessMass", 414, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_WeightLossRate", 416, PlcParamType.Int),
|
||||
|
||||
new PlcParamMapping("txt_DisintegrationSpeed", 330, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_DisintegrationTime", 420, PlcParamType.Float),
|
||||
|
||||
new PlcParamMapping("txt_DissolutionTime", 430, PlcParamType.Int),
|
||||
new PlcParamMapping("txt_Dissolution1SamplingInterval", 432, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_Dissolution2SamplingInterval", 442, PlcParamType.Float),
|
||||
|
||||
|
||||
new PlcParamMapping("txt_ForceCoefficient", 1320, PlcParamType.Float),
|
||||
new PlcParamMapping("txt_ForceProtection", 1322, PlcParamType.Float),
|
||||
|
||||
new PlcParamMapping("txt_TemperatureDisplay", 1430, PlcParamType.Float),
|
||||
|
||||
new PlcParamMapping("txt_MaxForceCollect", 72, PlcParamType.Float),//读取
|
||||
new PlcParamMapping("txt_ForceDisplay", 1314, PlcParamType.Float),//读取
|
||||
new PlcParamMapping("txt_TemperatureCoefficient", 1428, PlcParamType.Float),//读取
|
||||
};
|
||||
}
|
||||
|
||||
internal class PlcParamMapping
|
||||
{
|
||||
public string TextBoxName { get; }
|
||||
public int Address { get; }
|
||||
public PlcParamType Type { get; }
|
||||
|
||||
public PlcParamMapping(string textBoxName, int address, PlcParamType type)
|
||||
{
|
||||
TextBoxName = textBoxName;
|
||||
Address = address;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum PlcParamType
|
||||
{
|
||||
Int,
|
||||
Float
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"IpAddress": "192.168.1.10",
|
||||
"Port": 502,
|
||||
"SlaveId": 1,
|
||||
"FloatWordOrder": "LowWordFirst", // 三菱D寄存器REAL:低字在前,避免参数写入后PLC显示NaN
|
||||
//"HardnessValue": 72,
|
||||
"HardnessStartCoil": 70, //硬度工位1启动测试M70
|
||||
|
||||
@@ -21,10 +22,15 @@
|
||||
"HardnessSudu": 300, // 硬度速度输入mm/min
|
||||
"HardnessWeiyi": 310, // 硬度位移输入mm/min
|
||||
"HardnessPoSun": 400, // 硬度破损判定输入N
|
||||
"HardnessPressure": 1480, // 加压压力
|
||||
"HardnessMax": 72, //最大力采集
|
||||
"HardnessShishilizhi": 1314, //力显示
|
||||
|
||||
|
||||
|
||||
"FriabilityRpm": 320, // 脆碎度转速 r/min
|
||||
"FriabilityRounds": 410, // 脆碎圈数
|
||||
"FriabilityRoundsBox": 410, // 兼容旧字段:脆碎圈数
|
||||
"FriabilityRealtimeRounds": 82, // 脆碎实时圈数,32位浮点
|
||||
"DisintegrationSeconds": 420, //崩解时间
|
||||
|
||||
|
||||
|
||||
@@ -35,19 +41,23 @@
|
||||
"FriabilityStartCoilReset": 95, // 脆碎复位启动
|
||||
|
||||
|
||||
"FriabilityTestTime": 410, // 试验次数(次)
|
||||
"FriabilityTestTime": 0, // 脆碎试验时间由药典参数计算,D410用于脆碎圈数
|
||||
"FriabilityWeightBefore": 412, // 脆碎前质量(g)
|
||||
"FriabilityWeightAfter": 414, // 脆碎后质量(g)
|
||||
"FriabilityLossPercent": 416, // 失重率(%)
|
||||
"WeightBefore": 412,
|
||||
"WeightAfter": 414,
|
||||
"DisintegrationTemp": 1430,
|
||||
"DisintegrationTemp": 1430, // 所有温度显示D1430,float类型
|
||||
"DisintegrationMovingUpCoil": 30,
|
||||
"DisintegrationStartCoil": 50,
|
||||
"DisintegrationStopCoil": 53,
|
||||
"DisintegrationResetCoil": 100, // 崩解复位M100
|
||||
"DisintegrationSpeed": 330,
|
||||
"DisintegrationTime": 420,
|
||||
"DisintegrationCompleteCoils": [ 200, 201, 202, 203, 204, 205 ],
|
||||
"DissolutionRpm": 400,
|
||||
"Dissolution1Speed": 340,
|
||||
"Dissolution2Speed": 350,
|
||||
"DissolutionPercent": 402,
|
||||
"Dissolution1Percent": 402,
|
||||
"Dissolution2Percent": 0,
|
||||
@@ -59,7 +69,9 @@
|
||||
"Dissolution2StopCoil": 33,
|
||||
"Dissolution2SampleAckCoil": 34,
|
||||
"Dissolution1Time": 430,
|
||||
"Dissolution2Time": 440
|
||||
"Dissolution2Time": 440,
|
||||
"Dissolution1SampleInterval": 432, // 溶出1取样间隔,float类型
|
||||
"Dissolution2SampleInterval": 442 // 溶出2取样间隔,float类型
|
||||
},
|
||||
"PharmaStandard": {
|
||||
"StandardVersion": "中国药典2025",
|
||||
|
||||
Reference in New Issue
Block a user