using DBEntity; using Google.Protobuf; using ivf_tl_Com; using ivf_tl_Controller; using IvfTl.Control.Entity.DBEntitys; using IvfTl.Control.Entity.DTO; using IvfTl.Control.Entity.DTO.ApiRequestDTO; using IvfTl.Control.Entity.DTO.ApiResultDTO; using IvfTl.Control.Entity.GlobalEntitys; using IvfTl.Control.Entity.GlobalEnums; using IvfTl.Control.Entity.InitEntitys; using IvfTl.Control.Services; using IvfTl.Control.Services.HttpServices; using ivf_tl_ServicesImpl.DBServices; using ivf_tl_ServicesImpl.HttpServices; using ivf_tl_ServicesImpl.KafkaServices; using ivf_tl_ServicesImpl.LogServices; using ivf_tl_ServicesImpl.MqttServices; using ivf_tl_UtilHelper; using IvfTl.AutoFocus.Storage; using NetTaste; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Npgsql.TypeHandlers.GeometricHandlers; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Text.Json.Nodes; using System.Windows; using System.Windows.Markup; namespace ivf_tl_Control { public class AppData { public static AppData Instance = new Lazy(() => new AppData(), LazyThreadSafetyMode.ExecutionAndPublication).Value; public int guanbiTime = 0, csTime = 120, houseVentNum = 10, houseVentPre = 20, houseVentWaitTimeB = 1500, houseVentWaitTimeD = 5000, houseAutoWaitTime = 10, houseCCDAutoWaitTime = 5, houseCCDError = 0, houseCCDFailedNumber = 3, houseCCDFailedWaitTime = 15, queuAir = 0; public event Action StopProEvent; public bool MvcTest { get; set; } = false; public string TakePhotoString = string.Empty; private AppData() { _urlIp = $"{ConfigurationManager.AppSettings["urlIp"]}:{ConfigurationManager.AppSettings["urlPort"]}/"; kfkaIP = $"{ConfigurationManager.AppSettings["kfkaIP"]}:{ConfigurationManager.AppSettings["kfkaPort"]}"; // M5-01-2 / R8:mqttIp/mqttPort 启动期容错读取,缺键不崩在构造里(mqttIp 缺键回退空串,mqttPort 缺键/非数回退 1883)。 MqttIp = ConfigurationManager.AppSettings["mqttIp"] ?? string.Empty; MqttPort = int.TryParse(ConfigurationManager.AppSettings["mqttPort"], out int mqttPortVal) ? mqttPortVal : 1883; if (int.TryParse(ConfigurationManager.AppSettings["gbTime"].ToString(), out int newTime1)) guanbiTime = newTime1; if (int.TryParse(ConfigurationManager.AppSettings["csTime"].ToString(), out int newTime2)) csTime = newTime2; if (int.TryParse(ConfigurationManager.AppSettings["VentNum"].ToString(), out int newTime3)) houseVentNum = newTime3; if (int.TryParse(ConfigurationManager.AppSettings["VentPre"].ToString(), out int newTime4)) houseVentPre = newTime4; if (int.TryParse(ConfigurationManager.AppSettings["VentWaitTimeB"].ToString(), out int newTime5)) houseVentWaitTimeB = newTime5; if (int.TryParse(ConfigurationManager.AppSettings["VentWaitTimeD"].ToString(), out int newTime6)) houseVentWaitTimeD = newTime6; if (int.TryParse(ConfigurationManager.AppSettings["AutoWaitTime"].ToString(), out int newTime7)) houseAutoWaitTime = newTime7; if (int.TryParse(ConfigurationManager.AppSettings["CCDAutoWaitTime"].ToString(), out int newTime8)) houseCCDAutoWaitTime = newTime8; if (int.TryParse(ConfigurationManager.AppSettings["CCDError"].ToString(), out int newTime9)) houseCCDError = newTime9; if (int.TryParse(ConfigurationManager.AppSettings["CCDFailedNumber"].ToString(), out int newTime10)) houseCCDFailedNumber = newTime10; if (int.TryParse(ConfigurationManager.AppSettings["CCDFailedWaitTime"].ToString(), out int newTime11)) houseCCDFailedWaitTime = newTime11; if (int.TryParse(ConfigurationManager.AppSettings["QueuAir"].ToString(), out int newTime12)) queuAir = newTime12; TakePhotoString = GetLanguageStringByKey("D0010"); //_urlIp = "http://gateway.aivfo.com:36000/"; //MqttIp = "211.149.139.131"; //MqttPort = 61883; //_urlIp = "http://test-gateway.aivfo.com:36000/"; //MqttIp = "211.149.139.131"; //MqttPort = 62883; ImageDTODic = new Dictionary(); sqlitePath = $"{Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"DependFile\DB\aivfoTL.db")}"; LogService = new LogServiceImpl() { Pan = "C" }; HttpService = new HttpService(_urlIp); HttpService.ErrorLogEvent += LogService.TLLog; HttpService.ExceptionLogEvent += LogService.ExceptionLog; DBService = new DBService(); DBService.ErrorLogEvent += LogService.TLLog; DBService.ExceptionLogEvent += LogService.ExceptionLog; DBService.StartDbService(sqlitePath); // M2-04 标定结果存储:calibration.json 真相源 + 镜像 house_autofocus_calibration。 // JSON 路径走部署工作目录(DependFile\AutoFocus),不写死 autofocustool 测试外壳常量路径。 // DbMirror 绑定到 DBService.SaveAutofocusCalibration(scene 0 upsert / 1 append); // 任一步骤异常 CalibrationStore 内部已吞掉并经 Log 记录,不崩对焦/采集线程。 string calibJsonPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"DependFile\AutoFocus\calibration.json"); AutofocusStore = new CalibrationStore { JsonPath = calibJsonPath, Source = "LOCAL_JSON", Log = msg => LogService.TLLog(msg, LogEnum.RunError), DbMirror = (tlSn, houseSn, wellSn, scene, focusZ, exposure, horizontalPulse, peakRatio, circleFound, centerOffsetPct, note, source) => DBService.SaveAutofocusCalibration(tlSn, houseSn, wellSn, scene, focusZ, exposure, horizontalPulse, peakRatio, circleFound, centerOffsetPct, note, source), // M2-06 安全门降级:关闭本地对焦时按 scene=0 基准位置拍照,由此回调读基准 FocusZ。 BaselineReader = (tlSn, houseSn, wellSn) => DBService.GetBaselineFocusZ(tlSn, houseSn, wellSn), }; KafkaService = new KafkaService(kafkaTopic, kfkaIP); KafkaService.ErrorLogEvent += LogService.TLLog; KafkaService.ExceptionLogEvent += LogService.ExceptionLog; MqttService = new MqttService(MqttIp, MqttPort, MqttUserName, MqttPassword); MqttService.ErrorLogEvent += LogService.TLLog; MqttService.ExceptionLogEvent += LogService.ExceptionLog; MqttService.MessEvent += MqttMessage; MqttService.MqttAlarm += MqttAlarm; SerialBinController = new SerialBinController(HttpService, DBService); SerialBinController.LogEvent += LogService.TLLog; SerialBinController.ExceptionLogEvent += LogService.ExceptionLog; HouseBinController = new HouseBinController(HttpService, DBService); HouseBinController.LogEvent += LogService.TLLog; HouseBinController.ExceptionLogEvent += LogService.ExceptionLog; } public void TestEvent() { //string messageinfo = $"{GetLanguageStringByKey("D0007")}1{GetLanguageStringByKey("D0008")}"; //MessageBox.Show(messageinfo); //string ss = GetLanguageStringByKey("D0001"); //return; StopProEvent?.Invoke(); } public string _urlIp = "http://192.168.31.89:10010/"; //public string _urlIp = "http://gateway.aivfo.com:36000/"; private string kafkaTopic = "jiangxuebingPicTestTopic"; private string kfkaIP = "192.168.31.89:9092"; private string MqttIp = "211.149.139.131"; private int MqttPort = 1883; private string MqttUserName = "aivfo"; private string MqttPassword = "aivfo"; private string MqttClientId = "TL/DATA/tlSn"; private string MqttTopicName = "TL/DATA/tlSn"; private string sqlitePath = ""; private bool isSetZiDong = false; public Dictionary ImageDTODic { get; set; } // ── M5-04-3:图片补传补强 ── // (a) 去重防重传:StartUpLoadImage 每 5s 重扫落盘目录,单文件一次上传可能跨多个 5s 周期(慢链/超时), // 用 in-flight 集合标记「正在上传」,重扫时跳过,避免同名文件被两轮并发重传(现 ImageDTODic lock 只护缓存)。 private readonly HashSet _uploadingFiles = new HashSet(); // (b) 断网累积兜底告警阈值:落盘待传数超过此值视为断网堆积,置位告警(只读暴露给监控/提示),不静默堆积。 // [D10] 具体阈值留 M7 校准;此处为占位默认值。 public int PendingImageAlarmThreshold { get; set; } = 500; // 断网累积告警状态(只读):true=待传堆积超阈值。供 GetMonitorSnapshot/断线提示呈现,不触发删图。 public bool ImageBacklogAlarm { get; private set; } public int LastPendingImageCount { get; private set; } public SerialBinController SerialBinController { get; set; } public HouseBinController HouseBinController { get; set; } public ILogService LogService { get; set; } public HttpService HttpService { get; set; } public DBService DBService { get; set; } /// M2-04 标定结果存储入口(calibration.json 真相源 + house_autofocus_calibration 镜像)。 public CalibrationStore AutofocusStore { get; set; } public KafkaService KafkaService { get; set; } public MqttService MqttService { get; set; } public UserInfo CurrentUser { get; set; } /// 启动期坏舱清单(InitTL 写入;GetMonitorSnapshot 透出 /status;ReportStartupFaults 经 reportAlarm 闭环上报)。 public List StartupFaults { get; set; } = new List(); public TLSetting TLSetting; public HouseBin HouseBin1; public HouseBin HouseBin2; public HouseBin HouseBin3; public HouseBin HouseBin4; public HouseBin HouseBin5; public HouseBin HouseBin6; public HouseBin HouseBin7; public HouseBin HouseBin8; public HouseBin HouseBin9; public HouseBin HouseBin10; public BufferBottleBin BufferBottleBin; // ── M5-03-2:各链路「最后成功通讯时间」只读时间戳 ── // 仅在已有的成功分支里赋值(不改上报逻辑/不改报文),供 GetMonitorSnapshot 只读呈现「不假装实时」。 // LastHttpOkAt 由 operate 侧 HTTP 轮询(M5-04-4)写入,control 这里只持有/透传。 public DateTime? LastMqttOkAt { get; private set; } public DateTime? LastKafkaOkAt { get; private set; } public DateTime? LastHttpOkAt { get; set; } /// /// M5-03-1:聚合 control 内存/事件的当前状态为只读快照(需求 7/10)。 /// 纯读取 + 拷贝到值类型 DTO,不修改任何 control 状态、不暴露可写引用,监控页据此只读呈现。 /// 任一字段取值异常被吞掉(监控页不应因取数失败而崩),缺失项以默认值呈现。 /// public MonitorSnapshot GetMonitorSnapshot() { var snap = new MonitorSnapshot { SnapshotAt = DateTime.Now, ControlHosted = true }; try { // MQTT 连接态(来源 MqttService.MqttIsConnected()) try { snap.MqttConnected = MqttService != null && MqttService.MqttIsConnected(); } catch { snap.MqttConnected = false; } snap.LastMqttOkAt = LastMqttOkAt; try { snap.MqttLastConnectedAt = MqttService?.LastConnectedAt; } catch { } try { snap.MqttLastDisconnectedAt = MqttService?.LastDisconnectedAt; } catch { } snap.LastKafkaOkAt = LastKafkaOkAt; snap.LastHttpOkAt = LastHttpOkAt; // 服务器接口基址(脱敏展示,仅地址不含凭据) snap.ServerUrl = _urlIp; // 上传队列:内存缓存数 + 落盘目录待传数 try { lock (ImageDTODic) snap.ImageCacheCount = ImageDTODic == null ? 0 : ImageDTODic.Count; } catch { } try { snap.PendingDiskImageCount = CountPendingDiskImages(); } catch { } snap.ImageBacklogAlarm = ImageBacklogAlarm; snap.PendingImageAlarmThreshold = PendingImageAlarmThreshold; // 磁盘(来源 GetDiskInfo(缓存盘)) try { string pan = TLSetting != null && !string.IsNullOrEmpty(TLSetting.tmpDir) ? TLSetting.tmpDir : PathHelper.pan; if (!string.IsNullOrEmpty(pan)) { var disk = GetDiskInfo(pan); snap.DiskPath = disk.diskPath; snap.DiskExist = disk.diskExist == 0; snap.DiskFreeGb = disk.diskSpace; } } catch { } // 各舱室只读态 var bins = new HouseBin[] { HouseBin1, HouseBin2, HouseBin3, HouseBin4, HouseBin5, HouseBin6, HouseBin7, HouseBin8, HouseBin9, HouseBin10 }; int sn = 0; foreach (var bin in bins) { sn++; if (bin == null) continue; try { snap.Houses.Add(new HouseMonitorRow { HouseSn = sn, PortName = bin.PortName, RunState = bin.RunState, Temperature = bin.Temperature, Pressure = bin.Pressure, HouseState = bin.IsDoorOpen.ToString(), ComState = bin.ComBin != null ? "已连接" : "未连接", CcdState = bin.CCDError ? "异常" : "正常", CcdError = bin.CCDError, // 阶段2 §6 三块补充(只读) WorkingType = bin.WorkingType.ToString(), ValveState = bin.ValveState.ToString(), CapturePausedByGate = bin.CapturePausedByGate, }); } catch { } } } catch (Exception ex) { ExLog(ex, "GetMonitorSnapshot"); } // 舱故障透出(StartupFaults → HouseFaultRow,字符串化枚举跨端);独立 try,不影响其余快照。 try { snap.Faults = (StartupFaults ?? new List()) .Select(f => new HouseFaultRow { HouseSn = f.HouseSn, FaultType = f.Type.ToString(), Reason = f.Reason, Stage = f.Stage, At = f.At, Isolated = f.Isolated }).ToList(); } catch { } return snap; } /// 落盘目录(自动对焦 + CCD)待传 *.jpg 计数(只读,不删不改)。 private int CountPendingDiskImages() { int count = 0; try { string autoPath = PathHelper.GetAutoFocusSaveDirectory(); if (Directory.Exists(autoPath)) count += Directory.GetFiles(autoPath, "*.jpg").Length; } catch { } try { string ccdPath = PathHelper.GetEmbryoPicSaveDirectory(); if (Directory.Exists(ccdPath)) count += Directory.GetFiles(ccdPath, "*.jpg").Length; } catch { } return count; } private void ExLog(Exception ex, string name) { LogService.ExceptionLog(ex, $"AppData.{name}", null, LogEnum.RunException); } public bool Login(string _account, string _password) { try { CurrentUser = HttpService.Login(_account, _password); return CurrentUser != null; } catch (Exception ex) { ExLog(ex, "Login"); return false; } } /// /// 开启kafka、mqtt、历史记录上报 /// /// public async Task StartAsync() { KafkaService.KafkaSetNameAndIp(TLSetting.kafkaTopic, kfkaIP); var kafkaSuccess = await KafkaService.CreateTopicAsync(3); if (kafkaSuccess) { if (!MvcTest) StartUpLoadImage(); } else { LogService.TLLog($"kafka创建分区失败", LogEnum.RunError); } MqttClientId = $"TL/DATA/{TLSetting.tlSn}"; MqttService.StartMqtt(TLSetting.mqttQueue, MqttClientId); StartSendMqttMsg(); StartSendHistoryMsg(); StartPushMessageThread(); Task.Run(() => DeleteLog()); } /// /// 重启kafka、mqtt /// private void UpdataKafkaAndMqtt() { if (KafkaService.KafkaSetNameAndIp(TLSetting.kafkaTopic, kfkaIP)) { var kafkaSuccess = KafkaService.CreateTopicAsync(3).Result; if (!kafkaSuccess) { LogService.TLLog($"kafka创建分区失败", LogEnum.RunError); } } if (!MqttService.IsXiangTong(TLSetting.mqttQueue, $"TL/DATA/{TLSetting.tlSn}")) { MqttService.IsDispose = true; MqttService = new MqttService(MqttIp, MqttPort, MqttUserName, MqttPassword); MqttService.ErrorLogEvent += LogService.TLLog; MqttService.ExceptionLogEvent += LogService.ExceptionLog; MqttService.MessEvent += MqttMessage; MqttClientId = $"TL/DATA/{TLSetting.tlSn}"; MqttService.StartMqtt(TLSetting.mqttQueue, MqttClientId); } } /// /// 开启图片上传线程 /// public void StartUpLoadImage() { LogService.TLLog($"开始运行kafka线程", LogEnum.KafkaRecord); Task.Factory.StartNew(async () => { while (true) { try { string autoPath = PathHelper.GetAutoFocusSaveDirectory(); if (Directory.Exists(autoPath)) { var autoFileList = Directory.GetFiles(autoPath, "*.jpg"); foreach (var item in autoFileList) { await KafkaUploadImageAsync(item); } } string ccdPath = PathHelper.GetEmbryoPicSaveDirectory(); if (Directory.Exists(ccdPath)) { var ccdFileList = Directory.GetFiles(ccdPath, "*.jpg"); foreach (var item in ccdFileList) { await KafkaUploadImageAsync(item); } } // M5-04-3(b):断网累积兜底——每轮重扫后统计落盘待传数,超阈值置位告警(只读,供监控/断线提示)。 // 不删图、不丢图,只把「堆积」显性暴露,避免静默撑爆磁盘(配合 M5-03 磁盘水位)。 try { int pending = CountPendingDiskImages(); LastPendingImageCount = pending; bool backlog = pending > PendingImageAlarmThreshold; if (backlog && !ImageBacklogAlarm) LogService.TLLog($"图片待传堆积告警:落盘待传 {pending} 张已超阈值 {PendingImageAlarmThreshold},疑似上传链路断开", LogEnum.RunError); ImageBacklogAlarm = backlog; } catch { } await Task.Delay(1000 * 5); } catch (Exception ex) { LogService.ExceptionLog(ex, "上传图片", null, LogEnum.RunException); } } }, TaskCreationOptions.LongRunning); } /// /// 开启mqtt消息上报线程 /// public void StartSendMqttMsg() { LogService.TLLog($"Mqtt开始上报", LogEnum.MqttClient); Task.Factory.StartNew(async () => { while (true) { try { if (!MqttService.MqttIsConnected()) { await Task.Delay(3000); continue; } List houseMqttDataList = new List(); var a = GetHouseMqttData(HouseBin1); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin2); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin3); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin4); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin5); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin6); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin7); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin8); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin9); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(HouseBin10); if (a != null) houseMqttDataList.Add(a); a = GetHouseMqttData(BufferBottleBin); if (a != null) houseMqttDataList.Add(a); if (houseMqttDataList.Any() && MqttService.MqttIsConnected()) { string mqttMsg = JsonConvert.SerializeObject(houseMqttDataList); await MqttService.PublishAsync(mqttMsg); LastMqttOkAt = DateTime.Now; // M5-03-2:MQTT 上报成功时间戳(只读监控用,不改上报逻辑) } await Task.Delay(1000); } catch (Exception ex) { LogService.ExceptionLog(ex, "实时数据上报", null, LogEnum.RunException); } } }, TaskCreationOptions.LongRunning); } /// /// 开启仓时历史记录上报线程 /// public void StartSendHistoryMsg() { LogService.TLLog($"开启仓时历史记录上报线程", LogEnum.KafkaRecord); Task.Factory.StartNew(async () => { await Task.Delay(1000 * 60); List houseMqttDataList = new List(); HouseHistoryData a = null; while (true) { try { //只上报缓冲瓶的历史记录 houseMqttDataList.Clear(); a = GetHouseHistoryData(BufferBottleBin); if (a != null) houseMqttDataList.Add(a); if (houseMqttDataList.Any()) SerialBinController.ReportDataController(JsonConvert.SerializeObject(houseMqttDataList)); //List houseMqttDataList = new List(); //var a = GetHouseHistoryData(HouseBin1); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin2); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin3); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin4); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin5); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin6); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin7); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin8); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin9); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(HouseBin10); //if (a != null) houseMqttDataList.Add(a); //a = GetHouseHistoryData(BufferBottleBin); //if (a != null) houseMqttDataList.Add(a); //if (houseMqttDataList.Any()) //{ // string mqttMsg = JsonConvert.SerializeObject(houseMqttDataList); // SerialBinController.ReportDataController(mqttMsg); //} //await Task.Delay(1000 * TLSetting.historyCurveInterval); } catch (Exception ex) { LogService.ExceptionLog(ex, "仓时历史记录上报", null, LogEnum.RunException); } finally { await Task.Delay(1000 * TLSetting.historyCurveInterval); } } }, TaskCreationOptions.LongRunning); } /// /// 开启心跳线程 /// public void StartPushMessageThread() { int time = 1000 * 60 * 10; Task.Factory.StartNew(() => { while (true) { try { SerialBinController.PushMessageController(TLSetting.tlSn, 1, GetDiskInfo(TLSetting.tmpDir)); if (DateTime.Now.Hour != TLSetting.autoFocusTime) { if (isSetZiDong) isSetZiDong = false; continue; } if (isSetZiDong) continue; Task.Run(() => DeleteLog()); Task.Run(() => CleanAutofocusCalibration()); // G4-1:每日维护窗口同步清理对焦标定 scene=1 过期记录 isSetZiDong = true; if (HouseBin1 != null) { HouseBin1.FirstClearest = true; HouseBin1.ReCa = true; } if (HouseBin2 != null) { HouseBin2.FirstClearest = true; HouseBin2.ReCa = true; } if (HouseBin3 != null) { HouseBin3.FirstClearest = true; HouseBin3.ReCa = true; } if (HouseBin4 != null) { HouseBin4.FirstClearest = true; HouseBin4.ReCa = true; } if (HouseBin5 != null) { HouseBin5.FirstClearest = true; HouseBin5.ReCa = true; } if (HouseBin6 != null) { HouseBin6.FirstClearest = true; HouseBin6.ReCa = true; } if (HouseBin7 != null) { HouseBin7.FirstClearest = true; HouseBin7.ReCa = true; } if (HouseBin8 != null) { HouseBin8.FirstClearest = true; HouseBin8.ReCa = true; } if (HouseBin9 != null) { HouseBin9.FirstClearest = true; HouseBin9.ReCa = true; } if (HouseBin10 != null) { HouseBin10.FirstClearest = true; HouseBin10.ReCa = true; } } catch (Exception ex) { LogService.ExceptionLog(ex, "心跳线程", null, LogEnum.RunException); } finally { Thread.Sleep(time); } } }, TaskCreationOptions.LongRunning); } #region 仓室事件设置 /// /// 仓室事件初始化 /// /// public void InitHouseBinEvent(HouseBin houseBin) { houseBin.TLLogEvent += LogService.TLLog; houseBin.HouseLogEvent += LogService.HouseLog; houseBin.ExceptionLogEvent += LogService.ExceptionLog; houseBin.GetAutoFocusServiceEvent += HouseBin_GetAutoFocusServiceEvent; houseBin.GetAutoFocusDBEvent += HouseBin_GetAutoFocusDBEvent; // M2-04:注入标定结果存储入口(写 JSON 真相源 + 镜像库),对焦逐 well 标定后调用。 houseBin.AutofocusStore = AutofocusStore; houseBin.GetCCDServiceEvent += HouseBin_GetCCDServiceEvent; houseBin.GetCCDDBEvent += HouseBin_GetCCDDBEvent; houseBin.UploadImageEvent += HouseBin_UploadImageEvent; houseBin.UpdateAutofocusStateEvent += HouseBin_UpdateAutofocusStateEvent; houseBin.SavePicDbEvent += HouseBin_SavePicDbEvent; houseBin.HouseStateEvent += HouseBin_HouseStateEvent; houseBin.CCDStateEvent += HouseBinPhotoStateEvent; houseBin.OnHistoryEvent += HouseBin_OnHistoryEvent; houseBin.ChangeBufferBottleBinEvent += HouseBin_ChangeBufferBottleBinEvent; houseBin.HouseBinChangeBufferBottleBinEvent += HouseBin_HouseBinChangeBufferBottleBinEvent; //houseBin.CCDStateEvent += HouseBin_CCDStateEvent; houseBin.TongQi = csTime * 1000; houseBin.VentPre = houseVentPre; houseBin.VentNum = houseVentNum; houseBin.VentWaitTimeB = houseVentWaitTimeB; houseBin.VentWaitTimeD = houseVentWaitTimeD; houseBin.AutoWaitTime = houseAutoWaitTime * 60000; houseBin.CCDAutoWaitTime = houseCCDAutoWaitTime * 60000; houseBin.CCDFailedNumber = houseCCDFailedNumber; houseBin.CCDFailedWaitTime = houseCCDFailedWaitTime * 1000; if (houseCCDError == 1) { houseBin.CCDError = true; } else { houseBin.CCDError = false; } if (queuAir == 0) { houseBin.IsPai = false; } else { houseBin.IsPai = true; } houseBin.TakePhotoFailed = TakePhotoString; } private void HouseBin_CCDStateEvent(int houseSn, int ccdState) { if (ccdState == 1) { LogService.TLLog($"{houseSn}号舱室拍照异常,停止拍照", LogEnum.RunRecord); } else { LogService.TLLog($"{houseSn}号舱室拍照恢复正常", LogEnum.RunRecord); } } private bool HouseBin_HouseBinChangeBufferBottleBinEvent(HouseBin arg1, int arg2) { if (BufferBottleBin == null) { LogService.TLLog($"缓冲瓶为空,无法操作进气阀:{arg1}、{arg2}", LogEnum.RunError); return false; } if (arg2 == 0) { BufferBottleBin.HuanQiEnd(arg1); return true; } else if (arg2 == 1) { BufferBottleBin.HuanQiStart(arg1); return true; } else { LogService.TLLog($"状态码错误,无法操作进气阀:{arg1}、{arg2}", LogEnum.RunError); return false; } } private bool HouseBin_ChangeBufferBottleBinEvent(int arg1, int arg2) { if (BufferBottleBin == null) { LogService.TLLog($"缓冲瓶为空,无法操作进气阀:{arg1}、{arg2}", LogEnum.RunError); return false; } if (arg2 == 0) { BufferBottleBin.CloseIntakeValve(arg1); return true; } else if (arg2 == 1) { BufferBottleBin.OpenIntakeValve(arg1); return true; } else { LogService.TLLog($"状态码错误,无法操作进气阀:{arg1}、{arg2}", LogEnum.RunError); return false; } } private void HouseBin_OnHistoryEvent(HouseHistoryData obj) { SerialBinController.ReportDataController(JsonConvert.SerializeObject(new List { obj })); } private void HouseBin_HouseStateEvent(int housesn, int houseState, int comState, int photoState, int wellSN, int airSwapState) { SerialBinController.ReportAlarmController(TLSetting.tlSn, housesn, houseState, comState, photoState, wellSN, airSwapState); //if (photoState != 0) //{ // SerialBinController.ReportAlarmController(TLSetting.tlSn, housesn, houseState, comState, photoState, wellSN, airSwapState); // return; //} //for (int i = 1; i <= 16; i++) //{ // SerialBinController.ReportAlarmController(TLSetting.tlSn, housesn, houseState, comState, photoState, i, airSwapState); //} } private void HouseBinPhotoStateEvent(int housesn, int photoState, int wellSN) { if (photoState != 0) { SerialBinController.ReportAlarmController(TLSetting.tlSn, housesn, -1, -1, photoState, wellSN, -1); StopProEvent?.Invoke(); return; } for (int i = 1; i <= 16; i++) { SerialBinController.ReportAlarmController(TLSetting.tlSn, housesn, -1, -1, photoState, i, -1); } } /// /// 仓室图片保存到数据库 /// /// /// private void HouseBin_SavePicDbEvent(ImageDTO obj) { if (obj == null) return; SerialBinController.SavePictreController(obj); } /// /// 仓室上传图片 /// /// private void HouseBin_UploadImageEvent(ImageDTO obj) { if (obj == null) return; lock (ImageDTODic) { if (ImageDTODic.Count > 50) { LogService.TLLog($"内存当中保存的图片已经超过50张了{obj.HouseSn}、{obj.WellSn}、{obj.SourceImageName}", LogEnum.RunError); return; } if (!ImageDTODic.ContainsKey(obj.SourceImageName)) { ImageDTODic.Add(obj.SourceImageName, obj); } } } /// /// 从服务器获取自动对焦位置 /// /// /// /// /// private List HouseBin_GetAutoFocusServiceEvent(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, tlSn = TLSetting.tlSn, wellSnList = wellSnAndAutoTimes, }; return HouseBinController.GetAutoFocusController(positionRequestDTO); } /// /// 从数据库获取自动对焦位置 /// /// /// /// /// private List HouseBin_GetAutoFocusDBEvent(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, tlSn = TLSetting.tlSn, wellSnList = wellSnAndAutoTimes, }; return HouseBinController.DbGetPositionData(positionRequestDTO.wellSnList.Select(x => x.well).ToList(), positionRequestDTO.houseSn, positionRequestDTO.tlSn, 0); } /// /// 服务器获取仓室拍照位置 /// /// /// /// /// private PositionInfoResultDTO HouseBin_GetCCDServiceEvent(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, wellSnList = wellSnAndAutoTimes, tlSn = TLSetting.tlSn, }; var result = HouseBinController.GetCCDPositionController(positionRequestDTO); if (result != null && result.complete == 0)//完成 { List CCDPositionList = new List(); foreach (var item in result.positionVOList) { CCDPositionList.Add(ConvertHelper.ConvertToHouseWellPhoto(item)); } HouseBinController.DbUpdatePositionData(CCDPositionList, positionRequestDTO.houseSn, positionRequestDTO.tlSn, 1); } return result; } /// /// 数据库获取仓室拍照位置 /// /// /// /// /// private List HouseBin_GetCCDDBEvent(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, wellSnList = wellSnAndAutoTimes, tlSn = TLSetting.tlSn, }; return HouseBinController.DbGetPositionData(positionRequestDTO.wellSnList.Select(x => x.well).ToList(), positionRequestDTO.houseSn, positionRequestDTO.tlSn, 1); } private void HouseBin_UpdateAutofocusStateEvent(bool newValue, int housesn) { int autoFocus = 0; if (newValue) { autoFocus = 1; } SerialBinController.UpdateAutofocusStateController(TLSetting.tlSn, housesn, autoFocus); } /// /// 缓冲瓶事件初始化 /// /// public void InitBufferBottleBinEvent(BufferBottleBin BufferBottleBin) { BufferBottleBin.TLLogEvent += LogService.TLLog; BufferBottleBin.HouseLogEvent += LogService.HouseLog; BufferBottleBin.ExceptionLogEvent += LogService.ExceptionLog; BufferBottleBin.HouseStateEvent += HouseBin_HouseStateEvent; } #endregion #region mqtt消息处理 /// /// mqtt接受消息 /// /// public void MqttMessage(string message) { if (string.IsNullOrEmpty(message)) return; MqttResult mqttResult = null; bool isSuccess = true; string uuid = null; try { mqttResult = JsonConvert.DeserializeObject(message); if (mqttResult == null) { LogService.TLLog($"AppData.MqttMessage,mqtt消息反序列化失败,消息:{message}", LogEnum.MqttClient); return; } switch (mqttResult.type) { case (int)MqttEnum.StartBalance: isSuccess = StartBalance(mqttResult.data); break; case (int)MqttEnum.EndBalance: isSuccess = EndBalance(mqttResult.data); break; case (int)MqttEnum.StartDish: isSuccess = StartDish(mqttResult.data); break; case (int)MqttEnum.EndDish: isSuccess = EndDish(mqttResult.data); break; case (int)MqttEnum.EmbryoState: isSuccess = EmbryoState(mqttResult.data); break; case (int)MqttEnum.Update: isSuccess = UpDataSettingMqtt(mqttResult.data); break; case (int)MqttEnum.DebugStart: isSuccess = DebugStart(mqttResult.data, ref uuid); break; case (int)MqttEnum.HouseAutoFocus: isSuccess = HouseAutoFocus(mqttResult.data); break; case (int)MqttEnum.WellAutoFocus: isSuccess = WellAutoFocus(mqttResult.data); break; } } catch (Exception ex) { LogService.ExceptionLog(ex, "mqtt消息处理", null, LogEnum.RunException); isSuccess = false; } finally { if (mqttResult != null) { if (string.IsNullOrEmpty(uuid)) { SerialBinController.MqttResultController(isSuccess ? 200 : 400, isSuccess, mqttResult.messageId); } else { SerialBinController.MqttResultController(isSuccess ? 200 : 400, isSuccess, mqttResult.messageId, uuid); } } } } /// /// 开始平衡 /// /// /// private bool StartBalance(object data) { try { Balance balance = JsonConvert.DeserializeObject(data.ToString()); DBService.AddBalance(balance); HouseBin currentHouseBin = HouseSnToHouseBin(balance.houseSn); if (currentHouseBin == null) return false; currentHouseBin.StartBlance(balance); return true; } catch (Exception ex) { ExLog(ex, "StartBalance"); return false; } } private bool EndBalance(object data) { try { Balance balance = JsonConvert.DeserializeObject(data.ToString()); HouseBin currentHouseBin = HouseSnToHouseBin(balance.houseSn); if (currentHouseBin == null) return false; DBService.EndBalance(balance.id, balance.endTime); currentHouseBin.StopBlance(); return true; } catch (Exception ex) { ExLog(ex, "EndBalance"); return false; } } private bool StartDish(object data) { try { Dish dish = JsonConvert.DeserializeObject(data.ToString()); DBService.AddDish(dish, dish.tlSn); HouseBin currentHouseBin = HouseSnToHouseBin(dish.houseSn); if (currentHouseBin == null) return false; currentHouseBin.StartDish(dish); return true; } catch (Exception ex) { ExLog(ex, "StartDish"); return false; } } private bool EndDish(object data) { try { Dish dish = JsonConvert.DeserializeObject(data.ToString()); DBService.EndDish(dish.id, dish.endTime.Value, dish.tlSn); HouseBin currentHouseBin = HouseSnToHouseBin(dish.houseSn); if (currentHouseBin == null) return false; currentHouseBin.StopDish(); return true; } catch (Exception ex) { ExLog(ex, "EndDish"); return false; } } private bool DebugStart(object data, ref string uuid) { try { //System.Collections.IList list = param["list"] as System.Collections.IList; //JArray itemObject = (JArray)keyValuePair.Value; //foreach (KeyValuePair keyValuePair in jobject) if (data == null) return false; if (string.IsNullOrEmpty(data.ToString())) return false; JObject jObject = JObject.Parse(data.ToString()); string tlsn = jObject["tlSn"].ToString(); uuid = jObject["uuid"].ToString(); int housesn = int.Parse(jObject["houseSn"].ToString()); switch (housesn) { case 1: HouseBin1.IsDebug = true; break; case 2: HouseBin2.IsDebug = true; break; case 3: HouseBin3.IsDebug = true; break; case 4: HouseBin4.IsDebug = true; break; case 5: HouseBin5.IsDebug = true; break; case 6: HouseBin6.IsDebug = true; break; case 7: HouseBin7.IsDebug = true; break; case 8: HouseBin8.IsDebug = true; break; case 9: HouseBin9.IsDebug = true; break; case 10: HouseBin10.IsDebug = true; break; } bool IsDebugOk = false; do { switch (housesn) { case 1: IsDebugOk = HouseBin1.isDebugOk; break; case 2: IsDebugOk = HouseBin2.isDebugOk; break; case 3: IsDebugOk = HouseBin3.isDebugOk; break; case 4: IsDebugOk = HouseBin4.isDebugOk; break; case 5: IsDebugOk = HouseBin5.isDebugOk; break; case 6: IsDebugOk = HouseBin6.isDebugOk; break; case 7: IsDebugOk = HouseBin7.isDebugOk; break; case 8: IsDebugOk = HouseBin8.isDebugOk; break; case 9: IsDebugOk = HouseBin9.isDebugOk; break; case 10: IsDebugOk = HouseBin10.isDebugOk; break; } if (IsDebugOk) { break; } Thread.Sleep(1000); } while (true); return true; } catch (Exception ex) { LogService.ExceptionLog(ex, "mqtt.DebugStart", null, LogEnum.RunException); return false; } } private bool EmbryoState(object data) { try { Embryo embryo = JsonConvert.DeserializeObject(data.ToString()); DBService.ChangeEmbryoState(embryo); HouseBin currentHouseBin = HouseSnToHouseBin(embryo.houseSn); if (currentHouseBin == null) return false; currentHouseBin.ChangeEmbryoState(embryo); return true; } catch (Exception ex) { ExLog(ex, "EmbryoState"); return false; } } private bool UpDataSettingMqtt(object data) { try { if (data == null) return false; string dataString = data.ToString(); if (string.IsNullOrEmpty(dataString)) return false; JObject jObject = JObject.Parse(dataString); string tlsn = jObject["tlSn"].ToString(); string dateTime = jObject["updateTime"].ToString(); return UpdateSetting(tlsn); } catch (Exception ex) { ExLog(ex, "UpDataSettingMqtt"); return false; } } public bool HouseAutoFocus(object data) { try { HouseAutoFocusMqtt houseAutoFocus = JsonConvert.DeserializeObject(data.ToString()); if (houseAutoFocus == null || !houseAutoFocus.houseSnList.Any()) { LogService.TLLog($"json转换失败,不更新配置信息{data.ToString()}", LogEnum.RunError); return false; } if (!UpdateSetting(houseAutoFocus.tlSn)) { LogService.TLLog($"更新配置信息失败,不开启自动对焦", LogEnum.RunError); return false; } foreach (var houseSn in houseAutoFocus.houseSnList) { HouseBin currentHouseBin = HouseSnToHouseBin(houseSn); if (currentHouseBin == null) { LogService.TLLog($"开启自动对焦时,获取舱室为空,{houseSn}、{data.ToString()}", LogEnum.RunError); continue; } currentHouseBin.HouseAutoFocus(); } return true; } catch (Exception ex) { ExLog(ex, "HouseAutoFocus"); return false; } } public bool WellAutoFocus(object data) { try { WellAutoFocusMqtt wellAutoFocusMqtt = JsonConvert.DeserializeObject(data.ToString()); if (wellAutoFocusMqtt == null || !wellAutoFocusMqtt.houseSnList.Any()) return false; if (!UpdateSetting(wellAutoFocusMqtt.tlSn)) { LogService.TLLog($"更新配置信息失败,不开启自动对焦", LogEnum.RunError); return false; } foreach (var houseSn in wellAutoFocusMqtt.houseSnList) { HouseBin currentHouseBin = HouseSnToHouseBin(houseSn.house); if (currentHouseBin == null) continue; currentHouseBin.WellAutoFocus(houseSn.well); } return true; } catch (Exception ex) { ExLog(ex, "WellAutoFocus"); return false; } } public bool UpdateSetting(string tlsn) { try { var initTLResult = SerialBinController.UpdataSettingController(tlsn); if (initTLResult == null) { LogService.TLLog($"更新设置时返回为空", LogEnum.RunError); return false; } TLSetting = initTLResult.TLSetting; PathHelper.pan = initTLResult.TLSetting.tmpDir; LogService.Pan = initTLResult.TLSetting.tmpDir; for (int i = 1; i <= 10; i++) { HouseBin currentHouseBin = HouseSnToHouseBin(i); if (currentHouseBin == null) continue; currentHouseBin.UpdataHouseSetting(initTLResult); } //UpdataKafkaAndMqtt(); return true; } catch (Exception ex) { ExLog(ex, "UpdateSetting"); return false; } } #endregion /// /// mqtt报警 /// /// public void MqttAlarm(string message) { try { HttpService.AlarmApi1(TLSetting.tlSn, "CLOUD_SLAVE_MQTT_ALARM", new List { message }); } catch (Exception ex) { ExLog(ex, "mqtt报警"); return; } } /// /// 上报启动期坏舱告警(每坏舱一条),复用现有 reportAlarm 报警闭环: /// SerialBinController.ReportAlarmController → /reportAlarm → 报警责任链 → alarm 表 /// → front/operate 报警列表 + 短信/电话通知 + 可静音/恢复自动消警(与运行期 HouseStateEvent 同一入口)。 /// 维度码 0正常/1异常/-1跳过:相机类故障 → photoState=1;串口/编号/Init 类 → comState=1;其余维度 -1(不误清别的告警)。 /// 全 try 兜底,失败不影响启动。不走 reportCloudAlarm(群消息),无新增 alarmTypeKey、无 Java 改动。 /// public void ReportStartupFaults() { if (StartupFaults == null || StartupFaults.Count == 0) return; string tlSn = ""; try { tlSn = TLSetting?.tlSn ?? ""; } catch { } foreach (var f in StartupFaults) { // 舱号未知(相机/串口级)定位不到具体舱,不进 reportAlarm(仍在 /status Faults 里可见)。 if (f.HouseSn <= 0) continue; try { bool cameraFault = f.Type == HouseFaultType.CcdSnMissing || f.Type == HouseFaultType.CcdSnDuplicate || f.Type == HouseFaultType.CameraDuplicateSn || f.Type == HouseFaultType.CameraReadFailed; int comState = cameraFault ? -1 : 1; // 串口/编号/Init 异常 int photoState = cameraFault ? 1 : -1; // 相机异常 SerialBinController.ReportAlarmController(tlSn, f.HouseSn, -1, comState, photoState, -1, -1); } catch (Exception ex) { ExLog(ex, "ReportStartupFaults"); } } } public void KafkaAlarm(string tlsn, int housesn, ulong dishId, ulong embryoId, string imageName, int wellSn) { try { HttpService.AlarmApi(tlsn, housesn, wellSn, "CLOUD_SLAVE_KAFKA_ALARM", new List { dishId.ToString(), embryoId.ToString(), imageName }); } catch (Exception ex) { ExLog(ex, "图片传输失败报警"); return; } } public void KafkaAlarmChaoShi(string tlsn, int housesn, ulong dishId, ulong embryoId, string imageName, int wellSn) { try { HttpService.AlarmApi(tlsn, housesn, wellSn, "CLOUD_SLAVE_PICTURE_TRANSFER_ALARM", new List { dishId.ToString(), embryoId.ToString(), imageName }); } catch (Exception ex) { ExLog(ex, "图片传输超时报警"); return; } } private async Task KafkaUploadImageAsync(string fileFullPath) { // M5-04-3(a):去重防并发重传。同名文件在上一轮上传未结束(慢链/超时)时,5s 重扫会再次进入本方法, // 此处用 in-flight 集合保证同一文件同一时刻只有一条上传在跑;释放放在 finally,不影响「成功才删」语义。 string fileName = Path.GetFileName(fileFullPath); lock (_uploadingFiles) { if (_uploadingFiles.Contains(fileName)) return; // 正在上传,跳过本轮重扫的重复触发 _uploadingFiles.Add(fileName); } try { Stopwatch stopwatch = Stopwatch.StartNew(); //LogService.TLLog($"准备上传{fileFullPath}", LogEnum.KafkaRecord); ImageDTO imageDTO = null; lock (ImageDTODic) if (ImageDTODic.ContainsKey(fileName)) imageDTO = ImageDTODic[fileName]; if (imageDTO == null) { imageDTO = SerialBinController.SearchPictureController(fileName, TLSetting.tlSn); if (imageDTO == null) { LogService.TLLog($"数据库获取文件失败:{fileName}、{TLSetting.tlSn}:{fileFullPath}", LogEnum.RunError); } else { var imageBytes = AivfoHelper.GetImageData1(fileFullPath); if (imageBytes == null) { LogService.TLLog($"上传中止,图片不存在:{fileFullPath}", LogEnum.RunError); } else { imageDTO.ImageData = ByteString.CopyFrom(imageBytes); } } } if (imageDTO == null) { LogService.TLLog($"上传中止,imageDTO为空:{fileFullPath}", LogEnum.KafkaRecord); string newDir = PathHelper.GetErrorSaveDirectory(); if (!Directory.Exists(newDir)) Directory.CreateDirectory(newDir); File.Move(fileFullPath, Path.Combine(newDir, fileName)); return; } //LogService.TLLog($"fileName文件大小:{imageDTO.ImageData.Count()}", LogEnum.KafkaRecord); var time1 = stopwatch.Elapsed; //LogService.TLLog($"开始上传{fileFullPath},准备耗时:{time1}毫秒", LogEnum.KafkaRecord); var uploadResult = await KafkaService.kafkaProducerAsync(imageDTO); var time2 = stopwatch.Elapsed; //LogService.TLLog($"上传结束{fileFullPath},耗时:{time2 - time1}毫秒", LogEnum.KafkaRecord); if (!uploadResult) { KafkaAlarm(imageDTO.TlSn, imageDTO.HouseSn, imageDTO.EmbryoCultureRecordId, imageDTO.EmbryoId, imageDTO.SourceImageName, imageDTO.WellSn); LogService.TLLog($"上传失败:{fileFullPath}", LogEnum.RunError); return; } if ((time2 - time1).TotalMilliseconds > 1000) { KafkaAlarmChaoShi(imageDTO.TlSn, imageDTO.HouseSn, imageDTO.EmbryoCultureRecordId, imageDTO.EmbryoId, imageDTO.SourceImageName, imageDTO.WellSn); LogService.TLLog($"kafka上传超时:{fileFullPath}", LogEnum.RunError); } LastKafkaOkAt = DateTime.Now; // M5-03-2:Kafka 图片上传成功时间戳(只读监控用,不改上传逻辑) lock (ImageDTODic) if (ImageDTODic.ContainsKey(fileName)) ImageDTODic.Remove(fileName); if (File.Exists(fileFullPath)) { try { File.Delete(fileFullPath); } catch (Exception ex) { LogService.ExceptionLog(ex, "kafka上传完成删除图片", null, LogEnum.RunException); } //if (imageDTO.PhotographType == 1) //{ // string newpath = @"C:\TLData\AutofocusControlBreak"; // if (!Directory.Exists(newpath)) Directory.CreateDirectory(newpath); // File.Move(fileFullPath, Path.Combine(newpath, fileName)); //} //else //{ // string newpath = @"C:\TLData\EmbryosControlBreak"; // if (!Directory.Exists(newpath)) Directory.CreateDirectory(newpath); // File.Move(fileFullPath, Path.Combine(newpath, fileName)); //} } SerialBinController.DeletePictrueController(fileName, TLSetting.tlSn); //LogService.TLLog($"上传完成:{fileFullPath},总耗时:{stopwatch.ElapsedMilliseconds}毫秒", LogEnum.KafkaRecord); } catch (Exception ex) { LogService.ExceptionLog(ex, "kafka上传图片", null, LogEnum.RunException); return; } finally { // M5-04-3(a):无论成功/失败/中止,都释放 in-flight 标记,使下一轮重扫可重试(失败文件仍在落盘 → 不丢)。 lock (_uploadingFiles) _uploadingFiles.Remove(fileName); } } private HouseMqttData GetHouseMqttData(HouseBin currentHouseBin) { if (currentHouseBin == null) return null; if (currentHouseBin.House == null) return null; int cultureState = 0; if (currentHouseBin.Balance != null && currentHouseBin.Balance.id > 0) cultureState = 2; if (currentHouseBin.Dish != null && currentHouseBin.Dish.id > 0) cultureState = 1; return new HouseMqttData() { tlSn = TLSetting.tlSn, houseSn = currentHouseBin.House.houseSn, pressure = currentHouseBin.Pressure, temperature = currentHouseBin.Temperature, houseDoorState = currentHouseBin.IsDoorOpen == State.打开 ? 1 : 0, pressureDesc = currentHouseBin.ValveState.ToString(), houseDesc = currentHouseBin.RunState, cultureState = cultureState, houseState = (int)currentHouseBin.WorkingType, }; } private HouseMqttData GetHouseMqttData(BufferBottleBin currentBufferBottleBin) { if (currentBufferBottleBin == null) return null; if (currentBufferBottleBin.House == null) return null; return new HouseMqttData() { tlSn = TLSetting.tlSn, houseSn = 11, pressure = currentBufferBottleBin.BufferBottlePressure, temperature = 0, houseDoorState = 0, pressureDesc = currentBufferBottleBin.ValveState.ToString(), houseDesc = currentBufferBottleBin.RunState, }; } private HouseHistoryData GetHouseHistoryData(HouseBin currentHouseBin) { if (currentHouseBin == null) return null; if (currentHouseBin.House == null) return null; return new HouseHistoryData() { tlSn = TLSetting.tlSn, houseSn = currentHouseBin.House.houseSn, pressure = currentHouseBin.Pressure, temperature = currentHouseBin.Temperature, houseDoor = currentHouseBin.IsDoorOpen == State.打开 ? 1 : 0, airSwap = currentHouseBin.WorkingType == WorkingType.AirSwapWorking ? 1 : 0, temperatureLowerCover = currentHouseBin.Temperature2, temperatureUpperCover = currentHouseBin.Temperature1, temperatureLowerGlass = currentHouseBin.Temperature3, }; } private HouseHistoryData GetHouseHistoryData(BufferBottleBin currentBufferBottleBin) { if (currentBufferBottleBin == null) return null; if (currentBufferBottleBin.House == null) return null; return new HouseHistoryData() { tlSn = TLSetting.tlSn, houseSn = 11, pressure = currentBufferBottleBin.BufferBottlePressure, temperature = 0, houseDoor = 0, airSwap = 0, temperatureLowerCover = currentBufferBottleBin.Temperature1, temperatureUpperCover = currentBufferBottleBin.Temperature2, temperatureLowerGlass = 0, cultureState = 0, }; } private HouseBin HouseSnToHouseBin(int housesn) { HouseBin result = null; switch (housesn) { case 1: result = HouseBin1; break; case 2: result = HouseBin2; break; case 3: result = HouseBin3; break; case 4: result = HouseBin4; break; case 5: result = HouseBin5; break; case 6: result = HouseBin6; break; case 7: result = HouseBin7; break; case 8: result = HouseBin8; break; case 9: result = HouseBin9; break; case 10: result = HouseBin10; break; } return result; } public DiskInfo GetDiskInfo(string pan) { DiskInfo diskInfo = new DiskInfo() { diskPath = pan }; try { string panNew = pan.ToUpper(); diskInfo.diskExist = Directory.Exists($"{panNew}:\\") ? 0 : 1; if (diskInfo.diskExist == 1) return diskInfo; bool IsFind = false; DriveInfo[] allDirves = DriveInfo.GetDrives(); foreach (DriveInfo item in allDirves) { if (item.IsReady) { if (item.Name == $"{panNew}:\\") { IsFind = true; diskInfo.diskSpace = (decimal)(item.TotalFreeSpace / (1024.00 * 1024.00 * 1024.00)); break; } } } if (!IsFind) diskInfo.diskExist = 1; return diskInfo; } catch (Exception ex) { LogService.ExceptionLog(ex, "GetDiskInfo", null, LogEnum.RunException); diskInfo.diskExist = 1; return diskInfo; } } public void DeleteLog() { try { string newPath = $"{TLSetting.tmpDir}:\\TLData\\ivf_tl_Control_logs"; var newDir = Directory.GetDirectories(newPath, "*", SearchOption.TopDirectoryOnly); foreach (var item in newDir) { var dieName = System.IO.Path.GetFileName(item); if (DateTime.TryParse(dieName, out DateTime newTime)) { if (DateTime.Now.Subtract(newTime).Days >= 5) { Directory.Delete(item, true); } } } DeleteLogFile($"C:\\TLData\\ivf_tl_Control_logs\\LogError"); DeleteLogFile($"C:\\TLData\\ivf_tl_Control_logs\\Log"); } catch (Exception ex) { LogService.ExceptionLog(ex, "DeleteLog", null, LogEnum.RunException); } } /// /// G4-1 / 需求文档12 §2.7:对焦标定数据清理(每日维护窗口触发,与 DeleteLog 同源)。 /// 保留天数取服务器下发的 TLSetting.cleanAutofocusData(缺省/非正回退 30); /// 只删 scene=1 日常对焦记录、scene=0 出厂基准永久保留(由 DBService.CleanAutofocusData 保证)。 /// 全 try 兜底,异常吞掉记日志,绝不向上抛(不影响采集/对焦)。 /// public void CleanAutofocusCalibration() { try { int keepDays = TLSetting?.cleanAutofocusData ?? 30; if (keepDays <= 0) keepDays = 30; int n = DBService.CleanAutofocusData(keepDays); LogService.TLLog($"对焦标定清理(G4-1):保留 {keepDays} 天,删除 scene=1 共 {n} 条", LogEnum.RunRecord); } catch (Exception ex) { LogService.ExceptionLog(ex, "CleanAutofocusCalibration", null, LogEnum.RunException); } } private void DeleteLogFile(string newPath) { var newDir = Directory.GetFiles(newPath, "*.htm", SearchOption.TopDirectoryOnly); string fileName = ""; DateTime fileTime = DateTime.Now; DateTime nowTime = fileTime; foreach (var item in newDir) { fileName = System.IO.Path.GetFileNameWithoutExtension(item); if (fileName.Length == 8) { fileName = fileName.Insert(6, "-"); fileName = fileName.Insert(4, "-"); if (DateTime.TryParse(fileName, out fileTime)) { if (nowTime.Subtract(fileTime).Days >= 5) { try { File.Delete(item); } catch (Exception) { continue; } } } } } } public string ReadText(string fileName) { return File.ReadAllText(fileName); } public PositionInfoResultDTO HouseBin_GetCCDServiceEventTest(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, wellSnList = wellSnAndAutoTimes, tlSn = "NEO-1-20230410", }; var result = HouseBinController.GetCCDPositionController(positionRequestDTO); if (result != null && result.complete == 0)//完成 { List CCDPositionList = new List(); foreach (var item in result.positionVOList) { CCDPositionList.Add(ConvertHelper.ConvertToHouseWellPhoto(item)); } HouseBinController.DbUpdatePositionData(CCDPositionList, positionRequestDTO.houseSn, positionRequestDTO.tlSn, 1); } return result; } public List HouseBin_GetAutoFocusServiceEventTest(int arg1, Dictionary arg3) { List wellSnAndAutoTimes = new List(); foreach (var item in arg3) { wellSnAndAutoTimes.Add(new WellSnAndAutoTime { well = item.Key, autofocusTime = item.Value.HasValue ? item.Value.Value.ToString("yyyy-MM-dd HH:mm:ss") : null, }); } PositionRequestDTO positionRequestDTO = new PositionRequestDTO() { houseSn = arg1, tlSn = "NEO-1-20230410", wellSnList = wellSnAndAutoTimes, }; return HouseBinController.GetAutoFocusController(positionRequestDTO); } public string GetLanguageStringByKey(string key) { try { if (System.Windows.Application.Current == null) return ""; object value = System.Windows.Application.Current.TryFindResource(key); return value == null ? "" : value.ToString(); } catch (Exception) { return ""; } } } }