public async Task <CellPassesResponse> ExecuteAsync(CellPassesRequestArgument_ApplicationService arg) { var result = new CellPassesResponse() { ReturnCode = CellPassesReturnCode.Error }; if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0) { // Prepare the filters for use in cell passes operations. Failure to prepare any filter results in this request terminating if (!arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK)) { return(new CellPassesResponse { ReturnCode = CellPassesReturnCode.FailedToPrepareFilter }); } } var siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(arg.ProjectID); if (siteModel == null) { _log.LogError($"Failed to locate site model {arg.ProjectID}"); return(result); } if (!arg.CoordsAreGrid) { //WGS84 coords need to be converted to NEE var pointToConvert = new XYZ(arg.Point.X, arg.Point.Y, 0); arg.Point = DIContext.Obtain <ICoreXWrapper>().LLHToNEE(siteModel.CSIB(), pointToConvert.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians).ToTRex_XYZ(); result.Northing = arg.Point.Y; result.Easting = arg.Point.X; } var existenceMap = siteModel.ExistenceMap; // Determine the on-the-ground cell siteModel.Grid.CalculateIndexOfCellContainingPosition(arg.Point.X, arg.Point.Y, out var otgCellX, out var otgCellY); if (!existenceMap[otgCellX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, otgCellY >> SubGridTreeConsts.SubGridIndexBitsPerLevel]) { result.ReturnCode = CellPassesReturnCode.NoDataFound; return(result); } var computeArg = new CellPassesRequestArgument_ClusterCompute(arg.ProjectID, arg.Point, otgCellX, otgCellY, arg.Filters); var requestCompute = new CellPassesRequest_ClusterCompute(); var affinityKey = new SubGridSpatialAffinityKey(SubGridSpatialAffinityKey.DEFAULT_SPATIAL_AFFINITY_VERSION_NUMBER_TICKS, arg.ProjectID, otgCellX, otgCellY); var responseCompute = await requestCompute.ExecuteAsync(computeArg, affinityKey); result.ReturnCode = responseCompute.ReturnCode; result.CellPasses = responseCompute.CellPasses; return(result); }
/// <summary> /// Processes the request for type T. /// </summary> protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { try { var request = CastRequestObjectTo <TileRequest>(item); var filter1 = request.Filter1; var filter2 = request.Filter2; await PairUpAssetIdentifiers(request.ProjectUid.Value, filter1, filter2); await PairUpImportedFileIdentifiers(request.ProjectUid.Value, request.DesignDescriptor, filter1, filter2); if (request.ComputeVolumesType == VolumesType.Between2Filters) { if (!request.ExplicitFilters) { (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2); } } else { (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolumesType); } var trexRequest = new TRexTileRequest( request.ProjectUid.Value, request.Mode, request.Palettes, request.DesignDescriptor, filter1, filter2, request.BoundBoxLatLon, request.BoundBoxGrid, request.Width, request.Height, AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings), AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings), request.ComputeVolumesType ); log.LogDebug($"{nameof(TilesExecutor)} trexRequest {JsonConvert.SerializeObject(trexRequest)}"); var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(trexRequest, "/tile", customHeaders); using (var ms = new MemoryStream()) { fileResult.CopyTo(ms); return(new TileResult(ms.ToArray())); } } finally { ContractExecutionStates.ClearDynamic(); } }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { try { var request = CastRequestObjectTo <SummaryVolumesRequest>(item); var baseFilter = request.BaseFilter; var topFilter = request.TopFilter; await PairUpAssetIdentifiers(request.ProjectUid.Value, baseFilter, topFilter); await PairUpImportedFileIdentifiers(request.ProjectUid.Value, filter1 : baseFilter, filter2 : topFilter); var designDescriptors = new List <DesignDescriptor>(); designDescriptors.Add(request.BaseDesignDescriptor); designDescriptors.Add(request.TopDesignDescriptor); await PairUpImportedFileIdentifiers(request.ProjectUid.Value, designDescriptors); if (request.VolumeCalcType == VolumesType.Between2Filters) { if (!request.ExplicitFilters) { (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter); } } else { (baseFilter, topFilter) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(baseFilter, topFilter, request.VolumeCalcType); } var summaryVolumesRequest = new SummaryVolumesDataRequest( request.ProjectUid, baseFilter, topFilter, request.BaseDesignDescriptor.FileUid, request.BaseDesignDescriptor.Offset, request.TopDesignDescriptor.FileUid, request.TopDesignDescriptor.Offset, request.VolumeCalcType); log.LogDebug($"{nameof(SummaryVolumesExecutor)} trexRequest {JsonConvert.SerializeObject(summaryVolumesRequest)}"); return(await trexCompactionDataProxy.SendDataPostRequest <SummaryVolumesResult, SummaryVolumesDataRequest>(summaryVolumesRequest, "/volumes/summary", customHeaders)); } finally { ContractExecutionStates.ClearDynamic(); } }
private async Task <CompactionProfileResult <CompactionSummaryVolumesProfileCell> > ProcessSummaryVolumesWithTRexGateway(CompactionProfileProductionDataRequest request) { if (request.IsAlignmentDesign) { throw new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.ValidationError, "TRex unsupported request")); } var volumeCalcType = request.VolumeCalcType ?? VolumeCalcType.None; if (volumeCalcType != VolumeCalcType.None) { var baseFilter = request.BaseFilter; var topFilter = request.TopFilter; if (volumeCalcType == VolumeCalcType.GroundToGround && !request.ExplicitFilters) { (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter); } var liftBuildSettings = request.LiftBuildSettings; var summaryVolumesProfileDataRequest = new SummaryVolumesProfileDataRequest( request.ProjectUid ?? Guid.Empty, baseFilter, topFilter, request.VolumeDesignDescriptor?.FileUid, request.VolumeDesignDescriptor?.Offset, ConvertVolumeCalcType(volumeCalcType), request.GridPoints != null, request.GridPoints?.x1 ?? request.WGS84Points.lon1, request.GridPoints?.y1 ?? request.WGS84Points.lat1, request.GridPoints?.x2 ?? request.WGS84Points.lon2, request.GridPoints?.y2 ?? request.WGS84Points.lat2, AutoMapperUtility.Automapper.Map <OverridingTargets>(liftBuildSettings), AutoMapperUtility.Automapper.Map <LiftSettings>(liftBuildSettings) ); var trexResult = await trexCompactionDataProxy.SendDataPostRequest <ProfileDataResult <SummaryVolumeProfileCell>, SummaryVolumesProfileDataRequest>(summaryVolumesProfileDataRequest, "/volumes/summary/profile", customHeaders); return(trexResult != null?ConvertTRexSummaryVolumesProfileResult(trexResult, volumeCalcType) : null); } return(null); }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { try { var request = CastRequestObjectTo <PatchRequest>(item); var filter1 = request.Filter1; var filter2 = request.Filter2; if (request.ComputeVolType == VolumesType.Between2Filters) { (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2); } else { (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolType); } await PairUpAssetIdentifiers(request.ProjectUid.Value, filter1, filter2); await PairUpImportedFileIdentifiers(request.ProjectUid.Value, request.DesignDescriptor, filter1, filter2); var patchDataRequest = new PatchDataRequest( request.ProjectUid.Value, filter1, filter2, request.Mode, request.PatchNumber, request.PatchSize, AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings), AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings)); log.LogDebug($"{nameof(PatchExecutor)} patchDataRequest {JsonConvert.SerializeObject(patchDataRequest)}"); var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(patchDataRequest, "/patches", customHeaders); return(fileResult.Length > 0 ? ConvertPatchResult(fileResult, request) : new ContractExecutionResult(ContractExecutionStatesEnum.InternalProcessingError, "Null patch returned")); } finally { ContractExecutionStates.ClearDynamic(); } }
/// <summary> /// Processes the request for type of T. /// </summary> protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { try { var request = CastRequestObjectTo <TileRequest>(item); var filter1 = request.Filter1; var filter2 = request.Filter2; if (request.ComputeVolumesType == VolumesType.Between2Filters && !request.ExplicitFilters) { (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2); } var trexRequest = new TRexTileRequest( request.ProjectUid.Value, request.Mode, request.Palettes, request.DesignDescriptor, filter1, filter2, request.BoundBoxLatLon, request.BoundBoxGrid, request.Width, request.Height, AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings), AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings), request.ComputeVolumesType ); var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(trexRequest, "/tile", customHeaders); using (var ms = new MemoryStream()) { fileResult.CopyTo(ms); return(new TileResult(ms.ToArray())); } } finally { ContractExecutionStates.ClearDynamic(); } }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { // Note: The numPatches out parameter is ignored in favor of the same value returned in the PatchResult proper. This will be removed // in due course once the breaking modifications process is agreed with BC. try { var request = CastRequestObjectTo <PatchRequest>(item); var filter1 = request.Filter1; var filter2 = request.Filter2; if (request.ComputeVolType == VolumesType.Between2Filters) { (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2); } else { (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolType); } var patchDataRequest = new PatchDataRequest( request.ProjectUid.Value, filter1, filter2, request.Mode, request.PatchNumber, request.PatchSize, AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings), AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings)); var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(patchDataRequest, "/patches", customHeaders); return(fileResult.Length > 0 ? ConvertPatchResult(fileResult, true) : CreateNullPatchReturnedResult()); } finally { ContractExecutionStates.ClearDynamic(); } }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { try { var request = CastRequestObjectTo <SummaryVolumesRequest>(item); var baseFilter = request.BaseFilter; var topFilter = request.TopFilter; if (request.VolumeCalcType == VolumesType.Between2Filters) { if (!request.ExplicitFilters) { (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter); } } else { // Note: The use of the ReconcileTopFilterAndVolumeComputationMode() here breaks with the pattern of all the other V2 // end points which explicitly do not perform this step. It has been copied from the Raptor implementation of this end point (baseFilter, topFilter) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(baseFilter, topFilter, request.VolumeCalcType); } var summaryVolumesRequest = new SummaryVolumesDataRequest( request.ProjectUid, baseFilter, topFilter, request.BaseDesignDescriptor?.FileUid, request.BaseDesignDescriptor?.Offset, request.TopDesignDescriptor?.FileUid, request.TopDesignDescriptor?.Offset, request.VolumeCalcType); return(await trexCompactionDataProxy.SendDataPostRequest <SummaryVolumesResult, SummaryVolumesDataRequest>(summaryVolumesRequest, "/volumes/summary", customHeaders)); } finally { ContractExecutionStates.ClearDynamic(); } }
private void ConfigurePipeline(SubGridPipelineAggregative <SubGridsRequestArgument, SimpleVolumesResponse> PipeLine) { //PipeLine.TimeToLiveSeconds := VLPDSvcLocations.VLPDPSNode_VolumePipelineTTLSeconds; PipeLine.RequestDescriptor = RequestDescriptor; //PipeLine.ExternalDescriptor := FExternalDescriptor; PipeLine.DataModelID = SiteModel.ID; Log.LogDebug($"Volume calculation extents for DM={SiteModel.ID}, Request={RequestDescriptor}: {Extents}"); PipeLine.OverallExistenceMap = OverallExistenceMap; PipeLine.ProdDataExistenceMap = ProdDataExistenceMap; PipeLine.DesignSubGridOverlayMap = DesignSubgridOverlayMap; // Initialise a request analyzer to provide to the pipeline PipeLine.RequestAnalyser = DIContext.Obtain <IRequestAnalyser>(); PipeLine.RequestAnalyser.Pipeline = PipeLine; PipeLine.RequestAnalyser.WorldExtents.Assign(Extents); PipeLine.LiftParams = LiftParams; // Construct and assign the filter set into the pipeline IFilterSet FilterSet = FilterUtilities.ConstructFilters(new FilterSet(BaseFilter, TopFilter), VolumeType); if (FilterSet.Filters.Length == 3) { PipeLine.SubGridsRequestComputeStyle = SubGridsRequestComputeStyle.SimpleVolumeThreeWayCoalescing; } PipeLine.FilterSet = FilterSet; PipeLine.GridDataType = GridDataType.Height; if (FilteredTopSurveyedSurfaces.Count > 0 || FilteredBaseSurveyedSurfaces.Count > 0) { PipeLine.IncludeSurveyedSurfaceInformation = true; } }
/// <summary> /// Executes the progressive volumes computation returning a ProgressiveVolumesResponse with the results /// </summary> public async Task <ProgressiveVolumesResponse> ExecuteAsync() { var volumesResult = new ProgressiveVolumesResponse(); var resultBoundingExtents = BoundingWorldExtent3D.Null(); var requestDescriptor = Guid.NewGuid(); // TODO ASNodeImplInstance.NextDescriptor; _log.LogInformation($"#In# Performing {nameof(ComputeProgressiveVolumes_Coordinator)}.Execute for DataModel:{SiteModelID}"); try { try { ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequests.Increment(); // Prepare filter for use in the request var resultStatus = FilterUtilities.PrepareFiltersForUse(new[] { Filter, AdditionalSpatialFilter }, SiteModelID); if (resultStatus != RequestErrorStatus.OK) { return(volumesResult); } // Obtain the site model context for the request _siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(SiteModelID); if (_siteModel == null) { return(volumesResult); } // Determine the number of progressions that are required and establish the required aggregation states in the aggregator var numProgressions = (int)((EndDate.Ticks - StartDate.Ticks) / Interval.Ticks); if ((EndDate.Ticks - StartDate.Ticks) % Interval.Ticks == 0) { numProgressions++; } if (VolumeType == VolumeComputationType.Between2Filters) { // One fewer progressions will be calculated as each volume in the progression is computed across the interval of two // surfaces derived from production data. numProgressions--; } if (numProgressions > ClientProgressiveHeightsLeafSubGrid.MaxNumberOfHeightLayers) { throw new ArgumentException($"No more than {ClientProgressiveHeightsLeafSubGrid.MaxNumberOfHeightLayers} height layers may be requested at one time"); } // Create and configure the aggregator that contains the business logic for the underlying volume calculation Aggregator = new ProgressiveVolumesCalculationsAggregator { SiteModel = _siteModel, LiftParams = _liftParams, CellSize = _siteModel.CellSize, VolumeType = VolumeType, CutTolerance = CutTolerance, FillTolerance = FillTolerance, AggregationStates = Enumerable .Range(VolumeType == VolumeComputationType.Between2Filters ? 1 : 0, numProgressions) .Select(x => StartDate + x * Interval) .Select(d => new ProgressiveVolumeAggregationState(_siteModel.CellSize) { Date = d, CutTolerance = CutTolerance, FillTolerance = FillTolerance }).ToArray() }; // Create and configure the volumes calculation engine var computeVolumes = new ProgressiveVolumesCalculator { RequestDescriptor = requestDescriptor, SiteModel = _siteModel, Aggregator = Aggregator, Filter = Filter, VolumeType = VolumeType, LiftParams = _liftParams, StartDate = StartDate, EndDate = EndDate, Interval = Interval }; InitialiseVolumesCalculator(computeVolumes); // Perform the volume computation if (computeVolumes.ComputeVolumeInformation()) { resultStatus = RequestErrorStatus.OK; } else { resultStatus = computeVolumes.AbortedDueToTimeout ? RequestErrorStatus.AbortedDueToPipelineTimeout : RequestErrorStatus.Unknown; } if (resultStatus != RequestErrorStatus.OK) { _log.LogInformation($"Progressive volume result: Failure, error = {resultStatus}"); // Send the (empty) results back to the caller return(volumesResult); } // Instruct the Aggregator to perform any finalization logic before reading out the results Aggregator.Finalise(); var noProductionDataCount = 0; var invalidPlanExtentsCount = 0; foreach (var state in Aggregator.AggregationStates) { _log.LogInformation($"#Result# Progressive volume result: Cut={state.CutFillVolume.CutVolume:F3}, Fill={state.CutFillVolume.FillVolume:F3}, Area={state.CoverageArea:F3}"); if (!state.BoundingExtents.IsValidPlanExtent) { if (state.CoverageArea == 0 && state.CutFillVolume.CutVolume == 0 && state.CutFillVolume.FillVolume == 0) { noProductionDataCount++; } else { invalidPlanExtentsCount++; } } } if (noProductionDataCount == Aggregator.AggregationStates.Length) { resultStatus = RequestErrorStatus.NoProductionDataFound; } else if (invalidPlanExtentsCount == Aggregator.AggregationStates.Length) { resultStatus = RequestErrorStatus.InvalidPlanExtents; } if (resultStatus == RequestErrorStatus.NoProductionDataFound || resultStatus == RequestErrorStatus.InvalidPlanExtents) { _log.LogInformation($"Progressive volume invalid plan extents or no data found: {resultStatus}"); } volumesResult.ResultStatus = resultStatus; if (resultStatus == RequestErrorStatus.OK) { volumesResult.Volumes = Aggregator.AggregationStates.Select(aggregator => new ProgressiveVolumeResponseItem { Date = aggregator.Date, Volume = new SimpleVolumesResponse { Cut = aggregator.CutFillVolume.CutVolume, Fill = aggregator.CutFillVolume.FillVolume, TotalCoverageArea = aggregator.CoverageArea, CutArea = aggregator.CutArea, FillArea = aggregator.FillArea, BoundingExtentGrid = aggregator.BoundingExtents, BoundingExtentLLH = resultBoundingExtents } }).ToArray(); } } finally { ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequestsCompleted.Increment(); if (volumesResult.ResultStatus != RequestErrorStatus.OK) { ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequestsFailed.Increment(); } } } catch (Exception e) { _log.LogError(e, $"Failed to compute the progressive volumes. Site Model ID: {SiteModelID}"); } return(volumesResult); }
/// <summary> /// Executes the profiler /// </summary> public async Task <ProfileRequestResponse <T> > ExecuteAsync(ProfileRequestArgument_ApplicationService arg) { _log.LogInformation("Start execution"); try { if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0) { // Prepare the filters for use in profiling operations. Failure to prepare any filter results in this request terminating if (!arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK)) { return(new ProfileRequestResponse <T> { ResultStatus = RequestErrorStatus.FailedToPrepareFilter }); } } var arg2 = new ProfileRequestArgument_ClusterCompute { ProfileTypeRequired = arg.ProfileTypeRequired, ProfileStyle = arg.ProfileStyle, ProjectID = arg.ProjectID, Filters = arg.Filters, ReferenceDesign = arg.ReferenceDesign, ReturnAllPassesAndLayers = arg.ReturnAllPassesAndLayers, TRexNodeID = arg.TRexNodeID, VolumeType = arg.VolumeType, Overrides = arg.Overrides, LiftParams = arg.LiftParams }; // Perform coordinate conversion on the argument before broadcasting it: if (arg.PositionsAreGrid) { arg2.NEECoords = new[] { arg.StartPoint, arg.EndPoint }.Select(x => new XYZ(x.Lon, x.Lat)).ToArray(); } else { var siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(arg.ProjectID); if (siteModel != null) { arg2.NEECoords = DIContext.Obtain <ICoreXWrapper>().WGS84ToCalibration( siteModel.CSIB(), new[] { arg.StartPoint, arg.EndPoint } .ToCoreX_WGS84Point(), CoreX.Types.InputAs.Radians) .ToTRex_XYZ(); } } var request = new ProfileRequest_ClusterCompute <T>(); var profileResponse = await request.ExecuteAsync(arg2); profileResponse.GridDistanceBetweenProfilePoints = MathUtilities.Hypot(arg2.NEECoords[1].X - arg2.NEECoords[0].X, arg2.NEECoords[1].Y - arg2.NEECoords[0].Y); //... and then sort them to get the final result, as well as removing initial and duplicate null values // Remove null cells in the profiles list. Null cells are defined by cells with null CellLastHeight. // All duplicate null cells will be replaced by a by single null cell entry int firstNonNullIndex = 0; var _profileCells = profileResponse?.ProfileCells?.OrderBy(x => x.Station).ToList(); if (_profileCells != null) { profileResponse.ProfileCells = _profileCells.Where((x, i) => { // Remove all leading nulls if (_profileCells[i].IsNull() && i == firstNonNullIndex) { firstNonNullIndex++; return(false); } // Collapse all interior nulls to single nulls, unless the null is at the end. Leave any single terminating null return(i == 0 || !_profileCells[i].IsNull() || (_profileCells[i].IsNull() && !_profileCells[i - 1].IsNull())); }).ToList(); } // Return the care package to the caller return(profileResponse); } finally { _log.LogInformation("End execution"); } }
/// <summary> /// Builds the pipeline configured per the supplied state ready to execute the request /// </summary> public bool Build() { // Todo: This method is left as async as a reminder that the GetExistenveMap workflows could either be async (as they // potentially read from the persistent store), and/or they couild be cached in the site model designs/surveyed surfaces contexts // See Jira CCSSSCON-1481 try { var stopWatch = Stopwatch.StartNew(); // Ensure the task is initialised with the request descriptor Task.RequestDescriptor = RequestDescriptor; // Ensure the Task grid data type matches the pipeline processor Task.GridDataType = GridDataType; // Introduce the task and the pipeline to each other Pipeline.PipelineTask = Task; Task.PipeLine = Pipeline; (Pipeline as ISubGridPipelineBase <TSubGridsRequestArgument>).CustomArgumentInitializer = CustomArgumentInitializer; if ((Filters?.Filters?.Length ?? 0) == 0) { _log.LogError($"Filters supplied to pipeline processor is null or empty, replacing with single default filter"); Filters = new FilterSet(new CombinedFilter()); } // Construct an aggregated set of excluded surveyed surfaces for the filters used in the query foreach (var filter in Filters.Filters) { if (filter != null && SurveyedSurfaceExclusionList.Length > 0) { SurveyedSurfaceExclusionList = new Guid[filter.AttributeFilter.SurveyedSurfaceExclusionList.Length]; Array.Copy(filter.AttributeFilter.SurveyedSurfaceExclusionList, SurveyedSurfaceExclusionList, SurveyedSurfaceExclusionList.Length); } } // Get the SiteModel for the request SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelID); if (SiteModel == null) { Response.ResultStatus = RequestErrorStatus.NoSuchDataModel; return(false); } SpatialExtents = SiteModel.GetAdjustedDataModelSpatialExtents(SurveyedSurfaceExclusionList); if (!SpatialExtents.IsValidPlanExtent) { Response.ResultStatus = RequestErrorStatus.FailedToRequestDatamodelStatistics; // Or there was no data in the model return(false); } // Get the current production data existence map from the site model ProdDataExistenceMap = SiteModel.ExistenceMap; // Obtain the sub grid existence map for the project // Retrieve the existence map for the datamodel OverallExistenceMap = new SubGridTreeSubGridExistenceBitMask { CellSize = SubGridTreeConsts.SubGridTreeDimension * SiteModel.CellSize }; if (RequireSurveyedSurfaceInformation) { // Obtain local reference to surveyed surfaces (lock free access) var localSurveyedSurfaces = SiteModel.SurveyedSurfaces; if (localSurveyedSurfaces != null) { // Construct two filtered surveyed surface lists to act as a rolling pair used as arguments // to the ProcessSurveyedSurfacesForFilter method var filterSurveyedSurfaces = DIContext.Obtain <ISurveyedSurfaces>(); var filteredSurveyedSurfaces = DIContext.Obtain <ISurveyedSurfaces>(); SurveyedSurfacesExcludedViaTimeFiltering = Filters.Filters.Length > 0; foreach (var filter in Filters.Filters) { if (!localSurveyedSurfaces.ProcessSurveyedSurfacesForFilter(DataModelID, filter, filteredSurveyedSurfaces, filterSurveyedSurfaces, OverallExistenceMap)) { Response.ResultStatus = RequestErrorStatus.FailedToRequestSubgridExistenceMap; return(false); } SurveyedSurfacesExcludedViaTimeFiltering &= filterSurveyedSurfaces.Count == 0; } } } OverallExistenceMap.SetOp_OR(ProdDataExistenceMap); foreach (var filter in Filters.Filters) { if (filter != null) { if (!DesignFilterUtilities.ProcessDesignElevationsForFilter(SiteModel, filter, OverallExistenceMap)) { Response.ResultStatus = RequestErrorStatus.NoDesignProvided; return(false); } Response.ResultStatus = FilterUtilities.PrepareFilterForUse(filter, DataModelID); if (Response.ResultStatus != RequestErrorStatus.OK) { _log.LogInformation($"PrepareFilterForUse failed: Datamodel={DataModelID}"); return(false); } } } // Adjust the extents we have been given to encompass the spatial extent of the supplied filters (if any) Filters.ApplyFilterAndSubsetBoundariesToExtents(SpatialExtents); // If this request involves a relationship with a design then ensure the existence map // for the design is loaded in to memory to allow the request pipeline to confine // sub grid requests that overlay the actual design if (RequestRequiresAccessToDesignFileExistenceMap) { if (CutFillDesign == null || CutFillDesign.DesignID == Guid.Empty) { _log.LogError($"No design provided to cut fill, summary volume or thickness overlay render request for datamodel {DataModelID}"); Response.ResultStatus = RequestErrorStatus.NoDesignProvided; return(false); } DesignSubGridOverlayMap = GetExistenceMaps().GetSingleExistenceMap(DataModelID, Consts.EXISTENCE_MAP_DESIGN_DESCRIPTOR, CutFillDesign.DesignID); if (DesignSubGridOverlayMap == null) { _log.LogError($"Failed to request sub grid overlay index for design {CutFillDesign.DesignID} in datamodel {DataModelID}"); Response.ResultStatus = RequestErrorStatus.NoDesignProvided; return(false); } DesignSubGridOverlayMap.CellSize = SubGridTreeConsts.SubGridTreeDimension * SiteModel.CellSize; } // Impose the final restriction on the spatial extents from the client context SpatialExtents.Intersect(OverrideSpatialExtents); // Introduce the Request analyzer to the pipeline and spatial extents it requires RequestAnalyser.Pipeline = Pipeline; RequestAnalyser.WorldExtents = SpatialExtents; ConfigurePipeline(); _log.LogInformation($"Pipeline processor build phase completed in {stopWatch.Elapsed}"); return(true); } catch (Exception e) { _log.LogError(e, "Exception occurred in pipeline builder"); throw; } }
/// <summary> /// Builds a fully analyzed vector of profiled cells from the list of cell passed to it /// </summary> public override bool Analyze(List <SummaryVolumeProfileCell> profileCells, ISubGridSegmentCellPassIterator cellPassIterator) { Log.LogDebug($"Analyze Summary Volume ProfileCells. Processing {profileCells.Count}"); var CurrentSubgridOrigin = new SubGridCellAddress(int.MaxValue, int.MaxValue); ISubGrid SubGrid = null; IServerLeafSubGrid _SubGridAsLeaf = null; profileCell = null; // Construct the set of requestors to query elevation sub grids needed for the summary volume calculations. var filterSet = FilterUtilities.ConstructFilters(FilterSet, VolumeType); IntermediaryFilterRequired = filterSet.Filters.Length == 3; var utilities = DIContext.Obtain <IRequestorUtilities>(); Requestors = utilities.ConstructRequestors(null, SiteModel, Overrides, LiftParams, utilities.ConstructRequestorIntermediaries(SiteModel, filterSet, true, GridDataType.HeightAndTime), AreaControlSet.CreateAreaControlSet(), PDExistenceMap); var cellOverrideMask = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); for (int I = 0; I < profileCells.Count; I++) { profileCell = profileCells[I]; // get sub grid origin for cell address var thisSubgridOrigin = new SubGridCellAddress(profileCell.OTGCellX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, profileCell.OTGCellY >> SubGridTreeConsts.SubGridIndexBitsPerLevel); if (!CurrentSubgridOrigin.Equals(thisSubgridOrigin)) // if we have a new sub grid to fetch { // if we have an existing sub grid and a change in sub grid detected process the current sub grid profile cell list if (SubGrid != null) { ProcessSubGroup(new SubGridCellAddress(CurrentSubgridOrigin.X << SubGridTreeConsts.SubGridIndexBitsPerLevel, CurrentSubgridOrigin.Y << SubGridTreeConsts.SubGridIndexBitsPerLevel), PDExistenceMap[CurrentSubgridOrigin.X, CurrentSubgridOrigin.Y], cellOverrideMask); cellOverrideMask.Clear(); } SubGrid = null; cellCounter = 0; // Does the sub grid tree contain this node in it's existence map? if so get sub grid if (PDExistenceMap[thisSubgridOrigin.X, thisSubgridOrigin.Y]) { SubGrid = SubGridTrees.Server.Utilities.SubGridUtilities.LocateSubGridContaining (SiteModel.PrimaryStorageProxy, SiteModel.Grid, profileCell.OTGCellX, profileCell.OTGCellY, SiteModel.Grid.NumLevels, false, false); } _SubGridAsLeaf = SubGrid as ServerSubGridTreeLeaf; if (_SubGridAsLeaf == null) { continue; } CurrentSubgridOrigin = thisSubgridOrigin; // all good to proceed with this sub grid } profileCellList[cellCounter++] = profileCell; // add cell to list to process for this sub grid cellOverrideMask.SetBit(profileCell.OTGCellX & SubGridTreeConsts.SubGridLocalKeyMask, profileCell.OTGCellY & SubGridTreeConsts.SubGridLocalKeyMask); } if (cellCounter > 0 && SubGrid != null) // Make sure we process last list { ProcessSubGroup(new SubGridCellAddress(CurrentSubgridOrigin.X << SubGridTreeConsts.SubGridIndexBitsPerLevel, CurrentSubgridOrigin.Y << SubGridTreeConsts.SubGridIndexBitsPerLevel), PDExistenceMap[CurrentSubgridOrigin.X, CurrentSubgridOrigin.Y], cellOverrideMask); } return(true); }
/// <summary> /// Executes the profiler /// </summary> public async Task <StationOffsetReportRequestResponse_ApplicationService> ExecuteAsync(StationOffsetReportRequestArgument_ApplicationService arg) { _log.LogInformation($"Start {nameof(ComputeStationOffsetReportExecutor_ApplicationService)}"); try { if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0) { // Prepare the filters for use in stationOffset operations. Failure to prepare any filter results in this request terminating if (!(arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK))) { return(new StationOffsetReportRequestResponse_ApplicationService { ResultStatus = RequestErrorStatus.FailedToPrepareFilter }); } } // keep alignment design knowledge here and pass points var argClusterCompute = new StationOffsetReportRequestArgument_ClusterCompute { ProjectID = arg.ProjectID, Filters = arg.Filters, ReferenceDesign = arg.ReferenceDesign, TRexNodeID = arg.TRexNodeID, ReportElevation = arg.ReportElevation, ReportCmv = arg.ReportCmv, ReportMdp = arg.ReportMdp, ReportPassCount = arg.ReportPassCount, ReportTemperature = arg.ReportTemperature, ReportCutFill = arg.ReportCutFill, Points = new List <StationOffsetPoint>(), Overrides = arg.Overrides, LiftParams = arg.LiftParams }; var siteModel = DIContext.ObtainRequired <ISiteModels>().GetSiteModel(arg.ProjectID); var designFiles = DIContext.ObtainRequired <IDesignFiles>(); // alignment will convert interval/offsets into northing/eastings for the project var alignmentDesign = designFiles.Lock(arg.AlignmentDesignUid, siteModel, siteModel.CellSize, out var loadResult) as SVLAlignmentDesign; if (alignmentDesign == null || loadResult != DesignLoadResult.Success) { return(new StationOffsetReportRequestResponse_ApplicationService { ReturnCode = ReportReturnCode.NoData, ResultStatus = RequestErrorStatus.NoDesignProvided }); } try { argClusterCompute.Points = alignmentDesign.GetOffsetPointsInNEE(arg.CrossSectionInterval, arg.StartStation, arg.EndStation, arg.Offsets, out var calcResult); _log.LogInformation($"{nameof(StationOffsetReportRequestResponse_ApplicationService)}: pointCount: {argClusterCompute.Points.Count}, calcResult: {calcResult}"); if (argClusterCompute.Points.Count == 0) { return(new StationOffsetReportRequestResponse_ApplicationService { ReturnCode = ReportReturnCode.NoData, ResultStatus = RequestErrorStatus.NoProductionDataFound }); } } finally { designFiles.UnLock(arg.AlignmentDesignUid, alignmentDesign); } var request = new StationOffsetReportRequest_ClusterCompute(); var clusterComputeResponse = await request.ExecuteAsync(argClusterCompute); // Return the core package to the caller var applicationResponse = new StationOffsetReportRequestResponse_ApplicationService { ReturnCode = clusterComputeResponse.ReturnCode, ResultStatus = clusterComputeResponse.ResultStatus }; applicationResponse.LoadStationOffsets(clusterComputeResponse.StationOffsetRows); _log.LogInformation($"End {nameof(ComputeStationOffsetReportExecutor_ApplicationService)}: ReturnCode {applicationResponse.ReturnCode}."); return(applicationResponse); } catch (Exception e) { _log.LogError(e, $"{nameof(StationOffsetReportRequestResponse_ApplicationService)}: Unexpected exception."); throw; } }
/// <summary> /// Executor that implements requesting and rendering sub grid information to create the rendered tile /// </summary> public async Task <SKBitmap> ExecuteAsync() { // WorkingColorPalette : TICDisplayPaletteBase; _log.LogInformation($"Performing Execute for DataModel:{DataModelID}, Mode={Mode}"); ApplicationServiceRequestStatistics.Instance.NumMapTileRequests.Increment(); /* * if Assigned(ASNodeImplInstance.RequestCancellations) and * ASNodeImplInstance.RequestCancellations.IsRequestCancelled(FExternalDescriptor) then * begin * if ...SvcLocations.Debug_LogDebugRequestCancellationToFile then * SIGLogMessage.PublishNoODS(Self, 'Request cancelled: ' + FExternalDescriptor.ToString, ...Debug); * * ResultStatus = ...RequestHasBeenCancelled; * InterlockedIncrement64(ASNodeRequestStats.NumMapTileRequestsCancelled); * Exit; * end; * * // The governor is intended to restrict the numbers of heavy weight processes * // such as pipelines that interact with the PC layer to request sub grids * ScheduledWithGovernor = ASNodeImplInstance.Governor.Schedule(FExternalDescriptor, Self, gqWMS, ResultStatus); * if not ScheduledWithGovernor then * Exit; */ var RequestDescriptor = Guid.NewGuid(); if (_log.IsDebugEnabled()) { if (CoordsAreGrid) { _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " + $"Bound[BL/TR:X/Y]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}"); } else { _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " + $"Bound[BL/TR:Lon/Lat]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}"); } // Include the details of the filters with the logged tile parameters if (Filters != null) { for (var i = 0; i < Filters.Filters.Length; i++) { _log.LogDebug($"Filter({i}): {Filters.Filters[i]}"); } } } // Determine the grid (NEE) coordinates of the bottom/left, top/right WGS-84 positions // given the project's coordinate system. If there is no coordinate system then exit. var SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelID); if (SiteModel == null) { _log.LogWarning($"Failed to locate site model {DataModelID}"); return(null); } _log.LogInformation($"Got Site model {DataModelID}, production data extents are {SiteModel.SiteModelExtent}"); LLHCoords = new[] { new XYZ(BLPoint.X, BLPoint.Y, 0), new XYZ(TRPoint.X, TRPoint.Y, 0), new XYZ(BLPoint.X, TRPoint.Y, 0), new XYZ(TRPoint.X, BLPoint.Y, 0) }; _log.LogInformation($"LLHCoords for tile request {string.Concat(LLHCoords)}, CoordsAreGrid {CoordsAreGrid}"); if (CoordsAreGrid) { NEECoords = LLHCoords; } else { NEECoords = DIContext .Obtain <ICoreXWrapper>() .LLHToNEE(SiteModel.CSIB(), LLHCoords.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians) .ToTRex_XYZ(); } _log.LogInformation($"After conversion NEECoords are {string.Concat(NEECoords)}"); WorldTileHeight = MathUtilities.Hypot(NEECoords[0].X - NEECoords[2].X, NEECoords[0].Y - NEECoords[2].Y); WorldTileWidth = MathUtilities.Hypot(NEECoords[0].X - NEECoords[3].X, NEECoords[0].Y - NEECoords[3].Y); var dx = NEECoords[2].X - NEECoords[0].X; var dy = NEECoords[2].Y - NEECoords[0].Y; // Calculate the tile rotation as the mathematical angle turned from 0 (due east) to the vector defined by dy/dx TileRotation = Math.Atan2(dy, dx); // Convert TileRotation to represent the angular deviation rather than a bearing TileRotation = (Math.PI / 2) - TileRotation; RotatedTileBoundingExtents.SetInverted(); NEECoords.ForEach(xyz => RotatedTileBoundingExtents.Include(xyz.X, xyz.Y)); _log.LogInformation($"Tile render executing across tile: [Rotation:{TileRotation}, {MathUtilities.RadiansToDegrees(TileRotation)} degrees] " + $" [BL:{NEECoords[0].X}, {NEECoords[0].Y}, TL:{NEECoords[2].X},{NEECoords[2].Y}, " + $"TR:{NEECoords[1].X}, {NEECoords[1].Y}, BR:{NEECoords[3].X}, {NEECoords[3].Y}] " + $"World Width, Height: {WorldTileWidth}, {WorldTileHeight}, Rotated bounding extents: {RotatedTileBoundingExtents}"); // Construct the renderer, configure it, and set it on its way // WorkingColorPalette = Nil; using (var Renderer = new PlanViewTileRenderer()) { try { // Intersect the site model extents with the extents requested by the caller var adjustedSiteModelExtents = SiteModel.GetAdjustedDataModelSpatialExtents(null); _log.LogInformation($"Calculating intersection of bounding box and site model {DataModelID}:{adjustedSiteModelExtents}"); var dataSelectionExtent = new BoundingWorldExtent3D(RotatedTileBoundingExtents); dataSelectionExtent.Intersect(adjustedSiteModelExtents); if (!dataSelectionExtent.IsValidPlanExtent) { ResultStatus = RequestErrorStatus.InvalidCoordinateRange; _log.LogInformation($"Site model extents {adjustedSiteModelExtents}, do not intersect RotatedTileBoundingExtents {RotatedTileBoundingExtents}"); using var mapView = new MapSurface(); mapView.SetBounds(NPixelsX, NPixelsY); var canvas = mapView.BitmapCanvas; mapView.BitmapCanvas = null; return(canvas); } // Compute the override cell boundary to be used when processing cells in the sub grids // selected as a part of this pipeline // Increase cell boundary by one cell to allow for cells on the boundary that cross the boundary SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MinX, dataSelectionExtent.MinY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset, out var CellExtents_MinX, out var CellExtents_MinY); SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MaxX, dataSelectionExtent.MaxY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset, out var CellExtents_MaxX, out var CellExtents_MaxY); var CellExtents = new BoundingIntegerExtent2D(CellExtents_MinX, CellExtents_MinY, CellExtents_MaxX, CellExtents_MaxY); CellExtents.Expand(1); var filterSet = FilterUtilities.ConstructFilters(Filters, VolumeType); // Construct PipelineProcessor using var processor = DIContext.Obtain <IPipelineProcessorFactory>().NewInstanceNoBuild <SubGridsRequestArgument>( RequestDescriptor, DataModelID, GridDataFromModeConverter.Convert(Mode), new SubGridsPipelinedResponseBase(), filterSet, CutFillDesign, DIContext.Obtain <Func <PipelineProcessorTaskStyle, ITRexTask> >()(PipelineProcessorTaskStyle.PVMRendering), DIContext.Obtain <Func <PipelineProcessorPipelineStyle, ISubGridPipelineBase> >()(PipelineProcessorPipelineStyle.DefaultProgressive), DIContext.Obtain <IRequestAnalyser>(), Utilities.DisplayModeRequireSurveyedSurfaceInformation(Mode) && Utilities.FilterRequireSurveyedSurfaceInformation(Filters), requestRequiresAccessToDesignFileExistenceMap: Utilities.RequestRequiresAccessToDesignFileExistenceMap(Mode, CutFillDesign), CellExtents, LiftParams ); if (filterSet.Filters.Length == 3) { var pipeline = processor.Pipeline as SubGridPipelineProgressive <SubGridsRequestArgument, SubGridRequestsResponse>; pipeline.SubGridsRequestComputeStyle = SubGridsRequestComputeStyle.SimpleVolumeThreeWayCoalescing; } // Set the PVM rendering rexTask parameters for progressive processing processor.Task.TRexNodeID = RequestingTRexNodeID; ((IPVMRenderingTask)processor.Task).TileRenderer = Renderer; // Set the spatial extents of the tile boundary rotated into the north reference frame of the cell coordinate system to act as // a final restriction of the spatial extent used to govern data requests processor.OverrideSpatialExtents.Assign(RotatedTileBoundingExtents); // Prepare the processor if (!processor.Build()) { _log.LogError($"Failed to build pipeline processor for request to model {SiteModel.ID}"); ResultStatus = RequestErrorStatus.FailedToConfigureInternalPipeline; return(null); } // Test to see if the tile can be satisfied with a representational render indicating where // data is but not what it is (this is useful when the zoom level is far enough away that we // cannot meaningfully render the data). If the size of s sub grid is smaller than // the size of a pixel in the requested tile then do this. Just check the X dimension // as the data display is isotropic. // TODO: Could this be done before creation of the pipeline processor? if (Utilities.SubGridShouldBeRenderedAsRepresentationalDueToScale(WorldTileWidth, WorldTileHeight, NPixelsX, NPixelsY, processor.OverallExistenceMap.CellSize)) { return(RenderTileAsRepresentationalDueToScale(processor.OverallExistenceMap)); // There is no need to do anything else } /* TODO - Create a scaled palette to use when rendering the data * // Create a scaled palette to use when rendering the data * if not CreateAndInitialiseWorkingColorPalette then * begin * SIGLogMessage.PublishNoODS(Self, Format('Failed to create and initialise working color palette for data: %s in datamodel %d', [TypInfo.GetEnumName(TypeInfo(TICDisplayMode), Ord(FMode)), FDataModelID]), ...Warning); * Exit; * end; */ // Renderer.WorkingPalette = WorkingColorPalette; Renderer.IsWhollyInTermsOfGridProjection = true; // Ensure the renderer knows we are using grid projection coordinates Renderer.SetBounds(RotatedTileBoundingExtents.CenterX - WorldTileWidth / 2, RotatedTileBoundingExtents.CenterY - WorldTileHeight / 2, WorldTileWidth, WorldTileHeight, NPixelsX, NPixelsY); Renderer.TileRotation = TileRotation; var performRenderStopWatch = Stopwatch.StartNew(); ResultStatus = Renderer.PerformRender(Mode, processor, ColorPalettes, Filters, LiftParams); _log.LogInformation($"Renderer.PerformRender completed in {performRenderStopWatch.Elapsed}"); if (processor.Response.ResultStatus == RequestErrorStatus.OK) { var canvas = Renderer.Displayer.MapView.BitmapCanvas; Renderer.Displayer.MapView.BitmapCanvas = null; return(canvas); } } catch (Exception e) { _log.LogError(e, "Exception occurred"); ResultStatus = RequestErrorStatus.Exception; } } return(null); }
/// <summary> /// Executes the simple volumes computation returning a SimpleVolumesResponse with the results /// </summary> public SimpleVolumesResponse Execute() { var volumesResult = new SimpleVolumesResponse(); var resultBoundingExtents = BoundingWorldExtent3D.Null(); var requestDescriptor = Guid.NewGuid(); // TODO ASNodeImplInstance.NextDescriptor; Log.LogInformation($"#In# Performing {nameof(ComputeSimpleVolumes_Coordinator)}.Execute for DataModel:{SiteModelID}"); try { try { ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequests.Increment(); // Prepare filters for use in the request var resultStatus = FilterUtilities.PrepareFiltersForUse(new[] { BaseFilter, TopFilter, AdditionalSpatialFilter }, SiteModelID); if (resultStatus != RequestErrorStatus.OK) { return(volumesResult); } // Obtain the site model context for the request siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(SiteModelID); if (siteModel == null) { return(volumesResult); } // Create and configure the aggregator that contains the business logic for the // underlying volume calculation Aggregator = new SimpleVolumesCalculationsAggregator { SiteModel = siteModel, LiftParams = LiftParams, CellSize = siteModel.CellSize, VolumeType = VolumeType, CutTolerance = CutTolerance, FillTolerance = FillTolerance }; // Create and configure the volumes calculation engine var computeVolumes = new VolumesCalculator { RequestDescriptor = requestDescriptor, SiteModel = siteModel, Aggregator = Aggregator, BaseFilter = BaseFilter, TopFilter = TopFilter, VolumeType = VolumeType, LiftParams = LiftParams }; InitialiseVolumesCalculator(computeVolumes); // Perform the volume computation if (computeVolumes.ComputeVolumeInformation()) { resultStatus = RequestErrorStatus.OK; } else if (computeVolumes.AbortedDueToTimeout) { resultStatus = RequestErrorStatus.AbortedDueToPipelineTimeout; } else { resultStatus = RequestErrorStatus.Unknown; } if (resultStatus != RequestErrorStatus.OK) { Log.LogInformation($"Summary volume result: Failure, error = {resultStatus}"); // Send the (empty) results back to the caller return(volumesResult); } // Instruct the Aggregator to perform any finalization logic before reading out the results Aggregator.Finalise(); Log.LogInformation($"#Result# Summary volume result: Cut={Aggregator.CutFillVolume.CutVolume:F3}, Fill={Aggregator.CutFillVolume.FillVolume:F3}, Area={Aggregator.CoverageArea:F3}"); if (!Aggregator.BoundingExtents.IsValidPlanExtent) { if (Aggregator.CoverageArea == 0 && Aggregator.CutFillVolume.CutVolume == 0 && Aggregator.CutFillVolume.FillVolume == 0) { resultStatus = RequestErrorStatus.NoProductionDataFound; } else { resultStatus = RequestErrorStatus.InvalidPlanExtents; } Log.LogInformation($"Summary volume invalid PlanExtents or no data found: {resultStatus}"); return(volumesResult); } // Fill in the result object to pass back to the caller volumesResult.Cut = Aggregator.CutFillVolume.CutVolume; volumesResult.Fill = Aggregator.CutFillVolume.FillVolume; volumesResult.TotalCoverageArea = Aggregator.CoverageArea; volumesResult.CutArea = Aggregator.CutArea; volumesResult.FillArea = Aggregator.FillArea; volumesResult.BoundingExtentGrid = Aggregator.BoundingExtents; volumesResult.BoundingExtentLLH = resultBoundingExtents; } finally { ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequestsCompleted.Increment(); if (volumesResult.ResponseCode != SubGridRequestsResponseResult.OK) { ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequestsFailed.Increment(); } } } catch (Exception e) { Log.LogError(e, $"Failed to compute the simple volumes. Site Model ID: {SiteModelID}"); } return(volumesResult); }