From 5687583355b340f2e77637057c735462658c4814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 15 Aug 2023 10:05:20 +0200 Subject: [PATCH 01/17] WIP: fetch all mappings through cache --- .../components/NodeCacheProvider/NodeCache.ts | 217 ++++++++++++++++ .../NodeCacheProvider/NodeCacheProvider.tsx | 48 ++++ .../NodeCacheProvider/RevisionNodeCache.ts | 240 ++++++++++++++++++ .../components/NodeCacheProvider/SomeCache.ts | 40 +++ .../NodeCacheProvider/SpaceRevisionCache.ts | 20 ++ .../RevealContainer/RevealContainer.tsx | 7 +- .../useMappedEquipmentBy3DModelsList.tsx | 17 +- 7 files changed, 572 insertions(+), 17 deletions(-) create mode 100644 react-components/src/components/NodeCacheProvider/NodeCache.ts create mode 100644 react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx create mode 100644 react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts create mode 100644 react-components/src/components/NodeCacheProvider/SomeCache.ts create mode 100644 react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts new file mode 100644 index 00000000000..b8b5ba9c89f --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -0,0 +1,217 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { type CogniteClient } from '@cognite/sdk/dist/src'; +import { EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; +import { RevisionNodeCache, nodeIdsToTreeIndexes } from './RevisionNodeCache'; +import { type CogniteCadModel } from '@cognite/reveal'; +import { InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; +import { ModelRevisionId, ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; + +import assert from 'assert'; + +export type RevisionKey = `${number}-${number}`; +export type FdmKey = `${string}-${string}`; + +export type ModelId = number; +export type RevisionId = number; +export type TreeIndex = number; +export type RevisionTreeIndex = `${ModelId}-${RevisionId}-${TreeIndex}`; +export type FdmId = DmsUniqueIdentifier; + +export class FdmNodeCache { + private readonly _revisionNodeCaches = new Map(); + + private readonly _cdfClient: CogniteClient; + private readonly _fdmClient: FdmSDK; + + private readonly _globalCdfToFdmMap = new Map< + RevisionTreeIndex, + { values: FdmKey[]; complete: boolean } + >(); + + public constructor(cdfClient: CogniteClient, fdmClient: FdmSDK) { + this._cdfClient = cdfClient; + this._fdmClient = fdmClient; + } + + public async getAllMappingExternalIds( + modelRevisionIds: Array<{ modelId: number; revisionId: number }> + ): Promise { + + const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); + const edges = await getEdgesForRevisions(revisionIds, this._fdmClient); + const groupToModels = await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); + + for (const [revisionKey, data] of groupToModels.entries()) { + const [modelId, revisionId] = revisionKeyToIds(revisionKey); + + data.forEach(edge => { + const treeIndexKey = createRevisionTreeIndex(modelId, revisionId, edge.treeIndex); + + insertIntoSetMap(treeIndexKey, createFdmKey(edge.startNode.space, edge.startNode.externalId), this._globalCdfToFdmMap); + }); + } + + return groupToModels; + } + + public async getClosestParentExternalId( + modelId: number, + revisionId: number, + treeIndex: number + ): Promise { + const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); + + return await revisionCache.getClosestParentExternalIds(treeIndex); + } + + private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionNodeCache { + const revisionKey = getRevisionKey(modelId, revisionId); + if (!this._revisionNodeCaches.has(revisionKey)) { + this._revisionNodeCaches.set( + revisionKey, + new RevisionNodeCache( + this._cdfClient, + this._fdmClient, + modelId, + revisionId, + this._globalCdfToFdmMap + ) + ); + } + + return this._revisionNodeCaches.get(revisionKey)!; + } +} + +async function getNodeIdsForAsset( + space: string, + externalId: string +): Promise> { + return []; +} + +function getRevisionKey(modelId: number, revisionId: number): RevisionKey { + return `${modelId}-${revisionId}`; +} + +function revisionKeyToIds(revisionKey: RevisionKey): [number, number] { + const components = revisionKey.split('-'); + return [Number(components[0]), Number(components[1])]; +} + +export function createRevisionTreeIndex( + modelId: number, + revisionId: number, + treeIndex: number +): RevisionTreeIndex { + return `${modelId}-${revisionId}-${treeIndex}`; +} + +export function createFdmKey(spaceId: string, externalId: string): FdmKey { + return `${spaceId}-${externalId}`; +} + +function treeIndexKeyToId(key: RevisionTreeIndex): { + modelId: number; + revisionId: number; + treeIndex: number; +} { + const parts = key.split('-'); + + return { modelId: Number(parts[0]), revisionId: Number(parts[1]), treeIndex: Number(parts[2]) }; +} + +export function fdmKeyToId(fdmKey: FdmKey): FdmId { + const parts = fdmKey.split('-'); + + return { space: parts[0], externalId: parts[1] }; +} + +export function insertIntoSetMap( + key: T, + value: U, + globalMap: Map +): void { + const prevVal = globalMap.get(key); + + if (prevVal === undefined) { + globalMap.set(key, { complete: false, values: [value] }); + return; + } + + prevVal.values.push(value); +} + +async function groupToModelRevision( + edges: Array>, + modelRevisionIds: Array<{ modelId: number; revisionId: number }>, + cdfClient: CogniteClient +): Promise & { treeIndex: TreeIndex }>>> { + const nodeIdsPerRevision = edges.reduce( + (revisionNodeIdMap, edge) => { + const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); + if (nodeIdsInRevision !== undefined) { + nodeIdsInRevision.push(edge.properties.revisionNodeId); + } else { + revisionNodeIdMap.set(edge.properties.revisionId, [edge.properties.revisionNodeId]); + } + + return revisionNodeIdMap; + }, + new Map()); + + type RevisionNodeId = `${RevisionId}-${number}`; + const revisionNodeIdToTreeIndexList = new Map(); + + const treeIndexesPromises = [...nodeIdsPerRevision.entries()].map(async ([revisionId, nodeIds]) => { + const modelId = modelRevisionIds.find(p => p.revisionId === revisionId)?.modelId; + assert(modelId !== undefined); + + const treeIndexes = await nodeIdsToTreeIndexes(modelId, revisionId, nodeIds, cdfClient); + nodeIds.forEach((e, ind) => { + const revisionNodeIdKey = `${revisionId}-${e}` as const; + revisionNodeIdToTreeIndexList.set(revisionNodeIdKey, treeIndexes[ind]); + }); + }); + + await Promise.all(treeIndexesPromises); + + return edges.reduce((map, edge) => { + const edgeRevisionId = edge.properties.revisionId; + const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); + if (modelRevisionId === undefined) return map; + const modelRevisionIdKey: ModelRevisionId = getRevisionKey(modelRevisionId.modelId, modelRevisionId.revisionId); + const edgesForModel = map.get(modelRevisionIdKey); + const revisionNodeIdKey = `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; + const value = { ...edge, treeIndex: revisionNodeIdToTreeIndexList.get(revisionNodeIdKey)! }; + if (edgesForModel === undefined) { + map.set(modelRevisionIdKey, [value]); + } else { + edgesForModel.push(value); + } + + return map; + }, new Map & { treeIndex: TreeIndex }>>()); +} + +async function getEdgesForRevisions( + revisionIds: number[], + fdmClient: FdmSDK +): Promise>> { + const versionedPropertiesKey = `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`; + const filter = { + in: { + property: [SYSTEM_SPACE_3D_SCHEMA, versionedPropertiesKey, 'revisionId'], + values: revisionIds + } + }; + const mappings = await fdmClient.filterAllInstances( + filter, + 'edge', + SYSTEM_3D_EDGE_SOURCE + ); + return mappings.edges; +} diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx new file mode 100644 index 00000000000..3443870c75f --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -0,0 +1,48 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { ReactElement, ReactNode, createContext, useContext, useMemo } from 'react'; +import { FdmNodeCache } from './NodeCache'; +import { ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; +import { UseQueryResult, useQuery } from '@tanstack/react-query'; +import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; + +export type FdmNodeCacheContent = { + cache: FdmNodeCache; +}; + +export const FdmNodeCacheContext = createContext(undefined); + +export const useGetAllExternalIds = ( + modelRevisionIds: Array<{ modelId: number; revisionId: number }>, + enabled: boolean): UseQueryResult => { + const content = useContext(FdmNodeCacheContext); + + if (content === undefined) { + throw Error('Must use useNodeCache inside a NodeCacheContext'); + } + + return useQuery( + [ + 'reveal', + 'react-components', + ...modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId.toString()).sort() + ], + async () => await content.cache.getAllMappingExternalIds(modelRevisionIds), + { staleTime: Infinity, enabled: enabled && modelRevisionIds.length > 0 } + ); +}; + +export function NodeCacheProvider({ children }: { children?: ReactNode }): ReactElement { + const fdmClient = useFdmSdk(); + const cdfClient = useSDK(); + + const fdmCache = useMemo(() => new FdmNodeCache(cdfClient, fdmClient) + , []); + + + return + {children} + +} diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts new file mode 100644 index 00000000000..7ce8868925d --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -0,0 +1,240 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { type CogniteClient, type Node3D } from '@cognite/sdk'; +import { type EdgeItem, type FdmSDK } from '../../utilities/FdmSDK'; +import { + type FdmId, + type FdmKey, + type RevisionTreeIndex, + createFdmKey, + createRevisionTreeIndex, + fdmKeyToId, + insertIntoSetMap +} from './NodeCache'; + +import { maxBy } from 'lodash'; +import { type CogniteCadModel } from '@cognite/reveal'; + +export type CursorType = any; + +type MappingsResponse = { + results: Array; + nextCursor: any | undefined; +}; + +export class RevisionNodeCache { + private readonly _cogniteClient: CogniteClient; + private readonly _fdmClient: FdmSDK; + + private readonly _modelId: number; + private readonly _revisionId: number; + + private readonly _globalCdfToFdmMap: Map< + RevisionTreeIndex, + { values: FdmKey[]; complete: boolean } + >; + + private readonly _cursorToIdsCache = new Map< + CursorType | undefined, + { results: FdmId[]; nextCursor: CursorType | undefined } + >(); + + private readonly _mappedTreeIndexKeys = new Set(); + private readonly _mappedFdmKeys = new Set(); + + constructor( + cogniteClient: CogniteClient, + fdmClient: FdmSDK, + modelId: number, + revisionId: number, + globalCdfToFdmMap: Map + ) { + this._cogniteClient = cogniteClient; + this._fdmClient = fdmClient; + + this._globalCdfToFdmMap = globalCdfToFdmMap; + + this._modelId = modelId; + this._revisionId = revisionId; + } + + public hasMappingData(): boolean { + re + } + + public async getAllMappingExternalIds( + cursor: any | undefined + ): Promise<{ results: FdmId[]; nextCursor: CursorType | undefined }> { + if (this._cursorToIdsCache.has(cursor)) { + return this._cursorToIdsCache.get(cursor)!; + } + + const response: MappingsResponse = await fetchAllMappingExternalIdsWithTreeIndex( + this._modelId, + this._revisionId, + cursor + ); + + this.accumulateDataInCache(response, cursor); + + if (this.isLastData(response)) { + this.markMappedDataAsComplete(); + } + + return response; + } + + private accumulateDataInCache(data: MappingsResponse, cursor: CursorType | undefined): void { + data.results.forEach((e) => { + const treeIndexKey = createRevisionTreeIndex( + this._modelId, + this._revisionId, + e.treeIndex + ); + const fdmKey = createFdmKey(e.space, e.externalId); + + insertIntoSetMap(treeIndexKey, fdmKey, this._globalCdfToFdmMap); + insertIntoSetMap(fdmKey, treeIndexKey, this._globalFdmToCdfMap); + + this._mappedTreeIndexKeys.add(treeIndexKey); + this._mappedFdmKeys.add(fdmKey); + }); + + this._cursorToIdsCache.set(cursor, data); + } + + private isLastData(data: MappingsResponse): boolean { + return data.nextCursor === undefined; + } + + private markMappedDataAsComplete(): void { + this._mappedFdmKeys.forEach((key) => { + this._globalFdmToCdfMap.get(key)!.complete = true; + }); + + this._mappedTreeIndexKeys.forEach((key) => { + this._globalCdfToFdmMap.get(key)!.complete = true; + }); + } + + public async getClosestParentExternalIds(treeIndex: number): Promise { + const firstTreeIndexKey: RevisionTreeIndex = createRevisionTreeIndex(this._modelId, this._revisionId, treeIndex); + + if (this._globalCdfToFdmMap.get(firstTreeIndexKey)?.complete) { + const values = this._globalCdfToFdmMap.get(firstTreeIndexKey)!.values; + return values.map((key) => fdmKeyToId(key)); + } + + const { fdmIds, lowestAncestors, firstMappedTreeIndex } = + await this.queryClosestParentExternalIds(treeIndex); + + const fdmKeys = fdmIds.map((id) => createFdmKey(id.space, id.externalId)); + const firstMappedTreeIndexKey = createRevisionTreeIndex(this._modelId, this._revisionId, firstMappedTreeIndex); + + lowestAncestors.forEach((node) => { + const treeIndexKey = createRevisionTreeIndex(this._modelId, this._revisionId, node.treeIndex); + this._globalCdfToFdmMap.set(treeIndexKey, { complete: true, values: fdmKeys }); + }); + + fdmKeys.forEach((fdmKey) => { + insertIntoSetMap(fdmKey, firstMappedTreeIndexKey, this._globalFdmToCdfMap); + this._globalFdmToCdfMap.get(fdmKey)!.complete = true; + }); + + return fdmIds; + } + + private async queryClosestParentExternalIds( + treeIndex: number + ): Promise<{ fdmIds: FdmId[]; lowestAncestors: Node3D[]; firstMappedTreeIndex: number }> { + const ancestors: Node3D[] = await fetchAncestorNodesForTreeIndex( + this._modelId, + this._revisionId, + treeIndex, + this._cogniteClient + ); + + const ancestorMappings: { edges: Array>> } = + await getEdgesForNodes( + this._revisionId, + ancestors.map((a) => a.id) + ); + + if (ancestorMappings.edges.length === 0) { + return { fdmIds: [], lowestAncestors: [], firstMappedTreeIndex: 0 }; + } + + const mappings = ancestorMappings.edges.map((e) => ({ + externalId: e.startNode.externalId, + space: e.startNode.space, + treeIndex: ancestors.find((a) => a.id === e.properties.nodeId)!.treeIndex + })); + + const firstMappedTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)!.treeIndex; + const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedTreeIndex); + + return { + fdmIds: resultsInLowerTree, + lowestAncestors: ancestors.filter((a) => a.treeIndex >= firstMappedTreeIndex), + firstMappedTreeIndex + }; + } + + getIds(): { modelId: number; revisionId: number } { + return { + modelId: this._modelId, + revisionId: this._revisionId + }; + } +} + +async function fetchAncestorNodesForTreeIndex( + modelId: number, + revisionId: number, + treeIndex: number, + cogniteClient: CogniteClient +): Promise { + const nodeId = await treeIndexToNodeId(modelId, revisionId, treeIndex, cogniteClient); + + const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( + modelId, + revisionId, + nodeId + ); + + return ancestorNodes.items; +} + +async function fetchAllMappingExternalIdsWithTreeIndex( + modelId: number, + revisionId: number, + cursor: CursorType +): Promise { + +} + +export async function treeIndexesToNodeIds(modelId: number, revisionId: number, treeIndexes: number[], cogniteClient: CogniteClient): Promise { + const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ + cogniteClient.project + }/3d/models/${modelId}/revisions/${revisionId}/nodes/internalids/bytreeindices`; + const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { data: { items: treeIndexes } }); + if (response.status === 200) { + return response.data.items; + } else { + throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes}`); + } +} + +export async function nodeIdsToTreeIndexes(modelId: number, revisionId: number, nodeIds: number[], cogniteClient: CogniteClient): Promise { + const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ + cogniteClient.project + }/3d/models/${modelId}/revisions/${revisionId}/nodes/treeindices/byinternalids`; + const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { data: { items: nodeIds } }); + if (response.status === 200) { + return response.data.items; + } else { + throw Error(`nodeId-treeIndex translation failed for nodeIds ${nodeIds}`); + } +} diff --git a/react-components/src/components/NodeCacheProvider/SomeCache.ts b/react-components/src/components/NodeCacheProvider/SomeCache.ts new file mode 100644 index 00000000000..33ceadce104 --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/SomeCache.ts @@ -0,0 +1,40 @@ +/*! + * Copyright 2023 Cognite AS + */ + +/*! + * Copyright 2023 Cognite AS + */ +import { type FdmId } from './NodeCache'; +import { type SpaceRevisionCache } from './SpaceRevisionCache'; + +type RevisionId = number; +type Space = string; +type ScopeId = `${Space}-${RevisionId}`; + +export type NodeIndex = { + modelId: number; + revisionId: number; + treeIndex: number; +}; + +class SomeCache { + private readonly _spaceRevisionCaches = new Map(); + + public async getAllFdmMappings( + space: string, + revisionId: number + ): Promise> {} + + public async getFdmMappingsFor3DNode( + space: string, + modelId: number, + revisionId: number, + treeIndex: number + ): Promise {} + + public async getTreeIndexesForFdmNode( + space: string, + externalId: string + ): Promise<{ modelId: number; revisionId: number; treeIndex: number }> {} +} diff --git a/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts b/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts new file mode 100644 index 00000000000..029f2c67b37 --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts @@ -0,0 +1,20 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { type FdmId } from './NodeCache'; +import { type NodeIndex } from './SomeCache'; + +export class SpaceRevisionCache { + private readonly _space: string; + private readonly _revisionId: number; + + private readonly _treeIndexToFdm = new Map(); + + constructor(space: string, revisionId: number) { + this._space = space; + this._revisionId = revisionId; + } + + public async getAllFdmMappings(): Promise> {} +} diff --git a/react-components/src/components/RevealContainer/RevealContainer.tsx b/react-components/src/components/RevealContainer/RevealContainer.tsx index c59ba344991..a1a56edeb83 100644 --- a/react-components/src/components/RevealContainer/RevealContainer.tsx +++ b/react-components/src/components/RevealContainer/RevealContainer.tsx @@ -11,6 +11,7 @@ import { ModelsLoadingStateContext } from '../Reveal3DResources/ModelsLoadingCon import { SDKProvider } from './SDKProvider'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { useRevealKeepAlive } from '../RevealKeepAlive/RevealKeepAliveContext'; +import { FdmNodeCacheContext, NodeCacheProvider } from '../NodeCacheProvider/NodeCacheProvider'; type RevealContainerProps = { color?: Color; @@ -71,8 +72,10 @@ export function RevealContainer({ return ( <> - - {createPortal(children, viewerDomElement.current)} + + + {createPortal(children, viewerDomElement.current)} + diff --git a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx index 1b3239a652e..bc556cefe5f 100644 --- a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx +++ b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx @@ -9,6 +9,7 @@ import { SYSTEM_SPACE_3D_SCHEMA } from '../utilities/globalDataModels'; import { type FdmSDK, type EdgeItem } from '../utilities/FdmSDK'; +import { useGetAllExternalIds } from '../components/NodeCacheProvider/NodeCacheProvider'; export type ModelRevisionId = `${number}-${number}`; export type ModelRevisionToEdgeMap = Map>>; @@ -17,21 +18,7 @@ export const useMappedEquipmentByRevisionList = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, enabled = true ): UseQueryResult => { - const fdmClient = useFdmSdk(); - return useQuery( - [ - 'reveal', - 'react-components', - ...modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId.toString()).sort() - ], - async () => { - const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); - const edges = await getEdgesForRevisions(revisionIds, fdmClient); - const groupToModels = groupToModelRevision(edges, modelRevisionIds); - return groupToModels; - }, - { staleTime: Infinity, enabled: enabled && modelRevisionIds.length > 0 } - ); + return useGetAllExternalIds(modelRevisionIds, enabled); }; function groupToModelRevision( From c24caaaf59c98d337fe8eb9aa03d9c45b06b515f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 15 Aug 2023 15:59:33 +0200 Subject: [PATCH 02/17] feat: theoretically working cache --- .../components/NodeCacheProvider/NodeCache.ts | 112 +++++---- .../NodeCacheProvider/NodeCacheProvider.tsx | 25 ++ .../NodeCacheProvider/RevisionNodeCache.ts | 235 ++++++++++-------- .../RevealContainer/RevealContainer.tsx | 2 +- 4 files changed, 225 insertions(+), 149 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index b8b5ba9c89f..fca43b3ef69 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -2,21 +2,22 @@ * Copyright 2023 Cognite AS */ -import { type CogniteClient } from '@cognite/sdk/dist/src'; +import { type CogniteClient } from '@cognite/sdk'; import { EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; -import { RevisionNodeCache, nodeIdsToTreeIndexes } from './RevisionNodeCache'; +import { FdmNodeWithView, RevisionNodeCache, nodeIdsToTreeIndexes } from './RevisionNodeCache'; import { type CogniteCadModel } from '@cognite/reveal'; import { InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; import { ModelRevisionId, ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; import assert from 'assert'; -export type RevisionKey = `${number}-${number}`; -export type FdmKey = `${string}-${string}`; export type ModelId = number; export type RevisionId = number; export type TreeIndex = number; + +export type RevisionKey = `${ModelId}-${RevisionId}`; +export type FdmKey = `${string}-${string}`; export type RevisionTreeIndex = `${ModelId}-${RevisionId}-${TreeIndex}`; export type FdmId = DmsUniqueIdentifier; @@ -26,10 +27,7 @@ export class FdmNodeCache { private readonly _cdfClient: CogniteClient; private readonly _fdmClient: FdmSDK; - private readonly _globalCdfToFdmMap = new Map< - RevisionTreeIndex, - { values: FdmKey[]; complete: boolean } - >(); + private readonly _completeRevisions = new Set(); public constructor(cdfClient: CogniteClient, fdmClient: FdmSDK) { this._cdfClient = cdfClient; @@ -40,35 +38,83 @@ export class FdmNodeCache { modelRevisionIds: Array<{ modelId: number; revisionId: number }> ): Promise { - const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); - const edges = await getEdgesForRevisions(revisionIds, this._fdmClient); + const nonCachedRevisions = modelRevisionIds.filter(ids => { + const key = createRevisionKey(ids.modelId, ids.revisionId); + return !this._completeRevisions.has(key); + }); + + const cachedRevisionIds = modelRevisionIds.filter(ids => { + const key = createRevisionKey(ids.modelId, ids.revisionId); + return this._completeRevisions.has(key); + }); + + // Get cached data + const cachedEdges = cachedRevisionIds.map(id => { + const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); + const revisionKey = createRevisionKey(id.modelId, id.revisionId); + const cachedRevisionEdges = revisionCache.getAllEdges(); + return [revisionKey, cachedRevisionEdges] as const; + }); + + // Fetched non-cached data + const revisionIds = nonCachedRevisions.map((modelRevisionId) => modelRevisionId.revisionId); + const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); const groupToModels = await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); + // Cache newly fetched data for (const [revisionKey, data] of groupToModels.entries()) { const [modelId, revisionId] = revisionKeyToIds(revisionKey); + const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); data.forEach(edge => { - const treeIndexKey = createRevisionTreeIndex(modelId, revisionId, edge.treeIndex); - - insertIntoSetMap(treeIndexKey, createFdmKey(edge.startNode.space, edge.startNode.externalId), this._globalCdfToFdmMap); + revisionCache.insertTreeIndexMappings(edge.treeIndex, edge); }); + + this._completeRevisions.add(revisionKey); } - return groupToModels; + const truncatedGroupToModels = groupToModels as Map>>; + + // Merge with new with previously cached data + cachedEdges.forEach(([revisionKey, edges]) => { + truncatedGroupToModels.set(revisionKey, edges); + }); + + return truncatedGroupToModels } public async getClosestParentExternalId( modelId: number, revisionId: number, treeIndex: number - ): Promise { + ): Promise { const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); - return await revisionCache.getClosestParentExternalIds(treeIndex); + return await revisionCache.getClosestParentFdmData(treeIndex); + } + + private async getEdgesForRevisions( + revisionIds: number[], + fdmClient: FdmSDK + ): Promise>> { + const versionedPropertiesKey = `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`; + const filter = { + in: { + property: [SYSTEM_SPACE_3D_SCHEMA, versionedPropertiesKey, 'revisionId'], + values: revisionIds + } + }; + const mappings = await fdmClient.filterAllInstances( + filter, + 'edge', + SYSTEM_3D_EDGE_SOURCE + ); + return mappings.edges; } + private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionNodeCache { - const revisionKey = getRevisionKey(modelId, revisionId); + const revisionKey = createRevisionKey(modelId, revisionId); if (!this._revisionNodeCaches.has(revisionKey)) { this._revisionNodeCaches.set( revisionKey, @@ -76,8 +122,7 @@ export class FdmNodeCache { this._cdfClient, this._fdmClient, modelId, - revisionId, - this._globalCdfToFdmMap + revisionId ) ); } @@ -93,7 +138,7 @@ async function getNodeIdsForAsset( return []; } -function getRevisionKey(modelId: number, revisionId: number): RevisionKey { +function createRevisionKey(modelId: number, revisionId: number): RevisionKey { return `${modelId}-${revisionId}`; } @@ -133,16 +178,16 @@ export function fdmKeyToId(fdmKey: FdmKey): FdmId { export function insertIntoSetMap( key: T, value: U, - globalMap: Map + globalMap: Map ): void { const prevVal = globalMap.get(key); if (prevVal === undefined) { - globalMap.set(key, { complete: false, values: [value] }); + globalMap.set(key, [value]); return; } - prevVal.values.push(value); + prevVal.push(value); } async function groupToModelRevision( @@ -183,7 +228,7 @@ async function groupToModelRevision( const edgeRevisionId = edge.properties.revisionId; const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); if (modelRevisionId === undefined) return map; - const modelRevisionIdKey: ModelRevisionId = getRevisionKey(modelRevisionId.modelId, modelRevisionId.revisionId); + const modelRevisionIdKey: ModelRevisionId = createRevisionKey(modelRevisionId.modelId, modelRevisionId.revisionId); const edgesForModel = map.get(modelRevisionIdKey); const revisionNodeIdKey = `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; const value = { ...edge, treeIndex: revisionNodeIdToTreeIndexList.get(revisionNodeIdKey)! }; @@ -196,22 +241,3 @@ async function groupToModelRevision( return map; }, new Map & { treeIndex: TreeIndex }>>()); } - -async function getEdgesForRevisions( - revisionIds: number[], - fdmClient: FdmSDK -): Promise>> { - const versionedPropertiesKey = `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`; - const filter = { - in: { - property: [SYSTEM_SPACE_3D_SCHEMA, versionedPropertiesKey, 'revisionId'], - values: revisionIds - } - }; - const mappings = await fdmClient.filterAllInstances( - filter, - 'edge', - SYSTEM_3D_EDGE_SOURCE - ); - return mappings.edges; -} diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index 3443870c75f..b39b4e00e41 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -7,6 +7,7 @@ import { FdmNodeCache } from './NodeCache'; import { ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; import { UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; +import { FdmNodeWithView } from './RevisionNodeCache'; export type FdmNodeCacheContent = { cache: FdmNodeCache; @@ -34,6 +35,30 @@ export const useGetAllExternalIds = ( ); }; +export const useGetExternalId = ( + modelId: number, + revisionId: number, + treeIndex: number +): UseQueryResult => { + const content = useContext(FdmNodeCacheContext); + + if (content === undefined) { + throw Error('Must use useNodeCache inside a NodeCacheContext'); + } + + return useQuery( + [ + 'reveal', + 'react-components', + 'tree-index-to-external-id', + modelId, + revisionId, + treeIndex + ], + () => content.cache.getClosestParentExternalId(modelId, revisionId, treeIndex) + ); +} + export function NodeCacheProvider({ children }: { children?: ReactNode }): ReactElement { const fdmClient = useFdmSdk(); const cdfClient = useSDK(); diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index 7ce8868925d..fa0bb080cc3 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -2,8 +2,8 @@ * Copyright 2023 Cognite AS */ -import { type CogniteClient, type Node3D } from '@cognite/sdk'; -import { type EdgeItem, type FdmSDK } from '../../utilities/FdmSDK'; +import { CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; +import { DmsUniqueIdentifier, Source, type EdgeItem, type FdmSDK, InspectResultList } from '../../utilities/FdmSDK'; import { type FdmId, type FdmKey, @@ -11,13 +11,15 @@ import { createFdmKey, createRevisionTreeIndex, fdmKeyToId, - insertIntoSetMap + insertIntoSetMap, + TreeIndex } from './NodeCache'; import { maxBy } from 'lodash'; import { type CogniteCadModel } from '@cognite/reveal'; +import { INSTANCE_SPACE_3D_DATA, InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from '../../utilities/globalDataModels'; -export type CursorType = any; +export type FdmNodeWithView = { fdmId: DmsUniqueIdentifier, view: Source }; type MappingsResponse = { results: Array; @@ -31,124 +33,76 @@ export class RevisionNodeCache { private readonly _modelId: number; private readonly _revisionId: number; - private readonly _globalCdfToFdmMap: Map< - RevisionTreeIndex, - { values: FdmKey[]; complete: boolean } - >; + /* private readonly _globalCdfToFdmMap: Map< + RevisionTreeIndex, Array>>; - private readonly _cursorToIdsCache = new Map< - CursorType | undefined, - { results: FdmId[]; nextCursor: CursorType | undefined } - >(); + private readonly _treeIndexToNodeMap: Map< + RevisionTreeIndex, FdmNodeWithView> = new Map(); */ - private readonly _mappedTreeIndexKeys = new Set(); - private readonly _mappedFdmKeys = new Set(); + private readonly _treeIndexToFdmId: Map< + TreeIndex, Array>> = new Map(); + + private readonly _treeIndexToFdmData: Map< + TreeIndex, Array> = new Map(); constructor( cogniteClient: CogniteClient, fdmClient: FdmSDK, modelId: number, - revisionId: number, - globalCdfToFdmMap: Map + revisionId: number ) { this._cogniteClient = cogniteClient; this._fdmClient = fdmClient; - this._globalCdfToFdmMap = globalCdfToFdmMap; - this._modelId = modelId; this._revisionId = revisionId; } - public hasMappingData(): boolean { - re - } - - public async getAllMappingExternalIds( - cursor: any | undefined - ): Promise<{ results: FdmId[]; nextCursor: CursorType | undefined }> { - if (this._cursorToIdsCache.has(cursor)) { - return this._cursorToIdsCache.get(cursor)!; - } - - const response: MappingsResponse = await fetchAllMappingExternalIdsWithTreeIndex( - this._modelId, - this._revisionId, - cursor - ); + public async getClosestParentFdmData(searchTreeIndex: number): Promise { - this.accumulateDataInCache(response, cursor); + let nodeEdges: EdgeItem[]; + let equallyMappedAncestors: Node3D[] = []; - if (this.isLastData(response)) { - this.markMappedDataAsComplete(); + if (this._treeIndexToFdmData.has(searchTreeIndex)) { + console.log('Cache hit in first cache during tree index lookup'); + return this._treeIndexToFdmData.get(searchTreeIndex)!; } - return response; - } - - private accumulateDataInCache(data: MappingsResponse, cursor: CursorType | undefined): void { - data.results.forEach((e) => { - const treeIndexKey = createRevisionTreeIndex( - this._modelId, - this._revisionId, - e.treeIndex - ); - const fdmKey = createFdmKey(e.space, e.externalId); - - insertIntoSetMap(treeIndexKey, fdmKey, this._globalCdfToFdmMap); - insertIntoSetMap(fdmKey, treeIndexKey, this._globalFdmToCdfMap); - - this._mappedTreeIndexKeys.add(treeIndexKey); - this._mappedFdmKeys.add(fdmKey); - }); - - this._cursorToIdsCache.set(cursor, data); - } - - private isLastData(data: MappingsResponse): boolean { - return data.nextCursor === undefined; - } - - private markMappedDataAsComplete(): void { - this._mappedFdmKeys.forEach((key) => { - this._globalFdmToCdfMap.get(key)!.complete = true; - }); - - this._mappedTreeIndexKeys.forEach((key) => { - this._globalCdfToFdmMap.get(key)!.complete = true; - }); - } - - public async getClosestParentExternalIds(treeIndex: number): Promise { - const firstTreeIndexKey: RevisionTreeIndex = createRevisionTreeIndex(this._modelId, this._revisionId, treeIndex); - - if (this._globalCdfToFdmMap.get(firstTreeIndexKey)?.complete) { - const values = this._globalCdfToFdmMap.get(firstTreeIndexKey)!.values; - return values.map((key) => fdmKeyToId(key)); + if (this._treeIndexToFdmId.has(searchTreeIndex)) { + nodeEdges = this._treeIndexToFdmId.get(searchTreeIndex)!; + console.log('Cache hit in second cache during tree index lookup'); + } else { + const { edges, lowestAncestors, firstMappedTreeIndex } = + await this.getClosestParentExternalIds(searchTreeIndex); + + // If mapped parent exists in end-to-end-map... + if (this._treeIndexToFdmData.has(firstMappedTreeIndex)) { + // Fill treeIndex cache for all nodes on path + lowestAncestors.forEach(a => { + this._treeIndexToFdmData.set(a.treeIndex, this._treeIndexToFdmData.get(firstMappedTreeIndex)!); + }); + + // And return cached + return this._treeIndexToFdmData.get(firstMappedTreeIndex)!; + } + + nodeEdges = edges; + equallyMappedAncestors = lowestAncestors; } - const { fdmIds, lowestAncestors, firstMappedTreeIndex } = - await this.queryClosestParentExternalIds(treeIndex); - - const fdmKeys = fdmIds.map((id) => createFdmKey(id.space, id.externalId)); - const firstMappedTreeIndexKey = createRevisionTreeIndex(this._modelId, this._revisionId, firstMappedTreeIndex); + const nodeInspectionResults = await inspectNodes(this._fdmClient, nodeEdges.map(edge => edge.startNode)); - lowestAncestors.forEach((node) => { - const treeIndexKey = createRevisionTreeIndex(this._modelId, this._revisionId, node.treeIndex); - this._globalCdfToFdmMap.set(treeIndexKey, { complete: true, values: fdmKeys }); - }); + const dataWithViews = nodeEdges.map((edge, ind) => ({ fdmId: edge.startNode, + view: nodeInspectionResults.items[ind].inspectionResults.involvedViewsAndContainers.views[0] })); - fdmKeys.forEach((fdmKey) => { - insertIntoSetMap(fdmKey, firstMappedTreeIndexKey, this._globalFdmToCdfMap); - this._globalFdmToCdfMap.get(fdmKey)!.complete = true; - }); + equallyMappedAncestors.forEach(ancestor => this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews)); - return fdmIds; + return dataWithViews; } - private async queryClosestParentExternalIds( + private async getClosestParentExternalIds( treeIndex: number - ): Promise<{ fdmIds: FdmId[]; lowestAncestors: Node3D[]; firstMappedTreeIndex: number }> { + ): Promise<{ edges: Array>; lowestAncestors: Node3D[]; firstMappedTreeIndex: number }> { const ancestors: Node3D[] = await fetchAncestorNodesForTreeIndex( this._modelId, this._revisionId, @@ -156,32 +110,46 @@ export class RevisionNodeCache { this._cogniteClient ); - const ancestorMappings: { edges: Array>> } = - await getEdgesForNodes( + const ancestorMappings: { edges: Array> } = + await getMappingEdges( + this._modelId, this._revisionId, + this._fdmClient, ancestors.map((a) => a.id) ); if (ancestorMappings.edges.length === 0) { - return { fdmIds: [], lowestAncestors: [], firstMappedTreeIndex: 0 }; + return { edges: [], lowestAncestors: [], firstMappedTreeIndex: 0 }; } const mappings = ancestorMappings.edges.map((e) => ({ - externalId: e.startNode.externalId, - space: e.startNode.space, - treeIndex: ancestors.find((a) => a.id === e.properties.nodeId)!.treeIndex + edge: e, + treeIndex: ancestors.find((a) => a.id === e.properties.revisionNodeId)!.treeIndex })); const firstMappedTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)!.treeIndex; const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedTreeIndex); return { - fdmIds: resultsInLowerTree, + edges: resultsInLowerTree.map(result => result.edge), lowestAncestors: ancestors.filter((a) => a.treeIndex >= firstMappedTreeIndex), firstMappedTreeIndex }; } + public insertTreeIndexMappings(treeIndex: TreeIndex, edge: EdgeItem): void { + let edgeArray = this._treeIndexToFdmId.get(treeIndex); + if (edgeArray === undefined) { + this._treeIndexToFdmId.set(treeIndex, [edge]); + } else { + edgeArray.push(edge); + } + } + + public getAllEdges(): Array> { + return [...this._treeIndexToFdmId.values()].flat(); + } + getIds(): { modelId: number; revisionId: number } { return { modelId: this._modelId, @@ -207,14 +175,71 @@ async function fetchAncestorNodesForTreeIndex( return ancestorNodes.items; } -async function fetchAllMappingExternalIdsWithTreeIndex( + +async function getMappingEdges( modelId: number, revisionId: number, - cursor: CursorType -): Promise { + fdmClient: FdmSDK, + ancestorIds: CogniteInternalId[] +): Promise<{ edges: Array> }> { + const filter = { + and: [ + { + equals: { + property: ['edge', 'endNode'], + value: { + space: INSTANCE_SPACE_3D_DATA, + externalId: `${modelId}` + } + } + }, + { + equals: { + property: [ + SYSTEM_3D_EDGE_SOURCE.space, + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, + 'revisionId' + ], + value: revisionId + } + }, + { + in: { + property: [ + SYSTEM_3D_EDGE_SOURCE.space, + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, + 'revisionNodeId' + ], + values: ancestorIds + } + } + ] + }; + + return await fdmClient.filterAllInstances( + filter, + 'edge', + SYSTEM_3D_EDGE_SOURCE + ); +} +async function inspectNodes( + fdmClient: FdmSDK, + dataNodes: DmsUniqueIdentifier[] +): Promise { + const inspectionResult = await fdmClient.inspectInstances({ + inspectionOperations: { involvedViewsAndContainers: {} }, + items: dataNodes.map(node => ({ + instanceType: 'node', + externalId: node.externalId, + space: node.space + })) + }); + + return inspectionResult; } + export async function treeIndexesToNodeIds(modelId: number, revisionId: number, treeIndexes: number[], cogniteClient: CogniteClient): Promise { const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ cogniteClient.project diff --git a/react-components/src/components/RevealContainer/RevealContainer.tsx b/react-components/src/components/RevealContainer/RevealContainer.tsx index 70f1fec909e..c5aed1c9def 100644 --- a/react-components/src/components/RevealContainer/RevealContainer.tsx +++ b/react-components/src/components/RevealContainer/RevealContainer.tsx @@ -83,7 +83,7 @@ export function RevealContainer({ {createPortal(children, viewerDomElement.current)} - + From b6e80efa69dc7c984f8da8bfdc35563b0a3f35db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 11:40:21 +0200 Subject: [PATCH 03/17] fix: working cache --- .../components/NodeCacheProvider/NodeCache.ts | 27 +-- .../NodeCacheProvider/NodeCacheProvider.tsx | 29 ++-- .../NodeCacheProvider/RevisionNodeCache.ts | 61 +++---- .../src/components/Reveal3DResources/types.ts | 2 +- .../src/hooks/useCalculateModelsStyling.tsx | 4 +- react-components/src/hooks/useClickedNode.tsx | 12 +- .../useMappedEquipmentBy3DModelsList.tsx | 44 +---- .../src/hooks/useNodeMappedData.tsx | 155 +----------------- .../stories/HighlightNode.stories.tsx | 6 +- 9 files changed, 82 insertions(+), 258 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index fca43b3ef69..0489cf66ad8 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -2,9 +2,9 @@ * Copyright 2023 Cognite AS */ -import { type CogniteClient } from '@cognite/sdk'; +import { Node3D, type CogniteClient } from '@cognite/sdk'; import { EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; -import { FdmNodeWithView, RevisionNodeCache, nodeIdsToTreeIndexes } from './RevisionNodeCache'; +import { Fdm3dNodeData, FdmCadEdge, FdmEdgeWithNode, RevisionNodeCache, nodeIdsToNodes } from './RevisionNodeCache'; import { type CogniteCadModel } from '@cognite/reveal'; import { InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; import { ModelRevisionId, ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; @@ -66,14 +66,15 @@ export class FdmNodeCache { const [modelId, revisionId] = revisionKeyToIds(revisionKey); const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); - data.forEach(edge => { - revisionCache.insertTreeIndexMappings(edge.treeIndex, edge); + console.log("Inserting mappings - ", data.length, 'of them'); + data.forEach(edgeAndNode => { + revisionCache.insertTreeIndexMappings(edgeAndNode.node.treeIndex, edgeAndNode); }); this._completeRevisions.add(revisionKey); } - const truncatedGroupToModels = groupToModels as Map>>; + const truncatedGroupToModels = groupToModels; // Merge with new with previously cached data cachedEdges.forEach(([revisionKey, edges]) => { @@ -87,9 +88,11 @@ export class FdmNodeCache { modelId: number, revisionId: number, treeIndex: number - ): Promise { + ): Promise { const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); + console.log('In `getClosestParentExternalId`'); + return await revisionCache.getClosestParentFdmData(treeIndex); } @@ -194,7 +197,7 @@ async function groupToModelRevision( edges: Array>, modelRevisionIds: Array<{ modelId: number; revisionId: number }>, cdfClient: CogniteClient -): Promise & { treeIndex: TreeIndex }>>> { +): Promise>> { const nodeIdsPerRevision = edges.reduce( (revisionNodeIdMap, edge) => { const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); @@ -209,16 +212,16 @@ async function groupToModelRevision( new Map()); type RevisionNodeId = `${RevisionId}-${number}`; - const revisionNodeIdToTreeIndexList = new Map(); + const revisionNodeIdToNode = new Map(); const treeIndexesPromises = [...nodeIdsPerRevision.entries()].map(async ([revisionId, nodeIds]) => { const modelId = modelRevisionIds.find(p => p.revisionId === revisionId)?.modelId; assert(modelId !== undefined); - const treeIndexes = await nodeIdsToTreeIndexes(modelId, revisionId, nodeIds, cdfClient); + const nodes = await nodeIdsToNodes(modelId, revisionId, nodeIds, cdfClient); nodeIds.forEach((e, ind) => { const revisionNodeIdKey = `${revisionId}-${e}` as const; - revisionNodeIdToTreeIndexList.set(revisionNodeIdKey, treeIndexes[ind]); + revisionNodeIdToNode.set(revisionNodeIdKey, nodes[ind]); }); }); @@ -231,7 +234,7 @@ async function groupToModelRevision( const modelRevisionIdKey: ModelRevisionId = createRevisionKey(modelRevisionId.modelId, modelRevisionId.revisionId); const edgesForModel = map.get(modelRevisionIdKey); const revisionNodeIdKey = `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; - const value = { ...edge, treeIndex: revisionNodeIdToTreeIndexList.get(revisionNodeIdKey)! }; + const value = { edge, node: revisionNodeIdToNode.get(revisionNodeIdKey)! }; if (edgesForModel === undefined) { map.set(modelRevisionIdKey, [value]); } else { @@ -239,5 +242,5 @@ async function groupToModelRevision( } return map; - }, new Map & { treeIndex: TreeIndex }>>()); + }, new Map>()); } diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index b39b4e00e41..fafe9255b98 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -7,7 +7,7 @@ import { FdmNodeCache } from './NodeCache'; import { ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; import { UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; -import { FdmNodeWithView } from './RevisionNodeCache'; +import { Fdm3dNodeData } from './RevisionNodeCache'; export type FdmNodeCacheContent = { cache: FdmNodeCache; @@ -35,18 +35,14 @@ export const useGetAllExternalIds = ( ); }; -export const useGetExternalId = ( - modelId: number, - revisionId: number, - treeIndex: number -): UseQueryResult => { +export const useFdm3dNodeData = ( + modelId: number | undefined, + revisionId: number | undefined, + treeIndex: number | undefined +): UseQueryResult => { const content = useContext(FdmNodeCacheContext); - if (content === undefined) { - throw Error('Must use useNodeCache inside a NodeCacheContext'); - } - - return useQuery( + const result = useQuery( [ 'reveal', 'react-components', @@ -55,8 +51,17 @@ export const useGetExternalId = ( revisionId, treeIndex ], - () => content.cache.getClosestParentExternalId(modelId, revisionId, treeIndex) + () => content!.cache.getClosestParentExternalId(modelId!, revisionId!, treeIndex!), + { + enabled: content !== undefined && modelId !== undefined && revisionId !== undefined && treeIndex !== undefined + } ); + + if (content === undefined) { + throw Error('Must use useNodeCache inside a NodeCacheContext'); + } + + return result; } export function NodeCacheProvider({ children }: { children?: ReactNode }): ReactElement { diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index fa0bb080cc3..9fa7a7dfb2f 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -19,12 +19,9 @@ import { maxBy } from 'lodash'; import { type CogniteCadModel } from '@cognite/reveal'; import { INSTANCE_SPACE_3D_DATA, InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from '../../utilities/globalDataModels'; -export type FdmNodeWithView = { fdmId: DmsUniqueIdentifier, view: Source }; - -type MappingsResponse = { - results: Array; - nextCursor: any | undefined; -}; +export type Fdm3dNodeData = { fdmId: DmsUniqueIdentifier, view: Source, cadNode: Node3D }; +export type FdmCadEdge = EdgeItem; +export type FdmEdgeWithNode = { edge: FdmCadEdge, node: Node3D }; export class RevisionNodeCache { private readonly _cogniteClient: CogniteClient; @@ -33,17 +30,11 @@ export class RevisionNodeCache { private readonly _modelId: number; private readonly _revisionId: number; - /* private readonly _globalCdfToFdmMap: Map< - RevisionTreeIndex, Array>>; - - private readonly _treeIndexToNodeMap: Map< - RevisionTreeIndex, FdmNodeWithView> = new Map(); */ - private readonly _treeIndexToFdmId: Map< - TreeIndex, Array>> = new Map(); + TreeIndex, Array> = new Map(); private readonly _treeIndexToFdmData: Map< - TreeIndex, Array> = new Map(); + TreeIndex, Array> = new Map(); constructor( cogniteClient: CogniteClient, @@ -58,9 +49,9 @@ export class RevisionNodeCache { this._revisionId = revisionId; } - public async getClosestParentFdmData(searchTreeIndex: number): Promise { + public async getClosestParentFdmData(searchTreeIndex: number): Promise { - let nodeEdges: EdgeItem[]; + let nodeEdges: FdmEdgeWithNode[]; let equallyMappedAncestors: Node3D[] = []; if (this._treeIndexToFdmData.has(searchTreeIndex)) { @@ -86,17 +77,27 @@ export class RevisionNodeCache { return this._treeIndexToFdmData.get(firstMappedTreeIndex)!; } - nodeEdges = edges; + console.log('Lowest ancestors: ', lowestAncestors); + + const firstMappedAncestor = lowestAncestors.find(a => a.treeIndex === firstMappedTreeIndex)!; + + nodeEdges = edges.map(e => ({ edge: e, node: firstMappedAncestor })); equallyMappedAncestors = lowestAncestors; } - const nodeInspectionResults = await inspectNodes(this._fdmClient, nodeEdges.map(edge => edge.startNode)); + const nodeInspectionResults = await inspectNodes(this._fdmClient, nodeEdges.map(edge => edge.edge.startNode)); + console.log('Inspection results = ', nodeInspectionResults); - const dataWithViews = nodeEdges.map((edge, ind) => ({ fdmId: edge.startNode, - view: nodeInspectionResults.items[ind].inspectionResults.involvedViewsAndContainers.views[0] })); + const dataWithViews = nodeEdges.map((fdmEdgeWithNode, ind) => ({ + fdmId: fdmEdgeWithNode.edge.startNode, + view: nodeInspectionResults.items[ind].inspectionResults.involvedViewsAndContainers.views[0], + cadNode: fdmEdgeWithNode.node + })); equallyMappedAncestors.forEach(ancestor => this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews)); + console.log('Data with views = ', dataWithViews); + return dataWithViews; } @@ -137,7 +138,7 @@ export class RevisionNodeCache { }; } - public insertTreeIndexMappings(treeIndex: TreeIndex, edge: EdgeItem): void { + public insertTreeIndexMappings(treeIndex: TreeIndex, edge: FdmEdgeWithNode): void { let edgeArray = this._treeIndexToFdmId.get(treeIndex); if (edgeArray === undefined) { this._treeIndexToFdmId.set(treeIndex, [edge]); @@ -146,7 +147,7 @@ export class RevisionNodeCache { } } - public getAllEdges(): Array> { + public getAllEdges(): Array { return [...this._treeIndexToFdmId.values()].flat(); } @@ -164,12 +165,12 @@ async function fetchAncestorNodesForTreeIndex( treeIndex: number, cogniteClient: CogniteClient ): Promise { - const nodeId = await treeIndexToNodeId(modelId, revisionId, treeIndex, cogniteClient); + const nodeId = await treeIndexesToNodeIds(modelId, revisionId, [treeIndex], cogniteClient); const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( modelId, revisionId, - nodeId + nodeId[0] ); return ancestorNodes.items; @@ -252,14 +253,6 @@ export async function treeIndexesToNodeIds(modelId: number, revisionId: number, } } -export async function nodeIdsToTreeIndexes(modelId: number, revisionId: number, nodeIds: number[], cogniteClient: CogniteClient): Promise { - const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ - cogniteClient.project - }/3d/models/${modelId}/revisions/${revisionId}/nodes/treeindices/byinternalids`; - const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { data: { items: nodeIds } }); - if (response.status === 200) { - return response.data.items; - } else { - throw Error(`nodeId-treeIndex translation failed for nodeIds ${nodeIds}`); - } +export async function nodeIdsToNodes(modelId: number, revisionId: number, nodeIds: number[], cogniteClient: CogniteClient): Promise { + return cogniteClient.revisions3D.retrieve3DNodes(modelId, revisionId, nodeIds.map(id => ({ id }))); } diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index c98d13c83f7..16356a4258f 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -26,7 +26,7 @@ export type AddReveal3DModelOptions = AddModelOptions & { transform?: Matrix4 } export type TypedReveal3DModel = AddReveal3DModelOptions & { type: SupportedModelTypes }; export type NodeDataResult = { - nodeExternalId: string; + fdmNode: DmsUniqueIdentifier; view: Source; cadNode: Node3D; }; diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 153fb453ead..8625295499f 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -57,11 +57,11 @@ function useCalculateMappedStyling(models: TypedReveal3DModel[]): ModelStyleGrou return []; } return models.map((model) => { - const edges = mappedEquipmentEdges?.get(`${model.modelId}-${model.revisionId}`) ?? []; + const fdmData = mappedEquipmentEdges?.get(`${model.modelId}-${model.revisionId}`) ?? []; const styleGroup = model.styling?.mapped !== undefined - ? [getMappedStyleGroup(edges, model.styling.mapped)] + ? [getMappedStyleGroup(fdmData.map(data => data.edge), model.styling.mapped)] : []; return { model, styleGroup }; }); diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index a4d87abee75..6f40f257658 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -38,8 +38,16 @@ export const useClickedNodeData = (): ClickedNodeData | undefined => { const nodeData = useNodeMappedData(cadIntersection?.treeIndex, cadIntersection?.model); - if (nodeData === undefined || cadIntersection === undefined) { + if (cadIntersection === undefined || nodeData.length === 0) { return undefined; } - return { intersection: cadIntersection, ...nodeData }; + + const chosenNode = nodeData[0]; + + return { + intersection: cadIntersection, + fdmNode: chosenNode.fdmId, + view: chosenNode.view, + cadNode: chosenNode.cadNode + }; }; diff --git a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx index bc556cefe5f..f54403a09c8 100644 --- a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx +++ b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx @@ -5,14 +5,13 @@ import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk } from '../components/RevealContainer/SDKProvider'; import { type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE, - SYSTEM_SPACE_3D_SCHEMA } from '../utilities/globalDataModels'; import { type FdmSDK, type EdgeItem } from '../utilities/FdmSDK'; import { useGetAllExternalIds } from '../components/NodeCacheProvider/NodeCacheProvider'; +import { FdmEdgeWithNode } from '../components/NodeCacheProvider/RevisionNodeCache'; export type ModelRevisionId = `${number}-${number}`; -export type ModelRevisionToEdgeMap = Map>>; +export type ModelRevisionToEdgeMap = Map>; export const useMappedEquipmentByRevisionList = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, @@ -20,42 +19,3 @@ export const useMappedEquipmentByRevisionList = ( ): UseQueryResult => { return useGetAllExternalIds(modelRevisionIds, enabled); }; - -function groupToModelRevision( - edges: Array>, - modelRevisionIds: Array<{ modelId: number; revisionId: number }> -): Map>> { - return edges.reduce((map, edge) => { - const edgeRevisionId = edge.properties.revisionId; - const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); - if (modelRevisionId === undefined) return map; - const modelRevisionIdKey: ModelRevisionId = `${modelRevisionId.modelId}-${modelRevisionId.revisionId}`; - const edgesForModel = map.get(modelRevisionIdKey); - if (edgesForModel === undefined) { - map.set(modelRevisionIdKey, [edge]); - } else { - edgesForModel.push(edge); - } - - return map; - }, new Map>>()); -} - -async function getEdgesForRevisions( - revisionIds: number[], - fdmClient: FdmSDK -): Promise>> { - const versionedPropertiesKey = `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`; - const filter = { - in: { - property: [SYSTEM_SPACE_3D_SCHEMA, versionedPropertiesKey, 'revisionId'], - values: revisionIds - } - }; - const mappings = await fdmClient.filterAllInstances( - filter, - 'edge', - SYSTEM_3D_EDGE_SOURCE - ); - return mappings.edges; -} diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index d1afa4dafe7..2a0b9b0d499 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -2,160 +2,15 @@ * Copyright 2023 Cognite AS */ -import { useQuery } from '@tanstack/react-query'; - import { type CogniteCadModel } from '@cognite/reveal'; -import { type CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; -import { type NodeDataResult } from '../components/Reveal3DResources/types'; -import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; - -import assert from 'assert'; -import { - type FdmSDK, - type DmsUniqueIdentifier, - type EdgeItem, - type InspectResultList -} from '../utilities/FdmSDK'; -import { - INSTANCE_SPACE_3D_DATA, - SYSTEM_3D_EDGE_SOURCE, - SYSTEM_SPACE_3D_SCHEMA -} from '../utilities/globalDataModels'; +import { useFdm3dNodeData } from '../components/NodeCacheProvider/NodeCacheProvider'; +import { Fdm3dNodeData } from '../components/NodeCacheProvider/RevisionNodeCache'; export const useNodeMappedData = ( treeIndex: number | undefined, model: CogniteCadModel | undefined -): NodeDataResult | undefined => { - const cogniteClient = useSDK(); - const fdmClient = useFdmSdk(); - - const mappedDataHashKey = `${model?.modelId ?? ''}-${model?.revisionId ?? ''}-${treeIndex ?? ''}`; - - const queryResult = useQuery(['cdf', '3d', mappedDataHashKey], async () => { - if (model === undefined || treeIndex === undefined) { - return null; - } - - const ancestors = await fetchAncestorNodesForTreeIndex(model, treeIndex, cogniteClient); - - if (ancestors.length === 0) { - return null; - } - - const mappings = await fetchNodeMappingEdges( - model, - ancestors.map((n) => n.id), - fdmClient - ); - - const selectedEdge = - mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; - - const selectedNodeId = selectedEdge?.properties.revisionNodeId; - - const dataNode = selectedEdge?.startNode; - - if (dataNode === undefined) { - return null; - } - - const inspectionResult = await inspectNode(dataNode, fdmClient); - - const dataView = - inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; +): Fdm3dNodeData[] => { + const nodeCacheContent = useFdm3dNodeData(model?.modelId, model?.revisionId, treeIndex); - if (dataView === undefined) { - return null; - } - - const selectedNode = ancestors.find((n) => n.id === selectedNodeId); - - assert(selectedNode !== undefined); - - return { - nodeExternalId: dataNode.externalId, - view: dataView, - cadNode: selectedNode - }; - }); - - return queryResult.data ?? undefined; + return nodeCacheContent.data ?? []; }; - -async function fetchAncestorNodesForTreeIndex( - model: CogniteCadModel, - treeIndex: number, - cogniteClient: CogniteClient -): Promise { - const nodeId = await model.mapTreeIndexToNodeId(treeIndex); - - const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( - model.modelId, - model.revisionId, - nodeId - ); - - return ancestorNodes.items; -} - -async function fetchNodeMappingEdges( - model: CogniteCadModel, - ancestorIds: CogniteInternalId[], - fdmClient: FdmSDK -): Promise<{ edges: Array>> } | undefined> { - assert(ancestorIds.length !== 0); - - const filter = { - and: [ - { - equals: { - property: ['edge', 'endNode'], - value: { - space: INSTANCE_SPACE_3D_DATA, - externalId: `${model.modelId}` - } - } - }, - { - equals: { - property: [ - SYSTEM_SPACE_3D_SCHEMA, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionId' - ], - value: model.revisionId - } - }, - { - in: { - property: [ - SYSTEM_SPACE_3D_SCHEMA, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionNodeId' - ], - values: ancestorIds - } - } - ] - }; - - return await fdmClient.filterAllInstances(filter, 'edge', SYSTEM_3D_EDGE_SOURCE); -} - -async function inspectNode( - dataNode: DmsUniqueIdentifier, - fdmClient: FdmSDK -): Promise { - const inspectionResult = await fdmClient.inspectInstances({ - inspectionOperations: { involvedViewsAndContainers: {} }, - items: [ - { - instanceType: 'node', - externalId: dataNode.externalId, - space: dataNode.space - } - ] - }); - - return inspectionResult; -} diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 1f409501d73..5bd142ec153 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -65,12 +65,12 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React const cameraNavigation = useCameraNavigation(); useEffect(() => { - setHighlightedId(nodeData?.nodeExternalId); + setHighlightedId(nodeData?.fdmNode.externalId); if (nodeData === undefined) return; - void cameraNavigation.fitCameraToInstance(nodeData.nodeExternalId, 'pdms-mapping'); - }, [nodeData?.nodeExternalId]); + void cameraNavigation.fitCameraToInstance(nodeData.fdmNode.externalId, 'pdms-mapping'); + }, [nodeData?.fdmNode.externalId]); if (stylingGroupsRef.current.length === 1) { stylingGroupsRef.current.pop(); From 98a092bc82b80546c9d5124b6e03f0a332790a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 12:51:56 +0200 Subject: [PATCH 04/17] refactor: factor down a function somewhat --- .../components/NodeCacheProvider/NodeCache.ts | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index 0489cf66ad8..eaad371193d 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -9,6 +9,8 @@ import { type CogniteCadModel } from '@cognite/reveal'; import { InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; import { ModelRevisionId, ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; +import { partition } from 'lodash'; + import assert from 'assert'; @@ -38,15 +40,13 @@ export class FdmNodeCache { modelRevisionIds: Array<{ modelId: number; revisionId: number }> ): Promise { - const nonCachedRevisions = modelRevisionIds.filter(ids => { - const key = createRevisionKey(ids.modelId, ids.revisionId); - return !this._completeRevisions.has(key); - }); - - const cachedRevisionIds = modelRevisionIds.filter(ids => { - const key = createRevisionKey(ids.modelId, ids.revisionId); - return this._completeRevisions.has(key); - }); + const [cachedRevisionIds, nonCachedRevisionIds] = + partition( + modelRevisionIds, + ids => { + const key = createRevisionKey(ids.modelId, ids.revisionId); + return this._completeRevisions.has(key); + }); // Get cached data const cachedEdges = cachedRevisionIds.map(id => { @@ -56,15 +56,21 @@ export class FdmNodeCache { return [revisionKey, cachedRevisionEdges] as const; }); - // Fetched non-cached data - const revisionIds = nonCachedRevisions.map((modelRevisionId) => modelRevisionId.revisionId); - const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); - const groupToModels = await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); + const groupToModels = await this.getRevisionToEdgesMap(nonCachedRevisionIds); + + this.cacheRevisionData(groupToModels); + + cachedEdges.forEach(([revisionKey, edges]) => { + groupToModels.set(revisionKey, edges); + }); - // Cache newly fetched data - for (const [revisionKey, data] of groupToModels.entries()) { + return groupToModels; + } + + private cacheRevisionData(modelMap: Map): void { + for (const [revisionKey, data] of modelMap.entries()) { const [modelId, revisionId] = revisionKeyToIds(revisionKey); - const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); + const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); console.log("Inserting mappings - ", data.length, 'of them'); data.forEach(edgeAndNode => { @@ -73,15 +79,13 @@ export class FdmNodeCache { this._completeRevisions.add(revisionKey); } + } - const truncatedGroupToModels = groupToModels; - - // Merge with new with previously cached data - cachedEdges.forEach(([revisionKey, edges]) => { - truncatedGroupToModels.set(revisionKey, edges); - }); - - return truncatedGroupToModels + private async getRevisionToEdgesMap(modelRevisionIds: { modelId: number, revisionId: number }[]): Promise> { + // Fetched non-cached data + const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); + const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); + return await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); } public async getClosestParentExternalId( From 07b65315cdaafd1122b9364ad99da32bec79c80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 13:44:24 +0200 Subject: [PATCH 05/17] refactor: split into more functions --- .../components/NodeCacheProvider/NodeCache.ts | 24 +++--- .../NodeCacheProvider/RevisionNodeCache.ts | 80 +++++++++---------- 2 files changed, 51 insertions(+), 53 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index eaad371193d..c718e55b650 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -48,25 +48,26 @@ export class FdmNodeCache { return this._completeRevisions.has(key); }); - // Get cached data - const cachedEdges = cachedRevisionIds.map(id => { - const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); - const revisionKey = createRevisionKey(id.modelId, id.revisionId); - const cachedRevisionEdges = revisionCache.getAllEdges(); - return [revisionKey, cachedRevisionEdges] as const; - }); + const cachedEdges = cachedRevisionIds.map(id => this.getCachedEdgesForRevision(id)); - const groupToModels = await this.getRevisionToEdgesMap(nonCachedRevisionIds); + const revisionToEdgesMap = await this.getRevisionToEdgesMap(nonCachedRevisionIds); - this.cacheRevisionData(groupToModels); + this.cacheRevisionData(revisionToEdgesMap); cachedEdges.forEach(([revisionKey, edges]) => { - groupToModels.set(revisionKey, edges); + revisionToEdgesMap.set(revisionKey, edges); }); - return groupToModels; + return revisionToEdgesMap; } + private getCachedEdgesForRevision(id: { modelId: number, revisionId: number }): [RevisionKey, FdmEdgeWithNode[]] { + const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); + const revisionKey = createRevisionKey(id.modelId, id.revisionId); + const cachedRevisionEdges = revisionCache.getAllEdges(); + return [revisionKey, cachedRevisionEdges]; + } + private cacheRevisionData(modelMap: Map): void { for (const [revisionKey, data] of modelMap.entries()) { const [modelId, revisionId] = revisionKeyToIds(revisionKey); @@ -82,7 +83,6 @@ export class FdmNodeCache { } private async getRevisionToEdgesMap(modelRevisionIds: { modelId: number, revisionId: number }[]): Promise> { - // Fetched non-cached data const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); return await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index 9fa7a7dfb2f..036fd8f05bb 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -5,13 +5,6 @@ import { CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; import { DmsUniqueIdentifier, Source, type EdgeItem, type FdmSDK, InspectResultList } from '../../utilities/FdmSDK'; import { - type FdmId, - type FdmKey, - type RevisionTreeIndex, - createFdmKey, - createRevisionTreeIndex, - fdmKeyToId, - insertIntoSetMap, TreeIndex } from './NodeCache'; @@ -51,40 +44,47 @@ export class RevisionNodeCache { public async getClosestParentFdmData(searchTreeIndex: number): Promise { - let nodeEdges: FdmEdgeWithNode[]; - let equallyMappedAncestors: Node3D[] = []; - if (this._treeIndexToFdmData.has(searchTreeIndex)) { console.log('Cache hit in first cache during tree index lookup'); return this._treeIndexToFdmData.get(searchTreeIndex)!; } if (this._treeIndexToFdmId.has(searchTreeIndex)) { - nodeEdges = this._treeIndexToFdmId.get(searchTreeIndex)!; + const nodeEdges = this._treeIndexToFdmId.get(searchTreeIndex)!; console.log('Cache hit in second cache during tree index lookup'); - } else { - const { edges, lowestAncestors, firstMappedTreeIndex } = - await this.getClosestParentExternalIds(searchTreeIndex); - - // If mapped parent exists in end-to-end-map... - if (this._treeIndexToFdmData.has(firstMappedTreeIndex)) { - // Fill treeIndex cache for all nodes on path - lowestAncestors.forEach(a => { - this._treeIndexToFdmData.set(a.treeIndex, this._treeIndexToFdmData.get(firstMappedTreeIndex)!); - }); - - // And return cached - return this._treeIndexToFdmData.get(firstMappedTreeIndex)!; - } - console.log('Lowest ancestors: ', lowestAncestors); + return this.getDataWithViewsForFdmStartNodes(nodeEdges, []); + } + + return this.findNodeDataFromAncestors(searchTreeIndex); + } + + private async findNodeDataFromAncestors(treeIndex: TreeIndex): Promise { + + const { edges, ancestorsWithSameMapping, firstMappedAncestorTreeIndex } = + await this.getClosestParentMapping(treeIndex); - const firstMappedAncestor = lowestAncestors.find(a => a.treeIndex === firstMappedTreeIndex)!; + if (this._treeIndexToFdmData.has(firstMappedAncestorTreeIndex)) { + const fdmData = this._treeIndexToFdmData.get(firstMappedAncestorTreeIndex)! + this.setCacheForNodes(ancestorsWithSameMapping, fdmData); - nodeEdges = edges.map(e => ({ edge: e, node: firstMappedAncestor })); - equallyMappedAncestors = lowestAncestors; + return fdmData; } + const firstMappedAncestor = ancestorsWithSameMapping.find(a => a.treeIndex === firstMappedAncestorTreeIndex)!; + + const nodeEdges = edges.map(e => ({ edge: e, node: firstMappedAncestor })); + + return this.getDataWithViewsForFdmStartNodes(nodeEdges, ancestorsWithSameMapping); + } + + private setCacheForNodes(nodes: Node3D[], nodeData: Fdm3dNodeData[]): void { + nodes.forEach(n => { + this._treeIndexToFdmData.set(n.treeIndex, nodeData); + }); + } + + private async getDataWithViewsForFdmStartNodes(nodeEdges: FdmEdgeWithNode[], ancestorsWithSameMapping: Node3D[]): Promise { const nodeInspectionResults = await inspectNodes(this._fdmClient, nodeEdges.map(edge => edge.edge.startNode)); console.log('Inspection results = ', nodeInspectionResults); @@ -94,16 +94,15 @@ export class RevisionNodeCache { cadNode: fdmEdgeWithNode.node })); - equallyMappedAncestors.forEach(ancestor => this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews)); + ancestorsWithSameMapping.forEach(ancestor => this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews)); console.log('Data with views = ', dataWithViews); - return dataWithViews; } - private async getClosestParentExternalIds( + private async getClosestParentMapping( treeIndex: number - ): Promise<{ edges: Array>; lowestAncestors: Node3D[]; firstMappedTreeIndex: number }> { + ): Promise<{ edges: Array; ancestorsWithSameMapping: Node3D[]; firstMappedAncestorTreeIndex: number }> { const ancestors: Node3D[] = await fetchAncestorNodesForTreeIndex( this._modelId, this._revisionId, @@ -111,7 +110,7 @@ export class RevisionNodeCache { this._cogniteClient ); - const ancestorMappings: { edges: Array> } = + const ancestorMappings = await getMappingEdges( this._modelId, this._revisionId, @@ -120,7 +119,7 @@ export class RevisionNodeCache { ); if (ancestorMappings.edges.length === 0) { - return { edges: [], lowestAncestors: [], firstMappedTreeIndex: 0 }; + return { edges: [], ancestorsWithSameMapping: [], firstMappedAncestorTreeIndex: 0 }; } const mappings = ancestorMappings.edges.map((e) => ({ @@ -128,13 +127,13 @@ export class RevisionNodeCache { treeIndex: ancestors.find((a) => a.id === e.properties.revisionNodeId)!.treeIndex })); - const firstMappedTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)!.treeIndex; - const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedTreeIndex); + const firstMappedAncestorTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)!.treeIndex; + const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedAncestorTreeIndex); return { edges: resultsInLowerTree.map(result => result.edge), - lowestAncestors: ancestors.filter((a) => a.treeIndex >= firstMappedTreeIndex), - firstMappedTreeIndex + ancestorsWithSameMapping: ancestors.filter((a) => a.treeIndex >= firstMappedAncestorTreeIndex), + firstMappedAncestorTreeIndex }; } @@ -176,13 +175,12 @@ async function fetchAncestorNodesForTreeIndex( return ancestorNodes.items; } - async function getMappingEdges( modelId: number, revisionId: number, fdmClient: FdmSDK, ancestorIds: CogniteInternalId[] -): Promise<{ edges: Array> }> { +): Promise<{ edges: Array }> { const filter = { and: [ { From c7c9d1abd425490237020914b11b07f0f5b6b29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 14:10:43 +0200 Subject: [PATCH 06/17] chore: lint fix --- .../components/NodeCacheProvider/NodeCache.ts | 179 +++++++++-------- .../NodeCacheProvider/NodeCacheProvider.tsx | 50 +++-- .../NodeCacheProvider/RevisionNodeCache.ts | 180 ++++++++++-------- .../components/NodeCacheProvider/SomeCache.ts | 40 ---- .../NodeCacheProvider/SpaceRevisionCache.ts | 20 -- .../src/components/NodeCacheProvider/types.ts | 10 + .../RevealContainer/RevealContainer.tsx | 11 +- .../src/hooks/useCalculateModelsStyling.tsx | 7 +- .../useMappedEquipmentBy3DModelsList.tsx | 11 +- .../src/hooks/useNodeMappedData.tsx | 2 +- 10 files changed, 254 insertions(+), 256 deletions(-) delete mode 100644 react-components/src/components/NodeCacheProvider/SomeCache.ts delete mode 100644 react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts create mode 100644 react-components/src/components/NodeCacheProvider/types.ts diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index c718e55b650..a6a16af22dd 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -2,18 +2,24 @@ * Copyright 2023 Cognite AS */ -import { Node3D, type CogniteClient } from '@cognite/sdk'; -import { EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; -import { Fdm3dNodeData, FdmCadEdge, FdmEdgeWithNode, RevisionNodeCache, nodeIdsToNodes } from './RevisionNodeCache'; -import { type CogniteCadModel } from '@cognite/reveal'; -import { InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; -import { ModelRevisionId, ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; +import { type Node3D, type CogniteClient } from '@cognite/sdk'; +import { type EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; +import { RevisionNodeCache } from './RevisionNodeCache'; +import { type FdmEdgeWithNode, type Fdm3dNodeData } from './types'; +import { + type InModel3dEdgeProperties, + SYSTEM_3D_EDGE_SOURCE, + SYSTEM_SPACE_3D_SCHEMA +} from '../../utilities/globalDataModels'; +import { + type ModelRevisionId, + type ModelRevisionToEdgeMap +} from '../../hooks/useMappedEquipmentBy3DModelsList'; import { partition } from 'lodash'; import assert from 'assert'; - export type ModelId = number; export type RevisionId = number; export type TreeIndex = number; @@ -39,16 +45,12 @@ export class FdmNodeCache { public async getAllMappingExternalIds( modelRevisionIds: Array<{ modelId: number; revisionId: number }> ): Promise { + const [cachedRevisionIds, nonCachedRevisionIds] = partition(modelRevisionIds, (ids) => { + const key = createRevisionKey(ids.modelId, ids.revisionId); + return this._completeRevisions.has(key); + }); - const [cachedRevisionIds, nonCachedRevisionIds] = - partition( - modelRevisionIds, - ids => { - const key = createRevisionKey(ids.modelId, ids.revisionId); - return this._completeRevisions.has(key); - }); - - const cachedEdges = cachedRevisionIds.map(id => this.getCachedEdgesForRevision(id)); + const cachedEdges = cachedRevisionIds.map((id) => this.getCachedEdgesForRevision(id)); const revisionToEdgesMap = await this.getRevisionToEdgesMap(nonCachedRevisionIds); @@ -61,20 +63,23 @@ export class FdmNodeCache { return revisionToEdgesMap; } - private getCachedEdgesForRevision(id: { modelId: number, revisionId: number }): [RevisionKey, FdmEdgeWithNode[]] { - const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); - const revisionKey = createRevisionKey(id.modelId, id.revisionId); - const cachedRevisionEdges = revisionCache.getAllEdges(); - return [revisionKey, cachedRevisionEdges]; - } + private getCachedEdgesForRevision(id: { + modelId: number; + revisionId: number; + }): [RevisionKey, FdmEdgeWithNode[]] { + const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); + const revisionKey = createRevisionKey(id.modelId, id.revisionId); + const cachedRevisionEdges = revisionCache.getAllEdges(); + return [revisionKey, cachedRevisionEdges]; + } private cacheRevisionData(modelMap: Map): void { for (const [revisionKey, data] of modelMap.entries()) { const [modelId, revisionId] = revisionKeyToIds(revisionKey); const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); - console.log("Inserting mappings - ", data.length, 'of them'); - data.forEach(edgeAndNode => { + console.log('Inserting mappings - ', data.length, 'of them'); + data.forEach((edgeAndNode) => { revisionCache.insertTreeIndexMappings(edgeAndNode.node.treeIndex, edgeAndNode); }); @@ -82,7 +87,9 @@ export class FdmNodeCache { } } - private async getRevisionToEdgesMap(modelRevisionIds: { modelId: number, revisionId: number }[]): Promise> { + private async getRevisionToEdgesMap( + modelRevisionIds: Array<{ modelId: number; revisionId: number }> + ): Promise> { const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); return await groupToModelRevision(edges, modelRevisionIds, this._cdfClient); @@ -119,30 +126,26 @@ export class FdmNodeCache { return mappings.edges; } - private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionNodeCache { const revisionKey = createRevisionKey(modelId, revisionId); - if (!this._revisionNodeCaches.has(revisionKey)) { - this._revisionNodeCaches.set( - revisionKey, - new RevisionNodeCache( - this._cdfClient, - this._fdmClient, - modelId, - revisionId - ) - ); + + const revisionCache = this._revisionNodeCaches.get(revisionKey); + + if (revisionCache !== undefined) { + return revisionCache; } - return this._revisionNodeCaches.get(revisionKey)!; - } -} + const newRevisionCache = new RevisionNodeCache( + this._cdfClient, + this._fdmClient, + modelId, + revisionId + ); -async function getNodeIdsForAsset( - space: string, - externalId: string -): Promise> { - return []; + this._revisionNodeCaches.set(revisionKey, newRevisionCache); + + return newRevisionCache; + } } function createRevisionKey(modelId: number, revisionId: number): RevisionKey { @@ -166,27 +169,13 @@ export function createFdmKey(spaceId: string, externalId: string): FdmKey { return `${spaceId}-${externalId}`; } -function treeIndexKeyToId(key: RevisionTreeIndex): { - modelId: number; - revisionId: number; - treeIndex: number; -} { - const parts = key.split('-'); - - return { modelId: Number(parts[0]), revisionId: Number(parts[1]), treeIndex: Number(parts[2]) }; -} - export function fdmKeyToId(fdmKey: FdmKey): FdmId { const parts = fdmKey.split('-'); return { space: parts[0], externalId: parts[1] }; } -export function insertIntoSetMap( - key: T, - value: U, - globalMap: Map -): void { +export function insertIntoSetMap(key: T, value: U, globalMap: Map): void { const prevVal = globalMap.get(key); if (prevVal === undefined) { @@ -201,33 +190,33 @@ async function groupToModelRevision( edges: Array>, modelRevisionIds: Array<{ modelId: number; revisionId: number }>, cdfClient: CogniteClient -): Promise>> { - const nodeIdsPerRevision = edges.reduce( - (revisionNodeIdMap, edge) => { - const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); - if (nodeIdsInRevision !== undefined) { - nodeIdsInRevision.push(edge.properties.revisionNodeId); - } else { - revisionNodeIdMap.set(edge.properties.revisionId, [edge.properties.revisionNodeId]); - } +): Promise> { + const nodeIdsPerRevision = edges.reduce((revisionNodeIdMap, edge) => { + const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); + if (nodeIdsInRevision !== undefined) { + nodeIdsInRevision.push(edge.properties.revisionNodeId); + } else { + revisionNodeIdMap.set(edge.properties.revisionId, [edge.properties.revisionNodeId]); + } - return revisionNodeIdMap; - }, - new Map()); + return revisionNodeIdMap; + }, new Map()); type RevisionNodeId = `${RevisionId}-${number}`; const revisionNodeIdToNode = new Map(); - const treeIndexesPromises = [...nodeIdsPerRevision.entries()].map(async ([revisionId, nodeIds]) => { - const modelId = modelRevisionIds.find(p => p.revisionId === revisionId)?.modelId; - assert(modelId !== undefined); + const treeIndexesPromises = [...nodeIdsPerRevision.entries()].map( + async ([revisionId, nodeIds]) => { + const modelId = modelRevisionIds.find((p) => p.revisionId === revisionId)?.modelId; + assert(modelId !== undefined); - const nodes = await nodeIdsToNodes(modelId, revisionId, nodeIds, cdfClient); - nodeIds.forEach((e, ind) => { - const revisionNodeIdKey = `${revisionId}-${e}` as const; - revisionNodeIdToNode.set(revisionNodeIdKey, nodes[ind]); - }); - }); + const nodes = await nodeIdsToNodes(modelId, revisionId, nodeIds, cdfClient); + nodeIds.forEach((e, ind) => { + const revisionNodeIdKey = `${revisionId}-${e}` as const; + revisionNodeIdToNode.set(revisionNodeIdKey, nodes[ind]); + }); + } + ); await Promise.all(treeIndexesPromises); @@ -235,10 +224,19 @@ async function groupToModelRevision( const edgeRevisionId = edge.properties.revisionId; const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); if (modelRevisionId === undefined) return map; - const modelRevisionIdKey: ModelRevisionId = createRevisionKey(modelRevisionId.modelId, modelRevisionId.revisionId); + const modelRevisionIdKey: ModelRevisionId = createRevisionKey( + modelRevisionId.modelId, + modelRevisionId.revisionId + ); const edgesForModel = map.get(modelRevisionIdKey); - const revisionNodeIdKey = `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; - const value = { edge, node: revisionNodeIdToNode.get(revisionNodeIdKey)! }; + const revisionNodeIdKey = + `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; + + const node = revisionNodeIdToNode.get(revisionNodeIdKey); + assert(node !== undefined); + + const value = { edge, node }; + if (edgesForModel === undefined) { map.set(modelRevisionIdKey, [value]); } else { @@ -246,5 +244,18 @@ async function groupToModelRevision( } return map; - }, new Map>()); + }, new Map()); +} + +async function nodeIdsToNodes( + modelId: number, + revisionId: number, + nodeIds: number[], + cogniteClient: CogniteClient +): Promise { + return await cogniteClient.revisions3D.retrieve3DNodes( + modelId, + revisionId, + nodeIds.map((id) => ({ id })) + ); } diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index fafe9255b98..b763174314e 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -2,12 +2,14 @@ * Copyright 2023 Cognite AS */ -import { ReactElement, ReactNode, createContext, useContext, useMemo } from 'react'; +import { type ReactElement, type ReactNode, createContext, useContext, useMemo } from 'react'; import { FdmNodeCache } from './NodeCache'; -import { ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; -import { UseQueryResult, useQuery } from '@tanstack/react-query'; +import { type ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; +import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; -import { Fdm3dNodeData } from './RevisionNodeCache'; +import { type Fdm3dNodeData } from './Fdm3dNodeData'; + +import assert from 'assert'; export type FdmNodeCacheContent = { cache: FdmNodeCache; @@ -17,7 +19,8 @@ export const FdmNodeCacheContext = createContext, - enabled: boolean): UseQueryResult => { + enabled: boolean +): UseQueryResult => { const content = useContext(FdmNodeCacheContext); if (content === undefined) { @@ -43,17 +46,22 @@ export const useFdm3dNodeData = ( const content = useContext(FdmNodeCacheContext); const result = useQuery( - [ - 'reveal', - 'react-components', - 'tree-index-to-external-id', - modelId, - revisionId, - treeIndex - ], - () => content!.cache.getClosestParentExternalId(modelId!, revisionId!, treeIndex!), + ['reveal', 'react-components', 'tree-index-to-external-id', modelId, revisionId, treeIndex], + async () => { + assert( + content !== undefined && + modelId !== undefined && + revisionId !== undefined && + treeIndex !== undefined + ); + return await content.cache.getClosestParentExternalId(modelId, revisionId, treeIndex); + }, { - enabled: content !== undefined && modelId !== undefined && revisionId !== undefined && treeIndex !== undefined + enabled: + content !== undefined && + modelId !== undefined && + revisionId !== undefined && + treeIndex !== undefined } ); @@ -62,17 +70,17 @@ export const useFdm3dNodeData = ( } return result; -} +}; export function NodeCacheProvider({ children }: { children?: ReactNode }): ReactElement { const fdmClient = useFdmSdk(); const cdfClient = useSDK(); - const fdmCache = useMemo(() => new FdmNodeCache(cdfClient, fdmClient) - , []); + const fdmCache = useMemo(() => new FdmNodeCache(cdfClient, fdmClient), []); - - return - {children} + return ( + + {children} + ); } diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index 036fd8f05bb..b510db112f5 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -2,19 +2,23 @@ * Copyright 2023 Cognite AS */ -import { CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; -import { DmsUniqueIdentifier, Source, type EdgeItem, type FdmSDK, InspectResultList } from '../../utilities/FdmSDK'; +import { type CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; import { - TreeIndex -} from './NodeCache'; + type DmsUniqueIdentifier, + type FdmSDK, + type InspectResultList +} from '../../utilities/FdmSDK'; +import { type TreeIndex } from './NodeCache'; import { maxBy } from 'lodash'; -import { type CogniteCadModel } from '@cognite/reveal'; -import { INSTANCE_SPACE_3D_DATA, InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from '../../utilities/globalDataModels'; +import { + INSTANCE_SPACE_3D_DATA, + type InModel3dEdgeProperties, + SYSTEM_3D_EDGE_SOURCE +} from '../../utilities/globalDataModels'; -export type Fdm3dNodeData = { fdmId: DmsUniqueIdentifier, view: Source, cadNode: Node3D }; -export type FdmCadEdge = EdgeItem; -export type FdmEdgeWithNode = { edge: FdmCadEdge, node: Node3D }; +import assert from 'assert'; +import { type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './Fdm3dNodeData'; export class RevisionNodeCache { private readonly _cogniteClient: CogniteClient; @@ -23,11 +27,9 @@ export class RevisionNodeCache { private readonly _modelId: number; private readonly _revisionId: number; - private readonly _treeIndexToFdmId: Map< - TreeIndex, Array> = new Map(); + private readonly _treeIndexToFdmEdges = new Map(); - private readonly _treeIndexToFdmData: Map< - TreeIndex, Array> = new Map(); + private readonly _treeIndexToFdmData = new Map(); constructor( cogniteClient: CogniteClient, @@ -43,49 +45,61 @@ export class RevisionNodeCache { } public async getClosestParentFdmData(searchTreeIndex: number): Promise { + const cachedFdmData = this._treeIndexToFdmData.get(searchTreeIndex); - if (this._treeIndexToFdmData.has(searchTreeIndex)) { + if (cachedFdmData !== undefined) { console.log('Cache hit in first cache during tree index lookup'); - return this._treeIndexToFdmData.get(searchTreeIndex)!; + return cachedFdmData; } - if (this._treeIndexToFdmId.has(searchTreeIndex)) { - const nodeEdges = this._treeIndexToFdmId.get(searchTreeIndex)!; + const cachedFdmEdges = this._treeIndexToFdmEdges.get(searchTreeIndex); + + if (cachedFdmEdges !== undefined) { console.log('Cache hit in second cache during tree index lookup'); - return this.getDataWithViewsForFdmStartNodes(nodeEdges, []); + return await this.getDataWithViewsForFdmStartNodes(cachedFdmEdges, []); } - return this.findNodeDataFromAncestors(searchTreeIndex); + return await this.findNodeDataFromAncestors(searchTreeIndex); } private async findNodeDataFromAncestors(treeIndex: TreeIndex): Promise { - const { edges, ancestorsWithSameMapping, firstMappedAncestorTreeIndex } = await this.getClosestParentMapping(treeIndex); - if (this._treeIndexToFdmData.has(firstMappedAncestorTreeIndex)) { - const fdmData = this._treeIndexToFdmData.get(firstMappedAncestorTreeIndex)! - this.setCacheForNodes(ancestorsWithSameMapping, fdmData); + const cachedFdmData = this._treeIndexToFdmData.get(firstMappedAncestorTreeIndex); + + if (cachedFdmData !== undefined) { + this.setCacheForNodes(ancestorsWithSameMapping, cachedFdmData); - return fdmData; + return cachedFdmData; } - const firstMappedAncestor = ancestorsWithSameMapping.find(a => a.treeIndex === firstMappedAncestorTreeIndex)!; + const firstMappedAncestor = ancestorsWithSameMapping.find( + (a) => a.treeIndex === firstMappedAncestorTreeIndex + ); + + assert(firstMappedAncestor !== undefined); - const nodeEdges = edges.map(e => ({ edge: e, node: firstMappedAncestor })); + const nodeEdges = edges.map((e) => ({ edge: e, node: firstMappedAncestor })); - return this.getDataWithViewsForFdmStartNodes(nodeEdges, ancestorsWithSameMapping); + return await this.getDataWithViewsForFdmStartNodes(nodeEdges, ancestorsWithSameMapping); } private setCacheForNodes(nodes: Node3D[], nodeData: Fdm3dNodeData[]): void { - nodes.forEach(n => { - this._treeIndexToFdmData.set(n.treeIndex, nodeData); - }); + nodes.forEach((n) => { + this._treeIndexToFdmData.set(n.treeIndex, nodeData); + }); } - private async getDataWithViewsForFdmStartNodes(nodeEdges: FdmEdgeWithNode[], ancestorsWithSameMapping: Node3D[]): Promise { - const nodeInspectionResults = await inspectNodes(this._fdmClient, nodeEdges.map(edge => edge.edge.startNode)); + private async getDataWithViewsForFdmStartNodes( + nodeEdges: FdmEdgeWithNode[], + ancestorsWithSameMapping: Node3D[] + ): Promise { + const nodeInspectionResults = await inspectNodes( + this._fdmClient, + nodeEdges.map((edge) => edge.edge.startNode) + ); console.log('Inspection results = ', nodeInspectionResults); const dataWithViews = nodeEdges.map((fdmEdgeWithNode, ind) => ({ @@ -94,15 +108,19 @@ export class RevisionNodeCache { cadNode: fdmEdgeWithNode.node })); - ancestorsWithSameMapping.forEach(ancestor => this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews)); + ancestorsWithSameMapping.forEach((ancestor) => + this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews) + ); console.log('Data with views = ', dataWithViews); return dataWithViews; } - private async getClosestParentMapping( - treeIndex: number - ): Promise<{ edges: Array; ancestorsWithSameMapping: Node3D[]; firstMappedAncestorTreeIndex: number }> { + private async getClosestParentMapping(treeIndex: number): Promise<{ + edges: FdmCadEdge[]; + ancestorsWithSameMapping: Node3D[]; + firstMappedAncestorTreeIndex: number; + }> { const ancestors: Node3D[] = await fetchAncestorNodesForTreeIndex( this._modelId, this._revisionId, @@ -110,44 +128,54 @@ export class RevisionNodeCache { this._cogniteClient ); - const ancestorMappings = - await getMappingEdges( - this._modelId, - this._revisionId, - this._fdmClient, - ancestors.map((a) => a.id) - ); + const ancestorMappings = await getMappingEdgesForNodeIds( + this._modelId, + this._revisionId, + this._fdmClient, + ancestors.map((a) => a.id) + ); if (ancestorMappings.edges.length === 0) { return { edges: [], ancestorsWithSameMapping: [], firstMappedAncestorTreeIndex: 0 }; } - const mappings = ancestorMappings.edges.map((e) => ({ - edge: e, - treeIndex: ancestors.find((a) => a.id === e.properties.revisionNodeId)!.treeIndex - })); + const mappings = ancestorMappings.edges.map((e) => { + const mappingAncestor = ancestors.find((a) => a.id === e.properties.revisionNodeId); + + assert(mappingAncestor !== undefined); + + return { + edge: e, + treeIndex: mappingAncestor.treeIndex + }; + }); + + const firstMappedAncestorTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)?.treeIndex; + + assert(firstMappedAncestorTreeIndex !== undefined); - const firstMappedAncestorTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)!.treeIndex; const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedAncestorTreeIndex); return { - edges: resultsInLowerTree.map(result => result.edge), - ancestorsWithSameMapping: ancestors.filter((a) => a.treeIndex >= firstMappedAncestorTreeIndex), + edges: resultsInLowerTree.map((result) => result.edge), + ancestorsWithSameMapping: ancestors.filter( + (a) => a.treeIndex >= firstMappedAncestorTreeIndex + ), firstMappedAncestorTreeIndex }; } public insertTreeIndexMappings(treeIndex: TreeIndex, edge: FdmEdgeWithNode): void { - let edgeArray = this._treeIndexToFdmId.get(treeIndex); + const edgeArray = this._treeIndexToFdmEdges.get(treeIndex); if (edgeArray === undefined) { - this._treeIndexToFdmId.set(treeIndex, [edge]); + this._treeIndexToFdmEdges.set(treeIndex, [edge]); } else { edgeArray.push(edge); } } - public getAllEdges(): Array { - return [...this._treeIndexToFdmId.values()].flat(); + public getAllEdges(): FdmEdgeWithNode[] { + return [...this._treeIndexToFdmEdges.values()].flat(); } getIds(): { modelId: number; revisionId: number } { @@ -175,12 +203,12 @@ async function fetchAncestorNodesForTreeIndex( return ancestorNodes.items; } -async function getMappingEdges( +async function getMappingEdgesForNodeIds( modelId: number, revisionId: number, fdmClient: FdmSDK, ancestorIds: CogniteInternalId[] -): Promise<{ edges: Array }> { +): Promise<{ edges: FdmCadEdge[] }> { const filter = { and: [ { @@ -228,29 +256,31 @@ async function inspectNodes( ): Promise { const inspectionResult = await fdmClient.inspectInstances({ inspectionOperations: { involvedViewsAndContainers: {} }, - items: dataNodes.map(node => ({ - instanceType: 'node', - externalId: node.externalId, - space: node.space + items: dataNodes.map((node) => ({ + instanceType: 'node', + externalId: node.externalId, + space: node.space })) }); return inspectionResult; } - -export async function treeIndexesToNodeIds(modelId: number, revisionId: number, treeIndexes: number[], cogniteClient: CogniteClient): Promise { - const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ - cogniteClient.project - }/3d/models/${modelId}/revisions/${revisionId}/nodes/internalids/bytreeindices`; - const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { data: { items: treeIndexes } }); - if (response.status === 200) { - return response.data.items; - } else { - throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes}`); - } -} - -export async function nodeIdsToNodes(modelId: number, revisionId: number, nodeIds: number[], cogniteClient: CogniteClient): Promise { - return cogniteClient.revisions3D.retrieve3DNodes(modelId, revisionId, nodeIds.map(id => ({ id }))); +async function treeIndexesToNodeIds( + modelId: number, + revisionId: number, + treeIndexes: number[], + cogniteClient: CogniteClient +): Promise { + const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ + cogniteClient.project + }/3d/models/${modelId}/revisions/${revisionId}/nodes/internalids/bytreeindices`; + const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { + data: { items: treeIndexes } + }); + if (response.status === 200) { + return response.data.items; + } else { + throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes.join(',')}`); + } } diff --git a/react-components/src/components/NodeCacheProvider/SomeCache.ts b/react-components/src/components/NodeCacheProvider/SomeCache.ts deleted file mode 100644 index 33ceadce104..00000000000 --- a/react-components/src/components/NodeCacheProvider/SomeCache.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -/*! - * Copyright 2023 Cognite AS - */ -import { type FdmId } from './NodeCache'; -import { type SpaceRevisionCache } from './SpaceRevisionCache'; - -type RevisionId = number; -type Space = string; -type ScopeId = `${Space}-${RevisionId}`; - -export type NodeIndex = { - modelId: number; - revisionId: number; - treeIndex: number; -}; - -class SomeCache { - private readonly _spaceRevisionCaches = new Map(); - - public async getAllFdmMappings( - space: string, - revisionId: number - ): Promise> {} - - public async getFdmMappingsFor3DNode( - space: string, - modelId: number, - revisionId: number, - treeIndex: number - ): Promise {} - - public async getTreeIndexesForFdmNode( - space: string, - externalId: string - ): Promise<{ modelId: number; revisionId: number; treeIndex: number }> {} -} diff --git a/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts b/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts deleted file mode 100644 index 029f2c67b37..00000000000 --- a/react-components/src/components/NodeCacheProvider/SpaceRevisionCache.ts +++ /dev/null @@ -1,20 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import { type FdmId } from './NodeCache'; -import { type NodeIndex } from './SomeCache'; - -export class SpaceRevisionCache { - private readonly _space: string; - private readonly _revisionId: number; - - private readonly _treeIndexToFdm = new Map(); - - constructor(space: string, revisionId: number) { - this._space = space; - this._revisionId = revisionId; - } - - public async getAllFdmMappings(): Promise> {} -} diff --git a/react-components/src/components/NodeCacheProvider/types.ts b/react-components/src/components/NodeCacheProvider/types.ts new file mode 100644 index 00000000000..59c1d93c946 --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/types.ts @@ -0,0 +1,10 @@ +/*! + * Copyright 2023 Cognite AS + */ +import { type Node3D } from '@cognite/sdk'; +import { type EdgeItem, type DmsUniqueIdentifier, type Source } from '../../utilities/FdmSDK'; +import { type InModel3dEdgeProperties } from '../../utilities/globalDataModels'; + +export type Fdm3dNodeData = { fdmId: DmsUniqueIdentifier; view: Source; cadNode: Node3D }; +export type FdmCadEdge = EdgeItem; +export type FdmEdgeWithNode = { edge: FdmCadEdge; node: Node3D }; diff --git a/react-components/src/components/RevealContainer/RevealContainer.tsx b/react-components/src/components/RevealContainer/RevealContainer.tsx index d8609ce3733..7fed7ca89d4 100644 --- a/react-components/src/components/RevealContainer/RevealContainer.tsx +++ b/react-components/src/components/RevealContainer/RevealContainer.tsx @@ -77,12 +77,11 @@ export function RevealContainer({ return ( <> - - - - {createPortal(children, viewerDomElement.current)} - - + + + {createPortal(children, viewerDomElement.current)} + + ); diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 8625295499f..73eea6a0a63 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -61,7 +61,12 @@ function useCalculateMappedStyling(models: TypedReveal3DModel[]): ModelStyleGrou const styleGroup = model.styling?.mapped !== undefined - ? [getMappedStyleGroup(fdmData.map(data => data.edge), model.styling.mapped)] + ? [ + getMappedStyleGroup( + fdmData.map((data) => data.edge), + model.styling.mapped + ) + ] : []; return { model, styleGroup }; }); diff --git a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx index f54403a09c8..1a61c3adbae 100644 --- a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx +++ b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx @@ -1,17 +1,12 @@ /*! * Copyright 2023 Cognite AS */ -import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { useFdmSdk } from '../components/RevealContainer/SDKProvider'; -import { - type InModel3dEdgeProperties, -} from '../utilities/globalDataModels'; -import { type FdmSDK, type EdgeItem } from '../utilities/FdmSDK'; +import { type UseQueryResult } from '@tanstack/react-query'; import { useGetAllExternalIds } from '../components/NodeCacheProvider/NodeCacheProvider'; -import { FdmEdgeWithNode } from '../components/NodeCacheProvider/RevisionNodeCache'; +import { type FdmEdgeWithNode } from '../components/NodeCacheProvider/types'; export type ModelRevisionId = `${number}-${number}`; -export type ModelRevisionToEdgeMap = Map>; +export type ModelRevisionToEdgeMap = Map; export const useMappedEquipmentByRevisionList = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index 2a0b9b0d499..96e3225f064 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -4,7 +4,7 @@ import { type CogniteCadModel } from '@cognite/reveal'; import { useFdm3dNodeData } from '../components/NodeCacheProvider/NodeCacheProvider'; -import { Fdm3dNodeData } from '../components/NodeCacheProvider/RevisionNodeCache'; +import { type Fdm3dNodeData } from '../components/NodeCacheProvider/Fdm3dNodeData'; export const useNodeMappedData = ( treeIndex: number | undefined, From dbcf6b488f48fbcf728b214f7c262c3a0dcdbb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:06:05 +0200 Subject: [PATCH 07/17] refactor: more factoring, move requests to own file --- .../NodeCacheProvider/RevisionNodeCache.ts | 188 ++++++------------ .../components/NodeCacheProvider/requests.ts | 107 ++++++++++ 2 files changed, 167 insertions(+), 128 deletions(-) create mode 100644 react-components/src/components/NodeCacheProvider/requests.ts diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index b510db112f5..625537ced8f 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -2,23 +2,18 @@ * Copyright 2023 Cognite AS */ -import { type CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; +import { type CogniteClient, type Node3D } from '@cognite/sdk'; import { - type DmsUniqueIdentifier, type FdmSDK, - type InspectResultList } from '../../utilities/FdmSDK'; import { type TreeIndex } from './NodeCache'; -import { maxBy } from 'lodash'; -import { - INSTANCE_SPACE_3D_DATA, - type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE -} from '../../utilities/globalDataModels'; +import { fetchAncestorNodesForTreeIndex, getMappingEdgesForNodeIds, inspectNodes } from './requests'; + +import { max } from 'lodash'; import assert from 'assert'; -import { type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './Fdm3dNodeData'; +import { type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './types'; export class RevisionNodeCache { private readonly _cogniteClient: CogniteClient; @@ -57,7 +52,7 @@ export class RevisionNodeCache { if (cachedFdmEdges !== undefined) { console.log('Cache hit in second cache during tree index lookup'); - return await this.getDataWithViewsForFdmStartNodes(cachedFdmEdges, []); + return await this.getDataWithViewsForFdmEdges(cachedFdmEdges, []); } return await this.findNodeDataFromAncestors(searchTreeIndex); @@ -83,7 +78,7 @@ export class RevisionNodeCache { const nodeEdges = edges.map((e) => ({ edge: e, node: firstMappedAncestor })); - return await this.getDataWithViewsForFdmStartNodes(nodeEdges, ancestorsWithSameMapping); + return await this.getDataWithViewsForFdmEdges(nodeEdges, ancestorsWithSameMapping); } private setCacheForNodes(nodes: Node3D[], nodeData: Fdm3dNodeData[]): void { @@ -92,7 +87,7 @@ export class RevisionNodeCache { }); } - private async getDataWithViewsForFdmStartNodes( + private async getDataWithViewsForFdmEdges( nodeEdges: FdmEdgeWithNode[], ancestorsWithSameMapping: Node3D[] ): Promise { @@ -128,41 +123,52 @@ export class RevisionNodeCache { this._cogniteClient ); - const ancestorMappings = await getMappingEdgesForNodeIds( - this._modelId, - this._revisionId, - this._fdmClient, - ancestors.map((a) => a.id) - ); + const ancestorMappings = await this.getMappingEdgesForAncestors(ancestors); - if (ancestorMappings.edges.length === 0) { + if (ancestorMappings.length === 0) { return { edges: [], ancestorsWithSameMapping: [], firstMappedAncestorTreeIndex: 0 }; } - const mappings = ancestorMappings.edges.map((e) => { - const mappingAncestor = ancestors.find((a) => a.id === e.properties.revisionNodeId); + const edgesWithCorrespondingTreeIndex = this.combineEdgesWithTreeIndex(ancestorMappings, ancestors); - assert(mappingAncestor !== undefined); + const firstMappedAncestorTreeIndex = findLargestTreeIndex(edgesWithCorrespondingTreeIndex); + return getAncestorDataForTreeIndex(firstMappedAncestorTreeIndex, edgesWithCorrespondingTreeIndex, ancestors); + } + + private combineEdgesWithTreeIndex(mappingEdges: FdmCadEdge[], nodes: Node3D[]): { edge: FdmCadEdge, treeIndex: TreeIndex }[] { + return mappingEdges.map((edge) => { + const ancestorConnectedToEdge = nodes.find((a) => a.id === edge.properties.revisionNodeId); + + assert(ancestorConnectedToEdge !== undefined); return { - edge: e, - treeIndex: mappingAncestor.treeIndex + edge: edge, + treeIndex: ancestorConnectedToEdge.treeIndex }; }); + } - const firstMappedAncestorTreeIndex = maxBy(mappings, (mapping) => mapping.treeIndex)?.treeIndex; + private async getMappingEdgesForAncestors(ancestors: Node3D[]): Promise { + const cachedFirstMappedAncestor = ancestors.filter(a => { + this._treeIndexToFdmEdges.has(a.treeIndex) + }).sort((a, b) => b.treeIndex - a.treeIndex)[0]; - assert(firstMappedAncestorTreeIndex !== undefined); + if (cachedFirstMappedAncestor !== undefined) { + const edges = this._treeIndexToFdmEdges.get(cachedFirstMappedAncestor.treeIndex); - const resultsInLowerTree = mappings.filter((a) => a.treeIndex === firstMappedAncestorTreeIndex); + assert(edges !== undefined); - return { - edges: resultsInLowerTree.map((result) => result.edge), - ancestorsWithSameMapping: ancestors.filter( - (a) => a.treeIndex >= firstMappedAncestorTreeIndex - ), - firstMappedAncestorTreeIndex - }; + return edges + } + + const ancestorMappings = await getMappingEdgesForNodeIds( + this._modelId, + this._revisionId, + this._fdmClient, + ancestors.map((a) => a.id) + ); + + return ancestorMappings.edges; } public insertTreeIndexMappings(treeIndex: TreeIndex, edge: FdmEdgeWithNode): void { @@ -186,101 +192,27 @@ export class RevisionNodeCache { } } -async function fetchAncestorNodesForTreeIndex( - modelId: number, - revisionId: number, - treeIndex: number, - cogniteClient: CogniteClient -): Promise { - const nodeId = await treeIndexesToNodeIds(modelId, revisionId, [treeIndex], cogniteClient); - - const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( - modelId, - revisionId, - nodeId[0] - ); - - return ancestorNodes.items; +function findLargestTreeIndex(edgesWithTreeIndex: { edge: FdmCadEdge, treeIndex: TreeIndex }[]): TreeIndex { + const maxTreeIndex = max(edgesWithTreeIndex.map(e => e.treeIndex)); + assert(maxTreeIndex !== undefined); + return maxTreeIndex; } -async function getMappingEdgesForNodeIds( - modelId: number, - revisionId: number, - fdmClient: FdmSDK, - ancestorIds: CogniteInternalId[] -): Promise<{ edges: FdmCadEdge[] }> { - const filter = { - and: [ - { - equals: { - property: ['edge', 'endNode'], - value: { - space: INSTANCE_SPACE_3D_DATA, - externalId: `${modelId}` - } - } - }, - { - equals: { - property: [ - SYSTEM_3D_EDGE_SOURCE.space, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionId' - ], - value: revisionId - } - }, - { - in: { - property: [ - SYSTEM_3D_EDGE_SOURCE.space, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionNodeId' - ], - values: ancestorIds - } - } - ] - }; - - return await fdmClient.filterAllInstances( - filter, - 'edge', - SYSTEM_3D_EDGE_SOURCE +function getAncestorDataForTreeIndex(treeIndex: TreeIndex, + edgesWithTreeIndex: { edge: FdmCadEdge, treeIndex: TreeIndex }[], + ancestors: Node3D[]): { + edges: FdmCadEdge[]; + ancestorsWithSameMapping: Node3D[]; + firstMappedAncestorTreeIndex: number; +} { + const edgesForFirstMappedAncestor = edgesWithTreeIndex.filter((a) => a.treeIndex === treeIndex); + const ancestorsBetweenSearchNodeAndFirstMappedAncestor = ancestors.filter( + (a) => a.treeIndex >= treeIndex ); -} -async function inspectNodes( - fdmClient: FdmSDK, - dataNodes: DmsUniqueIdentifier[] -): Promise { - const inspectionResult = await fdmClient.inspectInstances({ - inspectionOperations: { involvedViewsAndContainers: {} }, - items: dataNodes.map((node) => ({ - instanceType: 'node', - externalId: node.externalId, - space: node.space - })) - }); - - return inspectionResult; -} - -async function treeIndexesToNodeIds( - modelId: number, - revisionId: number, - treeIndexes: number[], - cogniteClient: CogniteClient -): Promise { - const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ - cogniteClient.project - }/3d/models/${modelId}/revisions/${revisionId}/nodes/internalids/bytreeindices`; - const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { - data: { items: treeIndexes } - }); - if (response.status === 200) { - return response.data.items; - } else { - throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes.join(',')}`); - } + return { + edges: edgesForFirstMappedAncestor.map((result) => result.edge), + ancestorsWithSameMapping: ancestorsBetweenSearchNodeAndFirstMappedAncestor, + firstMappedAncestorTreeIndex: treeIndex + }; } diff --git a/react-components/src/components/NodeCacheProvider/requests.ts b/react-components/src/components/NodeCacheProvider/requests.ts new file mode 100644 index 00000000000..56967301ba8 --- /dev/null +++ b/react-components/src/components/NodeCacheProvider/requests.ts @@ -0,0 +1,107 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { CogniteClient, CogniteInternalId, Node3D } from '@cognite/sdk'; +import { DmsUniqueIdentifier, FdmSDK, InspectResultList } from '../../utilities/FdmSDK'; +import { FdmCadEdge } from './types'; +import { INSTANCE_SPACE_3D_DATA, InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from '../../utilities/globalDataModels'; + +export async function fetchAncestorNodesForTreeIndex( + modelId: number, + revisionId: number, + treeIndex: number, + cogniteClient: CogniteClient +): Promise { + const nodeId = await treeIndexesToNodeIds(modelId, revisionId, [treeIndex], cogniteClient); + + const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( + modelId, + revisionId, + nodeId[0] + ); + + return ancestorNodes.items; +} + +export async function getMappingEdgesForNodeIds( + modelId: number, + revisionId: number, + fdmClient: FdmSDK, + ancestorIds: CogniteInternalId[] +): Promise<{ edges: FdmCadEdge[] }> { + const filter = { + and: [ + { + equals: { + property: ['edge', 'endNode'], + value: { + space: INSTANCE_SPACE_3D_DATA, + externalId: `${modelId}` + } + } + }, + { + equals: { + property: [ + SYSTEM_3D_EDGE_SOURCE.space, + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, + 'revisionId' + ], + value: revisionId + } + }, + { + in: { + property: [ + SYSTEM_3D_EDGE_SOURCE.space, + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, + 'revisionNodeId' + ], + values: ancestorIds + } + } + ] + }; + + return await fdmClient.filterAllInstances( + filter, + 'edge', + SYSTEM_3D_EDGE_SOURCE + ); +} + +export async function inspectNodes( + fdmClient: FdmSDK, + dataNodes: DmsUniqueIdentifier[] +): Promise { + const inspectionResult = await fdmClient.inspectInstances({ + inspectionOperations: { involvedViewsAndContainers: {} }, + items: dataNodes.map((node) => ({ + instanceType: 'node', + externalId: node.externalId, + space: node.space + })) + }); + + return inspectionResult; +} + +export async function treeIndexesToNodeIds( + modelId: number, + revisionId: number, + treeIndexes: number[], + cogniteClient: CogniteClient +): Promise { + const outputsUrl = `${cogniteClient.getBaseUrl()}/api/v1/projects/${ + cogniteClient.project + }/3d/models/${modelId}/revisions/${revisionId}/nodes/internalids/bytreeindices`; + const response = await cogniteClient.post<{ items: number[] }>(outputsUrl, { + data: { items: treeIndexes } + }); + if (response.status === 200) { + return response.data.items; + } else { + throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes.join(',')}`); + } +} From 9a1b29c284c55e185bb9da8232e5674105747df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:08:19 +0200 Subject: [PATCH 08/17] fix: don't throw on finding mappings for non-mapped node --- .../src/components/NodeCacheProvider/RevisionNodeCache.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index 625537ced8f..1a05e713167 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -62,6 +62,10 @@ export class RevisionNodeCache { const { edges, ancestorsWithSameMapping, firstMappedAncestorTreeIndex } = await this.getClosestParentMapping(treeIndex); + if (edges.length === 0) { + return []; + } + const cachedFdmData = this._treeIndexToFdmData.get(firstMappedAncestorTreeIndex); if (cachedFdmData !== undefined) { From 98fb1a4010ad4395234c65a56beef4811c18ab47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:26:26 +0200 Subject: [PATCH 09/17] chore: lint fix and console log removal --- .../NodeCacheProvider/RevisionNodeCache.ts | 60 +++++++++++-------- .../components/NodeCacheProvider/requests.ts | 16 +++-- react-components/src/hooks/useClickedNode.tsx | 9 ++- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts index 1a05e713167..5a2b359b3cb 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts @@ -3,12 +3,14 @@ */ import { type CogniteClient, type Node3D } from '@cognite/sdk'; -import { - type FdmSDK, -} from '../../utilities/FdmSDK'; +import { type FdmSDK } from '../../utilities/FdmSDK'; import { type TreeIndex } from './NodeCache'; -import { fetchAncestorNodesForTreeIndex, getMappingEdgesForNodeIds, inspectNodes } from './requests'; +import { + fetchAncestorNodesForTreeIndex, + getMappingEdgesForNodeIds, + inspectNodes +} from './requests'; import { max } from 'lodash'; @@ -23,7 +25,6 @@ export class RevisionNodeCache { private readonly _revisionId: number; private readonly _treeIndexToFdmEdges = new Map(); - private readonly _treeIndexToFdmData = new Map(); constructor( @@ -43,15 +44,12 @@ export class RevisionNodeCache { const cachedFdmData = this._treeIndexToFdmData.get(searchTreeIndex); if (cachedFdmData !== undefined) { - console.log('Cache hit in first cache during tree index lookup'); return cachedFdmData; } const cachedFdmEdges = this._treeIndexToFdmEdges.get(searchTreeIndex); if (cachedFdmEdges !== undefined) { - console.log('Cache hit in second cache during tree index lookup'); - return await this.getDataWithViewsForFdmEdges(cachedFdmEdges, []); } @@ -99,7 +97,6 @@ export class RevisionNodeCache { this._fdmClient, nodeEdges.map((edge) => edge.edge.startNode) ); - console.log('Inspection results = ', nodeInspectionResults); const dataWithViews = nodeEdges.map((fdmEdgeWithNode, ind) => ({ fdmId: fdmEdgeWithNode.edge.startNode, @@ -111,7 +108,6 @@ export class RevisionNodeCache { this._treeIndexToFdmData.set(ancestor.treeIndex, dataWithViews) ); - console.log('Data with views = ', dataWithViews); return dataWithViews; } @@ -133,36 +129,46 @@ export class RevisionNodeCache { return { edges: [], ancestorsWithSameMapping: [], firstMappedAncestorTreeIndex: 0 }; } - const edgesWithCorrespondingTreeIndex = this.combineEdgesWithTreeIndex(ancestorMappings, ancestors); + const edgesWithCorrespondingTreeIndex = this.combineEdgesWithTreeIndex( + ancestorMappings, + ancestors + ); const firstMappedAncestorTreeIndex = findLargestTreeIndex(edgesWithCorrespondingTreeIndex); - return getAncestorDataForTreeIndex(firstMappedAncestorTreeIndex, edgesWithCorrespondingTreeIndex, ancestors); + return getAncestorDataForTreeIndex( + firstMappedAncestorTreeIndex, + edgesWithCorrespondingTreeIndex, + ancestors + ); } - private combineEdgesWithTreeIndex(mappingEdges: FdmCadEdge[], nodes: Node3D[]): { edge: FdmCadEdge, treeIndex: TreeIndex }[] { + private combineEdgesWithTreeIndex( + mappingEdges: FdmCadEdge[], + nodes: Node3D[] + ): Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }> { return mappingEdges.map((edge) => { const ancestorConnectedToEdge = nodes.find((a) => a.id === edge.properties.revisionNodeId); assert(ancestorConnectedToEdge !== undefined); return { - edge: edge, + edge, treeIndex: ancestorConnectedToEdge.treeIndex }; }); } private async getMappingEdgesForAncestors(ancestors: Node3D[]): Promise { - const cachedFirstMappedAncestor = ancestors.filter(a => { - this._treeIndexToFdmEdges.has(a.treeIndex) - }).sort((a, b) => b.treeIndex - a.treeIndex)[0]; + const cachedFirstMappedAncestor = ancestors + .filter((a) => this._treeIndexToFdmEdges.has(a.treeIndex)) + .sort((a, b) => b.treeIndex - a.treeIndex)[0]; if (cachedFirstMappedAncestor !== undefined) { - const edges = this._treeIndexToFdmEdges.get(cachedFirstMappedAncestor.treeIndex); + const edgesAndNodes = this._treeIndexToFdmEdges.get(cachedFirstMappedAncestor.treeIndex); - assert(edges !== undefined); + assert(edgesAndNodes !== undefined); - return edges + return edgesAndNodes.map((edge) => edge.edge); } const ancestorMappings = await getMappingEdgesForNodeIds( @@ -196,15 +202,19 @@ export class RevisionNodeCache { } } -function findLargestTreeIndex(edgesWithTreeIndex: { edge: FdmCadEdge, treeIndex: TreeIndex }[]): TreeIndex { - const maxTreeIndex = max(edgesWithTreeIndex.map(e => e.treeIndex)); +function findLargestTreeIndex( + edgesWithTreeIndex: Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }> +): TreeIndex { + const maxTreeIndex = max(edgesWithTreeIndex.map((e) => e.treeIndex)); assert(maxTreeIndex !== undefined); return maxTreeIndex; } -function getAncestorDataForTreeIndex(treeIndex: TreeIndex, - edgesWithTreeIndex: { edge: FdmCadEdge, treeIndex: TreeIndex }[], - ancestors: Node3D[]): { +function getAncestorDataForTreeIndex( + treeIndex: TreeIndex, + edgesWithTreeIndex: Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }>, + ancestors: Node3D[] +): { edges: FdmCadEdge[]; ancestorsWithSameMapping: Node3D[]; firstMappedAncestorTreeIndex: number; diff --git a/react-components/src/components/NodeCacheProvider/requests.ts b/react-components/src/components/NodeCacheProvider/requests.ts index 56967301ba8..a1f59a1354b 100644 --- a/react-components/src/components/NodeCacheProvider/requests.ts +++ b/react-components/src/components/NodeCacheProvider/requests.ts @@ -2,10 +2,18 @@ * Copyright 2023 Cognite AS */ -import { CogniteClient, CogniteInternalId, Node3D } from '@cognite/sdk'; -import { DmsUniqueIdentifier, FdmSDK, InspectResultList } from '../../utilities/FdmSDK'; -import { FdmCadEdge } from './types'; -import { INSTANCE_SPACE_3D_DATA, InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from '../../utilities/globalDataModels'; +import { type CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; +import { + type DmsUniqueIdentifier, + type FdmSDK, + type InspectResultList +} from '../../utilities/FdmSDK'; +import { type FdmCadEdge } from './types'; +import { + INSTANCE_SPACE_3D_DATA, + type InModel3dEdgeProperties, + SYSTEM_3D_EDGE_SOURCE +} from '../../utilities/globalDataModels'; export async function fetchAncestorNodesForTreeIndex( modelId: number, diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index 6f40f257658..b1ee93b6b00 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -5,7 +5,7 @@ import { type CadIntersection, type PointerEventData } from '@cognite/reveal'; import { useReveal, type NodeDataResult } from '../'; import { useEffect, useState } from 'react'; -import { useNodeMappedData } from './useNodeMappedData'; +import { useFdm3dNodeData } from '../components/NodeCacheProvider/NodeCacheProvider'; export type ClickedNodeData = NodeDataResult & { intersection: CadIntersection; @@ -36,7 +36,12 @@ export const useClickedNodeData = (): ClickedNodeData | undefined => { }; }, [viewer]); - const nodeData = useNodeMappedData(cadIntersection?.treeIndex, cadIntersection?.model); + const nodeData = + useFdm3dNodeData( + cadIntersection?.model.modelId, + cadIntersection?.model.revisionId, + cadIntersection?.treeIndex + ).data ?? []; if (cadIntersection === undefined || nodeData.length === 0) { return undefined; From 63506b94b890ca4b2bc442ad8de1d58d9aca53bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:26:37 +0200 Subject: [PATCH 10/17] chore: remove unused file --- react-components/src/hooks/useNodeMappedData.tsx | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 react-components/src/hooks/useNodeMappedData.tsx diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx deleted file mode 100644 index 96e3225f064..00000000000 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import { type CogniteCadModel } from '@cognite/reveal'; -import { useFdm3dNodeData } from '../components/NodeCacheProvider/NodeCacheProvider'; -import { type Fdm3dNodeData } from '../components/NodeCacheProvider/Fdm3dNodeData'; - -export const useNodeMappedData = ( - treeIndex: number | undefined, - model: CogniteCadModel | undefined -): Fdm3dNodeData[] => { - const nodeCacheContent = useFdm3dNodeData(model?.modelId, model?.revisionId, treeIndex); - - return nodeCacheContent.data ?? []; -}; From 7a39e528ba88a9d52b87d1ead5ef72193b5265da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:35:04 +0200 Subject: [PATCH 11/17] chore: remove more console logs --- react-components/src/components/NodeCacheProvider/NodeCache.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/NodeCache.ts index a6a16af22dd..765f48cf1cc 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/NodeCache.ts @@ -78,7 +78,6 @@ export class FdmNodeCache { const [modelId, revisionId] = revisionKeyToIds(revisionKey); const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); - console.log('Inserting mappings - ', data.length, 'of them'); data.forEach((edgeAndNode) => { revisionCache.insertTreeIndexMappings(edgeAndNode.node.treeIndex, edgeAndNode); }); @@ -102,8 +101,6 @@ export class FdmNodeCache { ): Promise { const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); - console.log('In `getClosestParentExternalId`'); - return await revisionCache.getClosestParentFdmData(treeIndex); } From 3d70ea549ab5a65322b1166b6385a7758cb88032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:41:21 +0200 Subject: [PATCH 12/17] fix: wrong file name, refactoring --- .../NodeCacheProvider/NodeCacheProvider.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index b763174314e..d2994acecab 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -7,7 +7,7 @@ import { FdmNodeCache } from './NodeCache'; import { type ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; -import { type Fdm3dNodeData } from './Fdm3dNodeData'; +import { type Fdm3dNodeData } from './types'; import assert from 'assert'; @@ -45,23 +45,20 @@ export const useFdm3dNodeData = ( ): UseQueryResult => { const content = useContext(FdmNodeCacheContext); + const enableQuery = + content !== undefined && + modelId !== undefined && + revisionId !== undefined && + treeIndex !== undefined; + const result = useQuery( ['reveal', 'react-components', 'tree-index-to-external-id', modelId, revisionId, treeIndex], async () => { - assert( - content !== undefined && - modelId !== undefined && - revisionId !== undefined && - treeIndex !== undefined - ); + assert(enableQuery); return await content.cache.getClosestParentExternalId(modelId, revisionId, treeIndex); }, { - enabled: - content !== undefined && - modelId !== undefined && - revisionId !== undefined && - treeIndex !== undefined + enabled: enableQuery } ); From a7f073adb4babb2d7336c055426d4d651ce0a441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:47:18 +0200 Subject: [PATCH 13/17] chore: rename files --- .../NodeCacheProvider/{NodeCache.ts => FdmNodeCache.ts} | 8 ++++---- .../components/NodeCacheProvider/NodeCacheProvider.tsx | 2 +- .../{RevisionNodeCache.ts => RevisionFdmNodeCache.ts} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename react-components/src/components/NodeCacheProvider/{NodeCache.ts => FdmNodeCache.ts} (97%) rename react-components/src/components/NodeCacheProvider/{RevisionNodeCache.ts => RevisionFdmNodeCache.ts} (98%) diff --git a/react-components/src/components/NodeCacheProvider/NodeCache.ts b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts similarity index 97% rename from react-components/src/components/NodeCacheProvider/NodeCache.ts rename to react-components/src/components/NodeCacheProvider/FdmNodeCache.ts index 765f48cf1cc..2998876c596 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts @@ -4,7 +4,7 @@ import { type Node3D, type CogniteClient } from '@cognite/sdk'; import { type EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; -import { RevisionNodeCache } from './RevisionNodeCache'; +import { RevisionFdmNodeCache } from './RevisionFdmNodeCache'; import { type FdmEdgeWithNode, type Fdm3dNodeData } from './types'; import { type InModel3dEdgeProperties, @@ -30,7 +30,7 @@ export type RevisionTreeIndex = `${ModelId}-${RevisionId}-${TreeIndex}`; export type FdmId = DmsUniqueIdentifier; export class FdmNodeCache { - private readonly _revisionNodeCaches = new Map(); + private readonly _revisionNodeCaches = new Map(); private readonly _cdfClient: CogniteClient; private readonly _fdmClient: FdmSDK; @@ -123,7 +123,7 @@ export class FdmNodeCache { return mappings.edges; } - private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionNodeCache { + private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionFdmNodeCache { const revisionKey = createRevisionKey(modelId, revisionId); const revisionCache = this._revisionNodeCaches.get(revisionKey); @@ -132,7 +132,7 @@ export class FdmNodeCache { return revisionCache; } - const newRevisionCache = new RevisionNodeCache( + const newRevisionCache = new RevisionFdmNodeCache( this._cdfClient, this._fdmClient, modelId, diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index d2994acecab..42845dc998e 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -3,7 +3,7 @@ */ import { type ReactElement, type ReactNode, createContext, useContext, useMemo } from 'react'; -import { FdmNodeCache } from './NodeCache'; +import { FdmNodeCache } from './FdmNodeCache'; import { type ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; diff --git a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts similarity index 98% rename from react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts rename to react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts index 5a2b359b3cb..ae4f68fdc7c 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts @@ -4,7 +4,7 @@ import { type CogniteClient, type Node3D } from '@cognite/sdk'; import { type FdmSDK } from '../../utilities/FdmSDK'; -import { type TreeIndex } from './NodeCache'; +import { type TreeIndex } from './FdmNodeCache'; import { fetchAncestorNodesForTreeIndex, @@ -17,7 +17,7 @@ import { max } from 'lodash'; import assert from 'assert'; import { type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './types'; -export class RevisionNodeCache { +export class RevisionFdmNodeCache { private readonly _cogniteClient: CogniteClient; private readonly _fdmClient: FdmSDK; From d09fe0fb32ca0843a875fbf89e1f3b9b0896c6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 15:50:59 +0200 Subject: [PATCH 14/17] chore: rename function --- .../src/components/NodeCacheProvider/FdmNodeCache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts index 2998876c596..7d7c10f8759 100644 --- a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts @@ -54,7 +54,7 @@ export class FdmNodeCache { const revisionToEdgesMap = await this.getRevisionToEdgesMap(nonCachedRevisionIds); - this.cacheRevisionData(revisionToEdgesMap); + this.writeRevisionDataToCache(revisionToEdgesMap); cachedEdges.forEach(([revisionKey, edges]) => { revisionToEdgesMap.set(revisionKey, edges); @@ -73,7 +73,7 @@ export class FdmNodeCache { return [revisionKey, cachedRevisionEdges]; } - private cacheRevisionData(modelMap: Map): void { + private writeRevisionDataToCache(modelMap: Map): void { for (const [revisionKey, data] of modelMap.entries()) { const [modelId, revisionId] = revisionKeyToIds(revisionKey); const revisionCache = this.getOrCreateRevisionCache(modelId, revisionId); From 29e43f537fdea7ff8d4f8f5207a16a8ab02ef3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 16:27:05 +0200 Subject: [PATCH 15/17] chore: some more refactoring --- .../NodeCacheProvider/FdmNodeCache.ts | 113 ++++++++++-------- .../src/components/NodeCacheProvider/types.ts | 11 ++ .../useMappedEquipmentBy3DModelsList.tsx | 4 +- 3 files changed, 79 insertions(+), 49 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts index 7d7c10f8759..328e4e174a7 100644 --- a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts @@ -3,16 +3,16 @@ */ import { type Node3D, type CogniteClient } from '@cognite/sdk'; -import { type EdgeItem, type DmsUniqueIdentifier, type FdmSDK } from '../../utilities/FdmSDK'; +import { type EdgeItem, type FdmSDK } from '../../utilities/FdmSDK'; import { RevisionFdmNodeCache } from './RevisionFdmNodeCache'; -import { type FdmEdgeWithNode, type Fdm3dNodeData } from './types'; +import { type FdmEdgeWithNode, type Fdm3dNodeData, FdmCadEdge, RevisionKey, RevisionTreeIndex, FdmKey, FdmId, RevisionId, NodeId, ModelNodeIdKey, ModelId } from './types'; import { type InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; import { - type ModelRevisionId, + type ModelRevisionKey, type ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; @@ -20,15 +20,6 @@ import { partition } from 'lodash'; import assert from 'assert'; -export type ModelId = number; -export type RevisionId = number; -export type TreeIndex = number; - -export type RevisionKey = `${ModelId}-${RevisionId}`; -export type FdmKey = `${string}-${string}`; -export type RevisionTreeIndex = `${ModelId}-${RevisionId}-${TreeIndex}`; -export type FdmId = DmsUniqueIdentifier; - export class FdmNodeCache { private readonly _revisionNodeCaches = new Map(); @@ -184,64 +175,92 @@ export function insertIntoSetMap(key: T, value: U, globalMap: Map) } async function groupToModelRevision( - edges: Array>, + edges: Array, modelRevisionIds: Array<{ modelId: number; revisionId: number }>, cdfClient: CogniteClient ): Promise> { - const nodeIdsPerRevision = edges.reduce((revisionNodeIdMap, edge) => { - const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); - if (nodeIdsInRevision !== undefined) { - nodeIdsInRevision.push(edge.properties.revisionNodeId); - } else { - revisionNodeIdMap.set(edge.properties.revisionId, [edge.properties.revisionNodeId]); - } + const revisionToNodeIdsMap = createRevisionToNodeIdMap(edges); + const modelNodeIdToNodeMap = await createModelNodeIdToNodeMap(revisionToNodeIdsMap, modelRevisionIds, cdfClient); - return revisionNodeIdMap; - }, new Map()); + return edges.reduce((map, edge) => { + const edgeRevisionId = edge.properties.revisionId; + const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); - type RevisionNodeId = `${RevisionId}-${number}`; - const revisionNodeIdToNode = new Map(); + if (modelRevisionId === undefined) return map; + + const value = createFdmEdgeWithNode(modelRevisionId, edge, modelNodeIdToNodeMap); + + insertEdgeIntoMapList(value, map, modelRevisionId); + + return map; + }, new Map()); +} + +function createFdmEdgeWithNode(modelRevisionId: { modelId: number, revisionId: number }, + edge: FdmCadEdge, + modelNodeIdToNodeMap: Map): FdmEdgeWithNode { + + const revisionNodeIdKey = + `${modelRevisionId.modelId}-${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; + + const node = modelNodeIdToNodeMap.get(revisionNodeIdKey); + assert(node !== undefined); + + return { edge, node }; +} - const treeIndexesPromises = [...nodeIdsPerRevision.entries()].map( +function insertEdgeIntoMapList(value: FdmEdgeWithNode, map: Map, modelRevisionId: { modelId: number, revisionId: number }): void { + + + const modelRevisionIdKey: ModelRevisionKey = createRevisionKey( + modelRevisionId.modelId, + modelRevisionId.revisionId + ); + + const edgesForModel = map.get(modelRevisionIdKey); + + if (edgesForModel === undefined) { + map.set(modelRevisionIdKey, [value]); + } else { + edgesForModel.push(value); + } +} + +async function createModelNodeIdToNodeMap(revisionToNodeIdsMap: Map, modelRevisionIds: Array<{ modelId: ModelId; revisionId: RevisionId }>, cdfClient: CogniteClient): Promise> { + + const revisionNodeIdToNode = new Map(); + + const nodePromises = [...revisionToNodeIdsMap.entries()].map( async ([revisionId, nodeIds]) => { const modelId = modelRevisionIds.find((p) => p.revisionId === revisionId)?.modelId; assert(modelId !== undefined); const nodes = await nodeIdsToNodes(modelId, revisionId, nodeIds, cdfClient); nodeIds.forEach((e, ind) => { - const revisionNodeIdKey = `${revisionId}-${e}` as const; - revisionNodeIdToNode.set(revisionNodeIdKey, nodes[ind]); + const modelNodeIdKey = `${modelId}-${revisionId}-${e}` as const; + revisionNodeIdToNode.set(modelNodeIdKey, nodes[ind]); }); } ); - await Promise.all(treeIndexesPromises); + await Promise.all(nodePromises); - return edges.reduce((map, edge) => { - const edgeRevisionId = edge.properties.revisionId; - const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); - if (modelRevisionId === undefined) return map; - const modelRevisionIdKey: ModelRevisionId = createRevisionKey( - modelRevisionId.modelId, - modelRevisionId.revisionId - ); - const edgesForModel = map.get(modelRevisionIdKey); - const revisionNodeIdKey = - `${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; + return revisionNodeIdToNode; +} - const node = revisionNodeIdToNode.get(revisionNodeIdKey); - assert(node !== undefined); +function createRevisionToNodeIdMap(edges: Array): Map { + return edges.reduce((revisionNodeIdMap, edge) => { - const value = { edge, node }; + const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); - if (edgesForModel === undefined) { - map.set(modelRevisionIdKey, [value]); + if (nodeIdsInRevision !== undefined) { + nodeIdsInRevision.push(edge.properties.revisionNodeId); } else { - edgesForModel.push(value); + revisionNodeIdMap.set(edge.properties.revisionId, [edge.properties.revisionNodeId]); } - return map; - }, new Map()); + return revisionNodeIdMap; + }, new Map()); } async function nodeIdsToNodes( diff --git a/react-components/src/components/NodeCacheProvider/types.ts b/react-components/src/components/NodeCacheProvider/types.ts index 59c1d93c946..8d4ad971242 100644 --- a/react-components/src/components/NodeCacheProvider/types.ts +++ b/react-components/src/components/NodeCacheProvider/types.ts @@ -8,3 +8,14 @@ import { type InModel3dEdgeProperties } from '../../utilities/globalDataModels'; export type Fdm3dNodeData = { fdmId: DmsUniqueIdentifier; view: Source; cadNode: Node3D }; export type FdmCadEdge = EdgeItem; export type FdmEdgeWithNode = { edge: FdmCadEdge; node: Node3D }; + +export type ModelId = number; +export type RevisionId = number; +export type TreeIndex = number; +export type NodeId = number; +export type FdmId = DmsUniqueIdentifier; + +export type RevisionKey = `${ModelId}-${RevisionId}`; +export type FdmKey = `${string}-${string}`; +export type RevisionTreeIndex = `${ModelId}-${RevisionId}-${TreeIndex}`; +export type ModelNodeIdKey = `${ModelId}-${RevisionId}-${NodeId}`; diff --git a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx index 1a61c3adbae..f417505fe4b 100644 --- a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx +++ b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx @@ -5,8 +5,8 @@ import { type UseQueryResult } from '@tanstack/react-query'; import { useGetAllExternalIds } from '../components/NodeCacheProvider/NodeCacheProvider'; import { type FdmEdgeWithNode } from '../components/NodeCacheProvider/types'; -export type ModelRevisionId = `${number}-${number}`; -export type ModelRevisionToEdgeMap = Map; +export type ModelRevisionKey = `${number}-${number}`; +export type ModelRevisionToEdgeMap = Map; export const useMappedEquipmentByRevisionList = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, From 6ade95a462375bc490e0eeaf530f835e3fcbae77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 17:04:17 +0200 Subject: [PATCH 16/17] chore: more refactoring --- .../NodeCacheProvider/FdmNodeCache.ts | 23 ++++--------------- .../NodeCacheProvider/NodeCacheProvider.tsx | 5 ++-- .../NodeCacheProvider/RevisionFdmNodeCache.ts | 2 +- .../components/NodeCacheProvider/requests.ts | 13 +++++++++++ .../src/hooks/useCalculateModelsStyling.tsx | 4 ++-- .../useMappedEquipmentBy3DModelsList.tsx | 16 ------------- 6 files changed, 23 insertions(+), 40 deletions(-) delete mode 100644 react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx diff --git a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts index 328e4e174a7..cb9be4a1e1c 100644 --- a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts @@ -11,14 +11,14 @@ import { SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; -import { - type ModelRevisionKey, - type ModelRevisionToEdgeMap -} from '../../hooks/useMappedEquipmentBy3DModelsList'; + +export type ModelRevisionKey = `${number}-${number}`; +export type ModelRevisionToEdgeMap = Map; import { partition } from 'lodash'; import assert from 'assert'; +import { fetchNodesForNodeIds } from './requests'; export class FdmNodeCache { private readonly _revisionNodeCaches = new Map(); @@ -235,7 +235,7 @@ async function createModelNodeIdToNodeMap(revisionToNodeIdsMap: Map p.revisionId === revisionId)?.modelId; assert(modelId !== undefined); - const nodes = await nodeIdsToNodes(modelId, revisionId, nodeIds, cdfClient); + const nodes = await fetchNodesForNodeIds(modelId, revisionId, nodeIds, cdfClient); nodeIds.forEach((e, ind) => { const modelNodeIdKey = `${modelId}-${revisionId}-${e}` as const; revisionNodeIdToNode.set(modelNodeIdKey, nodes[ind]); @@ -262,16 +262,3 @@ function createRevisionToNodeIdMap(edges: Array): Map()); } - -async function nodeIdsToNodes( - modelId: number, - revisionId: number, - nodeIds: number[], - cogniteClient: CogniteClient -): Promise { - return await cogniteClient.revisions3D.retrieve3DNodes( - modelId, - revisionId, - nodeIds.map((id) => ({ id })) - ); -} diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index 42845dc998e..35fe34ab589 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -3,8 +3,7 @@ */ import { type ReactElement, type ReactNode, createContext, useContext, useMemo } from 'react'; -import { FdmNodeCache } from './FdmNodeCache'; -import { type ModelRevisionToEdgeMap } from '../../hooks/useMappedEquipmentBy3DModelsList'; +import { FdmNodeCache, ModelRevisionToEdgeMap } from './FdmNodeCache'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; import { type Fdm3dNodeData } from './types'; @@ -17,7 +16,7 @@ export type FdmNodeCacheContent = { export const FdmNodeCacheContext = createContext(undefined); -export const useGetAllExternalIds = ( +export const useMappedEdgesForRevisions = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, enabled: boolean ): UseQueryResult => { diff --git a/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts index ae4f68fdc7c..22086537050 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts @@ -4,7 +4,7 @@ import { type CogniteClient, type Node3D } from '@cognite/sdk'; import { type FdmSDK } from '../../utilities/FdmSDK'; -import { type TreeIndex } from './FdmNodeCache'; +import { type TreeIndex } from './types'; import { fetchAncestorNodesForTreeIndex, diff --git a/react-components/src/components/NodeCacheProvider/requests.ts b/react-components/src/components/NodeCacheProvider/requests.ts index a1f59a1354b..42829f77a3a 100644 --- a/react-components/src/components/NodeCacheProvider/requests.ts +++ b/react-components/src/components/NodeCacheProvider/requests.ts @@ -113,3 +113,16 @@ export async function treeIndexesToNodeIds( throw Error(`treeIndex-nodeId translation failed for treeIndexes ${treeIndexes.join(',')}`); } } + +export async function fetchNodesForNodeIds( + modelId: number, + revisionId: number, + nodeIds: number[], + cogniteClient: CogniteClient +): Promise { + return await cogniteClient.revisions3D.retrieve3DNodes( + modelId, + revisionId, + nodeIds.map((id) => ({ id })) + ); +} diff --git a/react-components/src/hooks/useCalculateModelsStyling.tsx b/react-components/src/hooks/useCalculateModelsStyling.tsx index 73eea6a0a63..0e60723696d 100644 --- a/react-components/src/hooks/useCalculateModelsStyling.tsx +++ b/react-components/src/hooks/useCalculateModelsStyling.tsx @@ -10,7 +10,6 @@ import { type NodeStylingGroup, type CadModelStyling } from '../components/CadModelContainer/CadModelContainer'; -import { useMappedEquipmentByRevisionList } from './useMappedEquipmentBy3DModelsList'; import { type InModel3dEdgeProperties } from '../utilities/globalDataModels'; import { type EdgeItem } from '../utilities/FdmSDK'; import { type NodeAppearance } from '@cognite/reveal'; @@ -18,6 +17,7 @@ import { type ThreeDModelMappings } from './types'; import { type CogniteExternalId, type CogniteInternalId } from '@cognite/sdk'; import { useFdmAssetMappings } from './useFdmAssetMappings'; import { useEffect, useMemo } from 'react'; +import { useMappedEdgesForRevisions } from '../components/NodeCacheProvider/NodeCacheProvider'; type ModelStyleGroup = { model: TypedReveal3DModel; @@ -43,7 +43,7 @@ function useCalculateMappedStyling(models: TypedReveal3DModel[]): ModelStyleGrou (model) => model.styling?.mapped !== undefined ); const shouldFetchAllMappedEquipment = modelsRevisionsWithMappedEquipment.length > 0; - const { data: mappedEquipmentEdges } = useMappedEquipmentByRevisionList( + const { data: mappedEquipmentEdges } = useMappedEdgesForRevisions( modelsRevisionsWithMappedEquipment, shouldFetchAllMappedEquipment ); diff --git a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx b/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx deleted file mode 100644 index f417505fe4b..00000000000 --- a/react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ -import { type UseQueryResult } from '@tanstack/react-query'; -import { useGetAllExternalIds } from '../components/NodeCacheProvider/NodeCacheProvider'; -import { type FdmEdgeWithNode } from '../components/NodeCacheProvider/types'; - -export type ModelRevisionKey = `${number}-${number}`; -export type ModelRevisionToEdgeMap = Map; - -export const useMappedEquipmentByRevisionList = ( - modelRevisionIds: Array<{ modelId: number; revisionId: number }>, - enabled = true -): UseQueryResult => { - return useGetAllExternalIds(modelRevisionIds, enabled); -}; From 7fe16ad537d8328bd73d907b66abab793636f48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 17:15:31 +0200 Subject: [PATCH 17/17] chore: respond to review --- .../NodeCacheProvider/FdmNodeCache.ts | 75 ++++++++++++------- .../NodeCacheProvider/NodeCacheProvider.tsx | 2 +- .../NodeCacheProvider/RevisionFdmNodeCache.ts | 25 ++++--- 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts index cb9be4a1e1c..079911708af 100644 --- a/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/FdmNodeCache.ts @@ -5,21 +5,33 @@ import { type Node3D, type CogniteClient } from '@cognite/sdk'; import { type EdgeItem, type FdmSDK } from '../../utilities/FdmSDK'; import { RevisionFdmNodeCache } from './RevisionFdmNodeCache'; -import { type FdmEdgeWithNode, type Fdm3dNodeData, FdmCadEdge, RevisionKey, RevisionTreeIndex, FdmKey, FdmId, RevisionId, NodeId, ModelNodeIdKey, ModelId } from './types'; +import { + type FdmEdgeWithNode, + type Fdm3dNodeData, + type FdmCadEdge, + type RevisionKey, + type RevisionTreeIndex, + type FdmKey, + type FdmId, + type RevisionId, + type NodeId, + type ModelNodeIdKey, + type ModelId +} from './types'; import { type InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../../utilities/globalDataModels'; -export type ModelRevisionKey = `${number}-${number}`; -export type ModelRevisionToEdgeMap = Map; - import { partition } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds } from './requests'; +export type ModelRevisionKey = `${number}-${number}`; +export type ModelRevisionToEdgeMap = Map; + export class FdmNodeCache { private readonly _revisionNodeCaches = new Map(); @@ -175,12 +187,16 @@ export function insertIntoSetMap(key: T, value: U, globalMap: Map) } async function groupToModelRevision( - edges: Array, + edges: FdmCadEdge[], modelRevisionIds: Array<{ modelId: number; revisionId: number }>, cdfClient: CogniteClient ): Promise> { const revisionToNodeIdsMap = createRevisionToNodeIdMap(edges); - const modelNodeIdToNodeMap = await createModelNodeIdToNodeMap(revisionToNodeIdsMap, modelRevisionIds, cdfClient); + const modelNodeIdToNodeMap = await createModelNodeIdToNodeMap( + revisionToNodeIdsMap, + modelRevisionIds, + cdfClient + ); return edges.reduce((map, edge) => { const edgeRevisionId = edge.properties.revisionId; @@ -196,10 +212,11 @@ async function groupToModelRevision( }, new Map()); } -function createFdmEdgeWithNode(modelRevisionId: { modelId: number, revisionId: number }, - edge: FdmCadEdge, - modelNodeIdToNodeMap: Map): FdmEdgeWithNode { - +function createFdmEdgeWithNode( + modelRevisionId: { modelId: number; revisionId: number }, + edge: FdmCadEdge, + modelNodeIdToNodeMap: Map +): FdmEdgeWithNode { const revisionNodeIdKey = `${modelRevisionId.modelId}-${modelRevisionId.revisionId}-${edge.properties.revisionNodeId}` as const; @@ -209,9 +226,11 @@ function createFdmEdgeWithNode(modelRevisionId: { modelId: number, revisionId: n return { edge, node }; } -function insertEdgeIntoMapList(value: FdmEdgeWithNode, map: Map, modelRevisionId: { modelId: number, revisionId: number }): void { - - +function insertEdgeIntoMapList( + value: FdmEdgeWithNode, + map: Map, + modelRevisionId: { modelId: number; revisionId: number } +): void { const modelRevisionIdKey: ModelRevisionKey = createRevisionKey( modelRevisionId.modelId, modelRevisionId.revisionId @@ -226,31 +245,31 @@ function insertEdgeIntoMapList(value: FdmEdgeWithNode, map: Map, modelRevisionIds: Array<{ modelId: ModelId; revisionId: RevisionId }>, cdfClient: CogniteClient): Promise> { - +async function createModelNodeIdToNodeMap( + revisionToNodeIdsMap: Map, + modelRevisionIds: Array<{ modelId: ModelId; revisionId: RevisionId }>, + cdfClient: CogniteClient +): Promise> { const revisionNodeIdToNode = new Map(); - const nodePromises = [...revisionToNodeIdsMap.entries()].map( - async ([revisionId, nodeIds]) => { - const modelId = modelRevisionIds.find((p) => p.revisionId === revisionId)?.modelId; - assert(modelId !== undefined); + const nodePromises = [...revisionToNodeIdsMap.entries()].map(async ([revisionId, nodeIds]) => { + const modelId = modelRevisionIds.find((p) => p.revisionId === revisionId)?.modelId; + assert(modelId !== undefined); - const nodes = await fetchNodesForNodeIds(modelId, revisionId, nodeIds, cdfClient); - nodeIds.forEach((e, ind) => { - const modelNodeIdKey = `${modelId}-${revisionId}-${e}` as const; - revisionNodeIdToNode.set(modelNodeIdKey, nodes[ind]); - }); - } - ); + const nodes = await fetchNodesForNodeIds(modelId, revisionId, nodeIds, cdfClient); + nodeIds.forEach((e, ind) => { + const modelNodeIdKey = `${modelId}-${revisionId}-${e}` as const; + revisionNodeIdToNode.set(modelNodeIdKey, nodes[ind]); + }); + }); await Promise.all(nodePromises); return revisionNodeIdToNode; } -function createRevisionToNodeIdMap(edges: Array): Map { +function createRevisionToNodeIdMap(edges: FdmCadEdge[]): Map { return edges.reduce((revisionNodeIdMap, edge) => { - const nodeIdsInRevision = revisionNodeIdMap.get(edge.properties.revisionId); if (nodeIdsInRevision !== undefined) { diff --git a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx index 35fe34ab589..4b7cc2e015c 100644 --- a/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/NodeCacheProvider/NodeCacheProvider.tsx @@ -3,7 +3,7 @@ */ import { type ReactElement, type ReactNode, createContext, useContext, useMemo } from 'react'; -import { FdmNodeCache, ModelRevisionToEdgeMap } from './FdmNodeCache'; +import { FdmNodeCache, type ModelRevisionToEdgeMap } from './FdmNodeCache'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; import { type Fdm3dNodeData } from './types'; diff --git a/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts index 22086537050..02b75e531e1 100644 --- a/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts +++ b/react-components/src/components/NodeCacheProvider/RevisionFdmNodeCache.ts @@ -4,7 +4,7 @@ import { type CogniteClient, type Node3D } from '@cognite/sdk'; import { type FdmSDK } from '../../utilities/FdmSDK'; -import { type TreeIndex } from './types'; +import { type TreeIndex, type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './types'; import { fetchAncestorNodesForTreeIndex, @@ -15,7 +15,6 @@ import { import { max } from 'lodash'; import assert from 'assert'; -import { type Fdm3dNodeData, type FdmEdgeWithNode, type FdmCadEdge } from './types'; export class RevisionFdmNodeCache { private readonly _cogniteClient: CogniteClient; @@ -73,19 +72,19 @@ export class RevisionFdmNodeCache { } const firstMappedAncestor = ancestorsWithSameMapping.find( - (a) => a.treeIndex === firstMappedAncestorTreeIndex + (ancestor) => ancestor.treeIndex === firstMappedAncestorTreeIndex ); assert(firstMappedAncestor !== undefined); - const nodeEdges = edges.map((e) => ({ edge: e, node: firstMappedAncestor })); + const nodeEdges = edges.map((edge) => ({ edge, node: firstMappedAncestor })); return await this.getDataWithViewsForFdmEdges(nodeEdges, ancestorsWithSameMapping); } private setCacheForNodes(nodes: Node3D[], nodeData: Fdm3dNodeData[]): void { - nodes.forEach((n) => { - this._treeIndexToFdmData.set(n.treeIndex, nodeData); + nodes.forEach((node) => { + this._treeIndexToFdmData.set(node.treeIndex, nodeData); }); } @@ -147,7 +146,9 @@ export class RevisionFdmNodeCache { nodes: Node3D[] ): Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }> { return mappingEdges.map((edge) => { - const ancestorConnectedToEdge = nodes.find((a) => a.id === edge.properties.revisionNodeId); + const ancestorConnectedToEdge = nodes.find( + (ancestor) => ancestor.id === edge.properties.revisionNodeId + ); assert(ancestorConnectedToEdge !== undefined); @@ -160,8 +161,8 @@ export class RevisionFdmNodeCache { private async getMappingEdgesForAncestors(ancestors: Node3D[]): Promise { const cachedFirstMappedAncestor = ancestors - .filter((a) => this._treeIndexToFdmEdges.has(a.treeIndex)) - .sort((a, b) => b.treeIndex - a.treeIndex)[0]; + .filter((ancestor) => this._treeIndexToFdmEdges.has(ancestor.treeIndex)) + .sort((nodeA, nodeB) => nodeB.treeIndex - nodeA.treeIndex)[0]; if (cachedFirstMappedAncestor !== undefined) { const edgesAndNodes = this._treeIndexToFdmEdges.get(cachedFirstMappedAncestor.treeIndex); @@ -219,9 +220,11 @@ function getAncestorDataForTreeIndex( ancestorsWithSameMapping: Node3D[]; firstMappedAncestorTreeIndex: number; } { - const edgesForFirstMappedAncestor = edgesWithTreeIndex.filter((a) => a.treeIndex === treeIndex); + const edgesForFirstMappedAncestor = edgesWithTreeIndex.filter( + (edgeAndTreeIndex) => edgeAndTreeIndex.treeIndex === treeIndex + ); const ancestorsBetweenSearchNodeAndFirstMappedAncestor = ancestors.filter( - (a) => a.treeIndex >= treeIndex + (ancestor) => ancestor.treeIndex >= treeIndex ); return {