Skip to content

Commit

Permalink
Merge pull request #48 from cyberbit/feature/cache
Browse files Browse the repository at this point in the history
Feature/cache
  • Loading branch information
cyberbit authored Jun 5, 2024
2 parents 1e4c499 + 50778d2 commit d634d03
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: luarocks install luacc

- name: Install luamin
run: npm install -g https://github.com/cyberbit/luamin
run: sudo yarn global add https://github.com/cyberbit/luamin

- name: Build
run: ./build.sh
Expand Down
12 changes: 9 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"autobuild": true,
// "autobuild": true,
"Lua.diagnostics.globals": [
"colors",
"colours",
Expand All @@ -26,6 +26,12 @@
"turtle",
"vector",
"window",
"mekanismEnergyHelper"
]
"mekanismEnergyHelper",
"require"
],
"triggerTaskOnSave.tasks": {
"build": [
"src/telem/**/*.lua"
]
}
}
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": "silent",
"reveal": "always",
"panel": "shared"
}
}
Expand Down
8 changes: 8 additions & 0 deletions docs/reference/Backplane.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ Backplane:debug (state: boolean)

Set internal debug state. When `state` is `true`, Backplane will write verbose information to the terminal during a cycle. When `state` is `false`, Backplane will only write adapter faults to the terminal. Any adapters added *after* calling `debug()` will also have their debug state set to the same value.

### `cache`

```lua
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.

## Usage

```lua
Expand Down
10 changes: 5 additions & 5 deletions docs/reference/InputAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ self.components = {
}
```

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

Reads all assigned components.
```lua
InputAdapter:read (): MetricCollection
```

::: danger
Because InputAdapter is an abstract class, calling this method on InputAdapter or on an incomplete implementation of InputAdapter will throw an error.
:::
Reads data from implementation-defined sources into a [MetricCollection](MetricCollection) and returns it.
40 changes: 34 additions & 6 deletions docs/reference/OutputAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ outline: deep

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

OutputAdapter is an [abstract](https://en.wikipedia.org/wiki/Abstract_type) class that functions as a metric consumer. Typically, an OutputAdapter implementation will accept one or more parameters to its constructor method. These properties define what data sources and/or peripherals it should query when `read()` is called.
OutputAdapter is an [abstract](https://en.wikipedia.org/wiki/Abstract_type) class that functions as a metric consumer. Typically, an OutputAdapter implementation will accept one or more parameters to its constructor method. These properties define what targets and/or peripherals it should interact with when `write()` is called.

## Properties

Expand Down Expand Up @@ -77,14 +77,42 @@ self.components = {
}
```

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

```lua
OutputAdapter:write (metrics: MetricCollection)
```

Writes provided [MetricCollection](MetricCollection) to all assigned components. Specific behavior is implementation-dependent.
Writes provided [MetricCollection](MetricCollection) to implementation-defined targets. This will be called by the [Backplane](Backplane) during a cycle.

::: danger
Because OutputAdapter is an abstract class, calling this method on InputAdapter or on an incomplete implementation of InputAdapter will throw an error.
:::
### `debug`

```lua
OutputAdapter:debug (state: boolean)
```

Set internal debug state. When `state` is `true`, OutputAdapter will write verbose information to the terminal.

### `cacheable`

```lua
OutputAdapter:cacheable ()
```

Flag this OutputAdapter implementation as cacheable. This will notify the Backplane to call `getState()` and `loadState()` on this adapter when needed.

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

```lua
OutputAdapter:getState (): table
```

Returns a serializable version of the OutputAdapter's internal state.

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

```lua
OutputAdapter:loadState (state: table)
```

