Skip to content

Commit

Permalink
Merge remote-tracking branch 'equinor/main' into improvements-to-ense…
Browse files Browse the repository at this point in the history
…mble-selector
  • Loading branch information
rubenthoms committed Oct 16, 2023
2 parents b412344 + 9f94ec6 commit 25e874a
Show file tree
Hide file tree
Showing 33 changed files with 1,612 additions and 173 deletions.
5 changes: 3 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function App() {
const workbench = React.useRef<Workbench>(new Workbench());
const queryClient = useQueryClient();

React.useEffect(() => {
React.useEffect(function handleMount() {
if (!workbench.current.loadLayoutFromLocalStorage()) {
workbench.current.makeLayout(layout);
}
Expand All @@ -32,8 +32,9 @@ function App() {
});
}

return function () {
return function handleUnmount() {
workbench.current.clearLayout();
workbench.current.resetModuleInstanceNumbers();
};
}, []);

Expand Down
31 changes: 23 additions & 8 deletions frontend/src/framework/Broadcaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,17 @@ export type BroadcastChannelData = {
value: number | string;
};

export type InputBroadcastChannelDef = {
name: string;
displayName: string;
keyCategories?: BroadcastChannelKeyCategory[];
};

export function checkChannelCompatibility(
channelDef1: BroadcastChannelDef,
channelDef: BroadcastChannelDef,
channelKeyCategory: BroadcastChannelKeyCategory
): boolean {
if (channelDef1.key !== channelKeyCategory) {
if (channelDef.key !== channelKeyCategory) {
return false;
}

Expand All @@ -89,15 +95,17 @@ export function checkChannelCompatibility(

export class BroadcastChannel {
private _name: string;
private _displayName: string;
private _metaData: BroadcastChannelMeta | null;
private _moduleInstanceId: string;
private _subscribers: Set<(data: BroadcastChannelData[], metaData: BroadcastChannelMeta) => void>;
private _cachedData: BroadcastChannelData[] | null;
private _dataDef: BroadcastChannelDef;
private _dataGenerator: (() => BroadcastChannelData[]) | null;

constructor(name: string, def: BroadcastChannelDef, moduleInstanceId: string) {
constructor(name: string, displayName: string, def: BroadcastChannelDef, moduleInstanceId: string) {
this._name = name;
this._displayName = displayName;
this._subscribers = new Set();
this._cachedData = null;
this._dataDef = def;
Expand Down Expand Up @@ -142,6 +150,10 @@ export class BroadcastChannel {
return this._name;
}

getDisplayName(): string {
return this._displayName;
}

getDataDef(): BroadcastChannelDef {
return this._dataDef;
}
Expand Down Expand Up @@ -173,17 +185,15 @@ export class BroadcastChannel {
}

subscribe(
callbackChannelDataChanged: (data: BroadcastChannelData[], metaData: BroadcastChannelMeta) => void
callbackChannelDataChanged: (data: BroadcastChannelData[] | null, metaData: BroadcastChannelMeta | null) => void
): () => void {
this._subscribers.add(callbackChannelDataChanged);

if (this._subscribers.size === 1 && this._dataGenerator) {
this._cachedData = this.generateAndVerifyData(this._dataGenerator);
}

if (this._cachedData && this._metaData) {
callbackChannelDataChanged(this._cachedData, this._metaData);
}
callbackChannelDataChanged(this._cachedData ?? null, this._metaData ?? null);

return () => {
this._subscribers.delete(callbackChannelDataChanged);
Expand All @@ -202,10 +212,11 @@ export class Broadcaster {

registerChannel(
channelName: string,
displayName: string,
channelDef: BroadcastChannelDef,
moduleInstanceId: string
): BroadcastChannel {
const channel = new BroadcastChannel(channelName, channelDef, moduleInstanceId);
const channel = new BroadcastChannel(channelName, displayName, channelDef, moduleInstanceId);
this._channels.push(channel);
this.notifySubscribersAboutChannelsChanges();
return channel;
Expand All @@ -216,6 +227,10 @@ export class Broadcaster {
this.notifySubscribersAboutChannelsChanges();
}

getChannelsForModuleInstance(moduleInstanceId: string): BroadcastChannel[] {
return this._channels.filter((c) => c.getModuleInstanceId() === moduleInstanceId);
}

getChannel(channelName: string): BroadcastChannel | null {
const channel = this._channels.find((c) => c.getName() === channelName);
if (!channel) {
Expand Down
45 changes: 43 additions & 2 deletions frontend/src/framework/GuiMessageBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from "react";

import { Point } from "@lib/utils/geometry";

import { GlobalCursor } from "./internal/GlobalCursor";

export enum DrawerContent {
ModuleSettings = "ModuleSettings",
ModulesList = "ModulesList",
Expand All @@ -15,12 +17,23 @@ export enum GuiState {
SettingsPanelWidthInPercent = "settingsPanelWidthInPercent",
LoadingEnsembleSet = "loadingEnsembleSet",
ActiveModuleInstanceId = "activeModuleInstanceId",
DataChannelConnectionLayerVisible = "dataChannelConnectionLayerVisible",
}

export enum GuiEvent {
ModuleHeaderPointerDown = "moduleHeaderPointerDown",
NewModulePointerDown = "newModulePointerDown",
RemoveModuleInstanceRequest = "removeModuleInstanceRequest",
EditDataChannelConnectionsForModuleInstanceRequest = "editDataChannelConnectionsForModuleInstanceRequest",
ShowDataChannelConnectionsRequest = "showDataChannelConnectionsRequest",
HideDataChannelConnectionsRequest = "hideDataChannelConnectionsRequest",
HighlightDataChannelConnectionRequest = "highlightDataChannelConnectionRequest",
UnhighlightDataChannelConnectionRequest = "unhighlightDataChannelConnectionRequest",
DataChannelPointerUp = "dataChannelPointerUp",
DataChannelOriginPointerDown = "dataChannelOriginPointerDown",
DataChannelConnectionsChange = "dataChannelConnectionsChange",
DataChannelNodeHover = "dataChannelNodeHover",
DataChannelNodeUnhover = "dataChannelNodeUnhover",
}

export type GuiEventPayloads = {
Expand All @@ -37,13 +50,28 @@ export type GuiEventPayloads = {
[GuiEvent.RemoveModuleInstanceRequest]: {
moduleInstanceId: string;
};
[GuiEvent.EditDataChannelConnectionsForModuleInstanceRequest]: {
moduleInstanceId: string;
};
[GuiEvent.HighlightDataChannelConnectionRequest]: {
moduleInstanceId: string;
dataChannelName: string;
};
[GuiEvent.DataChannelOriginPointerDown]: {
moduleInstanceId: string;
originElement: HTMLElement;
};
[GuiEvent.DataChannelNodeHover]: {
connectionAllowed: boolean;
};
};

type GuiStateValueTypes = {
[GuiState.DrawerContent]: DrawerContent;
[GuiState.SettingsPanelWidthInPercent]: number;
[GuiState.LoadingEnsembleSet]: boolean;
[GuiState.ActiveModuleInstanceId]: string;
[GuiState.DataChannelConnectionLayerVisible]: boolean;
};

const defaultStates: Map<GuiState, any> = new Map();
Expand All @@ -58,15 +86,21 @@ export class GuiMessageBroker {
private _eventListeners: Map<GuiEvent, Set<(event: any) => void>>;
private _stateSubscribers: Map<GuiState, Set<(state: any) => void>>;
private _storedValues: Map<GuiState, any>;
private _globalCursor: GlobalCursor;

constructor() {
this._eventListeners = new Map();
this._stateSubscribers = new Map();
this._storedValues = defaultStates;
this._globalCursor = new GlobalCursor();

this.loadPersistentStates();
}

getGlobalCursor(): GlobalCursor {
return this._globalCursor;
}

private loadPersistentStates() {
persistentStates.forEach((state) => {
const value = localStorage.getItem(state);
Expand All @@ -84,7 +118,12 @@ export class GuiMessageBroker {
}
}

subscribeToEvent<T extends GuiEvent>(event: T, callback: (payload: GuiEventPayloads[T]) => void) {
subscribeToEvent<T extends Exclude<GuiEvent, keyof GuiEventPayloads>>(event: T, callback: () => void): () => void;
subscribeToEvent<T extends keyof GuiEventPayloads>(
event: T,
callback: (payload: GuiEventPayloads[T]) => void
): () => void;
subscribeToEvent<T extends GuiEvent>(event: T, callback: (payload?: any) => void): () => void {
const eventListeners = this._eventListeners.get(event) || new Set();
eventListeners.add(callback);
this._eventListeners.set(event, eventListeners);
Expand All @@ -94,7 +133,9 @@ export class GuiMessageBroker {
};
}

publishEvent<T extends GuiEvent>(event: T, payload: GuiEventPayloads[T]) {
publishEvent<T extends Exclude<GuiEvent, keyof GuiEventPayloads>>(event: T): void;
publishEvent<T extends keyof GuiEventPayloads>(event: T, payload: GuiEventPayloads[T]): void;
publishEvent<T extends GuiEvent>(event: T, payload?: any): void {
const eventListeners = this._eventListeners.get(event);
if (eventListeners) {
eventListeners.forEach((callback) => callback({ ...payload }));
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/framework/Module.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";

import { cloneDeep } from "lodash";

import { BroadcastChannelsDef } from "./Broadcaster";
import { BroadcastChannelsDef, InputBroadcastChannelDef } from "./Broadcaster";
import { InitialSettings } from "./InitialSettings";
import { ModuleContext } from "./ModuleContext";
import { ModuleInstance } from "./ModuleInstance";
Expand Down Expand Up @@ -45,12 +45,14 @@ export class Module<StateType extends StateBaseType> {
private _channelsDef: BroadcastChannelsDef;
private _drawPreviewFunc: DrawPreviewFunc | null;
private _description: string | null;
private _inputChannelDefs: InputBroadcastChannelDef[];

constructor(
name: string,
defaultTitle: string,
syncableSettingKeys: SyncSettingKey[] = [],
broadcastChannelsDef: BroadcastChannelsDef = {},
inputChannelDefs: InputBroadcastChannelDef[] = [],
drawPreviewFunc: DrawPreviewFunc | null = null,
description: string | null = null
) {
Expand All @@ -64,6 +66,7 @@ export class Module<StateType extends StateBaseType> {
this._workbench = null;
this._syncableSettingKeys = syncableSettingKeys;
this._channelsDef = broadcastChannelsDef;
this._inputChannelDefs = inputChannelDefs;
this._drawPreviewFunc = drawPreviewFunc;
this._description = description;
}
Expand Down Expand Up @@ -115,7 +118,13 @@ export class Module<StateType extends StateBaseType> {
throw new Error("Module must be added to a workbench before making an instance");
}

const instance = new ModuleInstance<StateType>(this, instanceNumber, this._channelsDef, this._workbench);
const instance = new ModuleInstance<StateType>(
this,
instanceNumber,
this._channelsDef,
this._workbench,
this._inputChannelDefs
);
this._moduleInstances.push(instance);
this.maybeImportSelf();
return instance;
Expand Down
47 changes: 42 additions & 5 deletions frontend/src/framework/ModuleContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";

import { BroadcastChannel } from "./Broadcaster";
import { BroadcastChannel, InputBroadcastChannelDef } from "./Broadcaster";
import { InitialSettings } from "./InitialSettings";
import { ModuleInstance } from "./ModuleInstance";
import { ModuleInstanceStatusController } from "./ModuleInstanceStatusController";
import { StateBaseType, StateStore, useSetStoreValue, useStoreState, useStoreValue } from "./StateStore";
Expand Down Expand Up @@ -50,15 +51,51 @@ export class ModuleContext<S extends StateBaseType> {
return keyArr;
}

getChannel(channelName: string): BroadcastChannel {
return this._moduleInstance.getBroadcastChannel(channelName);
}

setInstanceTitle(title: string): void {
this._moduleInstance.setTitle(title);
}

getStatusController(): ModuleInstanceStatusController {
return this._moduleInstance.getStatusController();
}

getChannel(channelName: string): BroadcastChannel {
return this._moduleInstance.getBroadcastChannel(channelName);
}

getInputChannel(name: string): BroadcastChannel | null {
return this._moduleInstance.getInputChannel(name);
}

setInputChannel(inputName: string, channelName: string): void {
this._moduleInstance.setInputChannel(inputName, channelName);
}

getInputChannelDef(name: string): InputBroadcastChannelDef | undefined {
return this._moduleInstance.getInputChannelDefs().find((channelDef) => channelDef.name === name);
}

useInputChannel(name: string, initialSettings?: InitialSettings): BroadcastChannel | null {
const [channel, setChannel] = React.useState<BroadcastChannel | null>(null);

React.useEffect(() => {
if (initialSettings) {
const setting = initialSettings.get(name, "string");
if (setting) {
this._moduleInstance.setInputChannel(name, setting);
}
}
}, [initialSettings]);

React.useEffect(() => {
function handleNewChannel(newChannel: BroadcastChannel | null) {
setChannel(newChannel);
}

const unsubscribeFunc = this._moduleInstance.subscribeToInputChannelChange(name, handleNewChannel);
return unsubscribeFunc;
}, [name]);

return channel;
}
}
Loading

0 comments on commit 25e874a

Please sign in to comment.