using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using AutoFocusTool.Imaging;
namespace AutoFocusTool
{
// 抓帧显示 / 实时预览 / 曝光增益 / 光源 / 存图
public partial class MainWindow
{
private byte[] _lastFrame; // 最近一帧原始 BGR
/// 抓一帧并刷新显示+清晰度分。返回是否成功。
private bool GrabAndShow()
{
if (_camera == null) { Log("无相机。"); return false; }
int r = _camera.GrabRgb();
if (r != 0) { Log($"抓帧失败(code={r})"); return false; }
byte[] buf = _camera.GetSourceBuffer();
_lastFrame = buf;
// 中心 ROI(画面中央 50%)
var roi = CenterRoi(_camWidth, _camHeight, 0.5);
double score = Sharpness.Compute(buf, _camWidth, _camHeight, roi);
var bmp = ImageConverter.ToBitmapSource(buf, _camWidth, _camHeight);
Dispatcher.Invoke(() =>
{
ImgPreview.Source = bmp;
TxtScore.Text = $"清晰度: {score:F1}";
DrawRoiOverlay(roi);
});
return true;
}
private static System.Drawing.Rectangle CenterRoi(int w, int h, double frac)
{
int rw = (int)(w * frac), rh = (int)(h * frac);
return new System.Drawing.Rectangle((w - rw) / 2, (h - rh) / 2, rw, rh);
}
// ── 抓一帧 ──
private void BtnGrab_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => GrabAndShow());
}
/// 刷新"当前各参数值"显示(曝光/增益/Z/水平)。在UI线程调用。
private void UpdateParamDisplay()
{
void Upd()
{
int exp = _camera?.Exposure ?? ParseInt(TxtExposure.Text, 0);
string gain = $"R{ParseInt(TxtGainR.Text, 0)} G{ParseInt(TxtGainG.Text, 0)} B{ParseInt(TxtGainB.Text, 0)}";
TxtCurValues.Text = $"当前值:曝光 {exp}×100µs({exp / 10.0}ms) | 增益 {gain} | Z {_lastZ} | 水平 {_lastH} | well {(_curWell > 0 ? _curWell.ToString() : "--")}";
}
if (Dispatcher.CheckAccess()) Upd(); else Dispatcher.BeginInvoke((Action)Upd);
}
// ── 实时预览 ──
private void TglLive_Click(object sender, RoutedEventArgs e)
{
if (TglLive.IsChecked == true)
{
_liveCts = new CancellationTokenSource();
var token = _liveCts.Token;
Log("实时预览开始。");
Task.Run(async () =>
{
int frames = 0;
var sw = System.Diagnostics.Stopwatch.StartNew();
while (!token.IsCancellationRequested)
{
if (!_busy) GrabAndShow();
frames++;
if (sw.ElapsedMilliseconds >= 1000)
{
double fps = frames * 1000.0 / sw.ElapsedMilliseconds;
Dispatcher.Invoke(() => TxtFps.Text = $"帧率: {fps:F1} fps");
frames = 0; sw.Restart();
}
await Task.Delay(30, token).ContinueWith(_ => { });
}
}, token);
}
else
{
_liveCts?.Cancel();
Log("实时预览停止。");
}
}
// ── 曝光 ──
private void BtnSetExp_Click(object sender, RoutedEventArgs e)
{
if (_camera == null) return;
int exp = ParseInt(TxtExposure.Text, 400);
int r = _camera.SetExposure(exp);
Log($"设置曝光 {exp}×100µs = {exp / 10.0}ms (code={r})");
UpdateParamDisplay();
// P0-3: 设曝光后传感器需时间生效,等待+丢弃旧帧,再抓显示当前曝光的画面
if (TglLive.IsChecked != true)
Task.Run(() =>
{
Thread.Sleep(Math.Max(200, exp / 5)); // 至少2×曝光时间(单位100µs)
if (_camera != null) _camera.GrabRgb(); // 丢弃旧曝光的帧
GrabAndShow();
});
}
// ── 增益 ──
private void BtnSetGain_Click(object sender, RoutedEventArgs e)
{
if (_camera == null) return;
byte rr = (byte)ParseInt(TxtGainR.Text, 25);
byte gg = (byte)ParseInt(TxtGainG.Text, 14);
byte bb = (byte)ParseInt(TxtGainB.Text, 25);
int r = _camera.SetGain(rr, gg, bb);
Log($"设置增益 R{rr} G{gg} B{bb} (code={r})");
UpdateParamDisplay();
if (TglLive.IsChecked != true) Task.Run(() => GrabAndShow());
}
// ── 光源 ──
private void BtnLedOn_Click(object sender, RoutedEventArgs e)
{
if (_motor == null) return;
Task.Run(() => { _busy = true; bool ok = _motor.OpenLED(); _busy = false; Log($"开光源 {(ok ? "OK" : "失败")}"); });
}
private void BtnLedOff_Click(object sender, RoutedEventArgs e)
{
if (_motor == null) return;
Task.Run(() => { _busy = true; bool ok = _motor.CloseLED(); _busy = false; Log($"关光源 {(ok ? "OK" : "失败")}"); });
}
// ── 存图 ──
private void BtnSaveImg_Click(object sender, RoutedEventArgs e)
{
if (_lastFrame == null) { Log("还没有图像。"); return; }
var dlg = new SaveFileDialog
{
Filter = "BMP 图片|*.bmp",
FileName = $"house{_current?.HouseSn}_{DateTime.Now:yyyyMMdd_HHmmss}.bmp"
};
if (dlg.ShowDialog() == true)
{
try
{
ImageConverter.SaveBmp(_lastFrame, _camWidth, _camHeight, dlg.FileName);
Log($"已存图: {dlg.FileName}");
}
catch (Exception ex) { Log($"存图失败: {ex.Message}"); }
}
}
}
}