2026-02-05 14:06
require("core")
加载模块组件通过函数参数声明数据依赖,Go引擎自动解析参数名并建立数据源绑定。
-- 基础示例: 物联网温度监控面板
local core = require("core")
-- 1. 创建应用实例
local app = core.App("IoT Temperature Monitor")
-- 2. 创建数据源对象 (关键设计)
-- 数据源对象注册到Go引擎的全局表中,通过参数名自动查找
local temp_val = core.source("pipe:///tmp/temp_sensor")
local humid_val = core.source("pipe:///tmp/humid_sensor")
local gpio_ctrl = core.source("fd:///dev/gpio0")
local location_data = core.source("socket://192.168.1.100:8080")
-- 3. 定义布局表 (Layout Table)
-- 布局容器用Lua表表示,组件用函数调用表示
-- 组件函数参数中的函数用于声明数据依赖和更新逻辑
local layout_table = {
type = "LinearLayout",
orientation = "vertical",
spacing = 10,
padding = {5, 5, 5, 5},
backgroundColor = "#F5F5F5",
children = {
-- 标题文本 (静态组件,无数据依赖)
core.Text("Temperature Monitor", {
fontSize = 24,
fontWeight = "bold",
textColor = "#333333",
margin = {bottom = 20}
}),
-- 第一行: 仪表盘和图表 (嵌套水平布局容器)
{
type = "LinearLayout",
orientation = "horizontal",
spacing = 15,
children = {
-- 温度仪表盘组件
-- 参数1: 标题,参数2: 更新函数,参数3: 样式配置
core.Gauge("Temperature", function(temp_val, humid_val)
-- Go引擎解析函数参数名 ["temp_val", "humid_val"]
-- 查找对应的数据源对象,将函数挂载到它们的on_data回调
-- 当数据源更新时,自动调用此函数,参数值为最新数据
-- 函数体内调用组件API更新界面
self:set_value(temp_val) -- 设置仪表盘当前值
self:set_label(string.format("Temp: %.1f°C\nHumid: %.1f%%",
temp_val, humid_val))
end, {
min = 0,
max = 100,
unit = "°C",
colors = {
low = "#00FF00", -- 绿色
mid = "#FFFF00", -- 黄色
high = "#FF0000" -- 红色
},
style = {
width = 300,
height = 300,
backgroundColor = "#FFFFFF",
borderColor = "#DDDDDD",
borderWidth = 1,
borderRadius = 8
}
}),
-- 温度历史图表
core.LineChart("Temperature History", function(temp_val)
-- 只依赖温度数据源
-- 添加数据点到图表
self:add_data_point("°C", temp_val) -- 摄氏度系列
self:add_data_point("°F", temp_val * 1.8 + 32) -- 华氏度系列
end, {
history = 100, -- 显示最近100个点
style = {
width = "flex", -- 弹性宽度,填充剩余空间
height = 300,
backgroundColor = "#FFFFFF",
padding = {10, 10, 10, 10},
xAxis = {label = "Time", showGrid = true},
yAxis = {label = "Temperature", showGrid = true}
}
})
}
},
-- 控制面板容器
{
type = "Container",
title = "Device Control",
layout = "horizontal",
spacing = 20,
margin = {top = 20},
children = {
-- 按钮组件 (点击事件回调)
core.Button("Power On", function(btn)
-- 按钮点击事件处理
gpio_ctrl:write("POWER_ON") -- 向数据源写入控制命令
print("Device powered on")
end, {
style = {
backgroundColor = "#009600",
textColor = "#FFFFFF",
fontSize = 16,
padding = {12, 24},
borderRadius = 6,
hover = {
backgroundColor = "#00B300"
}
}
}),
core.Button("Power Off", function(btn)
gpio_ctrl:write("POWER_OFF")
print("Device powered off")
end, {
style = {
backgroundColor = "#960000",
textColor = "#FFFFFF",
fontSize = 16,
padding = {12, 24},
borderRadius = 6,
hover = {
backgroundColor = "#B30000"
}
}
}),
-- 开关组件
core.Switch("Auto Mode", function(state)
print("Auto mode:", state and "ON" or "OFF")
-- 可以根据状态执行不同操作
end, {
defaultState = true,
style = {
width = 60,
height = 30
}
})
}
},
-- 地图组件 (复杂数据依赖)
core.Map("Device Location", function(location_data)
-- 解析位置数据并更新地图
local pos = core.json.decode(location_data)
self:set_center(pos.lat, pos.lng)
self:update_marker("device1", pos.lat, pos.lng,
string.format("Device #1\nTemp: %.1f°C", temp_val))
end, {
markers = {
{
position = {lat = 31.2304, lng = 121.4737},
label = "Device #1",
color = "#FF0000"
}
},
style = {
width = "100%",
height = 400,
margin = {top = 20}
}
})
}
}
-- 4. 事件处理系统
-- 全局键盘事件
app:on("key:space", function()
print("Space key pressed - taking screenshot")
end)
-- 窗口大小变化事件
app:on("window:resize", function(width, height)
print(string.format("Window resized to %dx%d", width, height))
-- 布局表自动响应式调整
end)
-- 5. 条件监控与告警
temp_val:watch(">", 80, function(value)
print(string.format("⚠️ High temperature: %.1f°C", value))
app:alert("High Temperature", string.format("Temperature reached %.1f°C", value))
end)
-- 6. 注册布局表并启动应用
app:set_layout(layout_table)
app:run({
width = 1200,
height = 800,
vsync = true,
resizable = true,
title = "IoT Temperature Monitor"
})-- 一行代码创建按钮 (组件函数直接调用)
core.Button("Turn On", function() gpio_source:write("ON") end)
-- 数据源链式转换
temp_source:transform(function(v) return v * 1.8 + 32 end) -- °C转°F
:bind(core.Gauge({title="Temperature"})) -- 绑定到仪表盘组件
-- 简洁的布局表定义
local layout = {
type = "LinearLayout",
children = {
core.Text("Hello World"),
core.Button("Click Me", function() print("Clicked!") end)
}
}-- 数据通过函数参数自动绑定到组件
local temp = core.source("pipe:///tmp/sensor")
local humid = core.source("pipe:///tmp/humid_sensor")
-- 仪表盘组件绑定两个数据源
core.Gauge("Environment", function(temp, humid)
-- 参数名 `temp` 和 `humid` 对应声明的数据源变量
-- Go引擎解析函数参数名,自动建立数据绑定
self:set_value(temp)
self:set_label(string.format("Temp: %.1f°C\nHumid: %.1f%%", temp, humid))
end, {min=0, max=100})
-- 图表组件只绑定温度数据
core.LineChart("Temperature History", function(temp)
-- 只依赖温度数据源
self:add_data_point("°C", temp)
end, {history=50})
-- 文本标签显示格式化数据
core.Text("Current Status", function(temp, humid)
self:set_content(string.format("Temperature: %.1f°C\nHumidity: %.1f%%", temp, humid))
end, {fontSize=16})
-- 当数据源更新时,所有绑定组件的函数自动调用
-- 参数值自动传入对应的最新数据-- 全局事件
app:on("key:f1", show_help)
app:on("mouse:click", handle_click)
-- 组件事件
button:on("click", toggle_power)
input:on("change", update_config)
map:on("click", add_marker)
-- 数据事件
source:on("data", process_data)
source:on("error", handle_error)
source:on("connected", log_connection)-- 百分比布局
layout:add("panel1", {
type = "panel",
width = "30%", -- 窗口宽度的30%
height = "100%", -- 窗口高度的100%
children = {...}
})
-- 自适应布局
layout:add("chart", {
type = "line_chart",
width = "auto", -- 根据内容自动调整
height = "auto",
expand = true -- 填充可用空间
})graph RL
subgraph "Lua Script Layer"
L1[Lua Script<br/>声明式配置]
L2[事件处理]
L3[数据转换函数]
end
subgraph "StreamVis Engine"
subgraph "Core Services"
IO[I/O Manager<br/>数据源管理]
LAYOUT[Layout Engine<br/>响应式布局]
EVENT[Event System<br/>事件分发]
STYLE[Style System<br/>样式管理]
end
subgraph "Component System"
COMP[Component Registry]
BTN[Button]
CHART[Chart/Graph]
MAP[Map]
INPUT[Input]
PROG[Progress]
TABLE[Table]
TEXT[Text]
end
end
subgraph "Rendering Layer"
EBITEN[Ebiten Renderer<br/>60FPS GPU加速]
end
subgraph "Data Sources"
PIPE[Pipe/FIFO]
SOCKET[Socket]
FILE[File/Descriptor]
GPIO[GPIO]
NET[Network]
end
%% Data flow
L1 --> IO
L1 --> LAYOUT
L1 --> EVENT
L1 --> STYLE
IO --> PIPE
IO --> SOCKET
IO --> FILE
IO --> GPIO
IO --> NET
PIPE --> IO
SOCKET --> IO
FILE --> IO
GPIO --> IO
NET --> IO
IO --> COMP
LAYOUT --> COMP
EVENT --> COMP
STYLE --> COMP
COMP --> BTN
COMP --> CHART
COMP --> MAP
COMP --> INPUT
COMP --> PROG
COMP --> TABLE
COMP --> TEXT
BTN --> EBITEN
CHART --> EBITEN
MAP --> EBITEN
INPUT --> EBITEN
PROG --> EBITEN
TABLE --> EBITEN
TEXT --> EBITEN
%% Event flow
EVENT --> L2
L2 --> L1
%% Style flow
STYLE --> L1
%% User interaction
EBITEN --> EVENT
%% Layout constraints
L1 --> LAYOUT
LAYOUT --> COMP
click L1 "https://github.com/yuin/gopher-lua" "GopherLua VM"
click EBITEN "https://ebiten.org" "Ebiten Game Engine"
架构说明: 1. Lua脚本层: 用户编写声明式配置,定义数据流、事件处理和布局 2. 数据源层: 多种I/O源(管道、套接字、文件、GPIO、网络)提供实时数据 3. 核心服务层: - I/O管理器: 统一数据源接口,支持读写双向操作 - 布局引擎: 响应式布局计算,支持百分比和自动尺寸 - 事件系统: 全局和组件级事件分发 - 样式系统: 样式管理和应用 4. 组件系统: 丰富的UI组件库,每个组件实现统一接口 5. 渲染层: Ebiten引擎提供60FPS GPU加速渲染
数据流方向: - 配置流: Lua → 核心服务 → 组件系统 - 数据流: 数据源 → I/O管理器 → 组件系统 → 渲染器 - 事件流: 渲染器 → 事件系统 → Lua回调 - 控制流: Lua → I/O管理器 → 数据源(写入操作)
internal/io/)io/
├── manager.go # I/O管理器接口
├── source.go # 数据源接口
├── factory.go # 源工厂 (根据URL创建)
├── pipe.go # 命名管道 (已存在)
├── socket.go # Unix/TCP/UDP套接字
├── file.go # 文件/文件描述符
├── gpio.go # GPIO接口 (Linux fs)
├── processor.go # 数据处理器 (JSON解析等)
└── writer.go # 写接口 (输出能力)
数据源接口:
type Source interface {
ID() string
Read() ([]byte, error) // 读取数据
Write([]byte) error // 写入数据 (新)
Close() error
IsConnected() bool
Reconnect() error
}
type SourceFactory interface {
Create(url string) (Source, error)
Supports(url string) bool
}internal/component/)component/
├── interface.go # 组件统一接口
├── base.go # 基础组件类
├── button.go # 按钮 (已存在,需增强)
├── text.go # 文字标签 (已存在)
├── input.go # 输入框 (新)
├── progress.go # 进度条 (新)
├── scrollbar.go # 滚动条 (新)
├── chart/ # 图表组件
│ ├── line_chart.go # 折线图 (已存在)
│ ├── bar_chart.go # 柱状图 (新)
│ ├── pie_chart.go # 饼图 (新)
│ └── base.go # 图表基类
├── map.go # 地图 (已存在)
├── table.go # 表格 (已存在)
└── container.go # 容器 (已存在)
组件接口:
type Component interface {
core.Element // 继承现有Element接口
// 新增通用方法
SetID(string)
GetID() string
SetStyle(Style)
GetStyle() Style
SetData(interface{}) error
GetData() interface{}
On(string, func(Event)) // 事件监听
Emit(string, Event) // 事件触发
}internal/layout/)layout/
├── engine.go # 布局引擎
├── manager.go # 布局管理器 (已存在)
├── responsive.go # 响应式布局
├── constraint.go # 约束系统 (已存在)
├── solver.go # 约束求解器
├── media_query.go # 媒体查询支持
└── managers/ # 具体布局管理器
├── vertical.go # 垂直布局 (已存在)
├── horizontal.go # 水平布局 (已存在)
├── grid.go # 网格布局 (已存在)
├── border.go # 边框布局 (已存在)
└── flex.go # 弹性布局 (新)
响应式特性: 1. 百分比尺寸 (width="50%")
2. 自动尺寸 (width="auto") 3. 最小/最大约束
(min_width=100, max_width="80%") 4. 媒体查询
(@media (max-width: 768px)) 5. 窗口大小监听和自动重排
internal/event/)event/
├── manager.go # 事件管理器
├── types.go # 事件类型定义
├── dispatcher.go # 事件分发器
├── keyboard.go # 键盘事件
├── mouse.go # 鼠标事件
├── window.go # 窗口事件
├── component.go # 组件事件
└── data.go # 数据事件
事件类型:
type EventType string
const (
EventKeyDown EventType = "key:down"
EventKeyUp EventType = "key:up"
EventMouseClick EventType = "mouse:click"
EventMouseMove EventType = "mouse:move"
EventWindowResize EventType = "window:resize"
EventData EventType = "data"
EventError EventType = "error"
EventCustom EventType = "custom"
)internal/style/) (后期)style/
├── manager.go # 样式管理器
├── types.go # 样式类型定义
├── parser.go # 样式解析器
├── theme.go # 主题系统
└── properties.go # 样式属性
internal/lua/)lua/
├── engine.go # Lua引擎 (已存在,需重构)
├── module.go # streamvis模块
├── binding.go # 数据绑定
├── component_bind.go # 组件绑定
├── event_bind.go # 事件绑定
├── layout_bind.go # 布局绑定
└── utils.go # 工具函数
┌─────────┐ 读取 ┌─────────┐ 解析 ┌─────────┐ 绑定 ┌─────────┐
│ 数据源 │ ──────→ │ 处理器 │ ──────→ │ 绑定器 │ ──────→ │ 组件 │
│ (I/O) │ │ (JSON等)│ │ (Lua) │ │ (UI) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
↑ ↑ ↑ ↑
│ │ │ │
└───────────────────┴───────────────────┴───────────────────┘
Lua脚本配置与控制
关键流程: 1. 数据采集: I/O管理器从各种源读取数据 2. 数据处理: 可选的JSON解析、Lua转换函数 3. 数据绑定: 自动更新到绑定的UI组件 4. 用户交互: 事件系统处理用户输入 5. 控制输出: 向I/O源写入控制命令
主线程 (渲染线程) I/O线程 (后台) Lua线程 (脚本)
├─────────────────┐ ├─────────────────┐ ├─────────────────┐
│ 60FPS渲染循环 │ │ 非阻塞I/O读取 │ │ Lua脚本执行 │
│ UI更新 │ │ 数据缓冲 │ │ 事件回调 │
│ 事件处理 │ │ 连接管理 │ │ 数据转换 │
│ 布局计算 │ │ 错误处理 │ │ 业务逻辑 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└─────────────────────────┼─────────────────────────┘
↓
线程安全数据通道 (Channel)
线程安全设计: 1. I/O操作在后台线程,不阻塞渲染 2. 数据通过缓冲通道传递到主线程 3. Lua回调在主线程执行,避免并发问题 4. 组件状态更新使用原子操作或通道
目标: 建立新的架构基础,保持向后兼容
internal/io/manager.go)
UnixSocketSource (unix://)PipeSource (pipe://)FileSource (file://)TCPSource (tcp://)GPIOSource (gpio://) -
通过Linux文件系统internal/component/interface.go)
TextInput)ProgressBar)Scrollbar)模块系统
(internal/lua/module.go)
require("streamvis") 模块app对象和相关方法新的Lua API:
// 示例: 注册app对象
func registerAppModule(L *lua.LState, app *App) {
mt := L.NewTypeMetatable("streamvis.app")
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
"source": luaSource,
"layout": luaLayout,
"add": luaAddComponent,
"on": luaOnEvent,
"run": luaRun,
}))
L.SetGlobal("streamvis", mt)
}目标: 实现完整的声明式语法和响应式布局
width="auto")目标: 添加高级功能,性能优化,完善生态系统
目标: 测试、bug修复、准备正式发布
高优先级风险: 1. 性能达不到60FPS - 缓解: 早期性能分析,优先优化热点路径 - 备选: 降低帧率要求,提供性能模式
中优先级风险: 1. Lua与Go交互性能瓶颈 - 缓解: 批量数据传递,减少跨语言调用 - 备选: 关键路径用Go实现,Lua只处理业务逻辑
StreamVis 2.0的设计目标是将现有的数据可视化工具转变为一个完整的物联网控制与可视化平台。通过声明式语法、响应式布局、扩展的I/O支持和丰富的组件库,它能够满足从简单嵌入式界面到复杂数据中心大屏的各种需求。
核心优势: 1. 简洁而强大: 最小实现哲学,核心功能完善 2. 实时性能: 60FPS渲染,低延迟数据流 3. 高度可扩展: Lua驱动,易于定制和扩展 4. 广泛适用: 从边缘设备到云端的完整解决方案
实施建议: 1. 采用增量开发: 按阶段逐步实现,每个阶段都有可交付成果 2. 保持向后兼容: 确保现有用户平稳过渡 3. 重视开发者体验: 文档、示例、工具链的完整性 4. 社区驱动发展: 早期用户反馈,开源协作模式
这个设计为StreamVis提供了清晰的发展路线图,使其能够成为一个在物联网和数据可视化领域有重要影响力的开源项目。