using MainShell.Common; using MainShell.Hardware; using MainShell.Log; using MainShell.Models; using MaxwellFramework.Core.Attributes; using MwFramework.Device; using MwFramework.Device.Model; using System; using System.Threading; using System.Threading.Tasks; using AppCameraType = MainShell.Common.CameraType; namespace MainShell.Vision { /// /// ???????????????? HardwareManager ??????????????????????????§Ö??????? /// [Singleton] public class ImageCaptureService : IImageCaptureService { private readonly HardwareManager _hardwareManager; private readonly object _upCameraCaptureLock = new object(); private readonly object _downCameraCaptureLock = new object(); private readonly object _mapCameraCaptureLock = new object(); private readonly object _upWsCameraCaptureLock = new object(); private readonly object _upWideCameraCaptureLock = new object(); public ImageCaptureService(HardwareManager hardwareManager) { _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager)); } /// /// ?????????????????????? /// public Task CaptureAsync( AppCameraType source, int timeoutMilliseconds, CameraCaptureMode captureMode = CameraCaptureMode.Stream, CancellationToken cancellationToken = default(CancellationToken)) { CameraCaptureOptions options = new CameraCaptureOptions(); options.TimeoutMilliseconds = timeoutMilliseconds; options.CaptureMode = captureMode; return CaptureAsync(source, options, cancellationToken); } /// /// ????????????????? /// ????????????????????????????????????????????????????????§¹?? /// public Task CaptureAsync( AppCameraType source, CameraCaptureOptions options, CancellationToken cancellationToken = default(CancellationToken)) { if (options == null) { throw new ArgumentNullException(nameof(options)); } int timeoutMilliseconds = options.TimeoutMilliseconds; if (timeoutMilliseconds <= 0) { timeoutMilliseconds = 5000; } MwCamera camera = GetCamera(source); if (camera == null) { string message = string.Format("ImageCaptureService: camera '{0}' not found in HardwareManager.", source); message.LogSysError(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.CameraNotFound, VisionErrorCode.CameraNotFound, VisionFailureCategory.Capture, VisionAlarmIds.CameraNotFound, MessageKey.VisionCameraNotFound, source, options.CaptureMode, message)); } if (!camera.IsOpen) { string message = string.Format("ImageCaptureService: camera '{0}' is not open.", source); message.LogSysError(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.CameraNotOpen, VisionErrorCode.CameraNotOpen, VisionFailureCategory.Capture, VisionAlarmIds.CameraNotOpen, MessageKey.VisionCameraNotOpen, source, options.CaptureMode, message)); } if (!camera.IsGrabbing) { string message = string.Format( "ImageCaptureService: camera '{0}' is not grabbing. Capture mode '{1}' requires grabbing to be prepared by caller.", source, options.CaptureMode); message.LogSysError(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.CameraNotGrabbing, VisionErrorCode.CameraNotGrabbing, VisionFailureCategory.Capture, VisionAlarmIds.CameraNotGrabbing, MessageKey.VisionCameraNotGrabbing, source, options.CaptureMode, message)); } string.Format( "ImageCaptureService: capturing from '{0}' (mode={1}, timeout={2}ms).", source, options.CaptureMode, timeoutMilliseconds).LogInfo(); object captureLock = GetCaptureLock(source); try { CameraData data; lock (captureLock) { cancellationToken.ThrowIfCancellationRequested(); switch (options.CaptureMode) { case CameraCaptureMode.SoftTrigger: data = ExecuteSoftTriggerSync(camera, source, timeoutMilliseconds, out bool softTriggerFailed); if (softTriggerFailed) { string softTriggerFailureMessage = string.Format( "ImageCaptureService: soft trigger capture failed for camera '{0}' (timeout={1}ms).", source, timeoutMilliseconds); softTriggerFailureMessage.LogSysError(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.SoftTriggerFailed, VisionErrorCode.SoftTriggerFailed, VisionFailureCategory.Driver, VisionAlarmIds.SoftTriggerFailed, MessageKey.VisionSoftTriggerFailed, source, options.CaptureMode, softTriggerFailureMessage)); } break; case CameraCaptureMode.Stream: data = camera.SnapImage(timeoutMilliseconds); break; default: throw new NotSupportedException(string.Format("ImageCaptureService: capture mode '{0}' is not supported.", options.CaptureMode)); } } cancellationToken.ThrowIfCancellationRequested(); if (data == null) { string message = string.Format( "ImageCaptureService: no frame received from '{0}' with capture mode '{1}'.", source, options.CaptureMode); message.LogSysError(); ImageCaptureStatus noFrameStatus = options.CaptureMode == CameraCaptureMode.SoftTrigger ? ImageCaptureStatus.Timeout : ImageCaptureStatus.NoFrame; VisionErrorCode noFrameErrorCode = options.CaptureMode == CameraCaptureMode.SoftTrigger ? VisionErrorCode.CaptureTimeout : VisionErrorCode.NoFrame; int? noFrameAlarmId = options.CaptureMode == CameraCaptureMode.SoftTrigger ? (int?)VisionAlarmIds.CaptureTimeout : (int?)VisionAlarmIds.NoFrame; MessageKey noFrameMessageKey = options.CaptureMode == CameraCaptureMode.SoftTrigger ? MessageKey.VisionCaptureTimeout : MessageKey.VisionNoFrameReturned; return Task.FromResult(ImageCaptureResult.Failure( noFrameStatus, noFrameErrorCode, VisionFailureCategory.Capture, noFrameAlarmId, noFrameMessageKey, source, options.CaptureMode, message)); } return Task.FromResult(ImageCaptureResult.Success(source, options.CaptureMode, new MxImage(data))); } catch (OperationCanceledException ex) { string message = string.Format("ImageCaptureService: capture from '{0}' was cancelled by caller.", source); message.LogInfo(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.Cancelled, VisionErrorCode.OperationCancelled, VisionFailureCategory.Cancelled, null, MessageKey.VisionOperationCancelled, source, options.CaptureMode, message, ex)); } catch (Exception ex) { string message = string.Format("ImageCaptureService: capture from '{0}' failed. {1}", source, ex.Message); message.LogSysError(); return Task.FromResult(ImageCaptureResult.Failure( ImageCaptureStatus.DriverError, VisionErrorCode.DriverError, VisionFailureCategory.Driver, VisionAlarmIds.DriverError, MessageKey.VisionDriverError, source, options.CaptureMode, message, ex)); } } private static CameraData ExecuteSoftTriggerSync(MwCamera camera, AppCameraType source, int timeoutMilliseconds, out bool softTriggerFailed) { softTriggerFailed = false; CameraData data = new CameraData(); DriverLibResult result = camera.ExcuteSoftTrigger(ref data); if (result != DriverLibResult.DriverLibNoError) { softTriggerFailed = true; string.Format( "ImageCaptureService: soft trigger capture failed for camera '{0}' (timeout={1}ms), driver result={2}.", source, timeoutMilliseconds, result).LogSysError(); return null; } if (data == null) { string.Format( "ImageCaptureService: soft trigger capture returned null frame for camera '{0}' (timeout={1}ms).", source, timeoutMilliseconds).LogSysError(); return null; } return data; } private MwCamera GetCamera(AppCameraType source) { return _hardwareManager.GetCamera(source).Camera; } private object GetCaptureLock(AppCameraType source) { switch (source) { case AppCameraType.TopPositionCamera: return _upCameraCaptureLock; case AppCameraType.TopWideCamera: return _mapCameraCaptureLock; case AppCameraType.MapCamera: return _downCameraCaptureLock; case AppCameraType.TopWsCamera: return _upWsCameraCaptureLock; case AppCameraType.TopWideWsCamera: return _upWideCameraCaptureLock; default: throw new ArgumentOutOfRangeException(nameof(source), source, "ImageCaptureService: unknown camera type for capture lock."); } } } }