Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/middleware #50

Merged
merged 4 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
{
// "autobuild": true,
"Lua.diagnostics.globals": [
"colors",
"colours",
"commands",
"disk",
"fs",
"gps",
"help",
"http",
"io",
"keys",
"multishell",
"os",
"paintutils",
"parallel",
"peripheral",
"pocket",
"rednet",
"redstone",
"settings",
"shell",
"term",
"textutils",
"turtle",
"vector",
"window",
"mekanismEnergyHelper",
"require"
],
"triggerTaskOnSave.tasks": {
"build": [
"src/telem/**/*.lua"
Expand Down
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"presentation": {
"close": true,
"focus": false,
"reveal": "always",
"reveal": "silent",
"panel": "shared"
}
}
Expand Down
11 changes: 11 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default defineConfig({
{ text: 'InputAdapter', link: '/reference/InputAdapter' },
{ text: 'OutputAdapter', link: '/reference/OutputAdapter' },
{ text: 'Backplane', link: '/reference/Backplane' },
{ text: 'Middleware', link: '/reference/Middleware' },
]
},
{
Expand Down Expand Up @@ -85,6 +86,16 @@ export default defineConfig({
]
}
]
},
{
text: 'Middleware',
collapsed: false,
items: [
{ text: 'Sort', link: '/reference/middleware/Sort' },
{ text: 'Calculate Average', link: '/reference/middleware/CalcAverage' },
{ text: 'Calculate Delta', link: '/reference/middleware/CalcDelta' },
{ text: 'Custom', link: '/reference/middleware/Custom' },
]
}
],

Expand Down
Binary file added docs/assets/middleware-calc-avg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/middleware-calc-delta.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 12 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
outline: deep
---

# Getting Started

## Install
Expand Down Expand Up @@ -37,22 +41,28 @@ A [MetricCollection](reference/MetricCollection) is a list of Metric elements,

There are two types of adapters: [InputAdapter](reference/InputAdapter) and [OutputAdapter](reference/OutputAdapter). The base classes themselves are abstract classes and cannot be instantiated. Instead, there are several implementations of these adapters targeting generic resource providers and machinery from supported mods.

Explore the reference pages to learn more about the specific adapters.
Explore the pages in the **Input** and **Output** sections to learn more about the specific adapters.

### Backplane

The [Backplane](reference/Backplane) is the main API interface of Telem. Any configured adapters will be attached here and read/written when a cycle is requested.

You can learn more about Backplane by visiting the Backplane API listing.

### Middleware

[Middleware](reference/Middleware) is a powerful feature of Telem that allows you to transform metrics in a variety of ways. You can calculate deltas, rates, and averages of existing metrics, or craft your own using custom middleware.

Take a look at the pages in the Middleware section to learn more.

## Hello World

To familiarize yourself with the syntax, create the following program:

```lua
local telem = require 'telem'

-- setup the fluent interface
-- setup the backplane
local backplane = telem.backplane()

-- Hello World input will always output a single metric with a specified value
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ features:
- title: Standardized data format
details: No more standardization headaches across different peripherals
- title: Intuitive API
details: Utilize dozens of built-in inputs and outputs with a fluent interface. Or write your own!
details: Utilize dozens of built-in inputs and outputs with a simple interface. Or write your own!
- title: Deep integrations
details: First-class support for Mekanism, Applied Energistics, and more
---
Expand Down
15 changes: 15 additions & 0 deletions docs/reference/Backplane.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ All properties are set by Backplane and should not be mutated outside of this cl
description: 'Dictionary of assigned outputs.',
setBy: true
},
{
name: 'middlewares',
type: 'Middleware[]',
default: '{}',
description: 'List of Middleware.',
setBy: true
},
{
name: 'inputKeys',
type: 'string[]',
Expand Down Expand Up @@ -159,6 +166,14 @@ Backplane:cache (state: boolean)

Set output cache state. When `state` is `true`, Backplane will save the data history of cacheable output adapters to the filesystem at regular intervals. If the program or computer halts and restarts, the cache will be loaded and the output adapters will be re-initialized with the cached data. An output adapter must call `self:cacheable()`, as well as implement `getState()` and `loadState()`, to be cacheable.

### `middleware`

```lua
Backplane:middleware (middleware1: Middleware, middleware2?: Middleware, ...): self
```

Set the middleware stack for this Backplane. Middleware will be executed in the order they are passed to this function. Middleware can be any class that inherits from [Middleware](Middleware).

## Usage

```lua
Expand Down
25 changes: 25 additions & 0 deletions docs/reference/Middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
outline: deep
---

# Middleware <Badge type="info" text="API" /> <RepoLink path="lib/BaseMiddleware.lua" />

