Skip to content

Commit

Permalink
docs: UI interfaces and Tauri starter kit
Browse files Browse the repository at this point in the history
  • Loading branch information
Zakrok09 committed Aug 29, 2024
1 parent cfc0798 commit e2c163e
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 26 deletions.
2 changes: 2 additions & 0 deletions Writerside/sdh.tree
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<toc-element topic="Windowing-Engine.md"/>
</toc-element>
<toc-element topic="Tauri-Starter-Kit-Provided.md">
<toc-element topic="Alerting.md"/>
<toc-element topic="Keybind-Engine.md"/>
</toc-element>
</toc-element>
Expand Down Expand Up @@ -56,6 +57,7 @@
<toc-element topic="Tauri-Event-Channels.md"/>
</toc-element>
<toc-element topic="Guides.md">
<toc-element topic="Making-own-adapter-config.md"/>
<toc-element topic="Achieving-safety-with-the-provided-code.md"/>
</toc-element>
<toc-element topic="epilogue.md"/>
Expand Down
3 changes: 3 additions & 0 deletions Writerside/topics/Alerting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Alerting over events

Start typing here...
2 changes: 2 additions & 0 deletions Writerside/topics/Alerts.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Alerts

Alerts (based on UI Toasts) are a safety feature for instant notification of an issue that occurred.