Loads a previously saved state into the OutputAdapter.
4 changes: 4 additions & 0 deletions docs/reference/output/ChartLine.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ telem.output.plotter.chartLine (
)
```

::: tip
This adapter is [cacheable](/reference/Backplane#cache).
:::

Search the available metrics using the syntax defined in [`find`](/reference/MetricCollection#find) and output a line chart to a specified window. If a matching metric is found, the metric value is pushed to the chart buffer.

The X axis (horizontal value) represents the number of data points recorded, and has a default width of `50`, which can be overridden by passing `maxEntries` in the constructor. The Y axis (vertical) represents the value of the metric over time. Once the fixed width of the graph is reached, the oldest values will be dropped from the buffer when new values are added. The minimum and maximum range of the Y axis is determined by the minimum and maximum metric values in the graph buffer. The background of the widget and all labels will be `bg`, and the graph/text color will be `fg`.
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/output/basalt/Graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ telem.output.basalt.graph (
Requires **[Basalt 1.7+](https://basalt.madefor.cc/)**. This is **not** included in the installer's dependencies due to size.
:::

::: tip
This adapter is [cacheable](/reference/Backplane#cache).
:::

Search the available metrics using the syntax defined in [`find`](/reference/MetricCollection#find) and output a graph to a specified [Basalt container](https://basalt.madefor.cc/#/objects/Container). If a matching metric is found, the metric value is pushed to the graph buffer.

The X axis (horizontal value) represents the number of data points recorded, and has a default width of `50`, which can be overridden by passing `maxEntries` in the constructor. The Y axis (vertical) represents the value of the metric over time. Once the fixed width of the graph is reached, the oldest values will be dropped from the buffer when new values are added. The minimum and maximum range of the Y axis is determined by the minimum and maximum metric values in the graph buffer. The background of the widget and all labels will be `bg`, and the graph/text color will be `fg`.
Expand Down
25 changes: 25 additions & 0 deletions init_devcontainer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# set up workspace
mkdir .luatmp
cd .luatmp

# install lua
# sudo apt update
sudo apt install lua5.3 liblua5.3-dev

# install luarocks
wget https://luarocks.org/releases/luarocks-3.11.1.tar.gz
tar zxpf luarocks-3.11.1.tar.gz
cd luarocks-3.11.1
./configure && make && sudo make install

# install luacc
sudo luarocks install luacc

# remove workspace
cd ../..
rm -rf .luatmp

# install luamin
sudo yarn global add https://github.com/cyberbit/luamin
4 changes: 2 additions & 2 deletions src/telem/init.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
-- Telem by cyberbit
-- MIT License
-- Version 0.5.3
-- Version 0.6.0

local _Telem = {
_VERSION = '0.5.3',
_VERSION = '0.6.0',
util = require 'telem.lib.util',
input = require 'telem.lib.input',
output = require 'telem.lib.output',
Expand Down
76 changes: 76 additions & 0 deletions src/telem/lib/Backplane.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Backplane.type = 'Backplane'

function Backplane:constructor ()
self.debugState = false

self.loaded = false
self.cacheState = false
self.lastCache = 0
self.cacheRate = 1

self.inputs = {}
self.outputs = {}
Expand Down Expand Up @@ -93,6 +98,33 @@ function Backplane:cycle()
self:dlog('Backplane:cycle :: ' .. os.date())
self:dlog('Backplane:cycle :: cycle START !')

-- load output states
if not self.loaded and self.cacheState then
self:dlog('Backplane:cycle :: loading state...')

local inf = fs.open('/.telem/state', 'r')

if inf then
local cache = textutils.unserialize(inf.readAll()) or {}
inf.close()

for k, v in pairs(cache) do
local output = self.outputs[k]

if output and output.isCacheable then
self:dlog('Backplane:cycle :: - ' .. k)

local results = {pcall(output.loadState, output, v)}

if not table.remove(results, 1) then
t.log('loadState fault for "' .. k .. '":')
t.pprint(table.remove(results, 1))
end
end
end
end
end

self:dlog('Backplane:cycle :: reading inputs...')

-- read inputs
Expand Down Expand Up @@ -131,8 +163,42 @@ function Backplane:cycle()

self:dlog('Backplane:cycle :: saving state...')

self:dlog('Backplane:cycle :: - Backplane')
self.collection = metrics

-- cache output states
if self.cacheState then
local time = os.epoch('utc')

if self.lastCache + self.cacheRate * 1000 <= time then
local cache = {}

for _, key in pairs(self.outputKeys) do
local output = self.outputs[key]

if output.isCacheable then
self:dlog('Backplane:cycle :: - ' .. key)

local results = {pcall(output.getState, output)}

if not table.remove(results, 1) then
t.log('getState fault for "' .. key .. '":')
t.pprint(table.remove(results, 1))
end

cache[key] = table.remove(results, 1)
end
end

fs.makeDir('/.telem')
local outf = fs.open('/.telem/state', 'w')
outf.write(textutils.serialize(cache, { compact = true }))
outf.flush()

self.lastCache = time
end
end

self:dlog('Backplane:cycle :: writing outputs...')

-- write outputs
Expand All @@ -149,6 +215,10 @@ function Backplane:cycle()
end
end

if not self.loaded then
self.loaded = true
end

self:dlog('Backplane:cycle :: cycle END !')

return self
Expand Down Expand Up @@ -201,6 +271,12 @@ function Backplane:updateLayouts()
self:dlog('Backplane:updateLayouts :: Layouts updated')
end

function Backplane:cache(cache)
self.cacheState = cache and true or false

return self
end

function Backplane:debug(debug)
self.debugState = debug and true or false

Expand Down
14 changes: 14 additions & 0 deletions src/telem/lib/OutputAdapter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ function OutputAdapter:constructor()

self.asyncCycleHandler = nil

self.isCacheable = false

-- boot components
self:setBoot(function ()
self.components = {}
Expand Down Expand Up @@ -51,6 +53,18 @@ function OutputAdapter:addComponentByPeripheralType (type)
self.components[key] = tempComponent
end

function OutputAdapter:cacheable ()
self.isCacheable = true
end

function OutputAdapter:getState ()
t.err(self.type .. ' has not implemented getState()')
end

function OutputAdapter:loadState (state)
t.err(self.type .. ' has not implemented loadState()')
end

function OutputAdapter:write (metrics)
t.err(self.type .. ' has not implemented write()')
end
Expand Down
47 changes: 47 additions & 0 deletions src/telem/lib/output/basalt/GraphOutputAdapter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ end
function GraphOutputAdapter:constructor (frame, filter, bg, fg, maxEntries)
self:super('constructor')

self:cacheable()

self.bBaseFrame = assert(frame, 'Frame is required')
self.filter = assert(filter, 'Filter is required')

Expand Down Expand Up @@ -135,4 +137,49 @@ function GraphOutputAdapter:write (collection)
return self
end

function GraphOutputAdapter:getState ()
local graphdata = {}

for k,v in ipairs(self.graphdata) do
graphdata[k] = v
end

return {
graphdata = graphdata,
tick = self.tick
}
end

function GraphOutputAdapter:loadState (state)
self.graphdata = state.graphdata
self.tick = state.tick

local newmin, newmax = graphtrackrange(self)

if newmin == newmax then
newmin = newmin - 1
newmax = newmax + 1
end

self.graph:setMinValue(newmin):setMaxValue(newmax)

for _,v in ipairs(self.graphdata) do
self.graph:addDataPoint(v)

if self.tick == self.SCALE_TICK then
self.graphscale:addDataPoint(100)
self.tick = 1
else
self.graphscale:addDataPoint(50)
self.tick = self.tick + 1
end
end

self.label:setFontSize(2)
self.label:setText(t.shortnum(self.graphdata[#self.graphdata]))

self.labelmax:setText(t.shortnum(newmax))
self.labelmin:setText(t.shortnum(newmin))
end

return GraphOutputAdapter
Loading

0 comments on commit d634d03

Please sign in to comment.