-
Notifications
You must be signed in to change notification settings - Fork 2
Develop
Modules are subclasses of GatewayModule
, and can be written easily.
Let's start with an example:
class ExampleModule(GatewayModule):
some_attribute: int = 0
def connect(self, channels: ExampleGatewayChannels):
# get channels with channels.get_channel
# set channels with channels.set_channel
...
def rest(self, app: "GatewayWebApp") -> None:
# add APIs to `app`
# GatewayWebApp is a subclass of FastAPI
...
def shutdown(self) -> None:
# anything to run on clean shutdown
# of the Gateway instance
...
GatewayModule
is a subclass of Pydantic BaseModel
, and so has type validation ands Hydra-driven configuration.
When you write a GatewayModule
, you can provide a method rest
to add or modify FastAPI routes.
Here is a simple example that just adds a "hello, world" route.
class ExampleModule(GatewayModule):
def connect(self, channels: ExampleGatewayChannels):
pass
def rest(self, app: GatewayWebApp) -> None:
# Get API Router
api_router: APIRouter = app.get_router("api")
# add route to return "hello world"
@api_router.get("hello", responses=get_default_responses(), response_model=str, tags=["Utility"])
async def hello_world() -> str:
return "hello world!"
csp-gateway
is designed as an all-in-one application builder.
However, sometimes it is convenient to white-label the frontend beyond what is currently exposed.
To this end, csp-gateway
publishes its Javascript frontend as a library to npmjs.com.
You can extend the frontend with customizations like so:
Install the Javascript library @point72/csp-gateway
into your project.
Here is an example React app which replaces the default csp-gateway
logo with a logo of a gate:
import React from "react";
import { createRoot } from "react-dom/client";
import { FaToriiGate } from "react-icons/fa";
import App from "@point72/csp-gateway";
function HeaderLogo() {
return <FaToriiGate size={40} />;
}
window.addEventListener("load", () => {
const container = document.getElementById("gateway-root");
const root = createRoot(container);
root.render(<App headerLogo={<HeaderLogo />} />);
});
Right now, the Javascript application exposes a small number of customizations, provided as props
to the App
React component.
We may extend these more in the future.
-
headerLogo
: React component to replace the top bar logo -
footerLogo
: React component to add a bottom bar logo (bottom left) -
processTables
: Custom function to preprocess Perspective tables, e.g. to configure default viewsprocessTables(default_perspective_layout_json, table_list, perspective_workspace, theme)
-
overrideSettingsButtons
: Customize settings buttons in the right-hand settings drawer -
extraSettingsButtons
: Add additional settings buttons in the right-hand settings drawer -
shutdown
: Customize the function invoked when calling the "Big Red Button"
Gateway
instances require a static GatewayChannels
instance in order to connect the list of GatewayModule
instances.
In layman's terms, all data channels need to be known in advance.
However, it is sometimes convenient to allow a GatewayModule
to create channels dynamically, e.g. not literally in code.
Although these can't be consumed by other GatewayModule
instances, they are still valuable for the API/UI.
A GatewayModule
may overload the dynamic_channels
method and return a dictionary mapping str
to GatewayStruct
subclass,
and these channels will become available in the GatewayChannels
instance and thus the REST API and UI.
def dynamic_channels(self) -> Optional[Dict[str, Union[Type[GatewayStruct], Type[List[GatewayStruct]]]]]:
"""
Channels that this module dynamically adds to the gateway channels when this module is included into the gateway.
Returns:
Dictionary keyed by channel name and type of the timeseries of the channel as values.
"""
GatewayModule
instances can get/set arbitrary channels from a GatewayChannels
instance.
By default, if any GatewayModule
gets a channel that no other GatewayChannel
sets, an exception will be thrown during graph construction.
Sometimes a GatewayModule
wants to get_channel
a channel in a GatewayChannels
, but not require that that channel ever tick.
For Example, a module like PrintChannels
wants to get every channel, but doesn't require that any tick.
For this purpose, any GatewayModule
can configure its attribute requires: Optional[ChannelSelection]
with the name of the channels it requires.
Any other channels that it gets will be considered optional.
For example, the PrintChannels
module set its default to be empty, indicating that it does not require any channels - all are optional.
requires: Optional[ChannelSelection] = []
ChannelSelection
is a class to representing channel selection options for filtering channels based on inclusion and exclusion criteria.
It is coercable from a python list, and has the following attributes:
-
include (
Optional[List[str]]
): A list of channel names to include in the selection. -
exclude (
Set[str]
): A list of channel names to exclude from the selection.
Any module in a csp-gateway
application can be disabled via configuration, or via the disabled: bool
attribute.
For example, in the following yaml configuration, the MountPerspectiveTables
module is disabled.
modules:
example_module:
_target_: csp_gateway.server.demo.simple.ExampleModule
mount_perspective_tables:
_target_: csp_gateway.MountPerspectiveTables
disabled: true
mount_rest_routes:
_target_: csp_gateway.MountRestRoutes
force_mount_all: True
Tip
This attribute, like all others, can be overridden for config. This makes it very convenient to write things like debug/instrumentation modules!
Documentation coming soon!
This wiki is autogenerated. To made updates, open a PR against the original source file in docs/wiki
.
Get Started
Key Components
For Developers
Modules
- API/UI Modules
- Logging Modules
- Replay Engine Modules
- Utility Modules
For Contributors