[//]: # (TODO)
70 changes: 69 additions & 1 deletion Writerside/topics/Command-Invoker.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
# Command Invoker

Start typing here...
The command invoker interface abstracts and decouples the functionality of calling commands to the backend.

## Two types of commands {collapsible="true"}

It is important to note the difference in the underlying split between types of commands:

- Pod command (used in the `<PodCommandButton>`): a command that acts like a message to the pod.
These commands are entirely meant as communication to the pod from the GUI.
- Backend command (used in the `<BackendCommandButton>`): a command that triggers auxiliary backend functionality (e.g.
loading procedures, stopping the server, initiating connection, etc.)

> Despite this, the interface of the Command Invoker has a single method to invoke a command.
> The actual implementation of the difference is done by specifying the name of the function for triggering such
> a pod message command inside the `SerpentaConfig` using the `pod_command_name` field.
{style="note"}

**Reasoning behind this split:** when developing the DH08 GUI, the initial implementation provided such one command
for sending messages to the pod in the Tauri backend, which acts as simply an intermediary between the pod and the GUI.
Other commands meant to change the state of the backend or trigger different functionality were implemented as separate
Tauri commands.

## Usage and place in the architecture

The original GUI used Tauri, and the necessity for such an interface was not apparent at first.
To support Serpenta for different stacks,
the actual implementation of communication to a backend was decoupled from the Serpenta UI kit logic.

### Implementation in the Tauri Starter Kit

When creating the provided [Tauri Starter Kit](Tauri-Starter-Kit.md), the implementation of the command invoker is
done by using the Tauri api for commands as done in the real DH08 GUI.
The option to swap it out for a different implementation still exists if desired.

## Reference

Below are the TSDoc comments of each method or field part of this interface.

### Methods

#### `invokeCommand<T>(...):Promise<T>;`

Invoke a command given its name/identifier and an object of arguments.

**Params:**
- commandName: the name/identifier of the command to be executed.
- args: the arguments with which the command will be called.
They are of the type `Record<string, unknown>` because they will take an
object representing the arguments we call the command with.

**Type params:**

- T: the return type of the invoked command.

**Returns:**
a Promise of type T—the return type of the command itself.

**Example:**

If we were to call a function (in an arbitrary language) that takes
arguments (width: integer, height: integer, label: string) which returns an integer,
we will call this `invokeCommand` method as:

```Typescript
const res:number = await invokeCommand<number>("function_name", {
width: 4,
height: 5,
label: "Rectangle"
});
```
60 changes: 42 additions & 18 deletions Writerside/topics/Configuration-Interface.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,66 @@
# Configuration Interface

Built to allow for dependency injection, the `<SerpentaShell />` component takes a configuration prop which is set as
context for all its children (recursively).
Built to allow for dependency injection and modularity, the `<SerpentaShell />` component takes a configuration prop
which is set as context for all its children (recursively).
Context in Svelte can be set by a parent component, and all the children nested within have access to it.

## Interface

The config itself has a describing interface:
The config itself has the following interface:

### Properties {collapsible="true"}

Each of these properties can be accessed by calling the `getSerpentaContext()` command within a component nested
inside the `<SerpentaShell>` one.

> If the component is not living inside the shell, get context will return an empty object.
> What's more, the context is set with the key "serpenta-context" which shall not appear for any other context within
> If the component is not living inside the shell, get context will return an empty object.
> What's more, the context is set with the key "serpenta-context" which shall not appear for any other context within
> the shell or its children.
| name | type | comments |
|--------------------------|-------------------------------------|-------------------------------------------------------------------------------------|
| `appWindow` | `any` | Interface for controlling the native app window. Used to make the custom title bar. |
| `pod_name` | `string` | The name of the hyperloop pod. Used in the bottom bar. |
| `grand_data_distributor` | `WindowEngine<any>` | |
| `window_engine` | `CommandInvocation` | |
| `grand_charter` | `Writable<Map<string, PlotBuffer>>` | |
| `big_error` | `Writable<ErrorStatus>` | |
| `latest_timestamp` | `Writable<number>` | |
| `generic_command_name` | `string` | |
| `stores.fsm_name` | `string` | |
| name | type | comments |
|--------------------|-------------------------------------|----------------------------------------------------------------------------------------------------------|
| `appWindow` | `any` | Interface for controlling the native app window. Used to make the custom title bar. |
| `pod_name` | `string` | The name of the hyperloop pod. Used in the bottom bar. |
| `data_distributor` | `WindowEngine<any>` | The windowing engine interface, used to spawn a window e.g. containing a chart |
| `window_engine` | `CommandInvocation` | Interface for invoking commands to the app backend. |
| `grand_charter` | `Writable<Map<string, PlotBuffer>>` | A map of all charts used inside the Serpenta UI. |
| `error_status` | `Writable<ErrorStatus>` | Store of a error status: 0 - safe, 1 - warning, 2 - unsafe. Used to light the shell for abnormal status. |
| `latest_timestamp` | `Writable<number>` | The latest timestamp (the current moment) used to paint stale data. |
| `pod_command_name` | `string` | The name of the command that sends a message/command to the pod itself. |
| `fsm_store_name` | `string` | The name of the store that holds the active state of the FSM. |

## Usage within the UI kit

All Serpenta components shall get data that shall be configurable from this context by using the following:

```html
<!-- Component.svelte -->
<!-- AnyComponent.svelte -->
<script lang="ts">
import { getSerpentaContext } from "$lib";
import { getSerpentaContext } from "@delft-hyperloop/serpenta";
const context = getSerpentaContext();
</script>
```

You will have to use this component inside the `<SerpentaShell />` parent component.
When using SvelteKit, this can easily be done by wrapping the entire body of the app within this component inside
the root `+layout.svelte` route:

```html
<!-- routes/+layout.svelte -->
<script lang="ts">
import {
SerpentaShell,
defineConfig
} from "@delft-hyperloop/serpenta";
const config = defineConfig(...);
</script>

<!-- short for <SerpentaShell config={config}> -->
<SerpentaShell {config} >
<slot />
</SerpentaShell>
```

Now all inner pages will be loaded within the shell component because of the Svelte slots
83 changes: 81 additions & 2 deletions Writerside/topics/Data-Distributor.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,83 @@
# Data Distributor

The data distributor interface of the middleware configuration is responsible for distributing the stores where needed,
updating them as necessary.
The data distributor interface of the middleware configuration is responsible for distributing the fsm_store_name where
needed, updating them as necessary.
This object must be start-able, kill-able and have all required methods to update, get and register the fsm store.

## Usage and place in the architecture

The Data Distributor was born as an idea when deciding on how to receive data from the pod over to the GUI.
Because a lot of data has to be available anywhere at any time and updated immediately throughout the UI, a system for
managing the income of all new data points and supplying them to any element was necessary.

This led to the creation of the `Grand Data Distributor`, which functioned as middleware between the Tauri backend
and the frontend of the DH08 GUI.
To allow for dependency injection as previously mentioned in the [architecture article](Architecture.md),
an interface allowing for it was created—the Data Distributor.

### Implementation in the Tauri Starter Kit

When creating the provided [Tauri Starter Kit](Tauri-Starter-Kit.md), the implementation of the data distributor is
done by using the Grand Data Distributor implementation used in the real DH08 GUI.
The option to swap it out for a different implementation still exists if desired.

## Reference

Below are the TSDoc comments of each method or field part of this interface.

### Methods

#### `start(interval: number): void;`

Start the data distributor middleware.
This function is expected to trigger a repeated data fetching operation with
the frequency determined by the interval parameter.

**Params:**
- interval: the interval between each fetching

> This method will be called exactly once when initiating the `<Serpenta Shell/>`
#### `fetchTestOnce(): Promise<void>;`

Fetch data from the Tauri backend exactly once.
The interface requires such a method for testing purposes.

#### `kill(): void;`

Kill the data distributor and stop its operation, clearing any intervals.
This shall free all the memory used by the `DataDistributor` implementing class.

#### `registerStore<T>(...): void`

Register a store inside the Data Distributor.
This function will add the store as a member of the distributor's collection and can be retrieved by its name further
in the application.

**Params:**

- name: the name of the store
- initial: initial value of the store
- processFunction: the function to process the data
- initialUnits: units of the store

**Type params:**

- T: the type of the value inside the Store

#### `updateStore(...): void;`

Update a store inside the Data Distributor.

**Params:**
- name: the name of the store
- style: the style of the store as to what colour it shall become
- units: the units of this store. These do not get changed often but are sent from the config, so they are kept.
- data: the data to update the store with

#### `getWritable<T>(name: string): Writable<Store<T>>;`

Gets the writable (value that can be subscribed to in Svelte) given its name.

**Params:**
- name: the name of the store whose writable will be grabbed.
7 changes: 7 additions & 0 deletions Writerside/topics/Making-own-adapter-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Making own adapter &amp; config

1. Setup a SvelteKit project
2. install serpenta
3. import the interfaces somewhere and implement them
4. in the root of the project routes put all its child elements within a serpenta shell with a config that uses your
implementations
4 changes: 2 additions & 2 deletions Writerside/topics/Stores.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ globally and have multiple appearances across the front-end of the GUI, as well
## Usage within the Serpenta GUI

All data that is received,
parsed and distributed by an implementation of a [Grand Data Distributor](Data-Distributor.md) is maintained within stores.
However, Serpenta uses its own interface that extends the svelte stores with additional
parsed and distributed by an implementation of a [Grand Data Distributor](Data-Distributor.md) is maintained within fsm_store_name.
However, Serpenta uses its own interface that extends the svelte fsm_store_name with additional
information that is closely tied to the received values.

```Typescript
Expand Down
11 changes: 9 additions & 2 deletions Writerside/topics/Tauri-Starter-Kit-Provided.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Tauri Starter Kit Provided

<primary-label ref="tauri-starter-kit"/>
<primary-label ref="tauri-starter-kit" />

Start typing here...
Because a lot of the Serpenta functionality is set up entirely in the backend of the app,
with a frontend being a listener.
As mentioned earlier in the [Tauri Starter Kit article](Tauri-Starter-Kit.md), a UI kit alone won't suffice
to get you started quickly.

Therefore, we provide a Tauri starter kit that is set up mostly the way the original DH08 GUI is.

The child topics of this section will document the features provided by the starter Tauri kit.
45 changes: 44 additions & 1 deletion Writerside/topics/Windowing-Engine.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
# Windowing Engine

Start typing here...
The windowing engine interface acts as a trigger to allow for the spawning of new windows with information.
This was done to make Serpenta a multi-monitor,
multi-window GUI—elements we consider critical for a safe and efficient mission-critical GUI.

## External "requirements" for windowing {collapsible="true"}

Because the original implementation used Tauri, the windowing engine relied on the Tauri window api.
Creating new windows is done by specifying the route of the page which is to be loaded in the new window.
The implementation of the interface was based around this.

Therefore, you are provided with url and a label when creating a window.
This can still be easily adapted to any implementation.
Extraction of identifying elements (e.g. id) can be done from the url,
which can then be used to determine what data to load even without using separate routes.

## Usage and place in the architecture

The windowing feature came very late in the original DH08 GUI, only being created during the starting days of the EHW.
It allows for spawning of additional windows to be put on different monitors for a multi-monitor setup.

### Implementation in the Tauri Starter Kit

When creating the provided [Tauri Starter Kit](Tauri-Starter-Kit.md), the implementation of the windowing engine is
done by using the Tauri api for windows as done in the real DH08 GUI.
The option to swap it out for a different implementation still exists if desired.

## Reference

Below are the TSDoc comments of each method or field part of this interface.

### Methods

#### `spawnWindow(...): void;`
Spawn a window given options of a generic type, url and a label.

**Params:**
- url: the url of the page that will be loaded in the new window.
- label: the label of the window in the title bar.
- options: options when defining the window. (optional)

**Type params:**
- OPTION: the type that defines the `options` parameter.
Can be used to determine what additional data can be passed when spawning a window.

0 comments on commit e2c163e

Please sign in to comment.