Middleware is an [abstract](https://en.wikipedia.org/wiki/Abstract_type) class that functions as a mutator for [MetricCollection](MetricCollection) instances at specific points in a cycle, such as just before writing to outputs. Middleware is free to add, modify, and remove metrics from the collection, or replace it entirely if needed.

## Methods

### `Middleware`

Creates a new Middleware instance.

::: danger
Because Middleware is an abstract class, this constructor should only be called from within classes inheriting `Middleware` by calling `self:super('constructor')`. This will set up any inherited properties with the correct default values. **Behavior of any other use is undefined.**
:::

### `handle` <Badge type="warning" text="abstract" />

```lua
Middleware:handle (target: MetricCollection): MetricCollection
```

Process the provided MetricCollection and return the resulting MetricCollection. In-place modification or replacement are both supported.
68 changes: 68 additions & 0 deletions docs/reference/middleware/CalcAverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
outline: deep
---

# Calculate Average Middleware <RepoLink path="lib/middleware/CalcAverageMiddleware.lua" />

```lua
telem.middleware.calcAvg (windowSize?: integer)
```

Calculates the average of each metric in a collection, observed over a number of cycles. Each average is appended to the collection as a new metric, with an `_avg` suffix appended to the name, and the source set to `middleware`.

<PropertiesTable
:properties="[
{
name: 'windowSize',
type: 'integer',
default: '50',
description: 'Number of cycles to include when calculating the average.'
}
]"
/>

## Methods

### `force`

```lua
CalcAverageMiddleware:force (): self
```

Force the middleware to process metrics from other middleware (`source = 'middleware'`), default is to ignore metrics from other middleware.

## Usage

```lua{8}
local telem = require 'telem'
local mw = telem.middleware

local backplane = telem.backplane()
:addInput('random', telem.input.custom(function ()
return { rand = math.random(1, 2) }
end))
:middleware(mw.calcAvg())
:cycleEvery(0.1)()
```

This will result in the following collection:

<MetricTable
show-heritage
:metrics="[
{
name: 'rand',
value: '1 or 2',
adapter: 'random'
},
{
name: 'rand_avg',
value: '~ 1.5',
source: 'middleware'
}
]"
/>

Observing the value over time shows that the average converges to the expected value:

![Output of rand_avg over time](/assets/middleware-calc-avg.png)
99 changes: 99 additions & 0 deletions docs/reference/middleware/CalcDelta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
outline: deep
---

# Calculate Delta Middleware <RepoLink path="lib/middleware/CalcDeltaMiddleware.lua" />

```lua
telem.middleware.calcDelta (windowSize?: integer)
```

Measures the delta and rate of each metric in a collection, in both instant and ranged variants. Each measurement is appended to the collection as a new metric, with a suffix appended to the name, and the source set to `middleware`.

The instant delta is calculated as the difference between the current and previous values, and the instant rate is calculated as the delta divided by the observed time between cycles. The ranged delta and rate are similar, but the previous value is taken from `windowSize` cycles ago.

<PropertiesTable
:properties="[
{
name: 'windowSize',
type: 'integer',
default: '50',
description: 'Number of cycles to include when calculating the delta and rate.'
}
]"
/>

## Methods

### `force`

```lua
CalcDeltaMiddleware:force (): self
```

Force the middleware to process metrics from other middleware (`source = 'middleware'`), default is to ignore metrics from other middleware.

### `interval`

```lua
CalcDeltaMiddleware:interval (interval: string): self
```

Set the interval used to calculate the rate. The default is `1s`, which will result in the rate being calculated in units per second.

The format is `(unitScale)(unit)`, where `unitScale` is an integer greater than 0, and `unit` is one of: `s` (second), `m` (minute), `h` (hour), `d` (day). Note that "day" is a 24-hour day, not an in-game day.

## Usage

```lua{12}
local telem = require 'telem'
local mw = telem.middleware

local state = 0

local backplane = telem.backplane()
:addInput('increments', telem.input.custom(function ()
state = state + 1

return { inc = state }
end))
:middleware(mw.calcDelta():interval('1m'))
:cycleEvery(0.1)()
```

This will result in the following collection:

<MetricTable
show-heritage
:metrics="[
{
name: 'inc',
value: '0 - inf',
adapter: 'increments'
},
{
name: 'inc_idelta',
value: '1',
source: 'middleware'
},
{
name: 'inc_delta',
value: '~ 49',
source: 'middleware'
},
{
name: 'inc_irate',
value: '~ 600',
source: 'middleware'
},
{
name: 'inc_rate',
value: '~ 600',
source: 'middleware'
}
]"
/>

Observing the value over time looks something like this:

![Output of delta middleware over time](/assets/middleware-calc-delta.png)
Loading
Loading