From 7ea39cafb162a408f4616c242ea4e0474b1cab18 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Fri, 14 Jun 2024 17:52:54 +0200 Subject: [PATCH 01/31] refactoring to use caching and use treeindex instead of numeric range for styling group creation - wip --- .../CacheProvider/AssetMappingCache.ts | 16 ++- .../Reveal3DResources/Reveal3DResources.tsx | 6 + .../RuleBasedOutputsSelector.tsx | 132 +++++++++++------- .../src/components/RuleBasedOutputs/utils.ts | 87 +++++------- .../useSearchMappedEquipmentAssetMappings.tsx | 1 - 5 files changed, 133 insertions(+), 109 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 49fdad14422..a1d06b30297 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -19,6 +19,7 @@ import { chunk, maxBy } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; import { modelRevisionNodesAssetsToKey, modelRevisionToKey } from './utils'; +import { delayMs } from '../RuleBasedOutputs/utils'; export type NodeAssetMappingResult = { node?: Node3D; mappings: AssetMapping[] }; @@ -81,7 +82,17 @@ export class AssetMappingCache { revisionId: RevisionId, assetIds: CogniteInternalId[] ): Promise> { - const assetMappings = await this.getAssetMappingsForAssetIds(modelId, revisionId, assetIds); + const listChunks = chunk(assetIds, 1000); + + const allAssetMappingsReturned = listChunks.map(async (itemChunk, index) => { + await delayMs(1000 * index); + const assetMappings = await this.getAssetMappingsForAssetIds(modelId, revisionId, itemChunk); + return assetMappings; + }); + + const allAssetMappings = await Promise.all(allAssetMappingsReturned); + const assetMappings = allAssetMappings.flat(); + const relevantAssetIds = new Set(assetIds); const relevantAssetMappings = assetMappings.filter((mapping) => @@ -131,7 +142,8 @@ export class AssetMappingCache { ): Promise { const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, ids); const idChunks = chunk(ids, 100); - const assetMappingsPromises = idChunks.map(async (idChunk) => { + const assetMappingsPromises = idChunks.map(async (idChunk, index) => { + await delayMs(200 * index); const filter = filterType === 'nodeIds' ? { nodeIds: idChunk } : { assetIds: idChunk }; return await this._sdk.assetMappings3D .filter(modelId, revisionId, { diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 155c3c74a25..5f60eaa9335 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -31,6 +31,8 @@ import { import { type ImageCollectionModelStyling } from '../Image360CollectionContainer/useApply360AnnotationStyling'; import { is360ImageAddOptions } from './typeGuards'; import { useRemoveNonReferencedModels } from './useRemoveNonReferencedModels'; +import { useAllMappedEquipmentAssetMappings } from '../../query/useSearchMappedEquipmentAssetMappings'; +import { useSDK } from '../RevealCanvas/SDKProvider'; export const Reveal3DResources = ({ resources, @@ -41,6 +43,8 @@ export const Reveal3DResources = ({ image360Settings }: Reveal3DResourcesProps): ReactElement => { const viewer = useReveal(); + const sdk = useSDK(); + const [reveal3DModels, setReveal3DModels] = useState([]); const numModelsLoaded = useRef(0); @@ -57,6 +61,8 @@ export const Reveal3DResources = ({ useRemoveNonReferencedModels(reveal3DModels, image360CollectionAddOptions, viewer); + useAllMappedEquipmentAssetMappings(viewer.models, sdk); + const cadModelOptions = useMemo( () => reveal3DModels.filter((model): model is CadModelOptions => model.type === 'cad'), [reveal3DModels] diff --git a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx index fd0b089f344..075296120fa 100644 --- a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx +++ b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx @@ -4,16 +4,17 @@ import { useEffect, type ReactElement, useState, useMemo } from 'react'; import { CogniteCadModel } from '@cognite/reveal'; -import { type ModelMappingsWithAssets, useAllMappedEquipmentAssetMappings } from '../..'; import { type RuleOutputSet, type AssetStylingGroupAndStyleIndex } from './types'; import { generateRuleBasedOutputs, traverseExpressionToGetTimeseries } from './utils'; import { use3dModels } from '../../hooks/use3dModels'; -import { EMPTY_ARRAY } from '../../utilities/constants'; -import { type Datapoints, type Asset } from '@cognite/sdk'; +import { type Datapoints, type Asset, type AssetMapping3D, type InternalId } from '@cognite/sdk'; import { isDefined } from '../../utilities/isDefined'; -import { type InfiniteData } from '@tanstack/react-query'; import { type AssetIdsAndTimeseries } from '../../utilities/types'; import { useAssetsAndTimeseriesLinkageDataQuery } from '../../query/useAssetsAndTimeseriesLinkageDataQuery'; +import { uniqBy } from 'lodash'; +import { useAssetMappedNodesForRevisions } from '../CacheProvider/AssetMappingCacheProvider'; +import { type CadModelOptions } from '../Reveal3DResources/types'; +import { useAssetsByIdsQuery } from '../../query/useAssetsByIdsQuery'; export type ColorOverlayProps = { ruleSet: RuleOutputSet | undefined; @@ -28,29 +29,61 @@ export function RuleBasedOutputsSelector({ const models = use3dModels(); - const [stylingGroups, setStylingsGroups] = useState(); + const [allContextualizedAssets, setAllContextualizedAssets] = useState(); - const { - data: assetMappings, - isFetching, - hasNextPage, - fetchNextPage - } = useAllMappedEquipmentAssetMappings(models); + const cadModels: CadModelOptions[] = models + .filter((model) => model instanceof CogniteCadModel) + .map((model) => { + return { type: 'cad', modelId: model.modelId, revisionId: model.revisionId }; + }); + + const { data: assetMappings } = useAssetMappedNodesForRevisions(cadModels); + + const assetIdsFromMapped = useMemo(() => { + const mappings = assetMappings?.map((item) => item.assetMappings).flat() ?? []; + const assetIds: InternalId[] = mappings.flatMap((item) => { + return { + id: item.assetId + }; + }); + const uniqueAssetIds = uniqBy(assetIds, 'id'); + return uniqueAssetIds; + }, [assetMappings]); + + const { data: mappedAssets, isFetched } = useAssetsByIdsQuery(assetIdsFromMapped); useEffect(() => { - if (!isFetching && (hasNextPage ?? false)) { - void fetchNextPage(); + if (isFetched) { + setAllContextualizedAssets(mappedAssets); } - }, [isFetching, hasNextPage, fetchNextPage]); + }, [mappedAssets, isFetched]); const contextualizedAssetNodes = useMemo(() => { - return ( - assetMappings?.pages - .flat() - .flatMap((item) => item.assets) - .map(convertAssetMetadataKeysToLowerCase) ?? [] - ); - }, [assetMappings]); + const metadataConvertedContextualizedAssetNodes = allContextualizedAssets + ?.filter(isDefined) + .map(convertAssetMetadataKeysToLowerCase); + + return metadataConvertedContextualizedAssetNodes ?? []; + }, [allContextualizedAssets]); + + const flatAssetsMappingsListPerModel = useMemo(() => { + const mappingsPerModel = new Map(); + models.forEach((model) => { + if (!(model instanceof CogniteCadModel)) { + return; + } + const flatAssetsMappingsList = + assetMappings + ?.filter((item) => item.model.modelId === model.modelId) + .map((item) => item.assetMappings) + .flat() + .filter(isDefined) ?? []; + + mappingsPerModel.set(model, flatAssetsMappingsList); + }); + + return mappingsPerModel; + }, [assetMappings, models]); const timeseriesExternalIds = useMemo(() => { const expressions = ruleSet?.rulesWithOutputs @@ -66,59 +99,56 @@ export function RuleBasedOutputsSelector({ }); useEffect(() => { - if (onRuleSetChanged !== undefined) onRuleSetChanged(stylingGroups); - }, [stylingGroups]); - - useEffect(() => { - if (assetMappings === undefined || models === undefined || isFetching) return; + if (assetMappings === undefined || models === undefined || !isFetched) return; if (timeseriesExternalIds.length > 0 && isLoadingAssetIdsAndTimeseriesData) return; - - setStylingsGroups(EMPTY_ARRAY); - if (ruleSet === undefined) return; - models.forEach(async (model) => { - if (!(model instanceof CogniteCadModel)) { - return; - } - setStylingsGroups( - await initializeRuleBasedOutputs({ - model, - assetMappings, - contextualizedAssetNodes, - ruleSet, - assetIdsAndTimeseries: assetIdsWithTimeseriesData?.assetIdsWithTimeseries ?? [], - timeseriesDatapoints: assetIdsWithTimeseriesData?.timeseriesDatapoints ?? [] + const ruleBasedInitilization = async (): Promise => { + const allStylings = await Promise.all( + models.map(async (model) => { + if (!(model instanceof CogniteCadModel)) { + return; + } + + const flatAssetsMappingsList = flatAssetsMappingsListPerModel.get(model) ?? []; + + if (flatAssetsMappingsList.length === 0) return []; + const stylings = await initializeRuleBasedOutputs({ + assetMappings: flatAssetsMappingsList, + contextualizedAssetNodes, + ruleSet, + assetIdsAndTimeseries: assetIdsWithTimeseriesData?.assetIdsWithTimeseries ?? [], + timeseriesDatapoints: assetIdsWithTimeseriesData?.timeseriesDatapoints ?? [] + }); + const filteredStylings = stylings.flat().filter(isDefined); + return filteredStylings; }) ); - }); - }, [isLoadingAssetIdsAndTimeseriesData, ruleSet]); + const filteredStylings = allStylings.flat().filter(isDefined).flat(); + if (onRuleSetChanged !== undefined) onRuleSetChanged(filteredStylings); + }; + void ruleBasedInitilization(); + }, [isLoadingAssetIdsAndTimeseriesData, ruleSet, assetMappings, allContextualizedAssets]); return <>; } async function initializeRuleBasedOutputs({ - model, assetMappings, contextualizedAssetNodes, ruleSet, assetIdsAndTimeseries, timeseriesDatapoints }: { - model: CogniteCadModel; - assetMappings: InfiniteData; + assetMappings: AssetMapping3D[]; contextualizedAssetNodes: Asset[]; ruleSet: RuleOutputSet; assetIdsAndTimeseries: AssetIdsAndTimeseries[]; timeseriesDatapoints: Datapoints[] | undefined; }): Promise { - const flatAssetsMappingsList = assetMappings.pages.flat().flatMap((item) => item.mappings); - const flatMappings = flatAssetsMappingsList.flatMap((node) => node.items); - const collectionStylings = await generateRuleBasedOutputs({ - model, contextualizedAssetNodes, - assetMappings: flatMappings, + assetMappings, ruleSet, assetIdsAndTimeseries, timeseriesDatapoints diff --git a/react-components/src/components/RuleBasedOutputs/utils.ts b/react-components/src/components/RuleBasedOutputs/utils.ts index 03f17090ff0..33286a51526 100644 --- a/react-components/src/components/RuleBasedOutputs/utils.ts +++ b/react-components/src/components/RuleBasedOutputs/utils.ts @@ -6,7 +6,6 @@ import { Color } from 'three'; import { type StringExpression, type ColorRuleOutput, - type NodeAndRange, type RuleOutput, type NumericExpression, type MetadataRuleTrigger, @@ -21,11 +20,7 @@ import { type TriggerTypeData, type TimeseriesAndDatapoints } from './types'; -import { - type CogniteCadModel, - TreeIndexNodeCollection, - type NodeAppearance -} from '@cognite/reveal'; +import { TreeIndexNodeCollection, type NodeAppearance } from '@cognite/reveal'; import { type AssetMapping3D, type Asset, type Datapoints } from '@cognite/sdk'; import { type AssetStylingGroup } from '../Reveal3DResources/types'; import { isDefined } from '../../utilities/isDefined'; @@ -270,14 +265,12 @@ function getExpressionTriggerTypes(expression: Expression): TriggerType[] { } export const generateRuleBasedOutputs = async ({ - model, contextualizedAssetNodes, assetMappings, ruleSet, assetIdsAndTimeseries, timeseriesDatapoints }: { - model: CogniteCadModel; contextualizedAssetNodes: Asset[]; assetMappings: AssetMapping3D[]; ruleSet: RuleOutputSet; @@ -307,7 +300,6 @@ export const generateRuleBasedOutputs = async ({ if (outputSelected === undefined) return; return await analyzeNodesAgainstExpression({ - model, contextualizedAssetNodes, assetIdsAndTimeseries, timeseriesDatapoints, @@ -339,7 +331,6 @@ const getRuleOutputFromTypeSelected = ( }; const analyzeNodesAgainstExpression = async ({ - model, contextualizedAssetNodes, assetIdsAndTimeseries, timeseriesDatapoints, @@ -347,7 +338,6 @@ const analyzeNodesAgainstExpression = async ({ expression, outputSelected }: { - model: CogniteCadModel; contextualizedAssetNodes: Asset[]; assetIdsAndTimeseries: AssetIdsAndTimeseries[]; timeseriesDatapoints: Datapoints[] | undefined; @@ -366,38 +356,38 @@ const analyzeNodesAgainstExpression = async ({ triggerData.push(metadataTriggerData); - const timeseriesDataForThisAsset = generateTimeseriesAndDatapointsFromTheAsset({ - contextualizedAssetNode, - assetIdsAndTimeseries, - timeseriesDatapoints - }); + if ( + timeseriesDatapoints !== undefined && + timeseriesDatapoints.length > 0 && + assetIdsAndTimeseries !== undefined && + assetIdsAndTimeseries.length > 0 + ) { + const timeseriesDataForThisAsset = generateTimeseriesAndDatapointsFromTheAsset({ + contextualizedAssetNode, + assetIdsAndTimeseries, + timeseriesDatapoints + }); - if (timeseriesDataForThisAsset.length > 0) { - const timeseriesTriggerData: TriggerTypeData = { - type: 'timeseries', - timeseries: { - timeseriesWithDatapoints: timeseriesDataForThisAsset, - linkedAssets: contextualizedAssetNode - } - }; + if (timeseriesDataForThisAsset.length > 0) { + const timeseriesTriggerData: TriggerTypeData = { + type: 'timeseries', + timeseries: { + timeseriesWithDatapoints: timeseriesDataForThisAsset, + linkedAssets: contextualizedAssetNode + } + }; - triggerData.push(timeseriesTriggerData); + triggerData.push(timeseriesTriggerData); + } } const finalGlobalOutputResult = traverseExpression(triggerData, [expression]); if (finalGlobalOutputResult[0] ?? false) { const nodesFromThisAsset = assetMappings.filter( - (mapping) => mapping.assetId === contextualizedAssetNode.id - ); - - // get the 3d nodes linked to the asset and with treeindex and subtreeRange - const nodesAndRange: NodeAndRange[] = await getThreeDNodesFromAsset( - nodesFromThisAsset, - model + (item) => item.assetId === contextualizedAssetNode.id ); - - return nodesAndRange; + return nodesFromThisAsset; } }) ); @@ -466,26 +456,8 @@ export const traverseExpressionToGetTimeseries = ( return timeseriesExternalIdResults; }; -const getThreeDNodesFromAsset = async ( - nodesFromThisAsset: AssetMapping3D[], - model: CogniteCadModel -): Promise => { - return await Promise.all( - nodesFromThisAsset.map(async (nodeFromAsset) => { - const subtreeRange = await model.getSubtreeTreeIndices(nodeFromAsset.treeIndex); - const node: NodeAndRange = { - nodeId: nodeFromAsset.nodeId, - treeIndex: nodeFromAsset.treeIndex, - subtreeRange, - assetId: nodeFromAsset.assetId - }; - return node; - }) - ); -}; - const applyNodeStyles = ( - treeNodes: NodeAndRange[], + treeNodes: AssetMapping3D[], outputSelected: ColorRuleOutput ): AssetStylingGroupAndStyleIndex => { const ruleOutputAndStyleIndex: RuleAndStyleIndex = { @@ -494,9 +466,11 @@ const applyNodeStyles = ( }; const nodeIndexSet = ruleOutputAndStyleIndex.styleIndex.getIndexSet(); - treeNodes.forEach((node) => { - nodeIndexSet.addRange(node.subtreeRange); + nodeIndexSet.clear(); + treeNodes?.forEach((node) => { + nodeIndexSet.add(node.treeIndex); }); + ruleOutputAndStyleIndex.styleIndex.updateSet(nodeIndexSet); const nodeAppearance: NodeAppearance = { color: new Color(outputSelected.fill) @@ -526,3 +500,6 @@ const convertExpressionStringMetadataKeyToLowerCase = (expression: Expression): expression.trigger.key = expression.trigger.key.toLowerCase(); }; + +export const delayMs = async (delayTimeMs: number) => + await new Promise((r) => setTimeout(r, delayTimeMs)); diff --git a/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx b/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx index 494a5fc7052..0c64d8ef551 100644 --- a/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx +++ b/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx @@ -36,7 +36,6 @@ export const useSearchMappedEquipmentAssetMappings = ( const sdk = useSDK(userSdk); const modelsKey = models.map((model) => [model.modelId, model.revisionId]); const { data: assetMappings, isFetched } = useAllMappedEquipmentAssetMappings(models, sdk); - return useQuery({ queryKey: ['reveal', 'react-components', 'search-mapped-asset-mappings', query, modelsKey], queryFn: async () => { From 246970fc6326eb81bc5c03094d8b2b91495a298f Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Thu, 20 Jun 2024 22:27:11 +0200 Subject: [PATCH 02/31] use the asset mappings per model cache to generate the cache per asset and implement an extra check per item when applying stylings - wip --- .../CacheProvider/AssetMappingCache.ts | 185 +++++++++++++++--- .../AssetMappingCacheProvider.tsx | 20 ++ .../src/components/CacheProvider/types.ts | 9 +- .../Reveal3DResources/Reveal3DResources.tsx | 11 +- .../RuleBasedOutputsSelector.tsx | 2 +- .../src/hooks/useCalculateModelsStyling.tsx | 22 ++- 6 files changed, 217 insertions(+), 32 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index a1d06b30297..6867f33fe18 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -13,13 +13,15 @@ import { type AssetId, type ModelId, type ModelRevisionKey, - type RevisionId + type RevisionId, + type ChunkInCacheTypes } from './types'; import { chunk, maxBy } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; import { modelRevisionNodesAssetsToKey, modelRevisionToKey } from './utils'; import { delayMs } from '../RuleBasedOutputs/utils'; +import { type ModelWithAssetMappings } from './AssetMappingCacheProvider'; export type NodeAssetMappingResult = { node?: Node3D; mappings: AssetMapping[] }; @@ -82,10 +84,16 @@ export class AssetMappingCache { revisionId: RevisionId, assetIds: CogniteInternalId[] ): Promise> { - const listChunks = chunk(assetIds, 1000); + const relevantAssetIds = new Set(assetIds); + + const amountOfChunks = 1; + const assetIdsList = Array.from(relevantAssetIds); + const chunkSize = Math.round(assetIdsList.length / amountOfChunks); + const listChunks = chunk(assetIdsList, chunkSize); - const allAssetMappingsReturned = listChunks.map(async (itemChunk, index) => { - await delayMs(1000 * index); + console.log('listChunks', listChunks.length, listChunks); + + const allAssetMappingsReturned = listChunks.map(async (itemChunk) => { const assetMappings = await this.getAssetMappingsForAssetIds(modelId, revisionId, itemChunk); return assetMappings; }); @@ -93,8 +101,6 @@ export class AssetMappingCache { const allAssetMappings = await Promise.all(allAssetMappingsReturned); const assetMappings = allAssetMappings.flat(); - const relevantAssetIds = new Set(assetIds); - const relevantAssetMappings = assetMappings.filter((mapping) => relevantAssetIds.has(mapping.assetId) ); @@ -120,6 +126,29 @@ export class AssetMappingCache { }, new Map()); } + public async generateAssetMappingsCachePerItemFromModelCache( + modelId: ModelId, + revisionId: RevisionId, + assetMappingsPerModel: ModelWithAssetMappings[] | undefined + ): Promise{ + assetMappingsPerModel?.forEach((modelMapping) => { + console.log(' assetMappings modelMapping ', modelMapping); + + modelMapping.assetMappings.forEach((item) => { + const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [item.assetId]); + const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); + if (currentAssetMappings !== undefined) { + this._nodeAssetIdsToAssetMappings.set( + key, + currentAssetMappings.then((value) => [...value, item]) + ); + } else { + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); + } + }); + }); + } + public async getAssetMappingsForModel( modelId: ModelId, revisionId: RevisionId @@ -134,27 +163,122 @@ export class AssetMappingCache { return await this.fetchAndCacheMappingsForModel(modelId, revisionId); } - private async fetchAndCacheMappingsForIds( + private async splitChunkInCache( + currentChunk: number[], modelId: ModelId, - revisionId: RevisionId, - ids: number[], - filterType: string + revisionId: RevisionId + ): Promise { + const chunkInCache: Array> = []; + const chunkNotCached: number[] = []; + + await Promise.all( + currentChunk.map(async (id) => { + const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); + const cachedResult = await this._nodeAssetIdsToAssetMappings.get(key); + if (cachedResult !== undefined) { + chunkInCache.push(...cachedResult); + } else { + chunkNotCached.push(id); + } + }) + ); + + return { chunkInCache, chunkNotInCache: chunkNotCached }; + } + + private async fetchAssetMappingsRequest( + currentChunk: number[], + filterType: string, + modelId: ModelId, + revisionId: RevisionId ): Promise { - const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, ids); - const idChunks = chunk(ids, 100); - const assetMappingsPromises = idChunks.map(async (idChunk, index) => { - await delayMs(200 * index); - const filter = filterType === 'nodeIds' ? { nodeIds: idChunk } : { assetIds: idChunk }; - return await this._sdk.assetMappings3D + let assetMapping3D: AssetMapping3D[] = []; + + const filter = + filterType === 'nodeIds' ? { nodeIds: currentChunk } : { assetIds: currentChunk }; + try { + assetMapping3D = await this._sdk.assetMappings3D .filter(modelId, revisionId, { limit: 1000, filter }) - .autoPagingToArray({ limit: Infinity }); + .autoPagingToArray({ limit: Infinity }) + .catch((error) => { + console.log(' chunk INNER error', error); + throw error; + }); + } catch (error) { + console.log(' chunk error', error); + await delayMs(1000); + return await this.fetchAssetMappingsRequest(currentChunk, filterType, modelId, revisionId); + } + + assetMapping3D.forEach((item) => { + const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, [ + item.assetId + ]); + const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); + if (currentAssetMappings !== undefined) { + this._nodeAssetIdsToAssetMappings.set( + key, + currentAssetMappings.then((value) => [...value, item]) + ); + return; + } + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); }); - const assetMappingsArrays = await Promise.all(assetMappingsPromises); - const assetMappings = assetMappingsArrays.flat(); - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(assetMappings)); + + console.log(' chunk success', assetMapping3D); + return assetMapping3D.filter(isValidAssetMapping); + } + + private async fetchMappingsInQueue( + index: number, + idChunks: number[][], + filterType: string, + modelId: ModelId, + revisionId: RevisionId, + assetMappingsList: Array> + ): Promise { + const assetMappings = await this.fetchAssetMappingsRequest( + idChunks[index], + filterType, + modelId, + revisionId + ); + + assetMappingsList = assetMappingsList.concat(assetMappings); + if (index >= idChunks.length - 1) { + return assetMappingsList; + } + + const nextIndex = index + 1; + return await this.fetchMappingsInQueue( + nextIndex, + idChunks, + filterType, + modelId, + revisionId, + assetMappingsList + ); + } + + private async fetchAndCacheMappingsForIds( + modelId: ModelId, + revisionId: RevisionId, + ids: number[], + filterType: string + ): Promise { + const idChunks = chunk(ids, 100); + const initialIndex = 0; + const assetMappings = await this.fetchMappingsInQueue( + initialIndex, + idChunks, + filterType, + modelId, + revisionId, + [] + ); return assetMappings; } @@ -178,13 +302,22 @@ export class AssetMappingCache { revisionId: RevisionId, assetIds: number[] ): Promise { - const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, assetIds); - const cachedResult = this._nodeAssetIdsToAssetMappings.get(key); + const { chunkNotInCache, chunkInCache } = await this.splitChunkInCache( + assetIds, + modelId, + revisionId + ); - if (cachedResult !== undefined) { - return await cachedResult; - } - return await this.fetchAndCacheMappingsForIds(modelId, revisionId, assetIds, 'assetIds'); + const notCachedAssetIds: number[] = chunkNotInCache; + + const assetMappings = await this.fetchAndCacheMappingsForIds( + modelId, + revisionId, + notCachedAssetIds, + 'assetIds' + ); + const allAssetMappings = chunkInCache.concat(assetMappings); + return allAssetMappings; } private async fetchAndCacheMappingsForModel( diff --git a/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx b/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx index 364d5621296..acf5faedf6b 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx +++ b/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx @@ -38,6 +38,26 @@ const useAssetMappingCache = (): AssetMappingCache => { return content.cache; }; +export const useGenerateAssetMappingCachePerItemFromModelCache = async ( + cadModelOptions: CadModelOptions[], + assetMappings: ModelWithAssetMappings[] | undefined +): Promise => { + const assetMappingCache = useAssetMappingCache(); + + cadModelOptions.forEach(async ({ modelId, revisionId }) => { + const assetMapping = assetMappings?.filter( + (item) => item.model.modelId === modelId && item.model.revisionId === revisionId + ); + if (assetMapping !== undefined && assetMapping.length > 0) { + await assetMappingCache.generateAssetMappingsCachePerItemFromModelCache( + modelId, + revisionId, + assetMapping + ); + } + }); +}; + export const useAssetMappedNodesForRevisions = ( cadModels: CadModelOptions[] ): UseQueryResult => { diff --git a/react-components/src/components/CacheProvider/types.ts b/react-components/src/components/CacheProvider/types.ts index c8cdece601c..107fe71ead1 100644 --- a/react-components/src/components/CacheProvider/types.ts +++ b/react-components/src/components/CacheProvider/types.ts @@ -6,7 +6,8 @@ import { type AnnotationModel, type AnnotationsBoundingVolume, type Node3D, - type AnnotationsCogniteAnnotationTypesImagesAssetLink + type AnnotationsCogniteAnnotationTypesImagesAssetLink, + AssetMapping3D } from '@cognite/sdk'; import { type DmsUniqueIdentifier, type Source, type EdgeItem } from '../../utilities/FdmSDK'; import { type InModel3dEdgeProperties } from '../../utilities/globalDataModels'; @@ -64,3 +65,9 @@ export type Image360AnnotationAssetInfo = { }; export type AnnotationId = number; + +export type ChunkInCacheTypes = { + chunkInCache: Array>; + chunkNotInCache: number[]; +}; + diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 5f60eaa9335..105e6eb65ec 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -2,7 +2,7 @@ * Copyright 2023 Cognite AS */ import { useRef, type ReactElement, useState, useEffect, useMemo } from 'react'; -import { type Cognite3DViewer } from '@cognite/reveal'; +import { CogniteCadModel, type Cognite3DViewer } from '@cognite/reveal'; import { CadModelContainer } from '../CadModelContainer/CadModelContainer'; import { type CadModelStyling } from '../CadModelContainer/useApplyCadModelStyling'; import { PointCloudContainer } from '../PointCloudContainer/PointCloudContainer'; @@ -33,6 +33,9 @@ import { is360ImageAddOptions } from './typeGuards'; import { useRemoveNonReferencedModels } from './useRemoveNonReferencedModels'; import { useAllMappedEquipmentAssetMappings } from '../../query/useSearchMappedEquipmentAssetMappings'; import { useSDK } from '../RevealCanvas/SDKProvider'; +import { useAssetMappedNodesForRevisions, useGenerateAssetMappingCachePerItemFromModelCache, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; +import { InternalId } from '@cognite/sdk'; +import { uniqBy } from 'lodash'; export const Reveal3DResources = ({ resources, @@ -61,13 +64,15 @@ export const Reveal3DResources = ({ useRemoveNonReferencedModels(reveal3DModels, image360CollectionAddOptions, viewer); - useAllMappedEquipmentAssetMappings(viewer.models, sdk); - const cadModelOptions = useMemo( () => reveal3DModels.filter((model): model is CadModelOptions => model.type === 'cad'), [reveal3DModels] ); + const { data: assetMappings } = useAssetMappedNodesForRevisions(cadModelOptions); + + void useGenerateAssetMappingCachePerItemFromModelCache(cadModelOptions, assetMappings); + const pointCloudModelOptions = useMemo( () => reveal3DModels.filter( diff --git a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx index 075296120fa..74a10ebef20 100644 --- a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx +++ b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx @@ -12,7 +12,7 @@ import { isDefined } from '../../utilities/isDefined'; import { type AssetIdsAndTimeseries } from '../../utilities/types'; import { useAssetsAndTimeseriesLinkageDataQuery } from '../../query/useAssetsAndTimeseriesLinkageDataQuery'; import { uniqBy } from 'lodash'; -import { useAssetMappedNodesForRevisions } from '../CacheProvider/AssetMappingCacheProvider'; +import { useAssetMappedNodesForRevisions, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; import { type CadModelOptions } from '../Reveal3DResources/types'; import { useAssetsByIdsQuery } from '../../query/useAssetsByIdsQuery'; diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 84441fa769b..3cc186ff0b6 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -9,7 +9,7 @@ import { } from '../components/Reveal3DResources/types'; import { NumericRange, type NodeAppearance, IndexSet } from '@cognite/reveal'; import { type ThreeDModelFdmMappings } from './types'; -import { type Node3D, type CogniteExternalId } from '@cognite/sdk'; +import { type Node3D, type CogniteExternalId, InternalId } from '@cognite/sdk'; import { useFdmAssetMappings, useMappedEdgesForRevisions @@ -32,6 +32,7 @@ import { } from '../components/CacheProvider/AssetMappingCacheProvider'; import { isSameModel } from '../utilities/isSameModel'; import { isAssetMappingStylingGroup, isFdmAssetStylingGroup } from '../utilities/StylingGroupUtils'; +import { uniqBy } from 'lodash'; type ModelStyleGroup = { model: CadModelOptions; @@ -78,6 +79,25 @@ function useCalculateMappedStyling( const { data: assetMappingData, isLoading: isAssetMappingLoading } = useAssetMappedNodesForRevisions(modelsRevisionsWithMappedEquipment); + const assetIdsFromMapped = useMemo(() => { + const mappings = assetMappingData?.map((item) => item.assetMappings).flat() ?? []; + const assetIds: InternalId[] = mappings.flatMap((item) => { + return { + id: item.assetId + }; + }); + const uniqueAssetIds = uniqBy(assetIds, 'id'); + return uniqueAssetIds; + }, [assetMappingData]); + + console.log(' chunk useCalculateMappedStyling ', assetIdsFromMapped.map((item) => item.id)); + + useNodesForAssets( + models, + assetIdsFromMapped.map((item) => item.id) + ); + + const modelsMappedFdmStyleGroups = useMemo(() => { const isFdmMappingUnavailableOrLoading = models.length === 0 || From 61d5aa4b4820876ca6ad9fed99b1829fa489ecd6 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Mon, 24 Jun 2024 11:28:21 +0200 Subject: [PATCH 03/31] add cache for node 3d when loading reveal 3d resources to speed up the rule base styling --- .../CacheProvider/AssetMappingCache.ts | 113 ++++++++++++++---- .../AssetMappingCacheProvider.tsx | 47 ++++++-- .../src/components/CacheProvider/types.ts | 4 + .../Reveal3DResources/Reveal3DResources.tsx | 3 +- 4 files changed, 136 insertions(+), 31 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 6867f33fe18..6bfd18e9073 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -14,7 +14,8 @@ import { type ModelId, type ModelRevisionKey, type RevisionId, - type ChunkInCacheTypes + type ChunkInCacheTypes, + type ChunkInCacheTypesNode3D } from './types'; import { chunk, maxBy } from 'lodash'; import assert from 'assert'; @@ -26,7 +27,6 @@ import { type ModelWithAssetMappings } from './AssetMappingCacheProvider'; export type NodeAssetMappingResult = { node?: Node3D; mappings: AssetMapping[] }; export type AssetMapping = Required; - export class AssetMappingCache { private readonly _sdk: CogniteClient; @@ -36,6 +36,8 @@ export class AssetMappingCache { Promise >(); + private readonly _nodeAssetIdsToNode3D = new Map>(); + constructor(sdk: CogniteClient) { this._sdk = sdk; } @@ -91,7 +93,7 @@ export class AssetMappingCache { const chunkSize = Math.round(assetIdsList.length / amountOfChunks); const listChunks = chunk(assetIdsList, chunkSize); - console.log('listChunks', listChunks.length, listChunks); + console.log('chunk listChunks', listChunks.length, listChunks); const allAssetMappingsReturned = listChunks.map(async (itemChunk) => { const assetMappings = await this.getAssetMappingsForAssetIds(modelId, revisionId, itemChunk); @@ -105,11 +107,10 @@ export class AssetMappingCache { relevantAssetIds.has(mapping.assetId) ); - const nodes = await fetchNodesForNodeIds( + const nodes = await this.getNodesForNodeIds( modelId, revisionId, - relevantAssetMappings.map((assetMapping) => assetMapping.nodeId), - this._sdk + relevantAssetMappings.map((assetMapping) => assetMapping.nodeId) ); return nodes.reduce((acc, node, index) => { @@ -126,11 +127,42 @@ export class AssetMappingCache { }, new Map()); } + public async getNodesForNodeIds( + modelId: ModelId, + revisionId: RevisionId, + nodeIds: number[] + ): Promise { + + const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheNode3D( + nodeIds, + modelId, + revisionId + ); + + console.log('chunk NODE IDS', chunkNotInCache, chunkInCache); + + const nodes = await fetchNodesForNodeIds(modelId, revisionId, chunkNotInCache, this._sdk); + const allNodes = chunkInCache.concat(nodes); + return allNodes; + } + + public async generateNode3DCachePerItem( + modelId: ModelId, + revisionId: RevisionId, + nodeIds: number[] | undefined + ): Promise { + const node3Ds = await this.getNodesForNodeIds(modelId, revisionId, nodeIds ?? []); + node3Ds.forEach((node) => { + const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [node.id]); + this._nodeAssetIdsToNode3D.set(key, Promise.resolve(node)); + }); + } + public async generateAssetMappingsCachePerItemFromModelCache( modelId: ModelId, revisionId: RevisionId, assetMappingsPerModel: ModelWithAssetMappings[] | undefined - ): Promise{ + ): Promise { assetMappingsPerModel?.forEach((modelMapping) => { console.log(' assetMappings modelMapping ', modelMapping); @@ -163,7 +195,7 @@ export class AssetMappingCache { return await this.fetchAndCacheMappingsForModel(modelId, revisionId); } - private async splitChunkInCache( + private async splitChunkInCacheAssetMappings( currentChunk: number[], modelId: ModelId, revisionId: RevisionId @@ -186,6 +218,29 @@ export class AssetMappingCache { return { chunkInCache, chunkNotInCache: chunkNotCached }; } + private async splitChunkInCacheNode3D( + currentChunk: number[], + modelId: ModelId, + revisionId: RevisionId + ): Promise { + const chunkInCache: Node3D[] = []; + const chunkNotCached: number[] = []; + + await Promise.all( + currentChunk.map(async (id) => { + const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); + const cachedResult = await this._nodeAssetIdsToNode3D.get(key); + if (cachedResult !== undefined) { + chunkInCache.push(cachedResult); + } else { + chunkNotCached.push(id); + } + }) + ); + + return { chunkInCache, chunkNotInCache: chunkNotCached }; + } + private async fetchAssetMappingsRequest( currentChunk: number[], filterType: string, @@ -213,16 +268,17 @@ export class AssetMappingCache { return await this.fetchAssetMappingsRequest(currentChunk, filterType, modelId, revisionId); } - assetMapping3D.forEach((item) => { + assetMapping3D.forEach(async (item) => { const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, [ item.assetId ]); - const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); + const currentAssetMappings = await this._nodeAssetIdsToAssetMappings.get(key); if (currentAssetMappings !== undefined) { - this._nodeAssetIdsToAssetMappings.set( - key, - currentAssetMappings.then((value) => [...value, item]) + const newNotDuplicatedMappings = currentAssetMappings.filter( + (mapping) => mapping.nodeId !== item.nodeId ); + newNotDuplicatedMappings.push(item); + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(newNotDuplicatedMappings)); return; } this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); @@ -269,6 +325,9 @@ export class AssetMappingCache { ids: number[], filterType: string ): Promise { + if (ids.length === 0) { + return []; + } const idChunks = chunk(ids, 100); const initialIndex = 0; const assetMappings = await this.fetchMappingsInQueue( @@ -288,13 +347,23 @@ export class AssetMappingCache { nodes: Node3D[] ): Promise { const nodeIds = nodes.map((node) => node.id); - const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, nodeIds); - const cachedResult = this._nodeAssetIdsToAssetMappings.get(key); - if (cachedResult !== undefined) { - return await cachedResult; - } - return await this.fetchAndCacheMappingsForIds(modelId, revisionId, nodeIds, 'nodeIds'); + const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheAssetMappings( + nodeIds, + modelId, + revisionId + ); + + const notCachedNodeIds: number[] = chunkNotInCache; + + const assetMappings = await this.fetchAndCacheMappingsForIds( + modelId, + revisionId, + notCachedNodeIds, + 'nodeIds' + ); + const allAssetMappings = chunkInCache.concat(assetMappings); + return allAssetMappings; } private async getAssetMappingsForAssetIds( @@ -302,12 +371,16 @@ export class AssetMappingCache { revisionId: RevisionId, assetIds: number[] ): Promise { - const { chunkNotInCache, chunkInCache } = await this.splitChunkInCache( + const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheAssetMappings( assetIds, modelId, revisionId ); + console.log('chunk assetIds', assetIds); + console.log('chunk chunkInCache', chunkInCache); + console.log('chunk chunkNotInCache', chunkNotInCache); + const notCachedAssetIds: number[] = chunkNotInCache; const assetMappings = await this.fetchAndCacheMappingsForIds( diff --git a/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx b/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx index acf5faedf6b..f85bffe665b 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx +++ b/react-components/src/components/CacheProvider/AssetMappingCacheProvider.tsx @@ -38,24 +38,51 @@ const useAssetMappingCache = (): AssetMappingCache => { return content.cache; }; -export const useGenerateAssetMappingCachePerItemFromModelCache = async ( +export const useGenerateNode3DCache = async ( cadModelOptions: CadModelOptions[], assetMappings: ModelWithAssetMappings[] | undefined ): Promise => { const assetMappingCache = useAssetMappingCache(); - cadModelOptions.forEach(async ({ modelId, revisionId }) => { - const assetMapping = assetMappings?.filter( - (item) => item.model.modelId === modelId && item.model.revisionId === revisionId - ); - if (assetMapping !== undefined && assetMapping.length > 0) { - await assetMappingCache.generateAssetMappingsCachePerItemFromModelCache( + useMemo(() => { + cadModelOptions.forEach(async ({ modelId, revisionId }) => { + const assetMapping = assetMappings?.filter( + (item) => item.model.modelId === modelId && item.model.revisionId === revisionId + ); + const nodeIdsFromAssetMappings = assetMapping?.flatMap((item) => + item.assetMappings.map((mapping) => mapping.nodeId) + ); + + if (nodeIdsFromAssetMappings === undefined || nodeIdsFromAssetMappings.length === 0) return; + + await assetMappingCache.generateNode3DCachePerItem( modelId, revisionId, - assetMapping + nodeIdsFromAssetMappings ); - } - }); + }); + }, [cadModelOptions, assetMappings]); +}; + +export const useGenerateAssetMappingCachePerItemFromModelCache = async ( + cadModelOptions: CadModelOptions[], + assetMappings: ModelWithAssetMappings[] | undefined +): Promise => { + const assetMappingCache = useAssetMappingCache(); + useMemo(() => { + cadModelOptions.forEach(async ({ modelId, revisionId }) => { + const assetMapping = assetMappings?.filter( + (item) => item.model.modelId === modelId && item.model.revisionId === revisionId + ); + if (assetMapping !== undefined && assetMapping.length > 0) { + await assetMappingCache.generateAssetMappingsCachePerItemFromModelCache( + modelId, + revisionId, + assetMapping + ); + } + }); + }, [cadModelOptions, assetMappings]); }; export const useAssetMappedNodesForRevisions = ( diff --git a/react-components/src/components/CacheProvider/types.ts b/react-components/src/components/CacheProvider/types.ts index 107fe71ead1..13b9980b53a 100644 --- a/react-components/src/components/CacheProvider/types.ts +++ b/react-components/src/components/CacheProvider/types.ts @@ -71,3 +71,7 @@ export type ChunkInCacheTypes = { chunkNotInCache: number[]; }; +export type ChunkInCacheTypesNode3D = { + chunkInCache: Node3D[]; + chunkNotInCache: number[]; +}; diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 105e6eb65ec..5b8c298f6f1 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -33,7 +33,7 @@ import { is360ImageAddOptions } from './typeGuards'; import { useRemoveNonReferencedModels } from './useRemoveNonReferencedModels'; import { useAllMappedEquipmentAssetMappings } from '../../query/useSearchMappedEquipmentAssetMappings'; import { useSDK } from '../RevealCanvas/SDKProvider'; -import { useAssetMappedNodesForRevisions, useGenerateAssetMappingCachePerItemFromModelCache, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; +import { useAssetMappedNodesForRevisions, useGenerateAssetMappingCachePerItemFromModelCache, useGenerateNode3DCache, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; import { InternalId } from '@cognite/sdk'; import { uniqBy } from 'lodash'; @@ -72,6 +72,7 @@ export const Reveal3DResources = ({ const { data: assetMappings } = useAssetMappedNodesForRevisions(cadModelOptions); void useGenerateAssetMappingCachePerItemFromModelCache(cadModelOptions, assetMappings); + void useGenerateNode3DCache(cadModelOptions, assetMappings); const pointCloudModelOptions = useMemo( () => From 71a3dd795d8863aa60aabe69fb25c07dde090ebb Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Wed, 26 Jun 2024 00:28:46 +0200 Subject: [PATCH 04/31] some wip refactoring on cache functions --- .../CacheProvider/AssetMappingCache.ts | 127 ++++++++++++------ .../Reveal3DResources/Reveal3DResources.tsx | 15 ++- .../src/hooks/useCalculateModelsStyling.tsx | 2 +- 3 files changed, 94 insertions(+), 50 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 6bfd18e9073..63fd74bf508 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -17,7 +17,7 @@ import { type ChunkInCacheTypes, type ChunkInCacheTypesNode3D } from './types'; -import { chunk, maxBy } from 'lodash'; +import { chunk, maxBy, uniqBy } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; import { modelRevisionNodesAssetsToKey, modelRevisionToKey } from './utils'; @@ -132,7 +132,6 @@ export class AssetMappingCache { revisionId: RevisionId, nodeIds: number[] ): Promise { - const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheNode3D( nodeIds, modelId, @@ -163,22 +162,28 @@ export class AssetMappingCache { revisionId: RevisionId, assetMappingsPerModel: ModelWithAssetMappings[] | undefined ): Promise { - assetMappingsPerModel?.forEach((modelMapping) => { + if (assetMappingsPerModel === undefined) { + return; + } + assetMappingsPerModel.forEach(async (modelMapping) => { console.log(' assetMappings modelMapping ', modelMapping); - modelMapping.assetMappings.forEach((item) => { + modelMapping.assetMappings.forEach(async (item) => { const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [item.assetId]); - const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); - if (currentAssetMappings !== undefined) { - this._nodeAssetIdsToAssetMappings.set( - key, - currentAssetMappings.then((value) => [...value, item]) - ); - } else { - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); - } + /* const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); + if (currentAssetMappings !== undefined) { + this._nodeAssetIdsToAssetMappings.set( + key, + currentAssetMappings.then((value) => [...value, item]) + ); + } else { + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); + } */ + + await this.setAssetMappingsCacheItem(key, item); }); }); + console.log(' assetMappings cache', this._nodeAssetIdsToAssetMappings); } public async getAssetMappingsForModel( @@ -206,7 +211,7 @@ export class AssetMappingCache { await Promise.all( currentChunk.map(async (id) => { const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); - const cachedResult = await this._nodeAssetIdsToAssetMappings.get(key); + const cachedResult = await this.getNodeAssetIdsToAssetMappingCacheItem(key); if (cachedResult !== undefined) { chunkInCache.push(...cachedResult); } else { @@ -249,45 +254,77 @@ export class AssetMappingCache { ): Promise { let assetMapping3D: AssetMapping3D[] = []; + if (currentChunk.length === 0) { + return []; + } const filter = filterType === 'nodeIds' ? { nodeIds: currentChunk } : { assetIds: currentChunk }; - try { - assetMapping3D = await this._sdk.assetMappings3D - .filter(modelId, revisionId, { - limit: 1000, - filter - }) - .autoPagingToArray({ limit: Infinity }) - .catch((error) => { - console.log(' chunk INNER error', error); - throw error; - }); - } catch (error) { - console.log(' chunk error', error); - await delayMs(1000); - return await this.fetchAssetMappingsRequest(currentChunk, filterType, modelId, revisionId); - } + + assetMapping3D = await this._sdk.assetMappings3D + .filter(modelId, revisionId, { + limit: 1000, + filter + }) + .autoPagingToArray({ limit: Infinity }); assetMapping3D.forEach(async (item) => { const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, [ item.assetId ]); - const currentAssetMappings = await this._nodeAssetIdsToAssetMappings.get(key); - if (currentAssetMappings !== undefined) { - const newNotDuplicatedMappings = currentAssetMappings.filter( - (mapping) => mapping.nodeId !== item.nodeId - ); - newNotDuplicatedMappings.push(item); - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(newNotDuplicatedMappings)); - return; - } - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); - }); + /* const currentAssetMappings = await this._nodeAssetIdsToAssetMappings.get(key); + if (currentAssetMappings !== undefined) { + const newNotDuplicatedMappings = currentAssetMappings.filter( + (mapping) => mapping.nodeId !== item.nodeId + ); + newNotDuplicatedMappings.push(item); + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(newNotDuplicatedMappings)); + return; + } + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); */ - console.log(' chunk success', assetMapping3D); + await this.setAssetMappingsCacheItem(key, item); + }); return assetMapping3D.filter(isValidAssetMapping); } + private async setAssetMappingsCacheItem(key: ModelNodeIdKey, item: AssetMapping): Promise { + /* const currentAssetMappings = await this.getNodeAssetIdsToAssetMappingCacheItem(key); + if (currentAssetMappings !== undefined) { + const newNotDuplicatedMappings = uniqBy(currentAssetMappings, 'nodeId'); + newNotDuplicatedMappings.push(item); + this.setNodeAssetIdsToAssetMappingCacheItem(key, newNotDuplicatedMappings); + } else { + this.setNodeAssetIdsToAssetMappingCacheItem(key, [item]); + } */ + + const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); + if (currentAssetMappings !== undefined) { + this._nodeAssetIdsToAssetMappings.set( + key, + currentAssetMappings.then((value) => { + const newNotDuplicatedMappings = uniqBy(value, 'nodeId'); + newNotDuplicatedMappings.push(item); + return newNotDuplicatedMappings; + }) + ); + } else { + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); + } + } + + private setNodeAssetIdsToAssetMappingCacheItem( + key: ModelNodeIdKey, + item: Array> + ): void { + this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(item)); + } + + private async getNodeAssetIdsToAssetMappingCacheItem( + key: ModelNodeIdKey + ): Promise { + return await this._nodeAssetIdsToAssetMappings.get(key); + } + private async fetchMappingsInQueue( index: number, idChunks: number[][], @@ -362,6 +399,12 @@ export class AssetMappingCache { notCachedNodeIds, 'nodeIds' ); + console.log( + 'chunk getAssetMappingsForNodes chunkNotInCache, chunkInCache ', + chunkNotInCache, + chunkInCache + ); + const allAssetMappings = chunkInCache.concat(assetMappings); return allAssetMappings; } diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 5b8c298f6f1..641cd4609c5 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -2,7 +2,7 @@ * Copyright 2023 Cognite AS */ import { useRef, type ReactElement, useState, useEffect, useMemo } from 'react'; -import { CogniteCadModel, type Cognite3DViewer } from '@cognite/reveal'; +import { type Cognite3DViewer } from '@cognite/reveal'; import { CadModelContainer } from '../CadModelContainer/CadModelContainer'; import { type CadModelStyling } from '../CadModelContainer/useApplyCadModelStyling'; import { PointCloudContainer } from '../PointCloudContainer/PointCloudContainer'; @@ -31,11 +31,11 @@ import { import { type ImageCollectionModelStyling } from '../Image360CollectionContainer/useApply360AnnotationStyling'; import { is360ImageAddOptions } from './typeGuards'; import { useRemoveNonReferencedModels } from './useRemoveNonReferencedModels'; -import { useAllMappedEquipmentAssetMappings } from '../../query/useSearchMappedEquipmentAssetMappings'; -import { useSDK } from '../RevealCanvas/SDKProvider'; -import { useAssetMappedNodesForRevisions, useGenerateAssetMappingCachePerItemFromModelCache, useGenerateNode3DCache, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; -import { InternalId } from '@cognite/sdk'; -import { uniqBy } from 'lodash'; +import { + useAssetMappedNodesForRevisions, + useGenerateAssetMappingCachePerItemFromModelCache, + useGenerateNode3DCache +} from '../CacheProvider/AssetMappingCacheProvider'; export const Reveal3DResources = ({ resources, @@ -46,7 +46,6 @@ export const Reveal3DResources = ({ image360Settings }: Reveal3DResourcesProps): ReactElement => { const viewer = useReveal(); - const sdk = useSDK(); const [reveal3DModels, setReveal3DModels] = useState([]); @@ -82,6 +81,8 @@ export const Reveal3DResources = ({ [reveal3DModels] ); + console.log('Reveal3DResources instanceStyling', instanceStyling); + const styledCadModelOptions = useCalculateCadStyling( cadModelOptions, instanceStyling?.filter(isCadAssetMappingStylingGroup) ?? EMPTY_ARRAY, diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 3cc186ff0b6..8d041da38aa 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -79,7 +79,7 @@ function useCalculateMappedStyling( const { data: assetMappingData, isLoading: isAssetMappingLoading } = useAssetMappedNodesForRevisions(modelsRevisionsWithMappedEquipment); - const assetIdsFromMapped = useMemo(() => { + const assetIdsFromMapped = useMemo(() => { const mappings = assetMappingData?.map((item) => item.assetMappings).flat() ?? []; const assetIds: InternalId[] = mappings.flatMap((item) => { return { From 4b848b343d895e29d2f0ae815119e0b392aa9e56 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Wed, 26 Jun 2024 00:51:34 +0200 Subject: [PATCH 05/31] minor refactoring --- .../src/components/CacheProvider/AssetMappingCache.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 63fd74bf508..0bc4059f118 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -299,7 +299,7 @@ export class AssetMappingCache { const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); if (currentAssetMappings !== undefined) { - this._nodeAssetIdsToAssetMappings.set( + this.setNodeAssetIdsToAssetMappingCacheItem( key, currentAssetMappings.then((value) => { const newNotDuplicatedMappings = uniqBy(value, 'nodeId'); @@ -308,13 +308,13 @@ export class AssetMappingCache { }) ); } else { - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); + this.setNodeAssetIdsToAssetMappingCacheItem(key, Promise.resolve([item])); } } private setNodeAssetIdsToAssetMappingCacheItem( key: ModelNodeIdKey, - item: Array> + item: Promise>> ): void { this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(item)); } From 8eb42fac4a44be57e9ca0a13c8d0083b69e54fda Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Wed, 26 Jun 2024 14:38:10 +0200 Subject: [PATCH 06/31] refactoring and adding cache for assets for no mappings to skip requesting again --- .../CacheProvider/AssetMappingCache.ts | 51 ++++++++++--------- .../src/hooks/useCalculateModelsStyling.tsx | 22 +------- 2 files changed, 29 insertions(+), 44 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 0bc4059f118..c052c2a3097 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -21,7 +21,6 @@ import { chunk, maxBy, uniqBy } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; import { modelRevisionNodesAssetsToKey, modelRevisionToKey } from './utils'; -import { delayMs } from '../RuleBasedOutputs/utils'; import { type ModelWithAssetMappings } from './AssetMappingCacheProvider'; export type NodeAssetMappingResult = { node?: Node3D; mappings: AssetMapping[] }; @@ -31,10 +30,7 @@ export class AssetMappingCache { private readonly _sdk: CogniteClient; private readonly _modelToAssetMappings = new Map>(); - private readonly _nodeAssetIdsToAssetMappings = new Map< - ModelNodeIdKey, - Promise - >(); + public readonly _nodeAssetIdsToAssetMappings = new Map>(); private readonly _nodeAssetIdsToNode3D = new Map>(); @@ -284,6 +280,16 @@ export class AssetMappingCache { await this.setAssetMappingsCacheItem(key, item); }); + + currentChunk.forEach(async (id) => { + const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); + const cachedResult = await this.getNodeAssetIdsToAssetMappingCacheItem(key); + if (cachedResult === undefined) { + this.setNodeAssetIdsToAssetMappingCacheItem(key, Promise.resolve([])); + } + console.log('chunk fetchAssetMappingsRequest key, cachedResult ', key, cachedResult); + }); + return assetMapping3D.filter(isValidAssetMapping); } @@ -297,29 +303,28 @@ export class AssetMappingCache { this.setNodeAssetIdsToAssetMappingCacheItem(key, [item]); } */ - const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); - if (currentAssetMappings !== undefined) { - this.setNodeAssetIdsToAssetMappingCacheItem( - key, - currentAssetMappings.then((value) => { - const newNotDuplicatedMappings = uniqBy(value, 'nodeId'); - newNotDuplicatedMappings.push(item); - return newNotDuplicatedMappings; - }) - ); - } else { - this.setNodeAssetIdsToAssetMappingCacheItem(key, Promise.resolve([item])); - } + const currentAssetMappings = this.getNodeAssetIdsToAssetMappingCacheItem(key); + this.setNodeAssetIdsToAssetMappingCacheItem( + key, + currentAssetMappings.then((value) => { + if (value === undefined) { + return [item]; + } + const newNotDuplicatedMappings = uniqBy(value, 'nodeId'); + newNotDuplicatedMappings.push(item); + return newNotDuplicatedMappings; + }) + ); } - private setNodeAssetIdsToAssetMappingCacheItem( + public setNodeAssetIdsToAssetMappingCacheItem( key: ModelNodeIdKey, item: Promise>> ): void { this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(item)); } - private async getNodeAssetIdsToAssetMappingCacheItem( + public async getNodeAssetIdsToAssetMappingCacheItem( key: ModelNodeIdKey ): Promise { return await this._nodeAssetIdsToAssetMappings.get(key); @@ -420,9 +425,9 @@ export class AssetMappingCache { revisionId ); - console.log('chunk assetIds', assetIds); - console.log('chunk chunkInCache', chunkInCache); - console.log('chunk chunkNotInCache', chunkNotInCache); + console.log('chunk assetIds modelId', modelId, assetIds); + console.log('chunk chunkInCache modelId ', modelId, chunkInCache); + console.log('chunk chunkNotInCache modelId ', modelId, chunkNotInCache); const notCachedAssetIds: number[] = chunkNotInCache; diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 8d041da38aa..84441fa769b 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -9,7 +9,7 @@ import { } from '../components/Reveal3DResources/types'; import { NumericRange, type NodeAppearance, IndexSet } from '@cognite/reveal'; import { type ThreeDModelFdmMappings } from './types'; -import { type Node3D, type CogniteExternalId, InternalId } from '@cognite/sdk'; +import { type Node3D, type CogniteExternalId } from '@cognite/sdk'; import { useFdmAssetMappings, useMappedEdgesForRevisions @@ -32,7 +32,6 @@ import { } from '../components/CacheProvider/AssetMappingCacheProvider'; import { isSameModel } from '../utilities/isSameModel'; import { isAssetMappingStylingGroup, isFdmAssetStylingGroup } from '../utilities/StylingGroupUtils'; -import { uniqBy } from 'lodash'; type ModelStyleGroup = { model: CadModelOptions; @@ -79,25 +78,6 @@ function useCalculateMappedStyling( const { data: assetMappingData, isLoading: isAssetMappingLoading } = useAssetMappedNodesForRevisions(modelsRevisionsWithMappedEquipment); - const assetIdsFromMapped = useMemo(() => { - const mappings = assetMappingData?.map((item) => item.assetMappings).flat() ?? []; - const assetIds: InternalId[] = mappings.flatMap((item) => { - return { - id: item.assetId - }; - }); - const uniqueAssetIds = uniqBy(assetIds, 'id'); - return uniqueAssetIds; - }, [assetMappingData]); - - console.log(' chunk useCalculateMappedStyling ', assetIdsFromMapped.map((item) => item.id)); - - useNodesForAssets( - models, - assetIdsFromMapped.map((item) => item.id) - ); - - const modelsMappedFdmStyleGroups = useMemo(() => { const isFdmMappingUnavailableOrLoading = models.length === 0 || From f46889c625a113485f5dbb5ef479c84f45ec27af Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Wed, 26 Jun 2024 14:39:07 +0200 Subject: [PATCH 07/31] cleanup --- .../CacheProvider/AssetMappingCache.ts | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index c052c2a3097..e3701f2f08a 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -166,16 +166,6 @@ export class AssetMappingCache { modelMapping.assetMappings.forEach(async (item) => { const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [item.assetId]); - /* const currentAssetMappings = this._nodeAssetIdsToAssetMappings.get(key); - if (currentAssetMappings !== undefined) { - this._nodeAssetIdsToAssetMappings.set( - key, - currentAssetMappings.then((value) => [...value, item]) - ); - } else { - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); - } */ - await this.setAssetMappingsCacheItem(key, item); }); }); @@ -267,17 +257,6 @@ export class AssetMappingCache { const key: ModelNodeIdKey = modelRevisionNodesAssetsToKey(modelId, revisionId, [ item.assetId ]); - /* const currentAssetMappings = await this._nodeAssetIdsToAssetMappings.get(key); - if (currentAssetMappings !== undefined) { - const newNotDuplicatedMappings = currentAssetMappings.filter( - (mapping) => mapping.nodeId !== item.nodeId - ); - newNotDuplicatedMappings.push(item); - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(newNotDuplicatedMappings)); - return; - } - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve([item])); */ - await this.setAssetMappingsCacheItem(key, item); }); @@ -294,15 +273,6 @@ export class AssetMappingCache { } private async setAssetMappingsCacheItem(key: ModelNodeIdKey, item: AssetMapping): Promise { - /* const currentAssetMappings = await this.getNodeAssetIdsToAssetMappingCacheItem(key); - if (currentAssetMappings !== undefined) { - const newNotDuplicatedMappings = uniqBy(currentAssetMappings, 'nodeId'); - newNotDuplicatedMappings.push(item); - this.setNodeAssetIdsToAssetMappingCacheItem(key, newNotDuplicatedMappings); - } else { - this.setNodeAssetIdsToAssetMappingCacheItem(key, [item]); - } */ - const currentAssetMappings = this.getNodeAssetIdsToAssetMappingCacheItem(key); this.setNodeAssetIdsToAssetMappingCacheItem( key, From 5232873e996c63c5c7fd9e8c0ef1906fb9738dc8 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Wed, 26 Jun 2024 17:21:44 +0200 Subject: [PATCH 08/31] separate caches for asset and node ids --- .../CacheProvider/AssetMappingCache.ts | 91 ++++++++++++++----- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index e3701f2f08a..7fdc9900839 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -17,7 +17,7 @@ import { type ChunkInCacheTypes, type ChunkInCacheTypesNode3D } from './types'; -import { chunk, maxBy, uniqBy } from 'lodash'; +import { chunk, maxBy } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; import { modelRevisionNodesAssetsToKey, modelRevisionToKey } from './utils'; @@ -30,7 +30,9 @@ export class AssetMappingCache { private readonly _sdk: CogniteClient; private readonly _modelToAssetMappings = new Map>(); - public readonly _nodeAssetIdsToAssetMappings = new Map>(); + private readonly _assetIdsToAssetMappings = new Map>(); + + private readonly _nodeIdsToAssetMappings = new Map>(); private readonly _nodeAssetIdsToNode3D = new Map>(); @@ -38,6 +40,8 @@ export class AssetMappingCache { this._sdk = sdk; } + // PUBLIC METHODS + public async getAssetMappingsForLowestAncestor( modelId: ModelId, revisionId: RevisionId, @@ -169,7 +173,7 @@ export class AssetMappingCache { await this.setAssetMappingsCacheItem(key, item); }); }); - console.log(' assetMappings cache', this._nodeAssetIdsToAssetMappings); + console.log(' assetMappings cache', this._assetIdsToAssetMappings); } public async getAssetMappingsForModel( @@ -186,10 +190,39 @@ export class AssetMappingCache { return await this.fetchAndCacheMappingsForModel(modelId, revisionId); } + public setAssetIdsToAssetMappingCacheItem( + key: ModelNodeIdKey, + item: Promise>> + ): void { + this._assetIdsToAssetMappings.set(key, Promise.resolve(item)); + } + + public async getAssetIdsToAssetMappingCacheItem( + key: ModelNodeIdKey + ): Promise { + return await this._assetIdsToAssetMappings.get(key); + } + + public setNodeIdsToAssetMappingCacheItem( + key: ModelNodeIdKey, + item: Promise>> + ): void { + this._nodeIdsToAssetMappings.set(key, Promise.resolve(item)); + } + + public async getNodeIdsToAssetMappingCacheItem( + key: ModelNodeIdKey + ): Promise { + return await this._nodeIdsToAssetMappings.get(key); + } + + // PRIVATE METHODS + private async splitChunkInCacheAssetMappings( currentChunk: number[], modelId: ModelId, - revisionId: RevisionId + revisionId: RevisionId, + type: string ): Promise { const chunkInCache: Array> = []; const chunkNotCached: number[] = []; @@ -197,7 +230,7 @@ export class AssetMappingCache { await Promise.all( currentChunk.map(async (id) => { const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); - const cachedResult = await this.getNodeAssetIdsToAssetMappingCacheItem(key); + const cachedResult = await this.getItemCacheResult(type, key); if (cachedResult !== undefined) { chunkInCache.push(...cachedResult); } else { @@ -262,43 +295,50 @@ export class AssetMappingCache { currentChunk.forEach(async (id) => { const key = modelRevisionNodesAssetsToKey(modelId, revisionId, [id]); - const cachedResult = await this.getNodeAssetIdsToAssetMappingCacheItem(key); + const cachedResult = this.getItemCacheResult(filterType, key); if (cachedResult === undefined) { - this.setNodeAssetIdsToAssetMappingCacheItem(key, Promise.resolve([])); + this.setItemCacheResult(filterType, key, cachedResult); } - console.log('chunk fetchAssetMappingsRequest key, cachedResult ', key, cachedResult); + // console.log('chunk fetchAssetMappingsRequest key, cachedResult ', key, cachedResult); }); return assetMapping3D.filter(isValidAssetMapping); } private async setAssetMappingsCacheItem(key: ModelNodeIdKey, item: AssetMapping): Promise { - const currentAssetMappings = this.getNodeAssetIdsToAssetMappingCacheItem(key); - this.setNodeAssetIdsToAssetMappingCacheItem( + const currentAssetMappings = this.getAssetIdsToAssetMappingCacheItem(key); + this.setAssetIdsToAssetMappingCacheItem( key, currentAssetMappings.then((value) => { if (value === undefined) { return [item]; } - const newNotDuplicatedMappings = uniqBy(value, 'nodeId'); - newNotDuplicatedMappings.push(item); - return newNotDuplicatedMappings; + value.push(item); + return value; }) ); } - public setNodeAssetIdsToAssetMappingCacheItem( + private async getItemCacheResult( + type: string, + key: ModelNodeIdKey + ): Promise { + return type === 'nodeIds' + ? await this.getNodeIdsToAssetMappingCacheItem(key) + : await this.getAssetIdsToAssetMappingCacheItem(key); + } + + private setItemCacheResult( + type: string, key: ModelNodeIdKey, - item: Promise>> + item: AssetMapping[] | undefined ): void { - this._nodeAssetIdsToAssetMappings.set(key, Promise.resolve(item)); + const value = Promise.resolve(item ?? []); + type === 'nodeIds' + ? this.setNodeIdsToAssetMappingCacheItem(key, value) + : this.setAssetIdsToAssetMappingCacheItem(key, value); } - public async getNodeAssetIdsToAssetMappingCacheItem( - key: ModelNodeIdKey - ): Promise { - return await this._nodeAssetIdsToAssetMappings.get(key); - } private async fetchMappingsInQueue( index: number, @@ -363,7 +403,8 @@ export class AssetMappingCache { const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheAssetMappings( nodeIds, modelId, - revisionId + revisionId, + 'nodeIds' ); const notCachedNodeIds: number[] = chunkNotInCache; @@ -377,7 +418,8 @@ export class AssetMappingCache { console.log( 'chunk getAssetMappingsForNodes chunkNotInCache, chunkInCache ', chunkNotInCache, - chunkInCache + chunkInCache, + nodeIds ); const allAssetMappings = chunkInCache.concat(assetMappings); @@ -392,7 +434,8 @@ export class AssetMappingCache { const { chunkNotInCache, chunkInCache } = await this.splitChunkInCacheAssetMappings( assetIds, modelId, - revisionId + revisionId, + 'assetIds' ); console.log('chunk assetIds modelId', modelId, assetIds); From f7baf3b5fc092e7c82503e763479be6b93779da7 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Thu, 27 Jun 2024 21:31:06 +0200 Subject: [PATCH 09/31] initial test for triggering callback to switch on off rule base styling loading --- .../CacheProvider/AssetMappingCache.ts | 69 +++++++++---------- .../Reveal3DResources/Reveal3DResources.tsx | 13 +++- .../src/components/Reveal3DResources/types.ts | 1 + .../RevealToolbar/RuleBasedOutputsButton.tsx | 17 ++++- .../RuleBasedOutputsSelector.tsx | 2 +- .../src/hooks/useCalculateModelsStyling.tsx | 30 ++++++-- 6 files changed, 86 insertions(+), 46 deletions(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingCache.ts b/react-components/src/components/CacheProvider/AssetMappingCache.ts index 7fdc9900839..b1c96be3631 100644 --- a/react-components/src/components/CacheProvider/AssetMappingCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingCache.ts @@ -265,6 +265,40 @@ export class AssetMappingCache { return { chunkInCache, chunkNotInCache: chunkNotCached }; } + private async setAssetMappingsCacheItem(key: ModelNodeIdKey, item: AssetMapping): Promise { + const currentAssetMappings = this.getAssetIdsToAssetMappingCacheItem(key); + this.setAssetIdsToAssetMappingCacheItem( + key, + currentAssetMappings.then((value) => { + if (value === undefined) { + return [item]; + } + value.push(item); + return value; + }) + ); + } + + private async getItemCacheResult( + type: string, + key: ModelNodeIdKey + ): Promise { + return type === 'nodeIds' + ? await this.getNodeIdsToAssetMappingCacheItem(key) + : await this.getAssetIdsToAssetMappingCacheItem(key); + } + + private setItemCacheResult( + type: string, + key: ModelNodeIdKey, + item: AssetMapping[] | undefined + ): void { + const value = Promise.resolve(item ?? []); + type === 'nodeIds' + ? this.setNodeIdsToAssetMappingCacheItem(key, value) + : this.setAssetIdsToAssetMappingCacheItem(key, value); + } + private async fetchAssetMappingsRequest( currentChunk: number[], filterType: string, @@ -305,41 +339,6 @@ export class AssetMappingCache { return assetMapping3D.filter(isValidAssetMapping); } - private async setAssetMappingsCacheItem(key: ModelNodeIdKey, item: AssetMapping): Promise { - const currentAssetMappings = this.getAssetIdsToAssetMappingCacheItem(key); - this.setAssetIdsToAssetMappingCacheItem( - key, - currentAssetMappings.then((value) => { - if (value === undefined) { - return [item]; - } - value.push(item); - return value; - }) - ); - } - - private async getItemCacheResult( - type: string, - key: ModelNodeIdKey - ): Promise { - return type === 'nodeIds' - ? await this.getNodeIdsToAssetMappingCacheItem(key) - : await this.getAssetIdsToAssetMappingCacheItem(key); - } - - private setItemCacheResult( - type: string, - key: ModelNodeIdKey, - item: AssetMapping[] | undefined - ): void { - const value = Promise.resolve(item ?? []); - type === 'nodeIds' - ? this.setNodeIdsToAssetMappingCacheItem(key, value) - : this.setAssetIdsToAssetMappingCacheItem(key, value); - } - - private async fetchMappingsInQueue( index: number, idChunks: number[][], diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 641cd4609c5..d7c321db5f1 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -43,6 +43,7 @@ export const Reveal3DResources = ({ instanceStyling, onResourcesAdded, onResourceLoadError, + onCallback, image360Settings }: Reveal3DResourcesProps): ReactElement => { const viewer = useReveal(); @@ -83,7 +84,7 @@ export const Reveal3DResources = ({ console.log('Reveal3DResources instanceStyling', instanceStyling); - const styledCadModelOptions = useCalculateCadStyling( + const { styledModels: styledCadModelOptions, modelMappingsIsFetched } = useCalculateCadStyling( cadModelOptions, instanceStyling?.filter(isCadAssetMappingStylingGroup) ?? EMPTY_ARRAY, defaultResourceStyling @@ -123,8 +124,18 @@ export const Reveal3DResources = ({ if (numModelsLoaded.current === expectedTotalLoadCount && onResourcesAdded !== undefined) { onResourcesAdded(); } + + console.log('Loaded models:', numModelsLoaded.current, 'Expected:', expectedTotalLoadCount); }; + useEffect(() => { + if (modelMappingsIsFetched && onCallback !== undefined) { + console.log('LOADING modelMappingsIsFetched', modelMappingsIsFetched); + onCallback(modelMappingsIsFetched); + } + console.log('LOADING modelMappingsIsFetched onCallback ', modelMappingsIsFetched, onCallback); + }, [modelMappingsIsFetched, onCallback]); + return ( <> {styledCadModelOptions.map(({ styleGroups, model }, index) => { diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index b65d0d12db9..93b2e1dbc25 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -108,4 +108,5 @@ export type CommonResourceContainerProps = { image360Settings?: CommonImage360Settings; onResourcesAdded?: () => void; onResourceLoadError?: (failedResource: AddResourceOptions, error: any) => void; + onCallback?: (data?: any) => void; }; diff --git a/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx b/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx index 97f39feb38d..476da574e94 100644 --- a/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx +++ b/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx @@ -22,10 +22,12 @@ import { RuleBasedSelectionItem } from '../RuleBasedOutputs/components/RuleBased type RuleBasedOutputsButtonProps = { onRuleSetStylingChanged?: (stylings: AssetStylingGroup[] | undefined) => void; onRuleSetSelectedChanged?: (ruleSet: RuleAndEnabled | undefined) => void; + callbackFunction?: (callback: (isLoaded: boolean) => void) => void; }; export const RuleBasedOutputsButton = ({ onRuleSetStylingChanged, - onRuleSetSelectedChanged + onRuleSetSelectedChanged, + callbackFunction }: RuleBasedOutputsButtonProps): ReactElement => { const [currentRuleSetEnabled, setCurrentRuleSetEnabled] = useState(); const [emptyRuleSelected, setEmptyRuleSelected] = useState(); @@ -72,14 +74,25 @@ export const RuleBasedOutputsButton = ({ if (onRuleSetStylingChanged !== undefined) onRuleSetStylingChanged(undefined); } - if (onRuleSetSelectedChanged !== undefined) onRuleSetSelectedChanged(selectedRule); + console.log(' LOADING SELECTED RULE', selectedRule); + /* if (onRuleSetSelectedChanged !== undefined) + onRuleSetSelectedChanged(selectedRule, (isLoaded) => { + console.log(' LOADING IS LOADED FINALY', isLoaded); + }); + */ + + if (callbackFunction !== undefined) callbackFunction(callbackLoaded); setEmptyRuleSelected(emptySelection); setCurrentRuleSetEnabled(selectedRule); }, [ruleInstances, onRuleSetStylingChanged, onRuleSetSelectedChanged] ); + const callbackLoaded = (isLoaded: boolean): void => { + console.log(' LOADING IS LOADED FINALY', isLoaded); + }; + const ruleSetStylingChanged = ( stylingGroups: AssetStylingGroupAndStyleIndex[] | undefined ): void => { diff --git a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx index 74a10ebef20..075296120fa 100644 --- a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx +++ b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx @@ -12,7 +12,7 @@ import { isDefined } from '../../utilities/isDefined'; import { type AssetIdsAndTimeseries } from '../../utilities/types'; import { useAssetsAndTimeseriesLinkageDataQuery } from '../../query/useAssetsAndTimeseriesLinkageDataQuery'; import { uniqBy } from 'lodash'; -import { useAssetMappedNodesForRevisions, useNodesForAssets } from '../CacheProvider/AssetMappingCacheProvider'; +import { useAssetMappedNodesForRevisions } from '../CacheProvider/AssetMappingCacheProvider'; import { type CadModelOptions } from '../Reveal3DResources/types'; import { useAssetsByIdsQuery } from '../../query/useAssetsByIdsQuery'; diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 84441fa769b..6c97f897475 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -38,6 +38,16 @@ type ModelStyleGroup = { styleGroup: Array; }; +type ModelStyleGroupWithMappingsFetched = { + combinedMappedStyleGroups: ModelStyleGroup[]; + modelMappingsIsFetched: boolean; +}; + +type StyledModelWithMappingsFetched = { + styledModels: StyledModel[]; + modelMappingsIsFetched: boolean; +}; + export type CadStyleGroup = NodeStylingGroup | TreeIndexStylingGroup; export type StyledModel = { @@ -49,19 +59,25 @@ export const useCalculateCadStyling = ( models: CadModelOptions[], instanceGroups: Array, defaultResourceStyling?: DefaultResourceStyling -): StyledModel[] => { +): StyledModelWithMappingsFetched => { const modelsMappedStyleGroups = useCalculateMappedStyling( models, defaultResourceStyling?.cad?.mapped ); - const modelInstanceStyleGroups = useCalculateInstanceStyling(models, instanceGroups); + const modelInstanceStyleGroupsAndMappingFetched = useCalculateInstanceStyling( + models, + instanceGroups + ); const joinedStyleGroups = useJoinStylingGroups( models, modelsMappedStyleGroups, - modelInstanceStyleGroups + modelInstanceStyleGroupsAndMappingFetched.combinedMappedStyleGroups ); - return joinedStyleGroups; + return { + styledModels: joinedStyleGroups, + modelMappingsIsFetched: modelInstanceStyleGroupsAndMappingFetched.modelMappingsIsFetched + }; }; function useCalculateMappedStyling( @@ -154,7 +170,7 @@ function useCalculateMappedStyling( function useCalculateInstanceStyling( models: CadModelOptions[], instanceGroups: Array -): ModelStyleGroup[] { +): ModelStyleGroupWithMappingsFetched { const { data: fdmAssetMappings } = useFdmAssetMappings( instanceGroups .filter(isFdmAssetStylingGroup) @@ -162,7 +178,7 @@ function useCalculateInstanceStyling( models ); - const { data: modelAssetMappings } = useNodesForAssets( + const { data: modelAssetMappings, isFetched } = useNodesForAssets( models, instanceGroups .filter(isAssetMappingStylingGroup) @@ -190,7 +206,7 @@ function useCalculateInstanceStyling( [fdmModelInstanceStyleGroups, assetMappingInstanceStyleGroups] ); - return combinedMappedStyleGroups; + return { combinedMappedStyleGroups, modelMappingsIsFetched: isFetched }; } function useAssetMappingInstanceStyleGroups( From 2c1857cc0c8c8007f02b13399c29f08ba5667027 Mon Sep 17 00:00:00 2001 From: Daniel Priori Date: Fri, 28 Jun 2024 16:45:42 +0200 Subject: [PATCH 10/31] add loading spinner and some ui changes --- .../RevealToolbar/RuleBasedOutputsButton.tsx | 28 +++++++++++-------- .../components/RuleBasedSelectionItem.tsx | 19 ++++++++++--- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx b/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx index 476da574e94..a6814552278 100644 --- a/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx +++ b/react-components/src/components/RevealToolbar/RuleBasedOutputsButton.tsx @@ -35,7 +35,8 @@ export const RuleBasedOutputsButton = ({ const { t } = useTranslation(); const models = use3dModels(); const cadModels = models.filter((model) => model.type === 'cad') as CadModelOptions[]; - const { isLoading } = useAssetMappedNodesForRevisions(cadModels); + const { isLoading: isAssetMappingsLoading } = useAssetMappedNodesForRevisions(cadModels); + const [isRuleLoading, setIsRuleLoading] = useState(false); const ruleInstancesResult = useFetchRuleInstances(); @@ -47,6 +48,9 @@ export const RuleBasedOutputsButton = ({ const onChange = useCallback( (data: string | undefined): void => { + + setIsRuleLoading(true); + ruleInstances?.forEach((item) => { if (item === undefined) return; item.isEnabled = false; @@ -74,15 +78,8 @@ export const RuleBasedOutputsButton = ({ if (onRuleSetStylingChanged !== undefined) onRuleSetStylingChanged(undefined); } - console.log(' LOADING SELECTED RULE', selectedRule); - - /* if (onRuleSetSelectedChanged !== undefined) - onRuleSetSelectedChanged(selectedRule, (isLoaded) => { - console.log(' LOADING IS LOADED FINALY', isLoaded); - }); - */ - if (callbackFunction !== undefined) callbackFunction(callbackLoaded); + setEmptyRuleSelected(emptySelection); setCurrentRuleSetEnabled(selectedRule); }, @@ -90,7 +87,7 @@ export const RuleBasedOutputsButton = ({ ); const callbackLoaded = (isLoaded: boolean): void => { - console.log(' LOADING IS LOADED FINALY', isLoaded); + setIsRuleLoading(!isLoaded); }; const ruleSetStylingChanged = ( @@ -112,7 +109,7 @@ export const RuleBasedOutputsButton = ({ appendTo={document.body}> {ruleInstances?.map((item) => ( ))} }> -