Skip to content

Commit

Permalink
SY-1098 Add Ability to Resize Most Schematic Symbols (#813)
Browse files Browse the repository at this point in the history
- feat(pluto): Add scaling to most schematic elements
- refactor(pluto): Make separate light aether component
  • Loading branch information
pjdotson authored Sep 13, 2024
1 parent 7cc4012 commit 9ba77f0
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 275 deletions.
98 changes: 70 additions & 28 deletions docs/site/src/pages/reference/console/schematics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: "Schematics"
heading: "Schematics"
description: "Control hardware using schematics."
---

import { Icon } from "@synnaxlabs/media";
import { Divider } from "@synnaxlabs/pluto";
import { Image, Video } from "@/components/Media";
Expand All @@ -13,7 +14,7 @@ A schematic is used to see a diagram of your physical or simulated system. It di
live sensor data and can be used to exercise manual control by changing the values of
channels tied to actuators. We've added a simple example of a schematic below:

<Image client:only="react" id="console/schematics/example" />
<Image client:only="react" id="console/schematics/example" />

<Divider.Divider direction="x" />

Expand All @@ -40,15 +41,22 @@ the controls in the bottom-left hand corner of the schematic.
</thead>
<tbody>
<tr>
<td><b>Edit</b></td>
<td>
<b>Edit</b>
</td>
<td>Symbols can be added and moved, and their properties can be edited.</td>
</tr>
<tr>
<td><b>View</b></td>
<td>Live data from the cluster is displayed on the schematic. Symbols are not editable and the user cannot change the system state.</td>
<td>
<b>View</b>
</td>
<td>Live data from the cluster is displayed on the schematic. Symbols are not
editable and the user cannot change the system state.</td>
</tr>
<tr>
<td><b>Control</b></td>
<td>
<b>Control</b>
</td>
<td>The user can click on symbols such as valves and buttons to control them.</td>
</tr>
</tbody>
Expand Down Expand Up @@ -87,36 +95,60 @@ other visual properties.
</thead>
<tbody>
<tr>
<td><b>Label</b></td>
<td>
<b>Label</b>
</td>
<td>The text that appears by the symbol.</td>
</tr>
<tr>
<td><b>Label Size</b></td>
<td>
<b>Label Size</b>
</td>
<td>The size of the label.</td>
</tr>
<tr>
<td><b>Color</b></td>
<td>
<b>Color</b>
</td>
<td>The color of the symbol.</td>
</tr>
<tr>
<td><b>Normally Open</b></td>
<td>Causes the body of the solenoid valve to look different when it is open or closed.</td>
<td>
<b>Normally Open</b>
</td>
<td>Causes the body of the solenoid valve to look different when it is open or
closed.</td>
</tr>
<tr>
<td><b>Width</b></td>
<td>
<b>Width</b>
</td>
<td>Changes the size of a tank.</td>
</tr>
<tr>
<td><b>Height</b></td>
<td>
<b>Height</b>
</td>
<td>Changes the size of a tank.</td>
</tr>
<tr>
<td><b>Units</b></td>
<td>
<b>Scale</b>
</td>
<td>Changes the size of an element.</td>
</tr>
<tr>
<td>
<b>Units</b>
</td>
<td>Determines what units a value symbol is displaying.</td>
</tr>
<tr>
<td><b>Orientation</b></td>
<td>Determines the orientation of the symbol and the relative placement of the label.</td>
<td>
<b>Orientation</b>
</td>
<td>Determines the orientation of the symbol and the relative placement of the
label.</td>
</tr>
</tbody>
</Table>
Expand All @@ -138,17 +170,23 @@ The telemetry tab can have the following properties:
</thead>
<tbody>
<tr>
<td><b>Input Channel</b></td>
<td>
<b>Input Channel</b>
</td>
<td>The channel to stream data from.</td>
</tr>
<tr>
<td><b>Precision</b></td>
<td>
<b>Precision</b>
</td>
<td>The number of decimal places to display.</td>
</tr>
<tr>
<td><b>Averaging Window</b></td>
<td>Can be used to display a rolling average. For example, a value of 2 will cause
the screen to display the average of the last 2 samples.</td>
<td>
<b>Averaging Window</b>
</td>
<td>Can be used to display a rolling average. For example, a value of 2 will cause
the screen to display the average of the last 2 samples.</td>
</tr>
</tbody>
</Table>
Expand All @@ -169,16 +207,20 @@ two main properties:
</thead>
<tbody>
<tr>
<td><b>State Channel</b></td>
<td>Represents the display state of the symbol. If the input channel has
a value of 1, the symbol will be display as activated.</td>
<td>
<b>State Channel</b>
</td>
<td>Represents the display state of the symbol. If the input channel has a value
of 1, the symbol will be display as activated.</td>
</tr>
<tr>
<td><b>Command Channel</b></td>
<td>
<b>Command Channel</b>
</td>
<td>Will be written to when you click on the symbol. If the symbol is
de-activated, clicking on it will write 1 to the output channel (an activation command).
If the symbol is activated, clicking on it will write 0 to the output channel (a
deactivation command)</td>
de-activated, clicking on it will write 1 to the output channel (an activation
command). If the symbol is activated, clicking on it will write 0 to the output
channel (a deactivation command).</td>
</tr>
</tbody>
</Table>
Expand Down Expand Up @@ -252,4 +294,4 @@ To download, right click on the schematics name in the resources toolbar and cli
To import a schematic, you can drag and drop from your file system or right click on a
workspace name in the ontology menu:

<Video client:only="react" id="console/schematics/upload" />
<Video client:only="react" id="console/schematics/upload" />
2 changes: 1 addition & 1 deletion pluto/src/input/DragButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import "@/input/DragButton.css";

import { Icon } from "@synnaxlabs/media";
import { box,type direction, xy } from "@synnaxlabs/x";
import { box, type direction, xy } from "@synnaxlabs/x";
import { type ReactElement, useCallback, useMemo, useRef } from "react";

import { Button } from "@/button";
Expand Down
2 changes: 2 additions & 0 deletions pluto/src/pluto/aether/pluto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { button } from "@/vis/button/aether";
import { canvas } from "@/vis/canvas/aether";
import { diagram } from "@/vis/diagram/aether";
import { eraser } from "@/vis/eraser/aether";
import { light } from "@/vis/light/aether";
import { line } from "@/vis/line/aether";
import { lineplot } from "@/vis/lineplot/aether";
import { range } from "@/vis/lineplot/range/aether";
Expand All @@ -42,6 +43,7 @@ export const render = (): void => {
...control.REGISTRY,
...diagram.REGISTRY,
...eraser.REGISTRY,
...light.REGISTRY,
...line.REGISTRY,
...lineplot.REGISTRY,
...measure.REGISTRY,
Expand Down
10 changes: 10 additions & 0 deletions pluto/src/vis/light/aether/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in the file
// licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business Source
// License, use of this software will be governed by the Apache License, Version 2.0,
// included in the file licenses/APL.txt.

export * as light from "@/vis/light/aether/light";
83 changes: 83 additions & 0 deletions pluto/src/vis/light/aether/light.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in the file
// licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business Source
// License, use of this software will be governed by the Apache License, Version 2.0,
// included in the file licenses/APL.txt.

import { type Destructor } from "@synnaxlabs/x";
import { z } from "zod";

import { aether } from "@/aether/aether";
import { status } from "@/status/aether";
import { telem } from "@/telem/aether";
import { type diagram } from "@/vis/diagram/aether";

export const lightStateZ = z.object({
enabled: z.boolean(),
source: telem.booleanSourceSpecZ.optional().default(telem.noopBooleanSourceSpec),
});

export type LightState = z.input<typeof lightStateZ>;

interface InternalState {
source: telem.BooleanSource;
addStatus: status.Aggregate;
stopListening: Destructor;
}

// Light is a component that listens to a boolean telemetry source to update its state.
export class Light
extends aether.Leaf<typeof lightStateZ, InternalState>
implements diagram.Element
{
static readonly TYPE = "Light";

schema = lightStateZ;

async afterUpdate(): Promise<void> {
this.internal.addStatus = status.useOptionalAggregate(this.ctx);
const { source: sourceProps } = this.state;
const { internal: i } = this;
this.internal.source = await telem.useSource(
this.ctx,
sourceProps,
this.internal.source,
);

await this.updateEnabledState();
i.stopListening?.();
i.stopListening = i.source.onChange(() =>
this.updateEnabledState().catch(this.reportError.bind(this)),
);
}

private reportError(e: Error): void {
this.internal.addStatus({
key: this.key,
variant: "error",
message: `Failed to update Light: ${e.message}`,
});
}

private async updateEnabledState(): Promise<void> {
const nextEnabled = await this.internal.source.value();
if (nextEnabled !== this.state.enabled)
this.setState((p) => ({ ...p, enabled: nextEnabled }));
}

async afterDelete(): Promise<void> {
await this.internalAfterDelete();
}

private async internalAfterDelete(): Promise<void> {
this.internal.stopListening();
await this.internal.source.cleanup?.();
}

async render(): Promise<void> {}
}

export const REGISTRY: aether.ComponentRegistry = { [Light.TYPE]: Light };
10 changes: 10 additions & 0 deletions pluto/src/vis/light/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in the file
// licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business Source
// License, use of this software will be governed by the Apache License, Version 2.0,
// included in the file licenses/APL.txt.

export * as Light from "@/vis/light/use";
43 changes: 43 additions & 0 deletions pluto/src/vis/light/use.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2024 Synnax Labs, Inc.
//
// Use of this software is governed by the Business Source License included in the file
// licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with the Business Source
// License, use of this software will be governed by the Apache License, Version 2.0,
// included in the file licenses/APL.txt.

import { useEffect } from "react";
import { type z } from "zod";

import { Aether } from "@/aether";
import { useMemoDeepEqualProps } from "@/memo";
import { light } from "@/vis/light/aether";

export interface UseProps extends Pick<z.input<typeof light.lightStateZ>, "source"> {
aetherKey: string;
}

export interface UseReturn
extends Pick<z.output<typeof light.lightStateZ>, "enabled"> {}

export const use = ({ aetherKey, source }: UseProps): UseReturn => {
const memoProps = useMemoDeepEqualProps({ source });
const [, { enabled }, setState] = Aether.use({
aetherKey,
type: light.Light.TYPE,
schema: light.lightStateZ,
initialState: {
enabled: false,
...memoProps,
},
});
console.log("use.ts: enabled", enabled);

useEffect(
() => setState((state) => ({ ...state, ...memoProps })),
[memoProps, setState],
);

return { enabled };
};
Loading

0 comments on commit 9ba77f0

Please sign in to comment.