/// <summary> /// Unpacks elements of the request argument that are represented as byte arrays in the Ignite request /// </summary> protected void PrepareArgument() { var originatingIgniteNodeId = Ignite.GetCluster().GetLocalNode().Id; Log.LogInformation($"Preparing argument with OriginatingIgniteNodeId = {originatingIgniteNodeId}, TRexNodeId = {TRexNodeId}"); arg = new TSubGridsRequestArgument { ProjectID = SiteModelID, RequestID = RequestID, GridDataType = RequestedGridDataType, IncludeSurveyedSurfaceInformation = IncludeSurveyedSurfaceInformation, ProdDataMaskBytes = ProdDataMask.ToBytes(), SurveyedSurfaceOnlyMaskBytes = SurveyedSurfaceOnlyMask.ToBytes(), Filters = Filters, OriginatingIgniteNodeId = originatingIgniteNodeId, TRexNodeID = TRexNodeId, ReferenceDesign = ReferenceDesign, AreaControlSet = AreaControlSet, SubGridsRequestComputeStyle = SubGridsRequestComputeStyle }; CustomArgumentInitializer?.Invoke(arg); }
private void PrepareForExecution() { CheckArguments(); // Construct the argument to be supplied to the compute cluster PrepareArgument(); Log.LogInformation($"Prepared argument has TRexNodeId = {arg.TRexNodeID}"); Log.LogInformation($"Production Data mask in argument to renderer contains {ProdDataMask.CountBits()} sub grids"); Log.LogInformation($"Surveyed Surface mask in argument to renderer contains {SurveyedSurfaceOnlyMask.CountBits()} sub grids"); CreateSubGridListener(); }
/// <summary> /// Overrides the base Execute() semantics to add a listener available for aggregated processing of sub grids in the request engine. /// </summary> public override TSubGridRequestsResponse Execute() { CheckArguments(); // Construct the argument to be supplied to the compute cluster PrepareArgument(); Log.LogInformation($"Prepared argument has TRexNodeId = {arg.TRexNodeID}"); Log.LogInformation($"Production Data mask in argument to renderer contains {ProdDataMask.CountBits()} sub grids"); Log.LogInformation($"Surveyed Surface mask in argument to renderer contains {SurveyedSurfaceOnlyMask.CountBits()} sub grids"); TSubGridRequestsResponse taskResult = null; var sw = Stopwatch.StartNew(); try { // Construct the function to be used var func = new SubGridsRequestComputeFuncAggregative <TSubGridsRequestArgument, TSubGridRequestsResponse>(TRexTask); // Invoke it. // Note that this is NOT asking the grid to perform a remote invocation of the request as this aggregative // processing is already executing on the cluster node containing sub grids. taskResult = func.Invoke(arg); } finally { Log.LogInformation($"TaskResult {(taskResult == null ? "<NullResult>" : taskResult.ResponseCode.ToString())}: SubGridRequests.Execute() for DM:{TRexTask.PipeLine.DataModelID} from node {TRexTask.TRexNodeID} for data type {TRexTask.GridDataType} took {sw.ElapsedMilliseconds}ms"); } // Advise the pipeline of all the sub grids that were examined in the aggregated processing TRexTask.PipeLine.SubGridsProcessed(taskResult?.NumSubgridsExamined ?? 0); // Notify the pipeline that all processing has been completed for it TRexTask.PipeLine.PipelineCompleted = true; // Send the appropriate response to the caller return(taskResult); }
/// <summary> /// Performs scanning operations across sub grids, determining if they should be included in the request /// </summary> protected bool SubGridEvent(ISubGrid SubGrid) { // The given sub grid is a leaf sub grid containing a bit mask recording sub grid inclusion in the overall sub grid map // being iterated over. This map includes, production data only sub grids, surveyed surface data only sub grids and // sub grids that will have both types of data retrieved for them. The analyzer needs to separate out the two in terms // of the masks of sub grids that needs to be queried, one for production data (and optionally surveyed surface data) and // one for surveyed surface data only. // Get the matching sub grid from the production data only bit mask sub grid tree and use this sub grid to be able to separate // the two sets of sub grids var ProdDataSubGrid = Pipeline.ProdDataExistenceMap.LocateSubGridContaining(SubGrid.OriginX, SubGrid.OriginY) as SubGridTreeLeafBitmapSubGrid; byte ScanMinXb, ScanMinYb, ScanMaxXb, ScanMaxYb; var OTGCellSize = SubGrid.Owner.CellSize / SubGridTreeConsts.SubGridTreeDimension; var CastSubGrid = (SubGridTreeLeafBitmapSubGrid)SubGrid; if (ScanningFullWorldExtent) { ScanMinXb = 0; ScanMinYb = 0; ScanMaxXb = SubGridTreeConsts.SubGridTreeDimensionMinus1; ScanMaxYb = SubGridTreeConsts.SubGridTreeDimensionMinus1; } else { // Calculate the range of cells in this sub grid we need to scan and request. The steps below // calculate the on-the-ground cell indices of the world coordinate bounding extents, then // determine the sub grid indices of the cell within this sub grid that contains those // cells, then determines the sub grid extents in this sub grid to scan over // Remember, each on-the-ground element (bit) in the existence map represents an // entire on-the-ground sub grid (32x32 OTG cells) in the matching sub grid tree. // Expand the number of cells scanned to create the rendered tile by a single cell width // on all sides to ensure the boundaries of tiles are rendered right to the edge of the tile. SubGrid.Owner.CalculateIndexOfCellContainingPosition(WorldExtents.MinX - OTGCellSize, WorldExtents.MinY - OTGCellSize, out var ScanMinX, out var ScanMinY); SubGrid.Owner.CalculateIndexOfCellContainingPosition(WorldExtents.MaxX + OTGCellSize, WorldExtents.MaxY + OTGCellSize, out var ScanMaxX, out var ScanMaxY); ScanMinX = Math.Max(CastSubGrid.OriginX, ScanMinX); ScanMinY = Math.Max(CastSubGrid.OriginY, ScanMinY); ScanMaxX = Math.Min(ScanMaxX, CastSubGrid.OriginX + SubGridTreeConsts.SubGridTreeDimensionMinus1); ScanMaxY = Math.Min(ScanMaxY, CastSubGrid.OriginY + SubGridTreeConsts.SubGridTreeDimensionMinus1); SubGrid.GetSubGridCellIndex(ScanMinX, ScanMinY, out ScanMinXb, out ScanMinYb); SubGrid.GetSubGridCellIndex(ScanMaxX, ScanMaxY, out ScanMaxXb, out ScanMaxYb); } // Iterate over the sub range of cells (bits) in this sub grid and request the matching sub grids for (byte I = ScanMinXb; I <= ScanMaxXb; I++) { for (byte J = ScanMinYb; J <= ScanMaxYb; J++) { if (CastSubGrid.Bits.BitSet(I, J)) { TotalNumberOfCandidateSubGrids++; // If there is a design sub grid overlay map supplied to the renderer then // check to see if this sub grid is in the map, and if so then continue. If it is // not in the map then it does not need to be considered. Design sub grid overlay // indices contain a single bit for each on the ground sub grid (32x32 cells), // which means they are only 5 levels deep. This means the (OriginX + I, OriginY + J) // origin coordinates correctly identify the single bits that denote the sub grids. if (Pipeline.DesignSubGridOverlayMap != null) { if (!Pipeline.DesignSubGridOverlayMap.GetCell(SubGrid.OriginX + I, SubGrid.OriginY + J)) { continue; } } // If there is a spatial filter in play then determine if the sub grid about to be requested intersects the spatial filter extent var SubGridSatisfiesFilter = true; foreach (var filter in Pipeline.FilterSet.Filters) { if (filter != null) { var spatialFilter = filter.SpatialFilter; if (spatialFilter.IsSpatial && spatialFilter.Fence != null && spatialFilter.Fence.NumVertices > 0) { SubGridSatisfiesFilter = spatialFilter.Fence.IntersectsExtent( CastSubGrid.Owner.GetCellExtents(CastSubGrid.OriginX + I, CastSubGrid.OriginY + J)); } else { if (spatialFilter.IsPositional) { CastSubGrid.Owner.GetCellCenterPosition(CastSubGrid.OriginX + I, CastSubGrid.OriginY + J, out var centerX, out var centerY); SubGridSatisfiesFilter = MathUtilities.Hypot(spatialFilter.PositionX - centerX, spatialFilter.PositionY - centerY) < spatialFilter.PositionRadius + (Math.Sqrt(2) * CastSubGrid.Owner.CellSize) / 2; } } if (!SubGridSatisfiesFilter) { break; } } } if (SubGridSatisfiesFilter) { TotalNumberOfSubGridsAnalysed++; if (SubmitSinglePageOfRequests) { if ((TotalNumberOfSubGridsAnalysed - 1) / SinglePageRequestSize < SinglePageRequestNumber) { continue; } if ((TotalNumberOfSubGridsAnalysed - 1) / SinglePageRequestSize > SinglePageRequestNumber) { return(false); // Returning false halts scanning of sub grids } } // Add the leaf sub grid identified by the address below, along with the production data and surveyed surface // flags to the sub grid tree being used to aggregate all the sub grids that need to be queried for the request TotalNumberOfSubGridsToRequest++; // If a single page of sub grids is being requested determine if the sub grid in question is a // part of the page, and if the page has been filled yet. if (CountingRequestsOnly) { continue; } // Set the ProdDataMask for the production data if (ProdDataSubGrid?.Bits.BitSet(I, J) == true) { ProdDataMask.SetCell(CastSubGrid.OriginX + I, CastSubGrid.OriginY + J, true); } else { // Note: This is ONLY recording the sub grids that have surveyed surface data required, but not production data // as a delta to the production data requests SurveyedSurfaceOnlyMask.SetCell(CastSubGrid.OriginX + I, CastSubGrid.OriginY + J, true); } } } } } return(true); }