using MainShell.Common; using MainShell.Log; using MainShell.Models; using MainShell.Vision.Common; using MaxwellFramework.Core.Attributes; using SemiconductorVisionAlgorithm.SemiCalib; using SemiconductorVisionAlgorithm.SemiParams; using System; using System.Threading; using System.Threading.Tasks; namespace MainShell.Vision { /// /// 抓圆服务实现 /// [Singleton] public class FindEdgeCircleService : IFindEdgeCircleService { private readonly IImageCaptureService _imageCaptureService; public FindEdgeCircleService(IImageCaptureService imageCaptureService) { _imageCaptureService = imageCaptureService ?? throw new ArgumentNullException(nameof(imageCaptureService)); } /// /// 采集图像并执行抓圆 /// public async Task> ProcessAsync( FindEdgeCircleRequest request, CancellationToken cancellationToken = default(CancellationToken)) { if (request == null) { throw new ArgumentNullException(nameof(request)); } FindEdgeCircleImageRequest imageRequest = CreateImageRequest(request); return await ExecuteWithCaptureAsync( request.CameraSource, request.CaptureOptions, imageRequest, ProcessImageAsync, cancellationToken).ConfigureAwait(false); } /// /// 基于已有图像直接执行抓圆 /// public Task> ProcessImageAsync( MxImage image, FindEdgeCircleImageRequest request, CancellationToken cancellationToken = default(CancellationToken)) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } FindEdgeCircleImageRequest normalizedRequest = NormalizeRequest(request); VisionProcessResult validationResult = ValidateRequest(normalizedRequest); if (validationResult != null) { return Task.FromResult(CreateFailureResult(validationResult)); } try { cancellationToken.ThrowIfCancellationRequested(); VisionProcessResult result = ExecuteEdgeCircle(image, normalizedRequest); if (!result.Succeeded) { result.Message.LogSysError(); } return Task.FromResult(result); } catch (OperationCanceledException ex) { string message = $"FindEdgeCircleService: process image was cancelled.Timeout ={ normalizedRequest.TimeoutMilliseconds}ms."; message.LogInfo(); return Task.FromResult(VisionProcessResult.Failure( VisionFailureCategory.Cancelled, VisionErrorCode.OperationCancelled, null, MessageKey.VisionOperationCancelled, message, ex)); } catch (Exception ex) { string message = $"FindEdgeCircleService: process image failed. {ex.Message}"; message.LogSysError(); return Task.FromResult(VisionProcessResult.Failure( VisionFailureCategory.Algorithm, VisionErrorCode.CommonAlgorithmExecutionFailed, VisionAlarmIds.CommonAlgorithmExecutionFailed, MessageKey.VisionCommonAlgorithmExecutionFailed, message, ex)); } } /// /// 执行边缘圆检测算法 /// private static VisionProcessResult ExecuteEdgeCircle( MxImage image, FindEdgeCircleImageRequest request) { Camera camera = image.Image; if (camera == null) { return VisionProcessResult.Failure( VisionFailureCategory.Algorithm, VisionErrorCode.ImageNull, VisionAlarmIds.ImageNull, MessageKey.VisionImageIsNull, "FindEdgeCircleService: input image does not contain a valid camera object."); } var parameters = request.Parameters; double[] hv_Parameter; bool flag; if (parameters.RoiRect != null) { // 使用 ROI 区域进行抓圆 flag = CalibManager.Instance.calib_edge_circle_by_roi( camera, parameters.CameraId, 0, parameters.RoiRect, parameters.FileDirectory, out hv_Parameter); } else { // 不使用 ROI 进行抓圆 flag = CalibManager.Instance.calib_edge_circle( camera, parameters.CameraId, 0, parameters.FileDirectory, out hv_Parameter); } if (!flag) { return VisionProcessResult.Failure( VisionFailureCategory.Algorithm, VisionErrorCode.CommonAlgorithmExecutionFailed, VisionAlarmIds.CommonAlgorithmExecutionFailed, MessageKey.VisionCommonAlgorithmExecutionFailed, "FindEdgeCircleService: algorithm calib_edge_circle returned false."); } if (hv_Parameter == null || hv_Parameter.Length < 2) { return VisionProcessResult.Failure( VisionFailureCategory.Algorithm, VisionErrorCode.CommonAlgorithmExecutionFailed, VisionAlarmIds.CommonAlgorithmExecutionFailed, MessageKey.VisionCommonAlgorithmExecutionFailed, "FindEdgeCircleService: algorithm returned invalid parameters."); } FindEdgeCircleResult result = new FindEdgeCircleResult(); result.CenterX = hv_Parameter[0]; result.CenterY = hv_Parameter[1]; return VisionProcessResult.Success(result); } private static FindEdgeCircleImageRequest NormalizeRequest(FindEdgeCircleImageRequest request) { FindEdgeCircleImageRequest normalizedRequest = new FindEdgeCircleImageRequest(); normalizedRequest.TimeoutMilliseconds = request.TimeoutMilliseconds > 0 ? request.TimeoutMilliseconds :3000; normalizedRequest.Parameters = CloneParameters(request.Parameters); return normalizedRequest; } private static VisionProcessResult ValidateRequest(FindEdgeCircleImageRequest request) { if (request.Parameters == null) { return VisionProcessResult.Failure( VisionFailureCategory.Validation, VisionErrorCode.RequestInvalid, VisionAlarmIds.RequestInvalid, MessageKey.VisionRequestInvalid, "FindEdgeCircleService: parameters cannot be null."); } if (request.TimeoutMilliseconds <= 0 || request.TimeoutMilliseconds > 60000) { return VisionProcessResult.Failure( VisionFailureCategory.Validation, VisionErrorCode.TimeoutInvalid, VisionAlarmIds.RequestInvalid, MessageKey.VisionTimeoutInvalid, string.Format("FindEdgeCircleService: TimeoutMilliseconds {0} is invalid. Expected range is [1,60000].", request.TimeoutMilliseconds)); } if (string.IsNullOrWhiteSpace(request.Parameters.FileDirectory)) { return VisionProcessResult.Failure( VisionFailureCategory.Validation, VisionErrorCode.RequestInvalid, VisionAlarmIds.RequestInvalid, MessageKey.VisionRequestInvalid, "FindEdgeCircleService: FileDirectory cannot be empty."); } if (string.IsNullOrWhiteSpace(request.Parameters.CameraId)) { return VisionProcessResult.Failure( VisionFailureCategory.Validation, VisionErrorCode.RequestInvalid, VisionAlarmIds.RequestInvalid, MessageKey.VisionRequestInvalid, "FindEdgeCircleService: CameraId cannot be empty."); } return null; } private static FindEdgeCircleParameters CloneParameters(FindEdgeCircleParameters parameters) { if (parameters == null) { return new FindEdgeCircleParameters(); } FindEdgeCircleParameters clone = new FindEdgeCircleParameters(); clone.UseRoi = parameters.UseRoi; clone.RoiName = parameters.RoiName; clone.FileDirectory = parameters.FileDirectory; clone.CameraId = parameters.CameraId; clone.RoiRect = parameters.RoiRect; return clone; } private static FindEdgeCircleImageRequest CreateImageRequest(FindEdgeCircleRequest request) { FindEdgeCircleImageRequest imageRequest = new FindEdgeCircleImageRequest(); imageRequest.TimeoutMilliseconds = request.TimeoutMilliseconds; imageRequest.Parameters = CloneParameters(request.Parameters); return imageRequest; } private async Task> ExecuteWithCaptureAsync( CameraType cameraSource, CameraCaptureOptions captureOptions, FindEdgeCircleImageRequest imageRequest, Func>> imageProcessor, CancellationToken cancellationToken) { ImageCaptureResult captureResult = null; MxImage image = null; try { captureResult = await _imageCaptureService.CaptureAsync( cameraSource, captureOptions, cancellationToken).ConfigureAwait(false); if (captureResult == null) { return VisionProcessResult.Failure( VisionFailureCategory.Capture, VisionErrorCode.NoFrame, VisionAlarmIds.NoFrame, MessageKey.VisionNoFrameReturned, string.Format( "FindEdgeCircleService: image capture returned null result for camera '{0}'.", cameraSource)); } if (!captureResult.Succeeded) { return CreateFailureResult(VisionResultMapper.CreateCaptureFailure(captureResult)); } image = captureResult.Image; if (image == null) { string message = string.Format( "FindEdgeCircleService: image capture succeeded but returned null image for camera '{0}'.", cameraSource); message.LogSysError(); return VisionProcessResult.Failure( VisionFailureCategory.Capture, VisionErrorCode.ImageNull, VisionAlarmIds.ImageNull, MessageKey.VisionImageIsNull, message); } return await imageProcessor(image, imageRequest, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException ex) { string message = $"FindEdgeCircleService: capture orchestration was cancelled for camera '{cameraSource}'."; message.LogInfo(); return VisionProcessResult.Failure( VisionFailureCategory.Cancelled, VisionErrorCode.OperationCancelled, null, MessageKey.VisionOperationCancelled, message, ex); } catch (Exception ex) { string message = $"FindEdgeCircleService: capture orchestration failed for camera '{cameraSource}'. { ex.Message} "; message.LogSysError(); return VisionProcessResult.Failure( VisionFailureCategory.Algorithm, VisionErrorCode.CommonAlgorithmExecutionFailed, VisionAlarmIds.CommonAlgorithmExecutionFailed, MessageKey.VisionCommonAlgorithmExecutionFailed, message, ex); } finally { if (image != null) { image.Dispose(); } } } private static VisionProcessResult CreateFailureResult(VisionProcessResult result) { return VisionProcessResult.Failure( result.FailureCategory, result.ErrorCode, result.AlarmId, result.UserMessageKey, result.Message, result.Exception); } } }