From deab2f2cae79fa15caa1dbf835d9005874872c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= <70905152+haakonflatval-cognite@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:41:26 +0200 Subject: [PATCH 1/3] chore(react-components): send 1000 assets to asset mappings filter (#4683) --- .../src/components/CacheProvider/AssetMappingAndNode3DCache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-components/src/components/CacheProvider/AssetMappingAndNode3DCache.ts b/react-components/src/components/CacheProvider/AssetMappingAndNode3DCache.ts index 2b6cb7aac18..e0f36c22c81 100644 --- a/react-components/src/components/CacheProvider/AssetMappingAndNode3DCache.ts +++ b/react-components/src/components/CacheProvider/AssetMappingAndNode3DCache.ts @@ -297,7 +297,7 @@ export class AssetMappingAndNode3DCache { if (ids.length === 0) { return []; } - const idChunks = chunk(ids, 100); + const idChunks = chunk(ids, 1000); const initialIndex = 0; const assetMappings = await this.fetchMappingsInQueue( initialIndex, From cd14a65a6ebff16589bd87693d7d0b1c708db173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= <70905152+haakonflatval-cognite@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:46:42 +0200 Subject: [PATCH 2/3] fix: don't consider hidden pointclouds when computing sectors to load and bump to 4.16.1 (#4682) * fix: don't consider hidden pointclouds when computing sectors to load * chore: bump to version 4.16.1 --- viewer/package.json | 2 +- viewer/packages/pointclouds/src/PointCloudManager.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/viewer/package.json b/viewer/package.json index 7593215a3fe..cde0b699972 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -1,6 +1,6 @@ { "name": "@cognite/reveal", - "version": "4.16.0", + "version": "4.16.1", "description": "WebGL based 3D viewer for CAD and point clouds processed in Cognite Data Fusion.", "homepage": "https://github.com/cognitedata/reveal/tree/master/viewer", "repository": { diff --git a/viewer/packages/pointclouds/src/PointCloudManager.ts b/viewer/packages/pointclouds/src/PointCloudManager.ts index b89b7e7c7bb..a3219f7d3f7 100644 --- a/viewer/packages/pointclouds/src/PointCloudManager.ts +++ b/viewer/packages/pointclouds/src/PointCloudManager.ts @@ -105,7 +105,7 @@ export class PointCloudManager { } updatePointClouds(camera: THREE.PerspectiveCamera): void { - const octrees = this._pointCloudNodes.map(node => node.octree); + const octrees = this._pointCloudNodes.filter(node => node.visible).map(node => node.octree); this._potreeInstance.updatePointClouds(octrees, camera, this._renderer); } From 43a84d22365c8b56086e06bda4420524bbc6cc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= <70905152+haakonflatval-cognite@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:53:13 +0200 Subject: [PATCH 3/3] refactor(react-components): factor out Fdm3dDataProvider, in preparations for CoreDM support (#4675) * chore: preliminary refactoring * temp: WIP legacy Fdm3dDataProvider * fix: compile errors * chore: finish current stage of legacy implementation * chore: lint fix * chore: type check fixes * chore: more renames * chore: use models in dependency array in revision mapping fetch * chore: cleanup * chore: remove comments * chore: remove unused code * chore: call things connections, not edges --- .../base/renderTarget/RevealRenderTarget.ts | 2 +- .../observations/ObservationsCache.ts | 2 +- .../observations/ObservationsDomainObject.ts | 2 +- .../concrete/observations/models.ts | 6 +- .../concrete/observations/network.ts | 2 +- .../concrete/observations/types.ts | 2 +- .../components/CacheProvider/FdmNodeCache.ts | 231 +++++----- .../CacheProvider/NodeCacheProvider.tsx | 17 +- .../CacheProvider/RevisionFdmNodeCache.ts | 199 +++++---- .../src/components/CacheProvider/requests.ts | 83 +--- .../src/components/CacheProvider/types.ts | 22 +- .../src/components/Reveal3DResources/types.ts | 2 +- .../useCalculateCadStyling.tsx | 4 +- .../RevealCanvas/FdmDataProviderContext.ts | 18 + .../components/RevealCanvas/SDKProvider.tsx | 29 +- .../components/RevealToolbar/SceneList.tsx | 2 +- .../RevealToolbar/SelectSceneButton.tsx | 2 +- .../RuleBasedOutputsSelector.tsx | 2 +- .../components/RuleBasedOutputs/constants.ts | 2 +- .../hooks/useDeleteRuleInstance.tsx | 2 +- .../src/components/RuleBasedOutputs/types.ts | 2 +- .../src/components/RuleBasedOutputs/utils.ts | 2 +- .../src/components/SceneContainer/Queries.tsx | 2 +- .../src/data-providers/Fdm3dDataProvider.ts | 51 +++ .../{utilities => data-providers}/FdmSDK.ts | 0 .../LegacyFdm3dDataProvider.ts | 85 ++++ .../createMappedEquipmentQuery.ts | 73 ++++ .../legacy-fdm-provider/dataModels.ts} | 13 +- .../fdmEdgesToCadConnections.ts | 17 + .../filterNodesByMappedTo3d.ts | 230 ++++++++++ .../getCadConnectionsForRevision.ts | 32 ++ .../getCadModelsForFdmInstance.ts | 11 +- .../legacy-fdm-provider/getDMSModels.ts | 30 ++ .../getEdgeConnected3dInstances.ts | 93 ++++ .../getFdmConnectionsForNodeIds.ts | 57 +++ .../legacy-fdm-provider/listMappedFdmNodes.ts | 76 ++++ .../{utilities => data-providers}/types.ts | 0 .../src/hooks/network/getRelationships.ts | 2 +- .../hooks/network/getSourceRelationships.ts | 2 +- .../hooks/network/getTargetRelationships.ts | 2 +- react-components/src/hooks/types.ts | 2 +- .../src/hooks/useCameraNavigation.tsx | 2 +- react-components/src/hooks/useClickedNode.tsx | 2 +- react-components/src/index.ts | 8 +- .../src/query/use3dNodeByExternalId.tsx | 7 +- .../query/use3dRelatedDirectConnections.ts | 28 +- .../src/query/use3dRelatedEdgeConnections.ts | 101 +---- react-components/src/query/use3dScenes.tsx | 2 +- .../useAssetsAndTimeseriesLinkageDataQuery.ts | 2 +- .../src/query/useModelsForInstanceQuery.ts | 19 +- react-components/src/query/useSceneConfig.ts | 2 +- .../src/query/useSearchMappedEquipmentFDM.tsx | 405 ++---------------- .../src/utilities/fdmViewsExist.ts | 2 +- .../src/utilities/removeEmptyProperties.ts | 23 + .../stories/CadStylingCache.stories.tsx | 2 +- .../stories/SearchHooks.stories.tsx | 43 +- 56 files changed, 1178 insertions(+), 883 deletions(-) create mode 100644 react-components/src/components/RevealCanvas/FdmDataProviderContext.ts create mode 100644 react-components/src/data-providers/Fdm3dDataProvider.ts rename react-components/src/{utilities => data-providers}/FdmSDK.ts (100%) create mode 100644 react-components/src/data-providers/legacy-fdm-provider/LegacyFdm3dDataProvider.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/createMappedEquipmentQuery.ts rename react-components/src/{utilities/globalDataModels.ts => data-providers/legacy-fdm-provider/dataModels.ts} (59%) create mode 100644 react-components/src/data-providers/legacy-fdm-provider/fdmEdgesToCadConnections.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/filterNodesByMappedTo3d.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/getCadConnectionsForRevision.ts rename react-components/src/{hooks/network => data-providers/legacy-fdm-provider}/getCadModelsForFdmInstance.ts (91%) create mode 100644 react-components/src/data-providers/legacy-fdm-provider/getDMSModels.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/getEdgeConnected3dInstances.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/getFdmConnectionsForNodeIds.ts create mode 100644 react-components/src/data-providers/legacy-fdm-provider/listMappedFdmNodes.ts rename react-components/src/{utilities => data-providers}/types.ts (100%) create mode 100644 react-components/src/utilities/removeEmptyProperties.ts diff --git a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts index 55a1e3a9561..c5c583d4460 100644 --- a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts +++ b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts @@ -31,7 +31,7 @@ import { Range3 } from '../utilities/geometry/Range3'; import { getBoundingBoxFromPlanes } from '../utilities/geometry/getBoundingBoxFromPlanes'; import { Changes } from '../domainObjectsHelpers/Changes'; import { type CogniteClient } from '@cognite/sdk/dist/src'; -import { FdmSDK } from '../../../utilities/FdmSDK'; +import { FdmSDK } from '../../../data-providers/FdmSDK'; const DIRECTIONAL_LIGHT_NAME = 'DirectionalLight'; diff --git a/react-components/src/architecture/concrete/observations/ObservationsCache.ts b/react-components/src/architecture/concrete/observations/ObservationsCache.ts index c5f6b05d26c..bc9cca6fe0d 100644 --- a/react-components/src/architecture/concrete/observations/ObservationsCache.ts +++ b/react-components/src/architecture/concrete/observations/ObservationsCache.ts @@ -1,7 +1,7 @@ /*! * Copyright 2024 Cognite AS */ -import { type FdmSDK } from '../../../utilities/FdmSDK'; +import { type FdmSDK } from '../../../data-providers/FdmSDK'; import { type ObservationFdmNode, type ObservationProperties } from './models'; import { type Observation } from './types'; diff --git a/react-components/src/architecture/concrete/observations/ObservationsDomainObject.ts b/react-components/src/architecture/concrete/observations/ObservationsDomainObject.ts index 21530203a68..c4bde335ff6 100644 --- a/react-components/src/architecture/concrete/observations/ObservationsDomainObject.ts +++ b/react-components/src/architecture/concrete/observations/ObservationsDomainObject.ts @@ -5,7 +5,7 @@ import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject' import { type ThreeView } from '../../base/views/ThreeView'; import { ObservationsView } from './ObservationsView'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; -import { type FdmSDK } from '../../../utilities/FdmSDK'; +import { type FdmSDK } from '../../../data-providers/FdmSDK'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { ObservationsCache } from './ObservationsCache'; import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; diff --git a/react-components/src/architecture/concrete/observations/models.ts b/react-components/src/architecture/concrete/observations/models.ts index 139bb37f811..346d16f2676 100644 --- a/react-components/src/architecture/concrete/observations/models.ts +++ b/react-components/src/architecture/concrete/observations/models.ts @@ -1,7 +1,11 @@ /*! * Copyright 2024 Cognite AS */ -import { type DmsUniqueIdentifier, type FdmNode, type Source } from '../../../utilities/FdmSDK'; +import { + type DmsUniqueIdentifier, + type FdmNode, + type Source +} from '../../../data-providers/FdmSDK'; export type ObservationProperties = { // "ID as the node appears in the Source system" diff --git a/react-components/src/architecture/concrete/observations/network.ts b/react-components/src/architecture/concrete/observations/network.ts index 98eb4d17a9a..09460c77bcd 100644 --- a/react-components/src/architecture/concrete/observations/network.ts +++ b/react-components/src/architecture/concrete/observations/network.ts @@ -7,7 +7,7 @@ import { type DmsUniqueIdentifier, type FdmSDK, type InstanceFilter -} from '../../../utilities/FdmSDK'; +} from '../../../data-providers/FdmSDK'; import { type ObservationFdmNode, OBSERVATION_SOURCE, type ObservationProperties } from './models'; import { v4 as uuid } from 'uuid'; diff --git a/react-components/src/architecture/concrete/observations/types.ts b/react-components/src/architecture/concrete/observations/types.ts index a7271501ec5..92ea743dda6 100644 --- a/react-components/src/architecture/concrete/observations/types.ts +++ b/react-components/src/architecture/concrete/observations/types.ts @@ -8,7 +8,7 @@ import { } from '@cognite/reveal'; import { type ObservationProperties } from './models'; import { type Vector3 } from 'three'; -import { type FdmNode } from '../../../utilities/FdmSDK'; +import { type FdmNode } from '../../../data-providers/FdmSDK'; import { type DomainObjectIntersection } from '../../base/domainObjectsHelpers/DomainObjectIntersection'; import { type ObservationsDomainObject } from './ObservationsDomainObject'; diff --git a/react-components/src/components/CacheProvider/FdmNodeCache.ts b/react-components/src/components/CacheProvider/FdmNodeCache.ts index 20f1b18e011..2132d33b899 100644 --- a/react-components/src/components/CacheProvider/FdmNodeCache.ts +++ b/react-components/src/components/CacheProvider/FdmNodeCache.ts @@ -3,58 +3,56 @@ */ import { type Node3D, type CogniteClient, type CogniteExternalId } from '@cognite/sdk'; -import { - type Source, - type DmsUniqueIdentifier, - type EdgeItem, - type FdmSDK -} from '../../utilities/FdmSDK'; +import { type Source, type DmsUniqueIdentifier, type FdmSDK } from '../../data-providers/FdmSDK'; import { RevisionFdmNodeCache } from './RevisionFdmNodeCache'; import { - type FdmEdgeWithNode, - type FdmCadEdge, + type FdmConnectionWithNode, + type FdmCadConnection, type ModelRevisionKey, type RevisionId, type NodeId, type ModelNodeIdKey, - type ModelRevisionToEdgeMap, + type ModelRevisionToConnectionMap, type ModelRevisionId, type FdmKey, type FdmNodeDataPromises } from './types'; + import { createFdmKey, createModelNodeIdKey, createModelRevisionKey, revisionKeyToIds } from './idAndKeyTranslation'; -import { - type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE, - SYSTEM_SPACE_3D_SCHEMA -} from '../../utilities/globalDataModels'; import { partition } from 'lodash'; import assert from 'assert'; import { fetchNodesForNodeIds, inspectNodes } from './requests'; import { type ThreeDModelFdmMappings } from '../../hooks/types'; +import { type Fdm3dDataProvider } from '../../data-providers/Fdm3dDataProvider'; export class FdmNodeCache { private readonly _revisionNodeCaches = new Map(); private readonly _cdfClient: CogniteClient; private readonly _fdmClient: FdmSDK; + private readonly _fdm3dDataProvider: Fdm3dDataProvider; private readonly _completeRevisions = new Set(); - public constructor(cdfClient: CogniteClient, fdmClient: FdmSDK) { + public constructor( + cdfClient: CogniteClient, + fdmClient: FdmSDK, + fdm3dDataProvider: Fdm3dDataProvider + ) { this._cdfClient = cdfClient; this._fdmClient = fdmClient; + this._fdm3dDataProvider = fdm3dDataProvider; } - public async getMappingsForFdmIds( - externalIds: DmsUniqueIdentifier[], + public async getMappingsForFdmInstances( + instances: DmsUniqueIdentifier[], modelRevisionIds: ModelRevisionId[] ): Promise { const [cachedModelRevisionIds, nonCachedModelRevisionIds] = partition( @@ -66,11 +64,11 @@ export class FdmNodeCache { ); const nonCachedModelMappings = await this.getNonCachedModelMappings( - externalIds, + instances, nonCachedModelRevisionIds ); - const cachedModelMappings = this.getCachedModelMappings(cachedModelRevisionIds, externalIds); + const cachedModelMappings = this.getCachedModelMappings(cachedModelRevisionIds, instances); const combinedList = [...cachedModelMappings, ...nonCachedModelMappings]; return combinedList; @@ -98,13 +96,16 @@ export class FdmNodeCache { modelRevisionId.revisionId ); - const relevantCachedEdgeData = intersectWithStartNodeIdSet( - revisionCache.getAllEdges(), + const relevantCachedConnectionData = intersectWithFdmKeySet( + revisionCache.getAllConnections(), relevantFdmKeySet ); const mappings = createMapWithAccumulatedValues( - relevantCachedEdgeData.map((data) => [data.edge.startNode.externalId, data.cadNode]) + relevantCachedConnectionData.map((data) => [ + data.connection.instance.externalId, + data.cadNode + ]) ); return { @@ -114,43 +115,47 @@ export class FdmNodeCache { } private async getNonCachedModelMappings( - uniqueIds: DmsUniqueIdentifier[], + instances: DmsUniqueIdentifier[], modelRevisions: ModelRevisionId[] ): Promise { - if (modelRevisions.length === 0 || uniqueIds.length === 0) { + if (modelRevisions.length === 0 || instances.length === 0) { return []; } - const fdmKeySet = new Set(uniqueIds.map((id) => createFdmKey(id.space, id.externalId))); + const fdmKeySet = new Set(instances.map((id) => createFdmKey(id.space, id.externalId))); - const revisionToEdgesMap = await this.getAndCacheRevisionToEdgesMap(modelRevisions, false); + const revisionToConnectionsMap = await this.getAndCacheRevisionToConnectionsMap( + modelRevisions, + false + ); - const modelDataPromises = modelRevisions.map(async ({ modelId, revisionId }) => { + return modelRevisions.map(({ modelId, revisionId }) => { const revisionKey = createModelRevisionKey(modelId, revisionId); - const edges = revisionToEdgesMap.get(revisionKey); + const connections = revisionToConnectionsMap.get(revisionKey); return this.getRelevantExternalIdToNodeMapForRevision( { modelId, revisionId }, - edges, + connections, fdmKeySet ); }); - - return await Promise.all(modelDataPromises); } private getRelevantExternalIdToNodeMapForRevision( { modelId, revisionId }: ModelRevisionId, - edges: FdmEdgeWithNode[] | undefined, + connections: FdmConnectionWithNode[] | undefined, relevantFdmKeySet: Set ): ThreeDModelFdmMappings { - if (edges === undefined || edges.length === 0) + if (connections === undefined || connections.length === 0) return { modelId, revisionId, mappings: new Map() }; - const relevantEdges = intersectWithStartNodeIdSet(edges, relevantFdmKeySet); + const relevantConnections = intersectWithFdmKeySet(connections, relevantFdmKeySet); const externalIdToNodeMap = createMapWithAccumulatedValues( - relevantEdges.map((edge) => [edge.edge.startNode.externalId, edge.cadNode]) + relevantConnections.map((connection) => [ + connection.connection.instance.externalId, + connection.cadNode + ]) ); return { @@ -163,7 +168,7 @@ export class FdmNodeCache { public async getAllMappingExternalIds( modelRevisionIds: ModelRevisionId[], fetchViews: boolean = false - ): Promise { + ): Promise { const [cachedRevisionIds, nonCachedRevisionIds] = partition(modelRevisionIds, (ids) => { const key = createModelRevisionKey(ids.modelId, ids.revisionId); return this._completeRevisions.has(key); @@ -173,18 +178,20 @@ export class FdmNodeCache { await this.fetchAllViewsForCachedRevisions(cachedRevisionIds); } - const cachedEdges = cachedRevisionIds.map((id) => this.getCachedEdgesForRevision(id)); + const cachedConnections = cachedRevisionIds.map((id) => + this.getCachedConnectionsForRevision(id) + ); - const revisionToEdgesMap = await this.getAndCacheRevisionToEdgesMap( + const revisionToConnectionsMap = await this.getAndCacheRevisionToConnectionsMap( nonCachedRevisionIds, fetchViews ); - cachedEdges.forEach(([revisionKey, edges]) => { - revisionToEdgesMap.set(revisionKey, edges); + cachedConnections.forEach(([revisionKey, connections]) => { + revisionToConnectionsMap.set(revisionKey, connections); }); - return revisionToEdgesMap; + return revisionToConnectionsMap; } private async fetchAllViewsForCachedRevisions( @@ -196,55 +203,58 @@ export class FdmNodeCache { for (const revision of revisions) { const revisionCache = this.getOrCreateRevisionCache(revision.modelId, revision.revisionId); - await revisionCache.fetchViewsForAllEdges(); + await revisionCache.fetchViewsForAllConnections(); } } - private getCachedEdgesForRevision(id: { + private getCachedConnectionsForRevision(id: { modelId: number; revisionId: number; - }): [ModelRevisionKey, FdmEdgeWithNode[]] { + }): [ModelRevisionKey, FdmConnectionWithNode[]] { const revisionCache = this.getOrCreateRevisionCache(id.modelId, id.revisionId); const revisionKey = createModelRevisionKey(id.modelId, id.revisionId); - const cachedRevisionEdges = revisionCache.getAllEdges(); + const cachedRevisionConnections = revisionCache.getAllConnections(); - return [revisionKey, cachedRevisionEdges]; + return [revisionKey, cachedRevisionConnections]; } - private writeRevisionDataToCache(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); - data.forEach((edgeAndNode) => { - revisionCache.insertTreeIndexMappings(edgeAndNode.cadNode.treeIndex, edgeAndNode); + data.forEach((connectionAndNode) => { + revisionCache.insertTreeIndexMappings( + connectionAndNode.cadNode.treeIndex, + connectionAndNode + ); }); this._completeRevisions.add(revisionKey); } } - private async getAndCacheRevisionToEdgesMap( + private async getAndCacheRevisionToConnectionsMap( modelRevisionIds: ModelRevisionId[], fetchViews: boolean - ): Promise> { + ): Promise> { const revisionIds = modelRevisionIds.map((modelRevisionId) => modelRevisionId.revisionId); - const edges = await this.getEdgesForRevisions(revisionIds, this._fdmClient); + const connections = await this._fdm3dDataProvider.getCadConnectionsForRevisions(revisionIds); - const edgesWithOptionalViews = fetchViews - ? await this.getViewsForEdges(edges) - : edges.map((edge) => ({ edge })); + const connectionsWithOptionalViews = fetchViews + ? await this.getViewsForConnections(connections) + : connections.map((connection) => ({ connection })); - const revisionToEdgesMap = await createRevisionToEdgesMap( - edgesWithOptionalViews, + const revisionToConnectionsMap = await createRevisionToConnectionsMap( + connectionsWithOptionalViews, modelRevisionIds, this._cdfClient ); - this.writeRevisionDataToCache(revisionToEdgesMap); + this.writeRevisionDataToCache(revisionToConnectionsMap); - return revisionToEdgesMap; + return revisionToConnectionsMap; } public getClosestParentDataPromises( @@ -257,43 +267,22 @@ export class FdmNodeCache { return revisionCache.getClosestParentFdmData(treeIndex); } - private async getViewsForEdges( - edges: FdmCadEdge[] - ): Promise> { + private async getViewsForConnections( + connections: FdmCadConnection[] + ): Promise> { const nodeInspectionResults = await inspectNodes( this._fdmClient, - edges.map((edge) => edge.startNode) + connections.map((connection) => connection.instance) ); - const dataWithViews = edges.map((edge, ind) => ({ - edge, + const dataWithViews = connections.map((connection, ind) => ({ + connection, view: nodeInspectionResults.items[ind].inspectionResults.involvedViews[0] })); return dataWithViews; } - private async getEdgesForRevisions( - revisionIds: number[], - fdmClient: FdmSDK - ): Promise>> { - if (revisionIds.length === 0) return []; - - 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.instances; - } - private getOrCreateRevisionCache(modelId: number, revisionId: number): RevisionFdmNodeCache { const revisionKey = createModelRevisionKey(modelId, revisionId); @@ -306,6 +295,7 @@ export class FdmNodeCache { const newRevisionCache = new RevisionFdmNodeCache( this._cdfClient, this._fdmClient, + this._fdm3dDataProvider, modelId, revisionId ); @@ -316,58 +306,58 @@ export class FdmNodeCache { } } -async function createRevisionToEdgesMap( - edgesWithViews: Array<{ edge: FdmCadEdge; view?: Source }>, +async function createRevisionToConnectionsMap( + connectionsWithView: Array<{ connection: FdmCadConnection; view?: Source }>, modelRevisionIds: ModelRevisionId[], cdfClient: CogniteClient -): Promise> { - const revisionToNodeIdsMap = createRevisionToNodeIdMap(edgesWithViews); +): Promise> { + const revisionToNodeIdsMap = createRevisionToNodeIdMap(connectionsWithView); const modelNodeIdToNodeMap = await createModelNodeIdToNodeMap( revisionToNodeIdsMap, modelRevisionIds, cdfClient ); - return edgesWithViews.reduce((map, edgeWithView) => { - const edgeRevisionId = edgeWithView.edge.properties.revisionId; - const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === edgeRevisionId); + return connectionsWithView.reduce((map, connectionWithView) => { + const connectionRevisionId = connectionWithView.connection.revisionId; + const modelRevisionId = modelRevisionIds.find((p) => p.revisionId === connectionRevisionId); if (modelRevisionId === undefined) return map; - const value = createFdmEdgeWithNode( + const value = createFdmConnectionWithNode( modelRevisionId, modelNodeIdToNodeMap, - edgeWithView.edge, - edgeWithView.view + connectionWithView.connection, + connectionWithView.view ); - insertEdgeIntoMapList(value, map, modelRevisionId); + insertConnectionIntoMapList(value, map, modelRevisionId); return map; - }, new Map()); + }, new Map()); } -function createFdmEdgeWithNode( +function createFdmConnectionWithNode( modelRevisionId: ModelRevisionId, modelNodeIdToNodeMap: Map, - edge: FdmCadEdge, + connection: FdmCadConnection, view?: Source -): FdmEdgeWithNode { +): FdmConnectionWithNode { const revisionNodeIdKey = createModelNodeIdKey( modelRevisionId.modelId, modelRevisionId.revisionId, - edge.properties.revisionNodeId + connection.nodeId ); const node = modelNodeIdToNodeMap.get(revisionNodeIdKey); assert(node !== undefined); - return { edge, cadNode: node, view }; + return { connection, cadNode: node, view }; } -function insertEdgeIntoMapList( - value: FdmEdgeWithNode, - map: Map, +function insertConnectionIntoMapList( + value: FdmConnectionWithNode, + map: Map, modelRevisionId: ModelRevisionId ): void { const modelRevisionIdKey: ModelRevisionKey = createModelRevisionKey( @@ -375,12 +365,12 @@ function insertEdgeIntoMapList( modelRevisionId.revisionId ); - const edgesForModel = map.get(modelRevisionIdKey); + const connectionsForModel = map.get(modelRevisionIdKey); - if (edgesForModel === undefined) { + if (connectionsForModel === undefined) { map.set(modelRevisionIdKey, [value]); } else { - edgesForModel.push(value); + connectionsForModel.push(value); } } @@ -408,29 +398,32 @@ async function createModelNodeIdToNodeMap( } function createRevisionToNodeIdMap( - edgesWithViews: Array<{ edge: FdmCadEdge; view?: Source }> + connections: Array<{ connection: FdmCadConnection; view?: Source }> ): Map { - return edgesWithViews.reduce((revisionNodeIdMap, edgeWithView) => { - const { revisionNodeId, revisionId } = edgeWithView.edge.properties; + return connections.reduce((revisionNodeIdMap, connectionWithView) => { + const { nodeId, revisionId } = connectionWithView.connection; const nodeIdsInRevision = revisionNodeIdMap.get(revisionId); if (nodeIdsInRevision !== undefined) { - nodeIdsInRevision.push(revisionNodeId); + nodeIdsInRevision.push(nodeId); } else { - revisionNodeIdMap.set(revisionId, [revisionNodeId]); + revisionNodeIdMap.set(revisionId, [nodeId]); } return revisionNodeIdMap; }, new Map()); } -function intersectWithStartNodeIdSet( - edges: FdmEdgeWithNode[], +function intersectWithFdmKeySet( + connections: FdmConnectionWithNode[], relevantFdmKeySet: Set -): FdmEdgeWithNode[] { - return edges.filter((edgeData) => { - const fdmKey = createFdmKey(edgeData.edge.startNode.space, edgeData.edge.startNode.externalId); +): FdmConnectionWithNode[] { + return connections.filter((connectionData) => { + const fdmKey = createFdmKey( + connectionData.connection.instance.space, + connectionData.connection.instance.externalId + ); return relevantFdmKeySet.has(fdmKey); }); } diff --git a/react-components/src/components/CacheProvider/NodeCacheProvider.tsx b/react-components/src/components/CacheProvider/NodeCacheProvider.tsx index f4a54e9c19b..7c5b97b54a0 100644 --- a/react-components/src/components/CacheProvider/NodeCacheProvider.tsx +++ b/react-components/src/components/CacheProvider/NodeCacheProvider.tsx @@ -5,11 +5,11 @@ import { type ReactElement, type ReactNode, createContext, useContext, useMemo } from 'react'; import { FdmNodeCache } from './FdmNodeCache'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { useFdmSdk, useSDK } from '../RevealCanvas/SDKProvider'; -import { type FdmNodeDataPromises, type ModelRevisionToEdgeMap } from './types'; +import { useFdm3dDataProvider, useFdmSdk, useSDK } from '../RevealCanvas/SDKProvider'; +import { type FdmNodeDataPromises, type ModelRevisionToConnectionMap } from './types'; import assert from 'assert'; -import { type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; +import { type DmsUniqueIdentifier } from '../../data-providers/FdmSDK'; import { type TypedReveal3DModel } from '../Reveal3DResources/types'; import { type ThreeDModelFdmMappings } from '../../hooks/types'; import { DEFAULT_QUERY_STALE_TIME } from '../../utilities/constants'; @@ -36,7 +36,7 @@ export const useMappedEdgesForRevisions = ( modelRevisionIds: Array<{ modelId: number; revisionId: number }>, fetchViews = false, enabled = true -): UseQueryResult => { +): UseQueryResult => { const content = useFdmNodeCache(); return useQuery({ @@ -99,9 +99,8 @@ export const useFdmAssetMappings = ( fdmAssetExternalIds, models.map((model) => [model.modelId, model.revisionId]) ], - queryFn: async () => { - return await nodeCacheContent.cache.getMappingsForFdmIds(fdmAssetExternalIds, models); - }, + queryFn: async () => + await nodeCacheContent.cache.getMappingsForFdmInstances(fdmAssetExternalIds, models), enabled: fdmAssetExternalIds.length > 0 && models.length > 0, staleTime: DEFAULT_QUERY_STALE_TIME }); @@ -109,12 +108,14 @@ export const useFdmAssetMappings = ( export function NodeCacheProvider({ children }: { children?: ReactNode }): ReactElement { const fdmClient = useFdmSdk(); + const fdm3dDataProvider = useFdm3dDataProvider(); const cdfClient = useSDK(); const revealKeepAliveData = useRevealKeepAlive(); const fdmCache = useMemo(() => { const cache = - revealKeepAliveData?.fdmNodeCache.current ?? new FdmNodeCache(cdfClient, fdmClient); + revealKeepAliveData?.fdmNodeCache.current ?? + new FdmNodeCache(cdfClient, fdmClient, fdm3dDataProvider); const isRevealKeepAliveContextProvided = revealKeepAliveData !== undefined; if (isRevealKeepAliveContextProvided) { diff --git a/react-components/src/components/CacheProvider/RevisionFdmNodeCache.ts b/react-components/src/components/CacheProvider/RevisionFdmNodeCache.ts index d30a563f65e..f40de99e7ef 100644 --- a/react-components/src/components/CacheProvider/RevisionFdmNodeCache.ts +++ b/react-components/src/components/CacheProvider/RevisionFdmNodeCache.ts @@ -3,54 +3,53 @@ */ import { type CogniteClient, type Node3D } from '@cognite/sdk'; -import { type Source, type FdmSDK, type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; +import { type Source, type FdmSDK, type DmsUniqueIdentifier } from '../../data-providers/FdmSDK'; import { type TreeIndex, - type FdmEdgeWithNode, - type FdmCadEdge, + type FdmConnectionWithNode, + type FdmCadConnection, type FdmNodeDataPromises, - type CadNodeWithEdges, + type CadNodeWithConnections, type AncestorQueryResult } from './types'; -import { - fetchAncestorNodesForTreeIndex, - getDMSModels, - getMappingEdgesForNodeIds, - inspectNodes -} from './requests'; +import { fetchAncestorNodesForTreeIndex, inspectNodes } from './requests'; import { max } from 'lodash'; import assert from 'assert'; +import { type Fdm3dDataProvider } from '../../data-providers/Fdm3dDataProvider'; export class RevisionFdmNodeCache { private readonly _cogniteClient: CogniteClient; private readonly _fdmClient: FdmSDK; + private readonly _fdm3dDataProvider: Fdm3dDataProvider; private readonly _modelId: number; private readonly _revisionId: number; - private readonly _treeIndexToFdmEdges = new Map(); + private readonly _treeIndexToFdmConnections = new Map(); private readonly _modelInstances: Promise; constructor( cogniteClient: CogniteClient, fdmClient: FdmSDK, + fdmDataProvider: Fdm3dDataProvider, modelId: number, revisionId: number ) { this._cogniteClient = cogniteClient; this._fdmClient = fdmClient; + this._fdm3dDataProvider = fdmDataProvider; this._modelId = modelId; this._revisionId = revisionId; - this._modelInstances = getDMSModels(this._modelId, this._fdmClient).catch(() => undefined); + this._modelInstances = fdmDataProvider.getDMSModels(this._modelId).catch(() => undefined); } public getClosestParentFdmData(searchTreeIndex: number): FdmNodeDataPromises { - const cachedFdmData = this._treeIndexToFdmEdges.get(searchTreeIndex); + const cachedFdmData = this._treeIndexToFdmConnections.get(searchTreeIndex); if (cachedFdmData === undefined) { return this.findAndCacheNodeDataFromAncestors(searchTreeIndex); @@ -65,7 +64,7 @@ export class RevisionFdmNodeCache { const cadAndFdmNodesPromise = Promise.resolve({ cadNode: cachedFdmData[0].cadNode, - fdmIds: cachedFdmData.map((data) => data.edge.startNode) + fdmIds: cachedFdmData.map((data) => data.connection.instance) }); const viewsPromise = this.assertOrFetchViewsForNodeData(searchTreeIndex, cachedFdmData); @@ -75,19 +74,19 @@ export class RevisionFdmNodeCache { private async assertOrFetchViewsForNodeData( searchTreeIndex: number, - cachedFdmData: FdmEdgeWithNode[] + cachedFdmData: FdmConnectionWithNode[] ): Promise { if (checkDefinedView(cachedFdmData)) { return cachedFdmData.map((data) => data.view); } const cadNode = cachedFdmData[0].cadNode; - const cadNodeWithEdges = { + const cadNodeWithConnections = { cadNode, - edges: cachedFdmData.map((data) => data.edge) + connections: cachedFdmData.map((data) => data.connection) }; - return await this.getAndCacheViewsPromiseForNodeData(cadNodeWithEdges, [ + return await this.getAndCacheViewsPromiseForNodeData(cadNodeWithConnections, [ cadNode.treeIndex, searchTreeIndex ]); @@ -96,15 +95,19 @@ export class RevisionFdmNodeCache { private findAndCacheNodeDataFromAncestors(treeIndex: TreeIndex): FdmNodeDataPromises { const ancestorDataPromise = this.getClosestParentMapping(treeIndex); - const cadAndEdgesPromise = this.getCadAndEdgesPromiseForAncestorData(ancestorDataPromise); - const cadAndFdmNodesPromise = cadAndEdgesPromise.then((cadAndEdges) => - cadAndEdges === undefined + const cadAndConnectionsPromise = + this.getCadAndConnectionsPromiseForAncestorData(ancestorDataPromise); + const cadAndFdmNodesPromise = cadAndConnectionsPromise.then((cadAndConnections) => + cadAndConnections === undefined ? undefined - : { cadNode: cadAndEdges.cadNode, fdmIds: cadAndEdges.edges.map((edge) => edge.startNode) } + : { + cadNode: cadAndConnections.cadNode, + fdmIds: cadAndConnections.connections.map((connection) => connection.instance) + } ); const viewsPromise = this.getViewsPromiseFromDataPromises( - cadAndEdgesPromise, + cadAndConnectionsPromise, ancestorDataPromise ); @@ -112,20 +115,20 @@ export class RevisionFdmNodeCache { } private async getViewsPromiseFromDataPromises( - cadAndEdgesPromise: Promise, + cadAndConnectionsPromise: Promise, ancestorDataPromise: Promise ): Promise { - const cadAndEdges = await cadAndEdgesPromise; + const cadAndConnections = await cadAndConnectionsPromise; const { ancestorsWithSameMapping } = await ancestorDataPromise; const ancestorTreeIndexes = ancestorsWithSameMapping.map((ancestor) => ancestor.treeIndex); const cachedTreeIndexesDescending = ancestorTreeIndexes - .filter((treeIndex) => this._treeIndexToFdmEdges.has(treeIndex)) + .filter((treeIndex) => this._treeIndexToFdmConnections.has(treeIndex)) .sort((a, b) => b - a); const cachedNodeData = cachedTreeIndexesDescending.length !== 0 - ? this._treeIndexToFdmEdges.get(cachedTreeIndexesDescending[0]) + ? this._treeIndexToFdmConnections.get(cachedTreeIndexesDescending[0]) : undefined; if (checkDefinedView(cachedNodeData)) { @@ -133,16 +136,16 @@ export class RevisionFdmNodeCache { return cachedNodeData.map((data) => data.view); } - return await this.getAndCacheViewsPromiseForNodeData(cadAndEdges, ancestorTreeIndexes); + return await this.getAndCacheViewsPromiseForNodeData(cadAndConnections, ancestorTreeIndexes); } - private async getCadAndEdgesPromiseForAncestorData( + private async getCadAndConnectionsPromiseForAncestorData( ancestorDataPromise: Promise - ): Promise { - const { edges, ancestorsWithSameMapping, firstMappedAncestorTreeIndex } = + ): Promise { + const { connections, ancestorsWithSameMapping, firstMappedAncestorTreeIndex } = await ancestorDataPromise; - if (edges.length === 0) { + if (connections.length === 0) { this.setCacheDataForTreeIndices( ancestorsWithSameMapping.map((a) => a.treeIndex), [] @@ -156,17 +159,20 @@ export class RevisionFdmNodeCache { assert(firstMappedAncestor !== undefined); - return { cadNode: firstMappedAncestor, edges }; + return { cadNode: firstMappedAncestor, connections }; } - private setCacheDataForTreeIndices(treeIndices: number[], nodeData: FdmEdgeWithNode[]): void { + private setCacheDataForTreeIndices( + treeIndices: number[], + nodeData: FdmConnectionWithNode[] + ): void { treeIndices.forEach((treeIndex) => { - this._treeIndexToFdmEdges.set(treeIndex, nodeData); + this._treeIndexToFdmConnections.set(treeIndex, nodeData); }); } private async getAndCacheViewsPromiseForNodeData( - cadAndFdmIds: CadNodeWithEdges | undefined, + cadAndFdmIds: CadNodeWithConnections | undefined, ancestorIndicesWithSameMapping: TreeIndex[] ): Promise { if (cadAndFdmIds === undefined) { @@ -176,15 +182,15 @@ export class RevisionFdmNodeCache { const nodeInspectionResults = await inspectNodes( this._fdmClient, - cadAndFdmIds.edges.map((edge) => edge.startNode) + cadAndFdmIds.connections.map((connection) => connection.instance) ); const views = nodeInspectionResults.items.map( (item) => item.inspectionResults.involvedViews[0] ); - const dataWithViews = cadAndFdmIds.edges.map((edge, ind) => ({ - edge, + const dataWithViews = cadAndFdmIds.connections.map((connection, ind) => ({ + connection, cadNode: cadAndFdmIds.cadNode, view: nodeInspectionResults.items[ind].inspectionResults.involvedViews[0] })); @@ -202,143 +208,156 @@ export class RevisionFdmNodeCache { this._cogniteClient ); - const ancestorMappings = await this.getMappingEdgesForAncestors(ancestors); + const ancestorMappings = await this.getMappingConnectionsForAncestors(ancestors); if (ancestorMappings.length === 0) { - return { edges: [], ancestorsWithSameMapping: ancestors, firstMappedAncestorTreeIndex: -1 }; + return { + connections: [], + ancestorsWithSameMapping: ancestors, + firstMappedAncestorTreeIndex: -1 + }; } - const edgesWithCorrespondingTreeIndex = this.combineEdgesWithTreeIndex( + const connectionsWithCorrespondingTreeIndex = this.combineConnectionsWithTreeIndex( ancestorMappings, ancestors ); - const firstMappedAncestorTreeIndex = findLargestTreeIndex(edgesWithCorrespondingTreeIndex); + const firstMappedAncestorTreeIndex = findLargestTreeIndex( + connectionsWithCorrespondingTreeIndex + ); return getAncestorDataForTreeIndex( firstMappedAncestorTreeIndex, - edgesWithCorrespondingTreeIndex, + connectionsWithCorrespondingTreeIndex, ancestors ); } - private combineEdgesWithTreeIndex( - mappingEdges: FdmCadEdge[], + private combineConnectionsWithTreeIndex( + mappingConnections: FdmCadConnection[], nodes: Node3D[] - ): Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }> { - return mappingEdges.map((edge) => { - const ancestorConnectedToEdge = nodes.find( - (ancestor) => ancestor.id === edge.properties.revisionNodeId - ); + ): Array<{ connection: FdmCadConnection; treeIndex: TreeIndex }> { + return mappingConnections.map((connection) => { + const nodeInConnection = nodes.find((node) => node.id === connection.nodeId); - assert(ancestorConnectedToEdge !== undefined); + assert(nodeInConnection !== undefined); return { - edge, - treeIndex: ancestorConnectedToEdge.treeIndex + connection, + treeIndex: nodeInConnection.treeIndex }; }); } - private async getMappingEdgesForAncestors(ancestors: Node3D[]): Promise { + private async getMappingConnectionsForAncestors( + ancestors: Node3D[] + ): Promise { const cachedFirstMappedAncestor = ancestors - .filter((ancestor) => this._treeIndexToFdmEdges.has(ancestor.treeIndex)) + .filter((ancestor) => this._treeIndexToFdmConnections.has(ancestor.treeIndex)) .sort((nodeA, nodeB) => nodeB.treeIndex - nodeA.treeIndex)[0]; if (cachedFirstMappedAncestor !== undefined) { - const edgesAndNodes = this._treeIndexToFdmEdges.get(cachedFirstMappedAncestor.treeIndex); + const connectionsAndNodes = this._treeIndexToFdmConnections.get( + cachedFirstMappedAncestor.treeIndex + ); - assert(edgesAndNodes !== undefined); + assert(connectionsAndNodes !== undefined); - return edgesAndNodes.map((edge) => edge.edge); + return connectionsAndNodes.map((connection) => connection.connection); } const modelInstances = await this._modelInstances; if (modelInstances === undefined) { return []; } - const ancestorMappings = await getMappingEdgesForNodeIds( + const ancestorMappings = await this._fdm3dDataProvider.getFdmConnectionsForNodeIds( modelInstances, this._revisionId, - this._fdmClient, ancestors.map((a) => a.id) ); - return ancestorMappings.edges; + return ancestorMappings; } - public async fetchViewsForAllEdges(): Promise { - const allEdgesWithoutView = this.getAllEdges().filter((edge) => edge.view === undefined); + public async fetchViewsForAllConnections(): Promise { + const allConnectionsWithoutView = this.getAllConnections().filter( + (connection) => connection.view === undefined + ); - if (allEdgesWithoutView.length === 0) { + if (allConnectionsWithoutView.length === 0) { return; } const nodeInspectionResults = await inspectNodes( this._fdmClient, - allEdgesWithoutView.map((edge) => edge.edge.startNode) + allConnectionsWithoutView.map((connection) => connection.connection.instance) ); - allEdgesWithoutView.forEach((fdmEdgeWithNode, ind) => { - const edgeWithView = { - ...fdmEdgeWithNode, + allConnectionsWithoutView.forEach((fdmConnectionWithNode, ind) => { + const connectionWithView = { + ...fdmConnectionWithNode, view: nodeInspectionResults.items[ind].inspectionResults.involvedViews[0] }; - this.insertTreeIndexMappings(edgeWithView.cadNode.treeIndex, edgeWithView); + this.insertTreeIndexMappings(connectionWithView.cadNode.treeIndex, connectionWithView); }); } - public insertTreeIndexMappings(treeIndex: TreeIndex, edge: FdmEdgeWithNode): void { - const edgeArray = this._treeIndexToFdmEdges.get(treeIndex); + public insertTreeIndexMappings(treeIndex: TreeIndex, connection: FdmConnectionWithNode): void { + const connectionArray = this._treeIndexToFdmConnections.get(treeIndex); - if (edgeArray === undefined) { - this._treeIndexToFdmEdges.set(treeIndex, [edge]); + if (connectionArray === undefined) { + this._treeIndexToFdmConnections.set(treeIndex, [connection]); } else { - const presentEdge = edgeArray?.find((e) => e.cadNode.id === edge.cadNode.id); + const presentConnection = connectionArray?.find( + (e) => e.cadNode.id === connection.cadNode.id + ); - if (presentEdge !== undefined) { - presentEdge.view = edge.view; + if (presentConnection !== undefined) { + presentConnection.view = connection.view; return; } - edgeArray.push(edge); + connectionArray.push(connection); } } - public getAllEdges(): FdmEdgeWithNode[] { - return [...this._treeIndexToFdmEdges.values()].flat(); + public getAllConnections(): FdmConnectionWithNode[] { + return [...this._treeIndexToFdmConnections.values()].flat(); } } function findLargestTreeIndex( - edgesWithTreeIndex: Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }> + connectionsWithTreeIndex: Array<{ connection: FdmCadConnection; treeIndex: TreeIndex }> ): TreeIndex { - const maxTreeIndex = max(edgesWithTreeIndex.map((e) => e.treeIndex)); + const maxTreeIndex = max(connectionsWithTreeIndex.map((e) => e.treeIndex)); assert(maxTreeIndex !== undefined); return maxTreeIndex; } function getAncestorDataForTreeIndex( treeIndex: TreeIndex, - edgesWithTreeIndex: Array<{ edge: FdmCadEdge; treeIndex: TreeIndex }>, + connectionsWithTreeIndex: Array<{ connection: FdmCadConnection; treeIndex: TreeIndex }>, ancestors: Node3D[] ): AncestorQueryResult { - const edgesForTreeIndex = edgesWithTreeIndex.filter( - (edgeAndTreeIndex) => edgeAndTreeIndex.treeIndex === treeIndex + const connectionsForTreeIndex = connectionsWithTreeIndex.filter( + (connectionAndTreeIndex) => connectionAndTreeIndex.treeIndex === treeIndex ); const ancestorsBelowTreeIndex = ancestors.filter((ancestor) => ancestor.treeIndex >= treeIndex); return { - edges: edgesForTreeIndex.map((result) => result.edge), + connections: connectionsForTreeIndex.map((result) => result.connection), ancestorsWithSameMapping: ancestorsBelowTreeIndex, firstMappedAncestorTreeIndex: treeIndex }; } export function checkDefinedView( - edges?: FdmEdgeWithNode[] -): edges is Array> { - if (edges === undefined) return false; + connections?: FdmConnectionWithNode[] +): connections is Array> { + if (connections === undefined) return false; - return edges?.every((edge): edge is Required => edge.view !== undefined); + return connections?.every( + (connection): connection is Required => connection.view !== undefined + ); } diff --git a/react-components/src/components/CacheProvider/requests.ts b/react-components/src/components/CacheProvider/requests.ts index 23f9a2071f7..439298ede92 100644 --- a/react-components/src/components/CacheProvider/requests.ts +++ b/react-components/src/components/CacheProvider/requests.ts @@ -2,21 +2,12 @@ * Copyright 2023 Cognite AS */ -import { type CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; +import { type CogniteClient, type Node3D } from '@cognite/sdk'; import { - type Source, type DmsUniqueIdentifier, type FdmSDK, type InspectResultList -} from '../../utilities/FdmSDK'; -import { type FdmCadEdge } from './types'; -import { - type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE, - SYSTEM_SPACE_3D_SCHEMA, - SYSTEM_SPACE_3D_MODEL_ID, - SYSTEM_SPACE_3D_MODEL_VERSION -} from '../../utilities/globalDataModels'; +} from '../../data-providers/FdmSDK'; import { chunk } from 'lodash'; export async function fetchAncestorNodesForTreeIndex( @@ -36,76 +27,6 @@ export async function fetchAncestorNodesForTreeIndex( return ancestorNodes.items; } -export async function getDMSModels( - modelId: number, - fdmClient: FdmSDK -): Promise { - const filter = { - equals: { - property: ['node', 'externalId'], - value: `${modelId}` - } - }; - const sources: Source = { - type: 'view', - space: SYSTEM_SPACE_3D_SCHEMA, - externalId: SYSTEM_SPACE_3D_MODEL_ID, - version: SYSTEM_SPACE_3D_MODEL_VERSION - }; - - const modelResults = await fdmClient.filterInstances(filter, 'node', sources); - return modelResults.instances; -} - -export async function getMappingEdgesForNodeIds( - models: DmsUniqueIdentifier[], - revisionId: number, - fdmClient: FdmSDK, - ancestorIds: CogniteInternalId[] -): Promise<{ edges: FdmCadEdge[] }> { - const filter = { - and: [ - { - in: { - property: ['edge', 'endNode'], - values: models.map((model) => ({ - externalId: model.externalId, - space: model.space - })) - } - }, - { - 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 - } - } - ] - }; - - const instances = await fdmClient.filterAllInstances( - filter, - 'edge', - SYSTEM_3D_EDGE_SOURCE - ); - - return { edges: instances.instances }; -} - export async function inspectNodes( fdmClient: FdmSDK, dataNodes: DmsUniqueIdentifier[] diff --git a/react-components/src/components/CacheProvider/types.ts b/react-components/src/components/CacheProvider/types.ts index e1ac16a90c4..f41eb2d28b6 100644 --- a/react-components/src/components/CacheProvider/types.ts +++ b/react-components/src/components/CacheProvider/types.ts @@ -8,16 +8,24 @@ import { type Node3D, type AnnotationsCogniteAnnotationTypesImagesAssetLink } from '@cognite/sdk'; -import { type DmsUniqueIdentifier, type Source, type EdgeItem } from '../../utilities/FdmSDK'; -import { type InModel3dEdgeProperties } from '../../utilities/globalDataModels'; +import { type DmsUniqueIdentifier, type Source } from '../../data-providers/FdmSDK'; import { type AssetAnnotationImage360Info } from '@cognite/reveal'; import { type Vector3 } from 'three'; -export type FdmCadEdge = EdgeItem; -export type FdmEdgeWithNode = { edge: FdmCadEdge; cadNode: Node3D; view?: Source }; +export type FdmCadConnection = { + instance: DmsUniqueIdentifier; + modelId: number; + revisionId: number; + nodeId: number; +}; +export type FdmConnectionWithNode = { + connection: FdmCadConnection; + cadNode: Node3D; + view?: Source; +}; export type CadNodeWithFdmIds = { cadNode: Node3D; fdmIds: DmsUniqueIdentifier[] }; -export type CadNodeWithEdges = { cadNode: Node3D; edges: FdmCadEdge[] }; +export type CadNodeWithConnections = { cadNode: Node3D; connections: FdmCadConnection[] }; export type FdmNodeDataPromises = { cadAndFdmNodesPromise: Promise; viewsPromise: Promise; @@ -30,7 +38,7 @@ export type ModelRevisionAssetNodesResult = { }; export type AncestorQueryResult = { - edges: FdmCadEdge[]; + connections: FdmCadConnection[]; ancestorsWithSameMapping: Node3D[]; firstMappedAncestorTreeIndex: number; }; @@ -49,7 +57,7 @@ export type FdmKey = `${string}/${string}`; export type ModelNodeIdKey = `${ModelId}/${RevisionId}/${NodeId}`; export type ModelAssetIdKey = `${ModelId}/${RevisionId}/${AssetId}`; -export type ModelRevisionToEdgeMap = Map; +export type ModelRevisionToConnectionMap = Map; export type PointCloudAnnotationModel = AnnotationModel & { data: AnnotationsBoundingVolume }; diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index d94e75e93a2..1b97ef561a8 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -9,7 +9,7 @@ import { } from '@cognite/reveal'; import { type Matrix4 } from 'three'; -import { type DmsUniqueIdentifier, type Source } from '../../utilities/FdmSDK'; +import { type DmsUniqueIdentifier, type Source } from '../../data-providers/FdmSDK'; import { type CogniteInternalId, type Node3D } from '@cognite/sdk'; import { type TreeIndexStylingGroup } from '../CadModelContainer/types'; diff --git a/react-components/src/components/Reveal3DResources/useCalculateCadStyling.tsx b/react-components/src/components/Reveal3DResources/useCalculateCadStyling.tsx index 8fa743a5482..c9365e183a1 100644 --- a/react-components/src/components/Reveal3DResources/useCalculateCadStyling.tsx +++ b/react-components/src/components/Reveal3DResources/useCalculateCadStyling.tsx @@ -16,7 +16,7 @@ import { import { useMemo } from 'react'; import { type NodeId, - type FdmEdgeWithNode, + type FdmConnectionWithNode, type AssetId, type ModelRevisionAssetNodesResult } from '../CacheProvider/types'; @@ -326,7 +326,7 @@ function extractDefaultStyles(typedModels: CadModelOptions[]): StyledModel[] { } function getMappedStyleGroupFromFdm( - edges: FdmEdgeWithNode[], + edges: FdmConnectionWithNode[], mapped: NodeAppearance ): TreeIndexStylingGroup { const indexSet = new IndexSet(); diff --git a/react-components/src/components/RevealCanvas/FdmDataProviderContext.ts b/react-components/src/components/RevealCanvas/FdmDataProviderContext.ts new file mode 100644 index 00000000000..94f7ef55d29 --- /dev/null +++ b/react-components/src/components/RevealCanvas/FdmDataProviderContext.ts @@ -0,0 +1,18 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type FdmSDK } from '../../data-providers/FdmSDK'; +import { type Fdm3dDataProvider } from '../../data-providers/Fdm3dDataProvider'; +import { createContext } from 'react'; + +/** + * Includes both the low-level SDK and a higher-level fdm3dDataprovider. + * The latter is adviced used (and extended) for all 3D-related queries, while the + * FdmSDK can be used for all other queries + */ +export type FdmDataProviderContextContent = { + fdmSdk: FdmSDK; + fdm3dDataProvider: Fdm3dDataProvider; +}; + +export const FdmSdkContext = createContext(null); diff --git a/react-components/src/components/RevealCanvas/SDKProvider.tsx b/react-components/src/components/RevealCanvas/SDKProvider.tsx index 77283bf5ba8..c4ff4889fff 100644 --- a/react-components/src/components/RevealCanvas/SDKProvider.tsx +++ b/react-components/src/components/RevealCanvas/SDKProvider.tsx @@ -3,20 +3,26 @@ */ import React, { useContext, createContext, useMemo } from 'react'; import { type CogniteClient } from '@cognite/sdk'; -import { FdmSDK } from '../../utilities/FdmSDK'; +import { FdmSdkContext } from './FdmDataProviderContext'; +import { FdmSDK } from '../../data-providers/FdmSDK'; +import { LegacyFdm3dDataProvider } from '../../data-providers/legacy-fdm-provider/LegacyFdm3dDataProvider'; +import { type Fdm3dDataProvider } from '../../data-providers/Fdm3dDataProvider'; const SdkContext = createContext(null); -const FdmSdkContext = createContext(null); + SdkContext.displayName = 'CogniteSdkProvider'; FdmSdkContext.displayName = 'FdmSdkProvider'; type Props = { sdk: CogniteClient; children: any }; + export function SDKProvider({ sdk, children }: Props): React.ReactElement { const fdmSdk = useMemo(() => new FdmSDK(sdk), [sdk]); + const fdm3dDataProvider = new LegacyFdm3dDataProvider(fdmSdk); + const content = useMemo(() => ({ fdmSdk, fdm3dDataProvider }), [fdmSdk, fdm3dDataProvider]); return ( - {children} + {children} ); } @@ -37,11 +43,22 @@ export const useSDK = (userSdk?: CogniteClient): CogniteClient => { }; export const useFdmSdk = (): FdmSDK => { - const fdmSdk = useContext(FdmSdkContext); - if (fdmSdk === null) { + const fdmProvider = useContext(FdmSdkContext); + if (fdmProvider === null) { throw new Error( `FdmSdkContext not found, add '' around your component/app` ); } - return fdmSdk; + return fdmProvider.fdmSdk; +}; + +export const useFdm3dDataProvider = (): Fdm3dDataProvider => { + const fdmProvider = useContext(FdmSdkContext); + if (fdmProvider === null) { + throw new Error( + `FdmSdkContext not found, add '' around your component/app` + ); + } + + return fdmProvider.fdm3dDataProvider; }; diff --git a/react-components/src/components/RevealToolbar/SceneList.tsx b/react-components/src/components/RevealToolbar/SceneList.tsx index 9aacc982b07..676114abcf2 100644 --- a/react-components/src/components/RevealToolbar/SceneList.tsx +++ b/react-components/src/components/RevealToolbar/SceneList.tsx @@ -5,7 +5,7 @@ import { type ReactElement } from 'react'; import { Menu } from '@cognite/cogs.js'; import { use3dScenes } from '../../query/use3dScenes'; -import { type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; +import { type DmsUniqueIdentifier } from '../../data-providers/FdmSDK'; export type SceneWithName = DmsUniqueIdentifier & { name: string }; diff --git a/react-components/src/components/RevealToolbar/SelectSceneButton.tsx b/react-components/src/components/RevealToolbar/SelectSceneButton.tsx index 9646b50427c..5da90c56ea8 100644 --- a/react-components/src/components/RevealToolbar/SelectSceneButton.tsx +++ b/react-components/src/components/RevealToolbar/SelectSceneButton.tsx @@ -6,7 +6,7 @@ import { useCallback, useState, type ReactElement } from 'react'; import { Button, Dropdown, Menu, Tooltip as CogsTooltip } from '@cognite/cogs.js'; import { use3dScenes } from '../../query/use3dScenes'; import { useTranslation } from '../i18n/I18n'; -import { type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; +import { type DmsUniqueIdentifier } from '../../data-providers/FdmSDK'; import { SceneList, type SceneWithName } from './SceneList'; import styled from 'styled-components'; diff --git a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx index 8173c898fa3..b1e48b76191 100644 --- a/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx +++ b/react-components/src/components/RuleBasedOutputs/RuleBasedOutputsSelector.tsx @@ -9,7 +9,7 @@ import { generateRuleBasedOutputs } from './utils'; import { use3dModels } from '../../hooks/use3dModels'; import { type Datapoints, type Asset, type AssetMapping3D } from '@cognite/sdk'; import { isDefined } from '../../utilities/isDefined'; -import { type AssetIdsAndTimeseries } from '../../utilities/types'; +import { type AssetIdsAndTimeseries } from '../../data-providers/types'; import { useAssetsAndTimeseriesLinkageDataQuery } from '../../query/useAssetsAndTimeseriesLinkageDataQuery'; import { useAssetMappedNodesForRevisions } from '../CacheProvider/AssetMappingAndNode3DCacheProvider'; import { type CadModelOptions } from '../Reveal3DResources/types'; diff --git a/react-components/src/components/RuleBasedOutputs/constants.ts b/react-components/src/components/RuleBasedOutputs/constants.ts index 11ce0b083c6..1aed5afdb6e 100644 --- a/react-components/src/components/RuleBasedOutputs/constants.ts +++ b/react-components/src/components/RuleBasedOutputs/constants.ts @@ -2,7 +2,7 @@ * Copyright 2024 Cognite AS */ -import { type Source } from '../../utilities/FdmSDK'; +import { type Source } from '../../data-providers/FdmSDK'; export const RULE_BASED_OUTPUTS_VIEW: Source = { type: 'view', diff --git a/react-components/src/components/RuleBasedOutputs/hooks/useDeleteRuleInstance.tsx b/react-components/src/components/RuleBasedOutputs/hooks/useDeleteRuleInstance.tsx index ba0326cc04d..9f23e204139 100644 --- a/react-components/src/components/RuleBasedOutputs/hooks/useDeleteRuleInstance.tsx +++ b/react-components/src/components/RuleBasedOutputs/hooks/useDeleteRuleInstance.tsx @@ -6,7 +6,7 @@ import { useFdmSdk } from '../../RevealCanvas/SDKProvider'; import { RULE_BASED_OUTPUTS_VIEW } from '../constants'; import { type ExternalIdsResultList, type RuleOutputSet } from '../types'; import { fdmViewsExist } from '../../../utilities/fdmViewsExist'; -import { type FdmNode } from '../../../utilities/FdmSDK'; +import { type FdmNode } from '../../../data-providers/FdmSDK'; export const useDeleteRuleInstance = (): (( ruleOutputSet: FdmNode diff --git a/react-components/src/components/RuleBasedOutputs/types.ts b/react-components/src/components/RuleBasedOutputs/types.ts index a4fa20cbedc..4bdd4311c03 100644 --- a/react-components/src/components/RuleBasedOutputs/types.ts +++ b/react-components/src/components/RuleBasedOutputs/types.ts @@ -3,7 +3,7 @@ */ import { type TreeIndexNodeCollection, type NumericRange } from '@cognite/reveal'; -import { type FdmNode, type EdgeItem, type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; +import { type FdmNode, type EdgeItem, type DmsUniqueIdentifier } from '../../data-providers/FdmSDK'; import { type AssetStylingGroup, type FdmPropertyType } from '../Reveal3DResources/types'; import { type Datapoints, type Asset, type Timeseries } from '@cognite/sdk'; diff --git a/react-components/src/components/RuleBasedOutputs/utils.ts b/react-components/src/components/RuleBasedOutputs/utils.ts index ac7753b20aa..c269f2d54e0 100644 --- a/react-components/src/components/RuleBasedOutputs/utils.ts +++ b/react-components/src/components/RuleBasedOutputs/utils.ts @@ -27,7 +27,7 @@ import { type AssetMapping3D, type Asset, type Datapoints } from '@cognite/sdk'; import { type AssetStylingGroup } from '../Reveal3DResources/types'; import { isDefined } from '../../utilities/isDefined'; import { assertNever } from '../../utilities/assertNever'; -import { type AssetIdsAndTimeseries } from '../../utilities/types'; +import { type AssetIdsAndTimeseries } from '../../data-providers/types'; const checkStringExpressionStatement = ( triggerTypeData: TriggerTypeData[], diff --git a/react-components/src/components/SceneContainer/Queries.tsx b/react-components/src/components/SceneContainer/Queries.tsx index 09c00771a4c..1bbdc68f1fe 100644 --- a/react-components/src/components/SceneContainer/Queries.tsx +++ b/react-components/src/components/SceneContainer/Queries.tsx @@ -2,7 +2,7 @@ * Copyright 2023 Cognite AS */ -import { type Query } from '../../utilities/FdmSDK'; +import { type Query } from '../../data-providers/FdmSDK'; export function createGetSceneQuery(sceneExternalId: string, sceneSpaceId: string): Query { return { diff --git a/react-components/src/data-providers/Fdm3dDataProvider.ts b/react-components/src/data-providers/Fdm3dDataProvider.ts new file mode 100644 index 00000000000..065050068e2 --- /dev/null +++ b/react-components/src/data-providers/Fdm3dDataProvider.ts @@ -0,0 +1,51 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { + type InstanceFilter, + type NodeItem, + type Source, + type DmsUniqueIdentifier, + type ViewItem +} from './FdmSDK'; +import { type AddModelOptions } from '@cognite/reveal'; +import { type InstancesWithView } from '../query/useSearchMappedEquipmentFDM'; +import { type FdmCadConnection } from '../components/CacheProvider/types'; +import { type TaggedAddResourceOptions } from '../components/Reveal3DResources/types'; + +export type Fdm3dDataProvider = { + is3dView: (view: ViewItem) => boolean; + + getDMSModels: (modelId: number) => Promise; + + getEdgeConnected3dInstances: (instance: DmsUniqueIdentifier) => Promise; + + getFdmConnectionsForNodeIds: ( + models: DmsUniqueIdentifier[], + revisionId: number, + nodeIds: number[] + ) => Promise; + + listMappedFdmNodes: ( + models: AddModelOptions[], + sourcesToSearch: Source[], + instancesFilter: InstanceFilter | undefined, + limit: number + ) => Promise; + + listAllMappedFdmNodes: ( + models: AddModelOptions[], + sourcesToSearch: Source[], + instanceFilter: InstanceFilter | undefined + ) => Promise; + + filterNodesByMappedTo3d: ( + nodes: InstancesWithView[], + models: AddModelOptions[], + spacesToSearch: string[] + ) => Promise; + + getCadModelsForInstance: (instance: DmsUniqueIdentifier) => Promise; + + getCadConnectionsForRevisions: (revisions: number[]) => Promise; +}; diff --git a/react-components/src/utilities/FdmSDK.ts b/react-components/src/data-providers/FdmSDK.ts similarity index 100% rename from react-components/src/utilities/FdmSDK.ts rename to react-components/src/data-providers/FdmSDK.ts diff --git a/react-components/src/data-providers/legacy-fdm-provider/LegacyFdm3dDataProvider.ts b/react-components/src/data-providers/legacy-fdm-provider/LegacyFdm3dDataProvider.ts new file mode 100644 index 00000000000..c56e1315a94 --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/LegacyFdm3dDataProvider.ts @@ -0,0 +1,85 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type AddModelOptions } from '@cognite/reveal'; +import { type FdmCadConnection } from '../../components/CacheProvider/types'; +import { type Fdm3dDataProvider } from '../Fdm3dDataProvider'; +import { + type DmsUniqueIdentifier, + type FdmSDK, + type InstanceFilter, + type NodeItem, + type Source, + type ViewItem +} from '../FdmSDK'; +import { type InstancesWithView } from '../../query/useSearchMappedEquipmentFDM'; +import { type TaggedAddResourceOptions } from '../../components/Reveal3DResources/types'; +import { getEdgeConnected3dInstances } from './getEdgeConnected3dInstances'; +import { getFdmConnectionsForNodeIds } from './getFdmConnectionsForNodeIds'; +import { getDMSModels } from './getDMSModels'; +import { listAllMappedFdmNodes, listMappedFdmNodes } from './listMappedFdmNodes'; +import { filterNodesByMappedTo3d } from './filterNodesByMappedTo3d'; +import { getCadModelsForFdmInstance } from './getCadModelsForFdmInstance'; +import { getCadConnectionsForRevision } from './getCadConnectionsForRevision'; + +export class LegacyFdm3dDataProvider implements Fdm3dDataProvider { + readonly _fdmSdk: FdmSDK; + + constructor(fdmSdk: FdmSDK) { + this._fdmSdk = fdmSdk; + } + + is3dView(view: ViewItem): boolean { + return Object.keys(view.properties).some((propName) => propName === 'inModel3d'); + } + + async getDMSModels(modelId: number): Promise { + return await getDMSModels(this._fdmSdk, modelId); + } + + async getEdgeConnected3dInstances(instance: DmsUniqueIdentifier): Promise { + return await getEdgeConnected3dInstances(this._fdmSdk, instance); + } + + async getFdmConnectionsForNodeIds( + models: DmsUniqueIdentifier[], + revisionId: number, + nodeIds: number[] + ): Promise { + return await getFdmConnectionsForNodeIds(this._fdmSdk, models, revisionId, nodeIds); + } + + async listMappedFdmNodes( + models: AddModelOptions[], + sourcesToSearch: Source[], + instanceFilter: InstanceFilter | undefined, + limit: number + ): Promise { + return await listMappedFdmNodes(this._fdmSdk, models, sourcesToSearch, instanceFilter, limit); + } + + async listAllMappedFdmNodes( + models: AddModelOptions[], + sourcesToSearch: Source[] + ): Promise { + return await listAllMappedFdmNodes(this._fdmSdk, models, sourcesToSearch); + } + + async filterNodesByMappedTo3d( + nodes: InstancesWithView[], + models: AddModelOptions[], + spacesToSearch: string[] + ): Promise { + return await filterNodesByMappedTo3d(this._fdmSdk, nodes, models, spacesToSearch); + } + + async getCadModelsForInstance( + instance: DmsUniqueIdentifier + ): Promise { + return await getCadModelsForFdmInstance(this._fdmSdk, instance); + } + + async getCadConnectionsForRevisions(revisions: number[]): Promise { + return await getCadConnectionsForRevision(revisions, this._fdmSdk); + } +} diff --git a/react-components/src/data-providers/legacy-fdm-provider/createMappedEquipmentQuery.ts b/react-components/src/data-providers/legacy-fdm-provider/createMappedEquipmentQuery.ts new file mode 100644 index 00000000000..2d2929b3165 --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/createMappedEquipmentQuery.ts @@ -0,0 +1,73 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type AddModelOptions } from '@cognite/reveal'; +import { type InstanceFilter, type Query, type Source } from '../FdmSDK'; +import { SYSTEM_3D_EDGE_SOURCE } from './dataModels'; + +export function createMappedEquipmentQuery( + models: AddModelOptions[], + views: Source[], + instanceFilter: InstanceFilter | undefined, + limit: number = 10000, + cursors?: Record +): Query { + return { + with: { + mapped_edges: { + edges: { + filter: createInModelsFilter(models) + }, + limit + }, + mapped_nodes: { + nodes: { + from: 'mapped_edges', + chainTo: 'source', + filter: instanceFilter + }, + limit + }, + mapped_edges_2: { + edges: { + from: 'mapped_nodes', + direction: 'inwards', + maxDistance: 1 + } + }, + mapped_nodes_2: { + nodes: { + from: 'mapped_edges_2', + chainTo: 'destination', + filter: instanceFilter + } + } + }, + cursors, + select: { + mapped_nodes_2: { + sources: views.map((view) => ({ source: view, properties: [] })) + }, + mapped_edges_2: {}, + mapped_edges: { + sources: [{ source: SYSTEM_3D_EDGE_SOURCE, properties: [] }] + }, + mapped_nodes: { + sources: views.map((view) => ({ source: view, properties: [] })) + } + } + }; +} + +function createInModelsFilter(models: AddModelOptions[]): { in: any } { + return { + in: { + property: [ + SYSTEM_3D_EDGE_SOURCE.space, + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, + 'revisionId' + ], + values: models.map((model) => model.revisionId) + } + }; +} diff --git a/react-components/src/utilities/globalDataModels.ts b/react-components/src/data-providers/legacy-fdm-provider/dataModels.ts similarity index 59% rename from react-components/src/utilities/globalDataModels.ts rename to react-components/src/data-providers/legacy-fdm-provider/dataModels.ts index c3260a5f679..c0b362cabbf 100644 --- a/react-components/src/utilities/globalDataModels.ts +++ b/react-components/src/data-providers/legacy-fdm-provider/dataModels.ts @@ -1,24 +1,13 @@ /*! * Copyright 2023 Cognite AS */ -import { type DmsUniqueIdentifier, type Source } from './FdmSDK'; +import { type Source } from '../FdmSDK'; export const SYSTEM_SPACE_3D_SCHEMA = 'cdf_3d_schema'; // Data model, views, containers and types for edges export const SYSTEM_SPACE_3D_MODEL_ID = 'Cdf3dModel'; export const SYSTEM_SPACE_3D_MODEL_VERSION = '1'; -export const SYSTEM_3D_NODE_TYPE: DmsUniqueIdentifier = { - externalId: 'Cdf3dEntity', - space: SYSTEM_SPACE_3D_SCHEMA -}; - -// Type of edge that connects equipment to 3D -export const SYSTEM_3D_EDGE_TYPE: DmsUniqueIdentifier = { - externalId: 'cdf3dEntityConnection', - space: SYSTEM_SPACE_3D_SCHEMA -}; - // Source of view that contains edge properties export const SYSTEM_3D_EDGE_SOURCE: Source = { type: 'view', diff --git a/react-components/src/data-providers/legacy-fdm-provider/fdmEdgesToCadConnections.ts b/react-components/src/data-providers/legacy-fdm-provider/fdmEdgesToCadConnections.ts new file mode 100644 index 00000000000..c6009087191 --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/fdmEdgesToCadConnections.ts @@ -0,0 +1,17 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type FdmCadConnection } from '../../components/CacheProvider/types'; +import { type EdgeItem } from '../FdmSDK'; +import { type InModel3dEdgeProperties } from './dataModels'; + +export function fdmEdgesToCadConnections( + edges: Array> +): FdmCadConnection[] { + return edges.map((instance) => ({ + nodeId: instance.properties.revisionNodeId, + revisionId: instance.properties.revisionId, + modelId: Number(instance.endNode.externalId), + instance: instance.startNode + })); +} diff --git a/react-components/src/data-providers/legacy-fdm-provider/filterNodesByMappedTo3d.ts b/react-components/src/data-providers/legacy-fdm-provider/filterNodesByMappedTo3d.ts new file mode 100644 index 00000000000..161e0b0d3bd --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/filterNodesByMappedTo3d.ts @@ -0,0 +1,230 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type AddModelOptions } from '@cognite/reveal'; +import { type InstancesWithView } from '../../query/useSearchMappedEquipmentFDM'; +import { + type DmsUniqueIdentifier, + type EdgeItem, + type ExternalId, + type FdmSDK, + type NodeItem, + type Query, + type Source +} from '../FdmSDK'; +import { SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from './dataModels'; +import { getDMSModels } from './getDMSModels'; +import { type FdmKey } from '../../components/CacheProvider/types'; + +export async function filterNodesByMappedTo3d( + fdmSdk: FdmSDK, + nodesWithViews: InstancesWithView[], + models: AddModelOptions[], + spacesToSearch: string[] +): Promise { + const nodes = nodesWithViews.flatMap((result) => result.instances); + const views = nodesWithViews.map((result) => result.view); + + if (nodes.length === 0) { + return nodesWithViews; + } + + const directlyMappedNodes = nodes.map((node) => getDirectRelationProperties(node)).flat(); + + const mappedEquipmentQuery = createCheckMappedEquipmentQuery( + nodes, + directlyMappedNodes, + models, + views + ); + const queryResult = await fdmSdk.queryNodesAndEdges(mappedEquipmentQuery); + + const { mappedEquipmentFirstLevelMap, equipmentSecondLevelMap } = await createMappedEquipmentMaps( + fdmSdk, + queryResult.items.mapped_edges as EdgeItem[], + models, + spacesToSearch + ); + + const filteredSearchResults: InstancesWithView[] = []; + + for (const searchResult of nodesWithViews) { + const filteredInstances = searchResult.instances.filter((instance) => + checkInstanceWithMappedEquipmentMaps( + mappedEquipmentFirstLevelMap, + equipmentSecondLevelMap, + instance + ) + ); + + filteredSearchResults.push({ view: searchResult.view, instances: filteredInstances }); + } + + return filteredSearchResults; +} + +function createCheckMappedEquipmentQuery( + instances: NodeItem[], + directlyMappedNodes: DmsUniqueIdentifier[], + models: AddModelOptions[], + views: Source[], + limit: number = 1000 +): Query { + return { + with: { + mapped_nodes: { + nodes: { + filter: { + in: { + property: ['node', 'externalId'], + values: instances + .map((instance) => instance.externalId) + .concat(directlyMappedNodes.map((node) => node.externalId)) + } + } + }, + limit + }, + mapped_edges: { + edges: { + from: 'mapped_nodes', + direction: 'outwards', + nodeFilter: { + in: { + property: ['node', 'externalId'], + values: models.map((model) => model.modelId.toString()) + } + }, + maxDistance: 2 + }, + limit: 10000 + } + }, + select: { + mapped_edges: { + sources: [{ source: SYSTEM_3D_EDGE_SOURCE, properties: [] }] + }, + mapped_nodes: { + sources: views.map((view) => ({ source: view, properties: [] })) + } + } + }; +} + +async function createMappedEquipmentMaps( + fdmSdk: FdmSDK, + allEdges: EdgeItem[], + models: AddModelOptions[], + spacesToSearch: string[] +): Promise<{ + mappedEquipmentFirstLevelMap: Record; + equipmentSecondLevelMap: Record; +}> { + const mappedEquipmentFirstLevelMap: Record = {}; + const equipmentSecondLevelMap: Record = {}; + + const modelsMap = await createModelsMap(fdmSdk, models); + + for (const edge of allEdges) { + const { space: endSpace, externalId: endExternalId } = edge.endNode; + const { space, externalId } = edge.startNode; + + const isModelsMapped = models.some( + ({ modelId, revisionId }) => + modelId.toString() === endExternalId && + revisionId.toString() === getRevisionIdFromEdge(edge) + ); + + const modelInstances = modelsMap.get(endExternalId); + + if (modelInstances?.find((model) => model.space === endSpace) !== undefined && isModelsMapped) { + const key = `${space}/${externalId}`; + + const keyEdges = mappedEquipmentFirstLevelMap[key]; + mappedEquipmentFirstLevelMap[key] = keyEdges !== undefined ? keyEdges.concat(edge) : [edge]; + continue; + } + + if (spacesToSearch.includes(endSpace)) { + const key = `${space}/${externalId}`; + + const keyEdges = equipmentSecondLevelMap[key]; + + equipmentSecondLevelMap[key] = keyEdges !== undefined ? keyEdges.concat(edge) : [edge]; + continue; + } + } + + return { mappedEquipmentFirstLevelMap, equipmentSecondLevelMap }; +} + +function getRevisionIdFromEdge(edge: EdgeItem): string | undefined { + return (edge.properties as any)?.[SYSTEM_SPACE_3D_SCHEMA]?.[ + `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}` + ]?.revisionId?.toString(); +} + +async function createModelsMap( + fdmSdk: FdmSDK, + models: AddModelOptions[] +): Promise> { + const modelInstances = await Promise.all( + models.map(async (model) => await getDMSModels(fdmSdk, model.modelId)) + ); + return new Map( + modelInstances.map((modelInstanceList, ind) => [`${models[ind].modelId}`, modelInstanceList]) + ); +} + +function checkInstanceWithMappedEquipmentMaps( + mappedEquipmentFirstLevelMap: Record, + equipmentSecondLevelMap: Record, + instance: NodeItem +): boolean { + const key: FdmKey = `${instance.space}/${instance.externalId}`; + const directRelationProperties = getDirectRelationProperties(instance); + const isMappedFirstLevel = mappedEquipmentFirstLevelMap[key] !== undefined; + const isSecondLevelWithEdge = equipmentSecondLevelMap[key] !== undefined; + const isSecondLevelWithDirectRelation = directRelationProperties.length > 0; + + if (isMappedFirstLevel) { + return true; + } + + if (isSecondLevelWithEdge) { + return equipmentSecondLevelMap[key].some((edge) => { + const { space, externalId } = edge.endNode; + + const secondLevelKey: FdmKey = `${space}/${externalId}`; + const isMappedWithEdge = mappedEquipmentFirstLevelMap[secondLevelKey] !== undefined; + + return isMappedWithEdge; + }); + } + + if (isSecondLevelWithDirectRelation) { + const isMappedWithDirectRelation = directRelationProperties.some( + ({ externalId, space }) => + mappedEquipmentFirstLevelMap[`${space}/${externalId}`] !== undefined + ); + + return isMappedWithDirectRelation; + } + + return false; +} + +function getDirectRelationProperties(searchResultNode: NodeItem): DmsUniqueIdentifier[] { + const directRelations: DmsUniqueIdentifier[] = []; + const nodeProperties = searchResultNode.properties; + + Object.keys(nodeProperties).forEach((propertyKey) => { + const { space, externalId } = nodeProperties[propertyKey] as any; + + if (space !== undefined && externalId !== undefined) { + directRelations.push({ space, externalId }); + } + }); + + return directRelations; +} diff --git a/react-components/src/data-providers/legacy-fdm-provider/getCadConnectionsForRevision.ts b/react-components/src/data-providers/legacy-fdm-provider/getCadConnectionsForRevision.ts new file mode 100644 index 00000000000..8496224cebf --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/getCadConnectionsForRevision.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type FdmCadConnection } from '../../components/CacheProvider/types'; +import { type FdmSDK } from '../FdmSDK'; +import { + type InModel3dEdgeProperties, + SYSTEM_3D_EDGE_SOURCE, + SYSTEM_SPACE_3D_SCHEMA +} from './dataModels'; +import { fdmEdgesToCadConnections } from './fdmEdgesToCadConnections'; + +export async function getCadConnectionsForRevision( + revisionIds: number[], + fdmClient: FdmSDK +): Promise { + if (revisionIds.length === 0) return []; + + 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 fdmEdgesToCadConnections(mappings.instances); +} diff --git a/react-components/src/hooks/network/getCadModelsForFdmInstance.ts b/react-components/src/data-providers/legacy-fdm-provider/getCadModelsForFdmInstance.ts similarity index 91% rename from react-components/src/hooks/network/getCadModelsForFdmInstance.ts rename to react-components/src/data-providers/legacy-fdm-provider/getCadModelsForFdmInstance.ts index e67685f7f4d..6ef01ee9b70 100644 --- a/react-components/src/hooks/network/getCadModelsForFdmInstance.ts +++ b/react-components/src/data-providers/legacy-fdm-provider/getCadModelsForFdmInstance.ts @@ -2,11 +2,8 @@ * Copyright 2024 Cognite AS */ import { type TaggedAddCadResourceOptions } from '../../components/Reveal3DResources/types'; -import { type DmsUniqueIdentifier, type EdgeItem, type FdmSDK } from '../../utilities/FdmSDK'; -import { - type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE -} from '../../utilities/globalDataModels'; +import { type DmsUniqueIdentifier, type EdgeItem, type FdmSDK } from '../FdmSDK'; +import { type InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from './dataModels'; import { isDefined } from '../../utilities/isDefined'; type ModelForInstancesResponse = { @@ -14,8 +11,8 @@ type ModelForInstancesResponse = { }; export async function getCadModelsForFdmInstance( - instance: DmsUniqueIdentifier, - sdk: FdmSDK + sdk: FdmSDK, + instance: DmsUniqueIdentifier ): Promise { const result = ( await sdk.queryNodesAndEdges({ diff --git a/react-components/src/data-providers/legacy-fdm-provider/getDMSModels.ts b/react-components/src/data-providers/legacy-fdm-provider/getDMSModels.ts new file mode 100644 index 00000000000..047a5996d2f --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/getDMSModels.ts @@ -0,0 +1,30 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type DmsUniqueIdentifier, type FdmSDK, type Source } from '../FdmSDK'; +import { + SYSTEM_SPACE_3D_MODEL_ID, + SYSTEM_SPACE_3D_MODEL_VERSION, + SYSTEM_SPACE_3D_SCHEMA +} from './dataModels'; + +export async function getDMSModels( + fdmClient: FdmSDK, + modelId: number +): Promise { + const filter = { + equals: { + property: ['node', 'externalId'], + value: `${modelId}` + } + }; + const sources: Source = { + type: 'view', + space: SYSTEM_SPACE_3D_SCHEMA, + externalId: SYSTEM_SPACE_3D_MODEL_ID, + version: SYSTEM_SPACE_3D_MODEL_VERSION + }; + + const modelResults = await fdmClient.filterInstances(filter, 'node', sources); + return modelResults.instances; +} diff --git a/react-components/src/data-providers/legacy-fdm-provider/getEdgeConnected3dInstances.ts b/react-components/src/data-providers/legacy-fdm-provider/getEdgeConnected3dInstances.ts new file mode 100644 index 00000000000..4ea8c044b6d --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/getEdgeConnected3dInstances.ts @@ -0,0 +1,93 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { SYSTEM_3D_EDGE_SOURCE } from './dataModels'; +import { type DmsUniqueIdentifier, type FdmSDK } from '../FdmSDK'; + +export async function getEdgeConnected3dInstances( + fdmSdk: FdmSDK, + instance: DmsUniqueIdentifier +): Promise { + const nodesResult = await fdmSdk.queryNodesAndEdges({ + ...related3dEdgesQuery, + parameters: { + instanceExternalId: instance.externalId, + instanceSpace: instance.space + } + }); + + return nodesResult.items.connected_objects_with_3d.map((obj) => ({ + instanceType: 'node' as const, + externalId: obj.externalId, + space: obj.space + })); +} + +const related3dEdgesQuery = { + with: { + start_instance: { + nodes: { + filter: { + and: [ + { + equals: { + property: ['node', 'externalId'], + value: { parameter: 'instanceExternalId' } + } + }, + { + equals: { + property: ['node', 'space'], + value: { parameter: 'instanceSpace' } + } + } + ] + } + }, + limit: 1 + }, + start_to_object_edges: { + edges: { + from: 'start_instance', + maxDistance: 1, + direction: 'outwards' + }, + limit: 1000 + }, + connected_objects_with_3d: { + nodes: { + from: 'start_to_object_edges', + chainTo: 'destination' + }, + limit: 1000 + }, + edges_of_3d_type: { + edges: { + from: 'connected_objects_with_3d', + maxDistance: 1, + direction: 'outwards', + filter: { + and: [ + { + hasData: [SYSTEM_3D_EDGE_SOURCE] + } + ] + } + } + }, + nodes_with_3d_connection: { + nodes: { + from: 'edges_of_3d_type', + chainTo: 'source' + }, + limit: 1000 + } + }, + select: { + start_instance: {}, + start_to_object_edges: {}, + connected_objects_with_3d: {}, + edges_of_3d_type: {}, + nodes_with_3d_connection: {} + } +} as const; diff --git a/react-components/src/data-providers/legacy-fdm-provider/getFdmConnectionsForNodeIds.ts b/react-components/src/data-providers/legacy-fdm-provider/getFdmConnectionsForNodeIds.ts new file mode 100644 index 00000000000..ad3bd5206c5 --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/getFdmConnectionsForNodeIds.ts @@ -0,0 +1,57 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type CogniteInternalId } from '@cognite/sdk/dist/src'; +import { type DmsUniqueIdentifier, type FdmSDK } from '../FdmSDK'; +import { type FdmCadConnection } from '../../components/CacheProvider/types'; +import { type InModel3dEdgeProperties, SYSTEM_3D_EDGE_SOURCE } from './dataModels'; +import { fdmEdgesToCadConnections } from './fdmEdgesToCadConnections'; + +export async function getFdmConnectionsForNodeIds( + fdmClient: FdmSDK, + models: DmsUniqueIdentifier[], + revisionId: number, + nodeIds: CogniteInternalId[] +): Promise { + const filter = { + and: [ + { + in: { + property: ['edge', 'endNode'], + values: models.map((model) => ({ + externalId: model.externalId, + space: model.space + })) + } + }, + { + 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: nodeIds + } + } + ] + }; + + const results = await fdmClient.filterAllInstances( + filter, + 'edge', + SYSTEM_3D_EDGE_SOURCE + ); + + return fdmEdgesToCadConnections(results.instances); +} diff --git a/react-components/src/data-providers/legacy-fdm-provider/listMappedFdmNodes.ts b/react-components/src/data-providers/legacy-fdm-provider/listMappedFdmNodes.ts new file mode 100644 index 00000000000..57f1369875d --- /dev/null +++ b/react-components/src/data-providers/legacy-fdm-provider/listMappedFdmNodes.ts @@ -0,0 +1,76 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type AddModelOptions } from '@cognite/reveal'; +import { + type FdmSDK, + type InstanceFilter, + type NodeItem, + type Query, + type Source +} from '../FdmSDK'; +import { createMappedEquipmentQuery } from './createMappedEquipmentQuery'; +import { chunk, isEqual } from 'lodash'; +import { removeEmptyProperties } from '../../utilities/removeEmptyProperties'; + +export async function listMappedFdmNodes( + fdmSdk: FdmSDK, + models: AddModelOptions[], + sourcesToSearch: Source[], + instanceFilter: InstanceFilter | undefined, + limit: number +): Promise { + const result = await fdmSdk.queryNodesAndEdges( + createMappedEquipmentQuery(models, sourcesToSearch, instanceFilter, limit) + ); + + return result.items.mapped_nodes.concat(result.items.mapped_nodes_2) as NodeItem[]; +} + +export async function listAllMappedFdmNodes( + fdmSdk: FdmSDK, + models: AddModelOptions[], + sourcesToSearch: Source[] +): Promise { + const queries = createChunkedMappedEquipmentQueries(models, sourcesToSearch, 10000); + + const mappedEquipment: NodeItem[] = []; + for (const query of queries) { + let currentPage = await fdmSdk.queryNodesAndEdges(query); + + const mappedNodes = currentPage.items.mapped_nodes as NodeItem[]; + const mappedNodesParents = currentPage.items.mapped_nodes_2 as NodeItem[]; + mappedEquipment.push( + ...mappedNodes.concat(mappedNodesParents).map((node) => removeEmptyProperties(node)) + ); + + while (!isEqual(currentPage.nextCursor, {})) { + query.cursors = currentPage.nextCursor; + + currentPage = await fdmSdk.queryNodesAndEdges(query); + + const cleanedNodes = currentPage.items.mapped_nodes.map((node) => + removeEmptyProperties(node as NodeItem) + ); + const cleanedNodesParents = currentPage.items.mapped_nodes_2.map((node) => + removeEmptyProperties(node as NodeItem) + ); + + mappedEquipment.push(...cleanedNodes.concat(cleanedNodesParents)); + } + } + + return mappedEquipment; +} + +function createChunkedMappedEquipmentQueries( + models: AddModelOptions[], + views: Source[], + limit: number = 10000, + cursors?: Record +): Query[] { + const viewChunks = chunk(views, 10); + return viewChunks.map((viewChunk) => + createMappedEquipmentQuery(models, viewChunk, undefined, limit, cursors) + ); +} diff --git a/react-components/src/utilities/types.ts b/react-components/src/data-providers/types.ts similarity index 100% rename from react-components/src/utilities/types.ts rename to react-components/src/data-providers/types.ts diff --git a/react-components/src/hooks/network/getRelationships.ts b/react-components/src/hooks/network/getRelationships.ts index b55a5468ca3..1a532016f4a 100644 --- a/react-components/src/hooks/network/getRelationships.ts +++ b/react-components/src/hooks/network/getRelationships.ts @@ -10,7 +10,7 @@ import { import { getSourceRelationships } from './getSourceRelationships'; import { getTargetRelationships } from './getTargetRelationships'; -import { type ExtendedRelationship } from '../../utilities/types'; +import { type ExtendedRelationship } from '../../data-providers/types'; type Payload = { resourceExternalIds: CogniteExternalId[]; diff --git a/react-components/src/hooks/network/getSourceRelationships.ts b/react-components/src/hooks/network/getSourceRelationships.ts index 8d3d7c1f815..4bc6f12a8ad 100644 --- a/react-components/src/hooks/network/getSourceRelationships.ts +++ b/react-components/src/hooks/network/getSourceRelationships.ts @@ -9,7 +9,7 @@ import { } from '@cognite/sdk'; import { filterRelationships } from './filterRelationships'; -import { type ExtendedRelationship } from '../../utilities/types'; +import { type ExtendedRelationship } from '../../data-providers/types'; type Payload = { targetExternalIds: CogniteExternalId[]; diff --git a/react-components/src/hooks/network/getTargetRelationships.ts b/react-components/src/hooks/network/getTargetRelationships.ts index 01f4425910f..baeca78e13c 100644 --- a/react-components/src/hooks/network/getTargetRelationships.ts +++ b/react-components/src/hooks/network/getTargetRelationships.ts @@ -9,7 +9,7 @@ import { } from '@cognite/sdk'; import { filterRelationships } from './filterRelationships'; -import { type ExtendedRelationship } from '../../utilities/types'; +import { type ExtendedRelationship } from '../../data-providers/types'; type Payload = { sourceExternalIds: CogniteExternalId[]; diff --git a/react-components/src/hooks/types.ts b/react-components/src/hooks/types.ts index 05154ed08f7..de917dd2588 100644 --- a/react-components/src/hooks/types.ts +++ b/react-components/src/hooks/types.ts @@ -2,7 +2,7 @@ * Copyright 2023 Cognite AS */ import { type Node3D, type CogniteExternalId, type Asset } from '@cognite/sdk'; -import { type DmsUniqueIdentifier } from '../utilities/FdmSDK'; +import { type DmsUniqueIdentifier } from '../data-providers/FdmSDK'; import { type AssetAnnotationImage360Info } from '@cognite/reveal'; export type ThreeDModelFdmMappings = { diff --git a/react-components/src/hooks/useCameraNavigation.tsx b/react-components/src/hooks/useCameraNavigation.tsx index 3930e3bf681..e71bc1ad24f 100644 --- a/react-components/src/hooks/useCameraNavigation.tsx +++ b/react-components/src/hooks/useCameraNavigation.tsx @@ -56,7 +56,7 @@ export const useCameraNavigation = (): CameraNavigationActions => { })); const modelMappings = ( - await fdmNodeCache.cache.getMappingsForFdmIds(instances, modelsRevisionIds) + await fdmNodeCache.cache.getMappingsForFdmInstances(instances, modelsRevisionIds) ).find((model) => model.mappings.size > 0); const nodeIds = [...(modelMappings?.mappings.values() ?? [])].flat().map((node) => node.id); diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index 4f65043ef6d..690ec1d2c60 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -16,7 +16,7 @@ import { type NodeAssetMappingResult } from '../components/CacheProvider/AssetMa import { usePointCloudAnnotationMappingForAssetId } from '../components/CacheProvider/PointCloudAnnotationCacheProvider'; import { type PointCloudAnnotationMappedAssetData } from './types'; import { MOUSE, Vector2 } from 'three'; -import { type DmsUniqueIdentifier, type Source } from '../utilities/FdmSDK'; +import { type DmsUniqueIdentifier, type Source } from '../data-providers/FdmSDK'; import { useRenderTarget, useReveal } from '../components/RevealCanvas/ViewerContext'; export type AssetMappingDataResult = { diff --git a/react-components/src/index.ts b/react-components/src/index.ts index e5e054f9764..19eb1b71bfa 100644 --- a/react-components/src/index.ts +++ b/react-components/src/index.ts @@ -58,7 +58,7 @@ export { use3dNodeByExternalId } from './query/use3dNodeByExternalId'; export { useAllMappedEquipmentFDM, useSearchMappedEquipmentFDM, - type SearchResultsWithView + type InstancesWithView } from './query/useSearchMappedEquipmentFDM'; export { useSearchMappedEquipmentAssetMappings, @@ -120,8 +120,8 @@ export { type ThreeDModelFdmMappings } from './hooks/types'; export type { CameraNavigationActions } from './hooks/useCameraNavigation'; -export type { Source, DmsUniqueIdentifier } from './utilities/FdmSDK'; -export type { FdmInstanceWithView } from './utilities/types'; +export type { Source, DmsUniqueIdentifier } from './data-providers/FdmSDK'; +export type { FdmInstanceWithView } from './data-providers/types'; export type { QualitySettings } from './components/RevealToolbar/SettingsContainer/types'; export type { SceneIdentifiers } from './components/SceneContainer/sceneTypes'; @@ -165,4 +165,4 @@ export { RuleBasedOutputsPanel } from './components/RuleBasedOutputs/RuleBasedOu // Functions export { getRuleTriggerTypes } from './components/RuleBasedOutputs/utils'; -export type { InstanceReference, AssetInstanceReference } from './utilities/types'; +export type { InstanceReference, AssetInstanceReference } from './data-providers/types'; diff --git a/react-components/src/query/use3dNodeByExternalId.tsx b/react-components/src/query/use3dNodeByExternalId.tsx index 6db9dad1d01..db82fcf3dff 100644 --- a/react-components/src/query/use3dNodeByExternalId.tsx +++ b/react-components/src/query/use3dNodeByExternalId.tsx @@ -4,7 +4,7 @@ import { type UseQueryResult, useQuery } from '@tanstack/react-query'; import { useFdmNodeCache } from '../components/CacheProvider/NodeCacheProvider'; import { type Node3D } from '@cognite/sdk'; -import { type DmsUniqueIdentifier } from '../utilities/FdmSDK'; +import { type DmsUniqueIdentifier } from '../data-providers/FdmSDK'; import { useReveal } from '../components/RevealCanvas/ViewerContext'; export const use3dNodeByExternalId = ({ @@ -30,7 +30,10 @@ export const use3dNodeByExternalId = ({ })); const modelMappings = ( - await fdmNodeCache.cache.getMappingsForFdmIds([{ externalId, space }], modelsRevisionIds) + await fdmNodeCache.cache.getMappingsForFdmInstances( + [{ externalId, space }], + modelsRevisionIds + ) ).find((model) => model.mappings.size > 0); const node3d = modelMappings?.mappings.get(externalId)?.[0]; diff --git a/react-components/src/query/use3dRelatedDirectConnections.ts b/react-components/src/query/use3dRelatedDirectConnections.ts index 968f7950c2c..71740214153 100644 --- a/react-components/src/query/use3dRelatedDirectConnections.ts +++ b/react-components/src/query/use3dRelatedDirectConnections.ts @@ -3,15 +3,16 @@ */ import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { useFdmSdk } from '../components/RevealCanvas/SDKProvider'; -import { type Source, type DmsUniqueIdentifier } from '../utilities/FdmSDK'; +import { useFdm3dDataProvider, useFdmSdk } from '../components/RevealCanvas/SDKProvider'; +import { type Source, type DmsUniqueIdentifier } from '../data-providers/FdmSDK'; import assert from 'assert'; -import { type FdmInstanceWithView } from '../utilities/types'; +import { type FdmInstanceWithView } from '../data-providers/types'; export function use3dRelatedDirectConnections( instance: DmsUniqueIdentifier | undefined ): UseQueryResult { const fdmSdk = useFdmSdk(); + const fdmDataProvider = useFdm3dDataProvider(); return useQuery({ queryKey: ['reveal-react-components', 'get-3d-related-direct-connections'], @@ -30,18 +31,15 @@ export function use3dRelatedDirectConnections( ) ).items[0]; - const directlyRelatedObjects = Object.values(instanceContent.properties) - .map((spaceScope) => - Object.values(spaceScope) - .map((fieldValues) => - Object.values(fieldValues).filter( - (value: any): value is DmsUniqueIdentifier => - value.externalId !== undefined && value.space !== undefined - ) + const directlyRelatedObjects = Object.values(instanceContent.properties).flatMap( + (spaceScope) => + Object.values(spaceScope).flatMap((fieldValues) => + Object.values(fieldValues).filter( + (value: any): value is DmsUniqueIdentifier => + value.externalId !== undefined && value.space !== undefined ) - .flat() - ) - .flat(); + ) + ); if (directlyRelatedObjects.length === 0) { return []; @@ -70,7 +68,7 @@ export function use3dRelatedDirectConnections( const viewResultIndex = viewToDeduplicatedIndexMap.get(createViewKey(view)); assert(viewResultIndex !== undefined); const propsForView = viewProps.items[viewResultIndex]; - return Object.keys(propsForView.properties).some((propName) => propName === 'inModel3d'); + return fdmDataProvider.is3dView(propsForView); }); return threeDRelatedViews.map(([index, view]) => ({ diff --git a/react-components/src/query/use3dRelatedEdgeConnections.ts b/react-components/src/query/use3dRelatedEdgeConnections.ts index 7add1876f3b..53a5879040b 100644 --- a/react-components/src/query/use3dRelatedEdgeConnections.ts +++ b/react-components/src/query/use3dRelatedEdgeConnections.ts @@ -3,17 +3,16 @@ */ import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { useFdmSdk } from '../components/RevealCanvas/SDKProvider'; -import { type DmsUniqueIdentifier } from '../utilities/FdmSDK'; +import { useFdm3dDataProvider, useFdmSdk } from '../components/RevealCanvas/SDKProvider'; +import { type DmsUniqueIdentifier } from '../data-providers/FdmSDK'; import { zipWith } from 'lodash'; -import { type FdmInstanceWithView } from '../utilities/types'; -import assert from 'assert'; -import { SYSTEM_3D_EDGE_SOURCE } from '../utilities/globalDataModels'; +import { type FdmInstanceWithView } from '../data-providers/types'; export function use3dRelatedEdgeConnections( fdmId: DmsUniqueIdentifier | undefined ): UseQueryResult { const fdmSdk = useFdmSdk(); + const fdmDataProvider = useFdm3dDataProvider(); return useQuery({ queryKey: [ @@ -23,31 +22,22 @@ export function use3dRelatedEdgeConnections( fdmId?.space ], queryFn: async () => { - assert(fdmId !== undefined); - const nodesResult = await fdmSdk.queryNodesAndEdges({ - ...related3dEdgesQuery, - parameters: { - instanceExternalId: fdmId.externalId, - instanceSpace: fdmId.space - } - }); + if (fdmId === undefined) { + return []; + } - const nodeIds = nodesResult.items.connected_objects_with_3d.map((obj) => ({ - instanceType: 'node' as const, - externalId: obj.externalId, - space: obj.space - })); + const relatedInstances = await fdmDataProvider.getEdgeConnected3dInstances(fdmId); - if (nodeIds.length === 0) { + if (relatedInstances.length === 0) { return []; } const views = await fdmSdk.inspectInstances({ inspectionOperations: { involvedViews: {} }, - items: nodeIds + items: relatedInstances.map((instance) => ({ ...instance, instanceType: 'node' })) }); - return zipWith(nodeIds, views.items, (node, view) => ({ + return zipWith(relatedInstances, views.items, (node, view) => ({ ...node, view: view.inspectionResults.involvedViews[0] })); @@ -55,72 +45,3 @@ export function use3dRelatedEdgeConnections( enabled: fdmId !== undefined }); } - -const related3dEdgesQuery = { - with: { - start_instance: { - nodes: { - filter: { - and: [ - { - equals: { - property: ['node', 'externalId'], - value: { parameter: 'instanceExternalId' } - } - }, - { - equals: { - property: ['node', 'space'], - value: { parameter: 'instanceSpace' } - } - } - ] - } - }, - limit: 1 - }, - start_to_object_edges: { - edges: { - from: 'start_instance', - maxDistance: 1, - direction: 'outwards' - }, - limit: 1000 - }, - connected_objects_with_3d: { - nodes: { - from: 'start_to_object_edges', - chainTo: 'destination' - }, - limit: 1000 - }, - edges_of_3d_type: { - edges: { - from: 'connected_objects_with_3d', - maxDistance: 1, - direction: 'outwards', - filter: { - and: [ - { - hasData: [SYSTEM_3D_EDGE_SOURCE] - } - ] - } - } - }, - nodes_with_3d_connection: { - nodes: { - from: 'edges_of_3d_type', - chainTo: 'source' - }, - limit: 1000 - } - }, - select: { - start_instance: {}, - start_to_object_edges: {}, - connected_objects_with_3d: {}, - edges_of_3d_type: {}, - nodes_with_3d_connection: {} - } -} as const; diff --git a/react-components/src/query/use3dScenes.tsx b/react-components/src/query/use3dScenes.tsx index 033900a3e9f..e1024e353fb 100644 --- a/react-components/src/query/use3dScenes.tsx +++ b/react-components/src/query/use3dScenes.tsx @@ -6,7 +6,7 @@ import { type QueryFunction, useQuery, type UseQueryResult } from '@tanstack/rea import { useSDK } from '../components/RevealCanvas/SDKProvider'; import { type CogniteClient } from '@cognite/sdk'; import { useMemo } from 'react'; -import { type EdgeItem, FdmSDK, type Query, type NodeItem } from '../utilities/FdmSDK'; +import { type EdgeItem, FdmSDK, type Query, type NodeItem } from '../data-providers/FdmSDK'; import { type SceneConfigurationProperties, type Cdf3dRevisionProperties, diff --git a/react-components/src/query/useAssetsAndTimeseriesLinkageDataQuery.ts b/react-components/src/query/useAssetsAndTimeseriesLinkageDataQuery.ts index b422e5287ea..c57288bca66 100644 --- a/react-components/src/query/useAssetsAndTimeseriesLinkageDataQuery.ts +++ b/react-components/src/query/useAssetsAndTimeseriesLinkageDataQuery.ts @@ -20,7 +20,7 @@ import { type AssetIdsAndTimeseries, type AssetAndTimeseriesIds, type AssetIdsAndTimeseriesData -} from '../utilities/types'; +} from '../data-providers/types'; import { queryKeys } from '../utilities/queryKeys'; import { getTimeseriesByIds } from '../hooks/network/getTimeseriesByIds'; import { isDefined } from '../utilities/isDefined'; diff --git a/react-components/src/query/useModelsForInstanceQuery.ts b/react-components/src/query/useModelsForInstanceQuery.ts index 3cee5c41a8d..316606312ef 100644 --- a/react-components/src/query/useModelsForInstanceQuery.ts +++ b/react-components/src/query/useModelsForInstanceQuery.ts @@ -2,20 +2,18 @@ * Copyright 2024 Cognite AS */ import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { type DmsUniqueIdentifier, type FdmSDK } from '../utilities/FdmSDK'; import { getCadModelsForAsset } from '../hooks/network/getCadModelsForAsset'; import { getPointCloudModelsForAsset } from '../hooks/network/getPointCloudModelsForAsset'; -import { useFdmSdk, useSDK } from '../components/RevealCanvas/SDKProvider'; +import { useFdm3dDataProvider, useSDK } from '../components/RevealCanvas/SDKProvider'; import { type CogniteClient } from '@cognite/sdk'; import { type TaggedAddResourceOptions } from '../components/Reveal3DResources/types'; -import { getCadModelsForFdmInstance } from '../hooks/network/getCadModelsForFdmInstance'; import { getImage360CollectionsForAsset } from '../hooks/network/getImage360CollectionsForAsset'; import { type AssetInstanceReference, type InstanceReference, isAssetInstance, isDmsInstance -} from '../utilities/types'; +} from '../data-providers/types'; import { uniqBy } from 'lodash'; import { createAddOptionsKey } from '../utilities/createAddOptionsKey'; @@ -23,7 +21,7 @@ export const useModelsForInstanceQuery = ( instance: InstanceReference | undefined ): UseQueryResult => { const cogniteClient = useSDK(); - const fdmSdk = useFdmSdk(); + const fdm3dDataProvider = useFdm3dDataProvider(); return useQuery({ queryKey: ['reveal', 'react-components', instance], @@ -37,7 +35,7 @@ export const useModelsForInstanceQuery = ( } if (isDmsInstance(instance)) { - return await getModelsForFdmInstance(instance, fdmSdk); + return await fdm3dDataProvider.getCadModelsForInstance(instance); } }, enabled: instance !== undefined @@ -61,12 +59,3 @@ async function getModelsForAssetInstance( return uniqBy(results, createAddOptionsKey); } - -async function getModelsForFdmInstance( - instance: DmsUniqueIdentifier, - fdmSdk: FdmSDK -): Promise { - const cadModelsPromise = getCadModelsForFdmInstance(instance, fdmSdk); - - return await cadModelsPromise; -} diff --git a/react-components/src/query/useSceneConfig.ts b/react-components/src/query/useSceneConfig.ts index 304eb274a5e..a0e5245a37f 100644 --- a/react-components/src/query/useSceneConfig.ts +++ b/react-components/src/query/useSceneConfig.ts @@ -19,7 +19,7 @@ import { type Skybox } from '../components/SceneContainer/sceneTypes'; import { useFdmSdk } from '../components/RevealCanvas/SDKProvider'; -import { type Source, type FdmSDK } from '../utilities/FdmSDK'; +import { type Source, type FdmSDK } from '../data-providers/FdmSDK'; import { type SceneConfigurationProperties } from '../hooks/types'; import { fdmViewsExist } from '../utilities/fdmViewsExist'; diff --git a/react-components/src/query/useSearchMappedEquipmentFDM.tsx b/react-components/src/query/useSearchMappedEquipmentFDM.tsx index 0995befaf26..45da5749830 100644 --- a/react-components/src/query/useSearchMappedEquipmentFDM.tsx +++ b/react-components/src/query/useSearchMappedEquipmentFDM.tsx @@ -1,44 +1,36 @@ /*! * Copyright 2023 Cognite AS */ -import { type CogniteClient } from '@cognite/sdk'; import { useMemo } from 'react'; import { - type EdgeItem, type NodeItem, - FdmSDK, + type FdmSDK, type Source, - type Query, type DmsUniqueIdentifier, - type Space, - type ExternalId, type InstanceFilter -} from '../utilities/FdmSDK'; -import { useSDK } from '../components/RevealCanvas/SDKProvider'; +} from '../data-providers/FdmSDK'; +import { useFdm3dDataProvider, useFdmSdk } from '../components/RevealCanvas/SDKProvider'; import { type UseQueryResult, useQuery } from '@tanstack/react-query'; -import { SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../utilities/globalDataModels'; import { type AddModelOptions } from '@cognite/reveal'; import { isEqual, uniq, chunk } from 'lodash'; -import { getDMSModels } from '../components/CacheProvider/requests'; +import { type Fdm3dDataProvider } from '../data-providers/Fdm3dDataProvider'; +import { removeEmptyProperties } from '../utilities/removeEmptyProperties'; -export type SearchResultsWithView = { view: Source; instances: NodeItem[] }; - -type FdmKey = `${Space}/${ExternalId}`; +export type InstancesWithView = { view: Source; instances: NodeItem[] }; export const useSearchMappedEquipmentFDM = ( query: string, viewsToSearch: DmsUniqueIdentifier[], models: AddModelOptions[], instancesFilter: InstanceFilter | undefined, - limit: number = 100, - userSdk?: CogniteClient -): UseQueryResult => { + limit: number = 100 +): UseQueryResult => { if (limit > 1000) { throw new Error('Limit cannot be greater than 1000'); } - const sdk = useSDK(userSdk); - const fdmSdk = useMemo(() => new FdmSDK(sdk), [sdk]); + const fdmSdk = useFdmSdk(); + const fdmDataProvider = useFdm3dDataProvider(); const spacesToSearch = useMemo( () => uniq(viewsToSearch.map((view) => view.space)), @@ -55,7 +47,7 @@ export const useSearchMappedEquipmentFDM = ( const sources = await createSourcesFromViews(viewsToSearch, fdmSdk); const chunkedSources = chunk(sources, 10); - const queryResults: SearchResultsWithView[] = []; + const queryResults: InstancesWithView[] = []; for (const sourceChunk of chunkedSources) { queryResults.push( @@ -66,6 +58,7 @@ export const useSearchMappedEquipmentFDM = ( models, instancesFilter, fdmSdk, + fdmDataProvider, limit )) ); @@ -83,16 +76,18 @@ const searchNodesWithViewsAndModels = async ( models: AddModelOptions[], instancesFilter: InstanceFilter | undefined, fdmSdk: FdmSDK, + fdmDataProvider: Fdm3dDataProvider, limit: number = 100 -): Promise => { +): Promise => { if (query === '') { - const result = await fdmSdk.queryNodesAndEdges( - createMappedEquipmentQuery(models, sourcesToSearch, limit) + const nodeItems = await fdmDataProvider.listMappedFdmNodes( + models, + sourcesToSearch, + instancesFilter, + limit ); - const transformedResults = convertQueryNodeItemsToSearchResultsWithViews( - result.items.mapped_nodes.concat(result.items.mapped_nodes_2) as NodeItem[] - ); + const transformedResults = convertQueryNodeItemsToSearchResultsWithViews(nodeItems); const combinedWithOtherViews = sourcesToSearch.map((view) => ({ view, @@ -105,7 +100,7 @@ const searchNodesWithViewsAndModels = async ( return combinedWithOtherViews; } - const searchResults: SearchResultsWithView[] = []; + const searchResults: InstancesWithView[] = []; for (const view of sourcesToSearch) { const result = await fdmSdk.searchInstances(view, query, 'node', limit, instancesFilter); @@ -115,86 +110,32 @@ const searchNodesWithViewsAndModels = async ( instances: result.instances }); } - const filteredSearchResults = await filterSearchResultsByMappedTo3DModels( - fdmSdk, - searchResults, - models, - spacesToSearch - ); - return filteredSearchResults; + return await fdmDataProvider.filterNodesByMappedTo3d(searchResults, models, spacesToSearch); }; export const useAllMappedEquipmentFDM = ( models: AddModelOptions[], - viewsToSearch: DmsUniqueIdentifier[], - userSdk?: CogniteClient + viewsToSearch: DmsUniqueIdentifier[] ): UseQueryResult => { - const sdk = useSDK(userSdk); - - const fdmSdk = useMemo(() => new FdmSDK(sdk), [sdk]); + const fdmSdk = useFdmSdk(); + const fdmDataProvider = useFdm3dDataProvider(); return useQuery({ queryKey: ['reveal', 'react-components', 'all-mapped-equipment-fdm', viewsToSearch], queryFn: async () => { const viewSources = await createSourcesFromViews(viewsToSearch, fdmSdk); - const queries = createChunkedMappedEquipmentQueries(models, viewSources, 10000); - - const mappedEquipment: NodeItem[] = []; - for (const query of queries) { - let currentPage = await fdmSdk.queryNodesAndEdges(query); - - const mappedNodes = currentPage.items.mapped_nodes as NodeItem[]; - const mappedNodesParents = currentPage.items.mapped_nodes_2 as NodeItem[]; - mappedEquipment.push( - ...mappedNodes.concat(mappedNodesParents).map((node) => removeEmptyProperties(node)) - ); - - while (!isEqual(currentPage.nextCursor, {})) { - query.cursors = currentPage.nextCursor; - - currentPage = await fdmSdk.queryNodesAndEdges(query); - - const cleanedNodes = currentPage.items.mapped_nodes.map((node) => - removeEmptyProperties(node as NodeItem) - ); - const cleanedNodesParents = currentPage.items.mapped_nodes_2.map((node) => - removeEmptyProperties(node as NodeItem) - ); - - mappedEquipment.push(...cleanedNodes.concat(cleanedNodesParents)); - } - } - return mappedEquipment; + return await fdmDataProvider.listAllMappedFdmNodes(models, viewSources, undefined); }, staleTime: Infinity }); }; -function removeEmptyProperties(queryResultNode: NodeItem): NodeItem { - Object.keys(queryResultNode.properties).forEach((space) => { - const currentSpaceProperties = queryResultNode.properties[space]; - const newProperties: Record> = {}; - - Object.keys(currentSpaceProperties).forEach((view) => { - const currentViewProperties = currentSpaceProperties[view]; - - if (Object.keys(currentViewProperties).length !== 0) { - newProperties[view] = currentViewProperties; - } - }); - - queryResultNode.properties[space] = newProperties; - }); - - return queryResultNode; -} - function convertQueryNodeItemsToSearchResultsWithViews( queryItems: NodeItem[] -): SearchResultsWithView[] { - return queryItems.reduce((acc, fdmNode) => { +): InstancesWithView[] { + return queryItems.reduce((acc, fdmNode) => { const cleanedNode = removeEmptyProperties(fdmNode); Object.keys(cleanedNode.properties).forEach((space) => { @@ -237,296 +178,6 @@ function convertQueryNodeItemsToSearchResultsWithViews( }, []); } -async function createMappedEquipmentMaps( - allEdges: EdgeItem[], - models: AddModelOptions[], - spacesToSearch: string[], - fdmSdk: FdmSDK -): Promise<{ - mappedEquipmentFirstLevelMap: Record; - equipmentSecondLevelMap: Record; -}> { - const mappedEquipmentFirstLevelMap: Record = {}; - const equipmentSecondLevelMap: Record = {}; - - const modelsMap = await createModelsMap(models, fdmSdk); - - for (const edge of allEdges) { - const { space: endSpace, externalId: endExternalId } = edge.endNode; - const { space, externalId } = edge.startNode; - - const isModelsMapped = models.some( - ({ modelId, revisionId }) => - modelId.toString() === endExternalId && - revisionId.toString() === getRevisionIdFromEdge(edge) - ); - - const modelInstances = modelsMap.get(endExternalId); - - if (modelInstances?.find((model) => model.space === endSpace) !== undefined && isModelsMapped) { - const key = `${space}/${externalId}`; - - const keyEdges = mappedEquipmentFirstLevelMap[key]; - mappedEquipmentFirstLevelMap[key] = keyEdges !== undefined ? keyEdges.concat(edge) : [edge]; - continue; - } - - if (spacesToSearch.includes(endSpace)) { - const key = `${space}/${externalId}`; - - const keyEdges = equipmentSecondLevelMap[key]; - - equipmentSecondLevelMap[key] = keyEdges !== undefined ? keyEdges.concat(edge) : [edge]; - continue; - } - } - - return { mappedEquipmentFirstLevelMap, equipmentSecondLevelMap }; -} - -async function createModelsMap( - models: AddModelOptions[], - fdmSdk: FdmSDK -): Promise> { - const modelInstances = await Promise.all( - models.map(async (model) => await getDMSModels(model.modelId, fdmSdk)) - ); - return new Map( - modelInstances.map((modelInstanceList, ind) => [`${models[ind].modelId}`, modelInstanceList]) - ); -} - -function createCheckMappedEquipmentQuery( - instances: NodeItem[], - directlyMappedNodes: DmsUniqueIdentifier[], - models: AddModelOptions[], - views: Source[], - limit: number = 1000 -): Query { - return { - with: { - mapped_nodes: { - nodes: { - filter: { - in: { - property: ['node', 'externalId'], - values: instances - .map((instance) => instance.externalId) - .concat(directlyMappedNodes.map((node) => node.externalId)) - } - } - }, - limit - }, - mapped_edges: { - edges: { - from: 'mapped_nodes', - direction: 'outwards', - nodeFilter: { - in: { - property: ['node', 'externalId'], - values: models.map((model) => model.modelId.toString()) - } - }, - maxDistance: 2 - }, - limit: 10000 - } - }, - select: { - mapped_edges: { - sources: [{ source: SYSTEM_3D_EDGE_SOURCE, properties: [] }] - }, - mapped_nodes: { - sources: views.map((view) => ({ source: view, properties: [] })) - } - } - }; -} - -function createChunkedMappedEquipmentQueries( - models: AddModelOptions[], - views: Source[], - limit: number = 10000, - cursors?: Record -): Query[] { - const viewChunks = chunk(views, 10); - return viewChunks.map((viewChunk) => - createMappedEquipmentQuery(models, viewChunk, limit, cursors) - ); -} - -function createMappedEquipmentQuery( - models: AddModelOptions[], - views: Source[], - limit: number = 10000, - cursors?: Record -): Query { - return { - with: { - mapped_edges: { - edges: { - filter: createInModelsFilter(models) - }, - limit - }, - mapped_nodes: { - nodes: { - from: 'mapped_edges', - chainTo: 'source' - }, - limit - }, - mapped_edges_2: { - edges: { - from: 'mapped_nodes', - direction: 'inwards', - maxDistance: 1 - } - }, - mapped_nodes_2: { - nodes: { - from: 'mapped_edges_2', - chainTo: 'destination' - } - } - }, - cursors, - select: { - mapped_nodes_2: { - sources: views.map((view) => ({ source: view, properties: [] })) - }, - mapped_edges_2: {}, - mapped_edges: { - sources: [{ source: SYSTEM_3D_EDGE_SOURCE, properties: [] }] - }, - mapped_nodes: { - sources: views.map((view) => ({ source: view, properties: [] })) - } - } - }; -} - -function createInModelsFilter(models: AddModelOptions[]): { in: any } { - return { - in: { - property: [ - SYSTEM_3D_EDGE_SOURCE.space, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionId' - ], - values: models.map((model) => model.revisionId) - } - }; -} - -async function filterSearchResultsByMappedTo3DModels( - fdmSdk: FdmSDK, - searchResults: SearchResultsWithView[], - models: AddModelOptions[], - spacesToSearch: string[] -): Promise { - const filteredSearchResults: SearchResultsWithView[] = []; - - const searchResultsNodes = searchResults.flatMap((result) => result.instances); - const searchResultsViews = searchResults.map((result) => result.view); - const directlyMappedNodes = searchResultsNodes - .map((node) => getDirectRelationProperties(node)) - .flat(); - - if (searchResultsNodes.length === 0) { - return searchResults; - } - - const mappedEquipmentQuery = createCheckMappedEquipmentQuery( - searchResultsNodes, - directlyMappedNodes, - models, - searchResultsViews - ); - const queryResult = await fdmSdk.queryNodesAndEdges(mappedEquipmentQuery); - - const { mappedEquipmentFirstLevelMap, equipmentSecondLevelMap } = await createMappedEquipmentMaps( - queryResult.items.mapped_edges as EdgeItem[], - models, - spacesToSearch, - fdmSdk - ); - - for (const searchResult of searchResults) { - const filteredInstances = searchResult.instances.filter((instance) => - checkInstanceWithMappedEquipmentMaps( - mappedEquipmentFirstLevelMap, - equipmentSecondLevelMap, - instance - ) - ); - - filteredSearchResults.push({ view: searchResult.view, instances: filteredInstances }); - } - - return filteredSearchResults; -} - -function checkInstanceWithMappedEquipmentMaps( - mappedEquipmentFirstLevelMap: Record, - equipmentSecondLevelMap: Record, - instance: NodeItem -): boolean { - const key: FdmKey = `${instance.space}/${instance.externalId}`; - const directRelationProperties = getDirectRelationProperties(instance); - const isMappedFirstLevel = mappedEquipmentFirstLevelMap[key] !== undefined; - const isSecondLevelWithEdge = equipmentSecondLevelMap[key] !== undefined; - const isSecondLevelWithDirectRelation = directRelationProperties.length > 0; - - if (isMappedFirstLevel) { - return true; - } - - if (isSecondLevelWithEdge) { - return equipmentSecondLevelMap[key].some((edge) => { - const { space, externalId } = edge.endNode; - - const secondLevelKey: FdmKey = `${space}/${externalId}`; - const isMappedWithEdge = mappedEquipmentFirstLevelMap[secondLevelKey] !== undefined; - - return isMappedWithEdge; - }); - } - - if (isSecondLevelWithDirectRelation) { - const isMappedWithDirectRelation = directRelationProperties.some( - ({ externalId, space }) => - mappedEquipmentFirstLevelMap[`${space}/${externalId}`] !== undefined - ); - - return isMappedWithDirectRelation; - } - - return false; -} - -function getRevisionIdFromEdge(edge: EdgeItem): string | undefined { - return (edge.properties as any)?.[SYSTEM_SPACE_3D_SCHEMA]?.[ - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}` - ]?.revisionId?.toString(); -} - -function getDirectRelationProperties(searchResultNode: NodeItem): DmsUniqueIdentifier[] { - const directRelations: DmsUniqueIdentifier[] = []; - const nodeProperties = searchResultNode.properties; - - Object.keys(nodeProperties).forEach((propertyKey) => { - const { space, externalId } = nodeProperties[propertyKey] as any; - - if (space !== undefined && externalId !== undefined) { - directRelations.push({ space, externalId }); - } - }); - - return directRelations; -} - async function createSourcesFromViews( viewsToSearch: DmsUniqueIdentifier[], fdmSdk: FdmSDK diff --git a/react-components/src/utilities/fdmViewsExist.ts b/react-components/src/utilities/fdmViewsExist.ts index e716f9a1d5a..3a4de2d3819 100644 --- a/react-components/src/utilities/fdmViewsExist.ts +++ b/react-components/src/utilities/fdmViewsExist.ts @@ -1,7 +1,7 @@ /*! * Copyright 2024 Cognite AS */ -import { type FdmSDK, type Source } from './FdmSDK'; +import { type FdmSDK, type Source } from '../data-providers/FdmSDK'; export async function fdmViewsExist(fdmSdk: FdmSDK, neededViews: Source[]): Promise { const views = await fdmSdk.getViewsByIds(neededViews); diff --git a/react-components/src/utilities/removeEmptyProperties.ts b/react-components/src/utilities/removeEmptyProperties.ts new file mode 100644 index 00000000000..2930e402bfd --- /dev/null +++ b/react-components/src/utilities/removeEmptyProperties.ts @@ -0,0 +1,23 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type NodeItem } from '../data-providers/FdmSDK'; + +export function removeEmptyProperties(queryResultNode: NodeItem): NodeItem { + Object.keys(queryResultNode.properties).forEach((space) => { + const currentSpaceProperties = queryResultNode.properties[space]; + const newProperties: Record> = {}; + + Object.keys(currentSpaceProperties).forEach((view) => { + const currentViewProperties = currentSpaceProperties[view]; + + if (Object.keys(currentViewProperties).length !== 0) { + newProperties[view] = currentViewProperties; + } + }); + + queryResultNode.properties[space] = newProperties; + }); + + return queryResultNode; +} diff --git a/react-components/stories/CadStylingCache.stories.tsx b/react-components/stories/CadStylingCache.stories.tsx index 601fdc9b551..804e3aa2493 100644 --- a/react-components/stories/CadStylingCache.stories.tsx +++ b/react-components/stories/CadStylingCache.stories.tsx @@ -59,7 +59,7 @@ const Models = ({ addModelOptions }: CogniteCadModelProps): JSX.Element => { () => data ?.get(`${platformModelOptions.modelId}/${platformModelOptions.revisionId}`) - ?.map((edgeWithNode) => edgeWithNode.edge.properties.revisionNodeId), + ?.map((edgeWithNode) => edgeWithNode.connection.nodeId), [data] ); diff --git a/react-components/stories/SearchHooks.stories.tsx b/react-components/stories/SearchHooks.stories.tsx index ef94d796c82..52368daf22d 100644 --- a/react-components/stories/SearchHooks.stories.tsx +++ b/react-components/stories/SearchHooks.stories.tsx @@ -35,7 +35,7 @@ import { useSearchAssetsMappedPointCloudAnnotations } from '../src/query/useSearchAssetsMappedPointCloudAnnotations'; import { isEqual } from 'lodash'; -import { type NodeItem } from '../src/utilities/FdmSDK'; +import { type NodeItem } from '../src/data-providers/FdmSDK'; import { Button, Input } from '@cognite/cogs.js'; import { is360ImageAddOptions } from '../src/components/Reveal3DResources/typeGuards'; @@ -71,8 +71,7 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React viewsToSearch, filteredResources, undefined, - 100, - sdk + 100 ); const { data: assetSearchData } = useSearchMappedEquipmentAssetMappings( @@ -82,7 +81,7 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React sdk ); - const { data: allEquipment } = useAllMappedEquipmentFDM(filteredResources, viewsToSearch, sdk); + const { data: allEquipment } = useAllMappedEquipmentFDM(filteredResources, viewsToSearch); const { data: allAssets, @@ -248,21 +247,19 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React return ( <> - - - - - - - + + + + +

Mapped equipment

{ return ( - - - + + + + + ); } };