using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.Json;
namespace AutoFocusTool.Calib
{
/// 单个 well 的标定结果。
public class WellCalib
{
public int Well;
public int HorizontalPulse; // 标定出的最居中水平位置(脉冲)
public int Exposure; // 合适曝光(×100µs)
public int FocusZ; // 最清晰层Z(脉冲)
public double CenterOffsetPct; // 标定后圆心偏移(%),残留偏移
public bool CircleFound;
public double PeakSharp; // 对焦峰值清晰度
public double PeakRatio; // max/mean,>1.2 视为有真实焦点
public string Note = "";
}
/// 单个舱室的标定结果。
public class HouseCalib
{
public int House;
public string Port;
public int CcdIndex;
public string CcdSn;
public List Wells = new List();
}
///
/// 整机标定档案 + JSON 读写(手写,不依赖第三方库)。
/// 日常使用直接读此档案的 HorizontalPulse/Exposure/FocusZ,不再逐项试。
/// 也是移植进主项目的交接数据。
///
public class CalibrationFile
{
public string TlSn = "";
public string Date = "";
public List Houses = new List();
public WellCalib Find(int house, int well)
=> Houses.Find(h => h.House == house)?.Wells.Find(w => w.Well == well);
/// 从 JSON 文件加载标定档案。
public static CalibrationFile Load(string path)
{
string json = File.ReadAllText(path, Encoding.UTF8);
// 关键:本类用 public 字段而非属性,System.Text.Json 默认忽略字段,
// 必须开 IncludeFields,否则反序列化得到空对象(舱数0),标定闭环静默失效。
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
IncludeFields = true
};
return JsonSerializer.Deserialize(json, options);
}
public void Save(string path)
{
var sb = new StringBuilder();
sb.Append("{\n");
sb.Append($" \"tlSn\": \"{Esc(TlSn)}\",\n");
sb.Append($" \"date\": \"{Esc(Date)}\",\n");
sb.Append(" \"houses\": [\n");
for (int i = 0; i < Houses.Count; i++)
{
var h = Houses[i];
sb.Append(" {\n");
sb.Append($" \"house\": {h.House}, \"port\": \"{Esc(h.Port)}\", \"ccdIndex\": {h.CcdIndex}, \"ccdSn\": \"{Esc(h.CcdSn)}\",\n");
sb.Append(" \"wells\": [\n");
for (int j = 0; j < h.Wells.Count; j++)
{
var w = h.Wells[j];
sb.Append(" {");
sb.Append($"\"well\": {w.Well}, \"horizontalPulse\": {w.HorizontalPulse}, \"exposure\": {w.Exposure}, \"focusZ\": {w.FocusZ}, ");
sb.Append($"\"centerOffsetPct\": {F(w.CenterOffsetPct)}, \"circleFound\": {(w.CircleFound ? "true" : "false")}, ");
sb.Append($"\"peakSharp\": {F(w.PeakSharp)}, \"peakRatio\": {F(w.PeakRatio)}, \"note\": \"{Esc(w.Note)}\"");
sb.Append(j < h.Wells.Count - 1 ? "},\n" : "}\n");
}
sb.Append(" ]\n");
sb.Append(i < Houses.Count - 1 ? " },\n" : " }\n");
}
sb.Append(" ]\n}\n");
File.WriteAllText(path, sb.ToString(), Encoding.UTF8);
}
static string F(double d) => d.ToString("0.###", CultureInfo.InvariantCulture);
static string Esc(string s) => (s ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"");
}
}