Newer
Older
LensAdjustmentTool / Camera / IcImagingCameraService.cs
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();
        }
    }
}