-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from cyberbit/feature/graphs
Add graphing output adapters
- Loading branch information
Showing
10 changed files
with
1,153 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
local o = require 'telem.lib.ObjectModel' | ||
local t = require 'telem.lib.util' | ||
|
||
local OutputAdapter = require 'telem.lib.OutputAdapter' | ||
local MetricCollection = require 'telem.lib.MetricCollection' | ||
|
||
local GraphOutputAdapter = o.class(OutputAdapter) | ||
GraphOutputAdapter.type = 'GraphOutputAdapter' | ||
|
||
GraphOutputAdapter.MAX_ENTRIES = 50 | ||
GraphOutputAdapter.SCALE_TICK = 10 | ||
|
||
local function graphtrackrange (self) | ||
local min = self.graphdata[1] | ||
local max = self.graphdata[1] | ||
|
||
for k,v in ipairs(self.graphdata) do | ||
if v < min then min = v end | ||
if v > max then max = v end | ||
end | ||
|
||
return min,max | ||
end | ||
|
||
function GraphOutputAdapter:constructor (frame, filter, bg, fg, fontSize) | ||
self:super('constructor') | ||
|
||
self.bBaseFrame = assert(frame, 'Frame is required') | ||
self.filter = assert(filter, 'Filter is required') | ||
|
||
self.graphdata = {} | ||
|
||
self:register(bg, fg, fontSize) | ||
end | ||
|
||
function GraphOutputAdapter:register (bg, fg, fontSize) | ||
local currentmin = 0 | ||
local currentmax = 1000 | ||
|
||
self.tick = 0 | ||
|
||
self.bInnerFrame = self.bBaseFrame:addFrame() | ||
:setBackground(bg) | ||
:setSize('{parent.w}', '{parent.h}') | ||
|
||
local fGraph = self.bInnerFrame:addFrame('fGraph'):setBackground(colors.black) | ||
:setPosition(1,1) | ||
:setSize('{parent.w - 2}', '{parent.h - 6}') | ||
|
||
local fLabel = self.bInnerFrame:addFrame('fLabel'):setBackground(colors.black) | ||
:setSize('{parent.w - 2}', 4) | ||
:setPosition(1,'{parent.h - 5}') | ||
|
||
local fLabelMax = self.bInnerFrame:addFrame('fLabelMax'):setBackground(colors.black) | ||
:setSize(6, 1) | ||
:setPosition('{parent.w - 7}',1) | ||
|
||
local fLabelMin = self.bInnerFrame:addFrame('fLabelMin'):setBackground(colors.black) | ||
:setSize(6, 1) | ||
:setPosition('{parent.w - 7}','{fLabel.y - 2}') | ||
|
||
self.label = fLabel:addLabel() | ||
:setText("-----") | ||
:setPosition('{parent.w/2-self.w/2}', 2) | ||
:setForeground(colors.white) | ||
:setBackground(colors.black) | ||
|
||
self.graph = fGraph:addGraph() | ||
:setPosition(1,1) | ||
:setSize('{parent.w - 1}', '{parent.h - 1}') | ||
:setMaxEntries(self.MAX_ENTRIES) | ||
:setBackground(colors.black) | ||
:setGraphColor(colors.red) | ||
:setGraphSymbol('\127') | ||
|
||
self.graphscale = fGraph:addGraph() | ||
:setGraphType('scatter') | ||
:setPosition(1,'{parent.h - 1}') | ||
:setSize('{parent.w - 1}', 2) | ||
:setMaxEntries(self.MAX_ENTRIES) | ||
:setBackground(colors.transparent) | ||
:setGraphSymbol('|') | ||
|
||
self.labelmax = fLabelMax:addLabel() | ||
:setPosition(1,1) | ||
:setText('-----') | ||
:setForeground(colors.white) | ||
:setBackground(colors.black) | ||
|
||
self.labelmin = fLabelMin:addLabel() | ||
:setPosition(1,1) | ||
:setText('-----') | ||
:setForeground(colors.white) | ||
:setBackground(colors.black) | ||
|
||
self.graph:setMinValue(currentmin):setMaxValue(currentmax) | ||
end | ||
|
||
function GraphOutputAdapter:write (collection) | ||
assert(o.instanceof(collection, MetricCollection), 'Collection must be a MetricCollection') | ||
|
||
local resultMetric = collection:find(self.filter) | ||
|
||
assert(resultMetric, 'could not find metric') | ||
|
||
t.constrainAppend(self.graphdata, resultMetric.value, self.MAX_ENTRIES) | ||
|
||
local newmin, newmax = graphtrackrange(self) | ||
|
||
self.graph:setMinValue(newmin):setMaxValue(newmax) | ||
|
||
self.graph:addDataPoint(resultMetric.value) | ||
|
||
self.label:setFontSize(2) | ||
self.label:setText(t.shortnum(resultMetric.value)) | ||
|
||
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 | ||
|
||
self.labelmax:setText(t.shortnum(newmax)) | ||
self.labelmin:setText(t.shortnum(newmin)) | ||
|
||
return self | ||
end | ||
|
||
return GraphOutputAdapter |
141 changes: 141 additions & 0 deletions
141
src/telem/lib/output/plotter/ChartLineOutputAdapter.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
local o = require 'telem.lib.ObjectModel' | ||
local t = require 'telem.lib.util' | ||
local vendor | ||
local plotterFactory | ||
|
||
local OutputAdapter = require 'telem.lib.OutputAdapter' | ||
local MetricCollection = require 'telem.lib.MetricCollection' | ||
|
||
local ChartLineOutputAdapter = o.class(OutputAdapter) | ||
ChartLineOutputAdapter.type = 'ChartLineOutputAdapter' | ||
|
||
ChartLineOutputAdapter.MAX_ENTRIES = 50 | ||
ChartLineOutputAdapter.X_TICK = 10 | ||
|
||
function ChartLineOutputAdapter:constructor (win, filter, bg, fg) | ||
self:super('constructor') | ||
|
||
self.win = assert(win, 'Window is required') | ||
self.filter = assert(filter, 'Filter is required') | ||
|
||
self.plotter = nil | ||
self.plotData = {} | ||
self.gridOffsetX = 0 | ||
|
||
self.filter = filter | ||
|
||
self.bg = bg or win.getBackgroundColor() or colors.black | ||
self.fg = fg or win.getTextColor() or colors.white | ||
|
||
self:register() | ||
end | ||
|
||
function ChartLineOutputAdapter:register () | ||
if not vendor then | ||
self:dlog('ChartLineOutputAdapter:boot :: Loading vendor modules...') | ||
|
||
vendor = require 'telem.vendor' | ||
|
||
self:dlog('ChartLineOutputAdapter:boot :: Vendor modules ready.') | ||
end | ||
|
||
if not plotterFactory then | ||
self:dlog('ChartLineOutputAdapter:boot :: Loading plotter...') | ||
|
||
plotterFactory = vendor.plotter | ||
|
||
self:dlog('ChartLineOutputAdapter:boot :: plotter ready.') | ||
end | ||
|
||
self.plotter = plotterFactory(self.win) | ||
|
||
for i = 1, self.MAX_ENTRIES do | ||
t.constrainAppend(self.plotData, self.plotter.NAN, self.MAX_ENTRIES) | ||
end | ||
end | ||
|
||
function ChartLineOutputAdapter:write (collection) | ||
assert(o.instanceof(collection, MetricCollection), 'Collection must be a MetricCollection') | ||
|
||
local resultMetric = collection:find(self.filter) | ||
|
||
assert(resultMetric, 'could not find metric') | ||
|
||
-- TODO data width setting | ||
self.gridOffsetX = self.gridOffsetX - t.constrainAppend(self.plotData, resultMetric and resultMetric.value or self.plotter.NAN, self.MAX_ENTRIES) | ||
|
||
-- TODO X_TICK setting | ||
if self.gridOffsetX % self.X_TICK == 0 then | ||
self.gridOffsetX = 0 | ||
end | ||
|
||
local dataw = #{self.plotData} | ||
|
||
local actualmin, actualmax = math.huge, -math.huge | ||
|
||
for _, v in ipairs(self.plotData) do | ||
-- skip NAN | ||
if v ~= self.plotter.NAN then | ||
if v < actualmin then actualmin = v end | ||
if v > actualmax then actualmax = v end | ||
end | ||
end | ||
|
||
local flatlabel = nil | ||
|
||
-- NaN data | ||
if actualmin == math.huge then | ||
flatlabel = 'NaN' | ||
|
||
actualmin, actualmax = 0, 0 | ||
end | ||
|
||
-- flat data | ||
if actualmin == actualmax then | ||
local minrange = 0.000001 | ||
|
||
if not flatlabel then | ||
flatlabel = t.shortnum2(actualmin) | ||
end | ||
|
||
actualmin = actualmin - minrange / 2 | ||
actualmax = actualmax + minrange / 2 | ||
end | ||
|
||
self.plotter:clear(self.bg) | ||
|
||
self.plotter:chartGrid(self.MAX_ENTRIES, actualmin, actualmax, self.gridOffsetX, colors.gray, { | ||
xGap = 10, | ||
yLinesMin = 5, -- yLinesMin: number >= 1 | ||
yLinesFactor = 2 -- yLinesFactor: integer >= 2 | ||
-- effective max density = yMinDensity * yBasis | ||
}) | ||
|
||
self.plotter:chartLine(self.plotData, self.MAX_ENTRIES, actualmin, actualmax, self.fg) | ||
|
||
local maxString = t.shortnum2(actualmax) | ||
local minString = t.shortnum2(actualmin) | ||
|
||
self.win.setVisible(false) | ||
|
||
self.plotter:render() | ||
|
||
self.win.setTextColor(self.fg) | ||
self.win.setBackgroundColor(self.bg) | ||
if not flatlabel then | ||
self.win.setCursorPos(self.plotter.box.term_width - #maxString + 1, 1) | ||
self.win.write(maxString) | ||
|
||
self.win.setCursorPos(self.plotter.box.term_width - #minString + 1, self.plotter.box.term_height) | ||
self.win.write(minString) | ||
else | ||
self.win.setCursorPos(self.plotter.box.term_width - #flatlabel + 1, self.plotter.math.round(self.plotter.box.term_height / 2)) | ||
self.win.write(flatlabel) | ||
end | ||
|
||
self.win.setVisible(true) | ||
|
||
return self | ||
end | ||
|
||
return ChartLineOutputAdapter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.