using Microsoft.Extensions.Configuration; using Microsoft.Data.Sqlite; using OfficeOpenXml; using System; using System.IO; using System.Windows; using TabletTester2025.Data; using TabletTester2025.Models; using TabletTester2025.Services; using TabletTester2025.ViewModels; namespace TabletTester2025 { public partial class App : Application { public static IPlcService PlcService { get; private set; } public static PlcConfiguration PlcConfig { get; private set; } public static PharmaParameters CurrentPharmaParams { get; set; } = new PharmaParameters(); protected override async void OnStartup(StartupEventArgs e) { ExcelPackage.LicenseContext = LicenseContext.NonCommercial; base.OnStartup(e); // 读取配置 var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); var configuration = builder.Build(); // 数据库初始化(手动建表) var connectionString = configuration.GetConnectionString("DefaultConnection") ?? "Data Source=TabletTests.db"; using (var connection = new Microsoft.Data.Sqlite.SqliteConnection(connectionString)) { connection.Open(); var cmd = connection.CreateCommand(); cmd.CommandText = @" CREATE TABLE IF NOT EXISTS TestBatches ( Id INTEGER PRIMARY KEY AUTOINCREMENT, TestTime TEXT NOT NULL, StationId INTEGER NOT NULL, SampleName TEXT, -- 硬度 HardnessAvg REAL, HardnessRSD REAL, HardnessMax REAL, HardnessMin REAL, HardnessTestCount INTEGER, -- 脆碎度 FriabilityLoss REAL, FriabilityTargetRpm REAL, FriabilityTargetTimeSec INTEGER, FriabilityClockwise INTEGER, -- bool 存储为 0/1 FriabilityRemainingRounds INTEGER, WeightBefore REAL, WeightAfter REAL, -- 崩解 DisintegrationTimeSec REAL, RemainingTubesAtEnd INTEGER, DisintegrationTargetFreq REAL, DisintegrationTemp REAL, -- 溶出 DissolutionChannel TEXT, DissolutionRate30Min REAL, DissolutionTargetRpm REAL, DissolutionRSquared REAL, DissolutionSampleInterval INTEGER, DissolutionUpDownFreq REAL, -- 综合 IsQualified INTEGER, -- 各项目单独合格标志 HardnessPass INTEGER, FriabilityPass INTEGER, DisintegrationPass INTEGER, DissolutionPass INTEGER, -- 测试类型(用于区分报表) TestType TEXT ); "; cmd.ExecuteNonQuery(); } EnsureColumnsExist(connectionString); // 绑定药典参数 configuration.GetSection("PharmaStandard").Bind(CurrentPharmaParams); // PLC配置 PlcConfig = configuration.GetSection("Plc").Get(); if (PlcConfig == null) throw new InvalidOperationException("PLC配置缺失"); // 创建PLC服务(真实或模拟) if (configuration["Plc:Type"] == "ModbusTcp") PlcService = new ModbusTcpPlcService(PlcConfig); else PlcService = new PlcSimulator(); try { await PlcService.ConnectAsync(); } catch (Exception ex) { MessageBox.Show($"PLC连接失败,将使用模拟模式。\n{ex.Message}", "警告", MessageBoxButton.OK, MessageBoxImage.Warning); PlcService = new PlcSimulator(); await PlcService.ConnectAsync(); } // 业务服务 var dbService = new DatabaseService(connectionString); var excelService = new ExcelExportService(); var alarmService = new AlarmService(); // 主窗口 var mainWindow = new MainWindow(); mainWindow.DataContext = new MainViewModel(PlcService, dbService, excelService, alarmService, PlcConfig); MainWindow = mainWindow; mainWindow.Show(); } private void EnsureColumnsExist(string connectionString) { var requiredColumns = new[] { ("TestBatches", "HardnessMax", "REAL", "0"), ("TestBatches", "HardnessMin", "REAL", "0"), ("TestBatches", "HardnessTestCount", "INTEGER", "6"), ("TestBatches", "FriabilityTargetRpm", "REAL", "25"), ("TestBatches", "FriabilityTargetTimeSec", "INTEGER", "240"), ("TestBatches", "FriabilityClockwise", "INTEGER", "1"), ("TestBatches", "FriabilityRemainingRounds", "INTEGER", "100"), ("TestBatches", "WeightBefore", "REAL", "0"), ("TestBatches", "WeightAfter", "REAL", "0"), ("TestBatches", "DisintegrationTargetFreq", "REAL", "31"), ("TestBatches", "DisintegrationTemp", "REAL", "37"), ("TestBatches", "DissolutionChannel", "TEXT", "''"), ("TestBatches", "DissolutionTargetRpm", "REAL", "50"), ("TestBatches", "DissolutionRSquared", "REAL", "0"), ("TestBatches", "DissolutionSampleInterval", "INTEGER", "5"), ("TestBatches", "DissolutionUpDownFreq", "REAL", "32"), ("TestBatches", "HardnessRSD", "REAL", "0"), // 已存在但确保 ("TestBatches", "RemainingTubesAtEnd", "INTEGER", "0"), // 新增合格列 ("TestBatches", "HardnessPass", "INTEGER", "0"), ("TestBatches", "FriabilityPass", "INTEGER", "0"), ("TestBatches", "DisintegrationPass", "INTEGER", "0"), ("TestBatches", "DissolutionPass", "INTEGER", "0"), ("TestBatches", "TestType", "TEXT", "") }; using var connection = new SqliteConnection(connectionString); connection.Open(); foreach (var (table, column, colType, defaultValue) in requiredColumns) { // 检查列是否存在 var pragmaCmd = connection.CreateCommand(); pragmaCmd.CommandText = $"PRAGMA table_info({table})"; using var reader = pragmaCmd.ExecuteReader(); bool columnExists = false; while (reader.Read()) { if (reader.GetString(1) == column) { columnExists = true; break; } } if (!columnExists) { // 添加缺失的列 var alterCmd = connection.CreateCommand(); alterCmd.CommandText = $"ALTER TABLE {table} ADD COLUMN {column} {colType} DEFAULT {defaultValue}"; alterCmd.ExecuteNonQuery(); System.Diagnostics.Debug.WriteLine($"已添加缺失的列: {table}.{column}"); } } } } }