using System;
using System.IO;
using LensAdjustmentTool.Configuration;
using LensAdjustmentTool.Infrastructure;
using LensAdjustmentTool.Models;
using LensAdjustmentTool.Processing;
using TIS.Imaging;
namespace LensAdjustmentTool.Camera
{
public sealed class IcImagingCameraService : IDisposable
{
private readonly ICImagingControl _ic;
private readonly LensAdjustmentProcessor _processor;
private readonly LatestResultMailbox _mailbox;
private readonly AppLogger _logger;
private FrameQueueSink _sink;
private volatile bool _running;
private string _statusText = "未初期化";
public string StatusText => _statusText;
public bool IsRunning => _running;
public CameraInfo CameraInfo { get; private set; }
public IcImagingCameraService(
ICImagingControl ic,
LensAdjustmentProcessor processor,
LatestResultMailbox mailbox,
AppLogger logger)
{
_ic = ic ?? throw new ArgumentNullException(nameof(ic));
_processor = processor ?? throw new ArgumentNullException(nameof(processor));
_mailbox = mailbox ?? throw new ArgumentNullException(nameof(mailbox));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void InitializeAndStart(string baseDirectory)
{
if (_running) return;
string profilePath = Path.Combine(baseDirectory, AppSettings.DeviceProfileRelativePath);
if (!File.Exists(profilePath))
{
throw new FileNotFoundException("Camera profile not found.", profilePath);
}
_statusText = "カメラ設定読込中";
_logger.Info("Loading camera profile: " + profilePath);
// OpenDevice=true opens the exact device stored in the profile and applies its settings.
_ic.LoadDeviceStateFromFile(profilePath, true);
if (!_ic.DeviceValid)
{
throw new InvalidOperationException("DFK33UX178 camera could not be opened.");
}
string deviceName = _ic.DeviceCurrent?.Name ?? "Unknown";
if (deviceName.IndexOf(AppSettings.ExpectedDeviceNameFragment, StringComparison.OrdinalIgnoreCase) < 0)
{
throw new InvalidOperationException(
$"Unexpected camera: {deviceName}. Expected {AppSettings.ExpectedDeviceNameFragment}.");
}
var vf = _ic.VideoFormatCurrent;
if (vf == null)
{
throw new InvalidOperationException("No current video format is available.");
}
if (vf.Width != AppSettings.ExpectedFrameWidth || vf.Height != AppSettings.ExpectedFrameHeight)
{
throw new InvalidOperationException(
$"Unexpected video format: {vf.Width}x{vf.Height}. " +
$"Expected RGB24 {AppSettings.ExpectedFrameWidth}x{AppSettings.ExpectedFrameHeight}.");
}
// The sink requests RGB24 explicitly. IC Imaging Control RGB24 is BGR byte order on Windows.
_sink = new FrameQueueSink(OnFrameQueued, MediaSubtypes.RGB24, AppSettings.SinkBufferCount);
_ic.Sink = _sink;
_ic.LiveDisplay = false;
_statusText = "撮影開始中";
_ic.LiveStart();
_running = true;
CameraInfo = new CameraInfo
{
DeviceName = deviceName,
Width = vf.Width,
Height = vf.Height,
FrameRate = _ic.DeviceFrameRate
};
_statusText = "接続済み";
_logger.Info("Capture started: " + CameraInfo);
}
private FrameQueuedResult OnFrameQueued(IFrameQueueBuffer buffer)
{
try
{
FrameAnalysisResult result = _processor.Process(buffer);
_mailbox.Publish(result);
return FrameQueuedResult.ReQueue;
}
catch (Exception ex)
{
_statusText = "画像処理エラー";
_logger.Error("Frame processing failed.", ex);
return FrameQueuedResult.ReQueue;
}
}
public void Stop()
{
if (!_running) return;
try
{
_statusText = "停止中";
if (_ic.LiveVideoRunning)
{
_ic.LiveStop();
}
}
finally
{
_running = false;
_statusText = "停止";
_mailbox.Clear();
_logger.Info("Capture stopped.");
}
}
public void Dispose()
{
Stop();
}
}
}