Skip to content

Commit

Permalink
feat(react-components): fdm support in rule based core (#4706)
Browse files Browse the repository at this point in the history
* add a hook to get all instances from all direct connections and merge all relevant data for each instance - wip

* move direct connection with properties hook and adapt the initial functions to support fdm trigger type and styling - wip

* adapting styling groups accross components to support fdm styling groups also - wip

* insert fdm types for rule type definitions

* refactoring expression types

* add datetime and boolean expressions statements

* refactoring and add property trigger for numeric expression supporting fdm

* add optional parameters for the fdm typing type

* fix property types

* fix date statement and fdm externalid for styling list

* fix datetime limit type for between and notbetween

* add expression safeguards

* refactoring and remove duplicated timeserie ids when traversing expression to get time series

* update and export fdm types and some cleanup

* add fdm new conditions

* stories cleanup

* cleanup fdmsdk log

* use isLoading flag for the spinner accuracy and some refactoring on loading and caching

* reduce complexity a bit

* refactoring for some small optimizations

* lint fix

* export fdm types for rule based

* refactoring some rule type definitions to support correct typing data

* fix datetimecondition definition

* fix boolean condition types and export unique types list for numeric and datetime

* add into index

* dont request assetids from ts if it is empty

* changes from cr

* lint fix

* use createFdmKey - cr

* add createFdmKey for uniqBy
  • Loading branch information
danpriori authored Aug 28, 2024
1 parent f24bab3 commit 4b456b9
Show file tree
Hide file tree
Showing 15 changed files with 999 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import { Button, Dropdown, Menu, Tooltip as CogsTooltip } from '@cognite/cogs.js
import { RuleBasedOutputsSelector } from '../RuleBasedOutputs/RuleBasedOutputsSelector';
import {
type EmptyRuleForSelection,
type AssetStylingGroupAndStyleIndex,
type RuleAndEnabled
type RuleAndEnabled,
type AllMappingStylingGroupAndStyleIndex,
type AllRuleBasedStylingGroups
} from '../RuleBasedOutputs/types';
import { useTranslation } from '../i18n/I18n';
import { useFetchRuleInstances } from '../RuleBasedOutputs/hooks/useFetchRuleInstances';
import { use3dModels } from '../../hooks/use3dModels';
import { type AssetStylingGroup } from '../..';
import { type CadModelOptions } from '../Reveal3DResources/types';
import { useAssetMappedNodesForRevisions } from '../CacheProvider/AssetMappingAndNode3DCacheProvider';
import { RuleBasedSelectionItem } from '../RuleBasedOutputs/components/RuleBasedSelectionItem';
import { generateEmptyRuleForSelection, getRuleBasedById } from '../RuleBasedOutputs/utils';
import { useReveal3DResourcesStylingLoading } from '../Reveal3DResources/Reveal3DResourcesInfoContext';

type RuleBasedOutputsButtonProps = {
onRuleSetStylingChanged?: (stylings: AssetStylingGroup[] | undefined) => void;
onRuleSetStylingChanged?: (stylings: AllRuleBasedStylingGroups | undefined) => void;
onRuleSetSelectedChanged?: (ruleSet: RuleAndEnabled | undefined) => void;
};
export const RuleBasedOutputsButton = ({
Expand All @@ -36,7 +36,7 @@ export const RuleBasedOutputsButton = ({
const [currentRuleSetEnabled, setCurrentRuleSetEnabled] = useState<RuleAndEnabled>();
const [emptyRuleSelected, setEmptyRuleSelected] = useState<EmptyRuleForSelection>();
const [currentStylingGroups, setCurrentStylingGroups] = useState<
AssetStylingGroupAndStyleIndex[] | undefined
AllMappingStylingGroupAndStyleIndex[] | undefined
>();
const [ruleInstances, setRuleInstances] = useState<RuleAndEnabled[] | undefined>();

Expand All @@ -47,6 +47,8 @@ export const RuleBasedOutputsButton = ({
const [newRuleSetEnabled, setNewRuleSetEnabled] = useState<RuleAndEnabled>();
const isRuleLoadingFromContext = useReveal3DResourcesStylingLoading();

const [isAllMappingsFetched, setIsAllMappingsFetched] = useState(false);

const { data: ruleInstancesResult } = useFetchRuleInstances();

useEffect(() => {
Expand All @@ -64,11 +66,13 @@ export const RuleBasedOutputsButton = ({

useEffect(() => {
const hasRuleLoading =
currentStylingGroups !== undefined &&
currentStylingGroups.length > 0 &&
isRuleLoadingFromContext;
(currentStylingGroups !== undefined &&
currentStylingGroups.length > 0 &&
isRuleLoadingFromContext) ||
!isAllMappingsFetched;

setIsRuleLoading(hasRuleLoading);
}, [isRuleLoadingFromContext, currentStylingGroups]);
}, [isAllMappingsFetched, currentStylingGroups, isRuleLoadingFromContext, newRuleSetEnabled]);

const onChange = useCallback(
(data: string | undefined): void => {
Expand All @@ -89,19 +93,28 @@ export const RuleBasedOutputsButton = ({
emptySelection.isEnabled = true;
if (onRuleSetStylingChanged !== undefined) onRuleSetStylingChanged(undefined);
}

setEmptyRuleSelected(emptySelection);
setNewRuleSetEnabled(selectedRule);
},
[ruleInstances, onRuleSetStylingChanged, onRuleSetSelectedChanged]
[ruleInstances, emptyRuleSelected, onRuleSetStylingChanged, onRuleSetSelectedChanged]
);

const ruleSetStylingChanged = (
stylingGroups: AssetStylingGroupAndStyleIndex[] | undefined
stylingGroups: AllMappingStylingGroupAndStyleIndex[] | undefined
): void => {
setCurrentStylingGroups(stylingGroups);
const assetStylingGroups = stylingGroups?.map((group) => group.assetStylingGroup);
if (onRuleSetStylingChanged !== undefined) onRuleSetStylingChanged(assetStylingGroups);
const assetStylingGroups = stylingGroups?.map(
(group) => group.assetMappingsStylingGroupAndIndex.assetStylingGroup
);
const fdmStylingGroups = stylingGroups?.map(
(group) => group.fdmStylingGroupAndStyleIndex.fdmStylingGroup
);
const allStylingGroups: AllRuleBasedStylingGroups = {
assetStylingGroup: assetStylingGroups ?? [],
fdmStylingGroup: fdmStylingGroups ?? []
};

if (onRuleSetStylingChanged !== undefined) onRuleSetStylingChanged(allStylingGroups);
};

if (ruleInstances === undefined || ruleInstances.length === 0) {
Expand Down Expand Up @@ -158,6 +171,7 @@ export const RuleBasedOutputsButton = ({
{ruleInstances !== undefined && ruleInstances?.length > 0 && (
<RuleBasedOutputsSelector
onRuleSetChanged={ruleSetStylingChanged}
onAllMappingsFetched={setIsAllMappingsFetched}
ruleSet={currentRuleSetEnabled?.rule.properties}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/*!
* Copyright 2024 Cognite AS
*/
import { useEffect, type ReactElement, useState } from 'react';
import { useEffect, type ReactElement, useState, useMemo } from 'react';

import { CogniteCadModel } from '@cognite/reveal';
import { type RuleOutputSet, type AssetStylingGroupAndStyleIndex } from './types';
import {
type RuleOutputSet,
type AllMappingStylingGroupAndStyleIndex,
type FdmInstanceNodeWithConnectionAndProperties
} from './types';
import { generateRuleBasedOutputs } from './utils';
import { use3dModels } from '../../hooks/use3dModels';
import { type Datapoints, type Asset, type AssetMapping3D } from '@cognite/sdk';
Expand All @@ -18,17 +22,21 @@ import { useCreateAssetMappingsMapPerModel } from '../../hooks/useCreateAssetMap
import { useExtractUniqueAssetIdsFromMapped } from './hooks/useExtractUniqueAssetIdsFromMapped';
import { useConvertAssetMetadatasToLowerCase } from './hooks/useConvertAssetMetadatasToLowerCase';
import { useExtractTimeseriesIdsFromRuleSet } from './hooks/useExtractTimeseriesIdsFromRuleSet';
import { useMappedEdgesForRevisions } from '../CacheProvider/NodeCacheProvider';
import { useAll3dDirectConnectionsWithProperties } from '../../query/useAll3dDirectConnectionsWithProperties';

const ruleSetStylingCache = new Map<string, AllMappingStylingGroupAndStyleIndex[]>();

export type ColorOverlayProps = {
ruleSet: RuleOutputSet | undefined;
onRuleSetChanged?: (currentStylings: AssetStylingGroupAndStyleIndex[] | undefined) => void;
onRuleSetChanged?: (currentStylings: AllMappingStylingGroupAndStyleIndex[] | undefined) => void;
onAllMappingsFetched: (value: boolean) => void;
};

const ruleSetStylingCache = new Map<string, AssetStylingGroupAndStyleIndex[]>();

export function RuleBasedOutputsSelector({
ruleSet,
onRuleSetChanged
onRuleSetChanged,
onAllMappingsFetched
}: ColorOverlayProps): ReactElement | undefined {
if (ruleSet === undefined) return;

Expand All @@ -42,17 +50,44 @@ export function RuleBasedOutputsSelector({
return { type: 'cad', modelId: model.modelId, revisionId: model.revisionId };
});

const { data: assetMappings } = useAssetMappedNodesForRevisions(cadModels);
const { data: assetMappings, isLoading: isAssetMappingsLoading } =
useAssetMappedNodesForRevisions(cadModels);

const assetIdsFromMapped = useExtractUniqueAssetIdsFromMapped(assetMappings);

const { data: mappedAssets, isFetched } = useAssetsByIdsQuery(assetIdsFromMapped);
const {
data: mappedAssets,
isLoading: isAssetMappedLoading,
isFetched: isAssetMappingsFetched
} = useAssetsByIdsQuery(assetIdsFromMapped);

const { data: fdmMappedEquipmentEdges, isLoading: isFdmMappingsEdgesLoading } =
useMappedEdgesForRevisions(cadModels, true);

const fdmConnectionWithNodeAndViewList = useMemo(() => {
return fdmMappedEquipmentEdges !== undefined
? Array.from(fdmMappedEquipmentEdges.values()).flat()
: [];
}, [fdmMappedEquipmentEdges]);

const { data: fdmMappings, isLoading: isFdmMappingsLoading } =
useAll3dDirectConnectionsWithProperties(fdmConnectionWithNodeAndViewList);

const allMappingsLoaded =
!isAssetMappingsLoading &&
!isAssetMappedLoading &&
!isFdmMappingsEdgesLoading &&
!isFdmMappingsLoading;

useEffect(() => {
if (isFetched) {
if (isAssetMappingsFetched) {
setAllContextualizedAssets(mappedAssets);
}
}, [mappedAssets, isFetched]);
}, [mappedAssets, isAssetMappingsFetched]);

useEffect(() => {
onAllMappingsFetched(allMappingsLoaded);
}, [allMappingsLoaded]);

const contextualizedAssetNodes = useConvertAssetMetadatasToLowerCase(allContextualizedAssets);

Expand All @@ -67,9 +102,10 @@ export function RuleBasedOutputsSelector({
const flatAssetsMappingsListPerModel = useCreateAssetMappingsMapPerModel(models, assetMappings);

useEffect(() => {
if (assetMappings === undefined || models === undefined || !isFetched) return;
if ((assetMappings === undefined && fdmMappings === undefined) || models === undefined) return;
if (timeseriesExternalIds.length > 0 && isLoadingAssetIdsAndTimeseriesData) return;
if (ruleSet === undefined) return;
if (!allMappingsLoaded) return;

const ruleBasedInitilization = async (): Promise<void> => {
const allStylings = await Promise.all(
Expand All @@ -81,50 +117,63 @@ export function RuleBasedOutputsSelector({
const flatAssetsMappingsList = flatAssetsMappingsListPerModel.get(model) ?? [];

if (flatAssetsMappingsList.length === 0) return [];
const stylings = await initializeRuleBasedOutputs({
assetMappings: flatAssetsMappingsList,

const mappingsStylings = await initializeRuleBasedOutputs({
assetMappings: flatAssetsMappingsList ?? [],
fdmMappings: fdmMappings ?? [],
contextualizedAssetNodes,
ruleSet,
assetIdsAndTimeseries: assetIdsWithTimeseriesData?.assetIdsWithTimeseries ?? [],
timeseriesDatapoints: assetIdsWithTimeseriesData?.timeseriesDatapoints ?? []
});
const filteredStylings = stylings.flat().filter(isDefined);
return filteredStylings;

return mappingsStylings;
})
);
const filteredStylings = allStylings.flat().filter(isDefined).flat();
ruleSetStylingCache.set(ruleSet.id, filteredStylings);

ruleSetStylingCache.set(ruleSet.id, filteredStylings);
if (onRuleSetChanged !== undefined) {
onRuleSetChanged(filteredStylings);
}
};
if (!ruleSetStylingCache.has(ruleSet.id)) {
void ruleBasedInitilization();
} else {
onAllMappingsFetched(true);
if (onRuleSetChanged !== undefined) onRuleSetChanged(ruleSetStylingCache.get(ruleSet.id));
}
}, [isLoadingAssetIdsAndTimeseriesData, ruleSet, assetMappings, allContextualizedAssets]);
}, [
ruleSet,
assetMappings,
fdmMappings,
contextualizedAssetNodes,
assetIdsWithTimeseriesData,
models
]);

return <></>;
}

async function initializeRuleBasedOutputs({
assetMappings,
fdmMappings,
contextualizedAssetNodes,
ruleSet,
assetIdsAndTimeseries,
timeseriesDatapoints
}: {
assetMappings: AssetMapping3D[];
fdmMappings: FdmInstanceNodeWithConnectionAndProperties[];
contextualizedAssetNodes: Asset[];
ruleSet: RuleOutputSet;
assetIdsAndTimeseries: AssetIdsAndTimeseries[];
timeseriesDatapoints: Datapoints[] | undefined;
}): Promise<AssetStylingGroupAndStyleIndex[]> {
}): Promise<AllMappingStylingGroupAndStyleIndex[]> {
const collectionStylings = await generateRuleBasedOutputs({
contextualizedAssetNodes,
assetMappings,
fdmMappings,
ruleSet,
assetIdsAndTimeseries,
timeseriesDatapoints
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*!
* Copyright 2024 Cognite AS
*/

import { type FdmPropertyType } from '../../Reveal3DResources/types';
import { type Expression, type TriggerTypeData } from '../types';
import { getFdmPropertyTrigger } from '../utils';

export const checkBooleanExpressionStatement = (
triggerTypeData: TriggerTypeData[],
expression: Expression
): boolean | undefined => {
const condition = expression.type === 'booleanExpression' ? expression.condition : undefined;
const trigger = expression.type === 'booleanExpression' ? expression.trigger : undefined;

let expressionResult: boolean | undefined = false;

if (condition === undefined || trigger === undefined) return;

const currentTriggerData = triggerTypeData.find(
(triggerType) => triggerType.type === trigger?.type
);

const isFdmTrigger = trigger?.type === 'fdm' && currentTriggerData?.type === 'fdm';

if (isFdmTrigger && currentTriggerData.instanceNode.items.length === 0) return;

const fdmItemsTrigger =
isFdmTrigger && currentTriggerData.instanceNode.items[0] !== undefined
? currentTriggerData.instanceNode.items[0]
: undefined;

const fdmPropertyTrigger = isFdmTrigger
? (fdmItemsTrigger?.properties as FdmPropertyType<unknown>)
: undefined;

const propertyTrigger = getFdmPropertyTrigger<boolean>(fdmPropertyTrigger, trigger);

switch (condition.type) {
case 'equals': {
expressionResult = propertyTrigger === true;
break;
}
case 'notEquals': {
expressionResult = propertyTrigger === false;
break;
}
}
return expressionResult;
};
Loading

0 comments on commit 4b456b9

Please sign in to comment.