
代码
local lgi = require 'lgi'
local Gtk = lgi.require 'Gtk'
local serial = require 'serial' -- luaserial 库(需额外安装)
-- 应用主类
local ScaleDialog = {}
ScaleDialog.__index = ScaleDialog
-- 构造函数:接收串口号和窗口标题
function ScaleDialog.new(port_name, title)
local self = setmetatable({}, ScaleDialog)
self.port_name = port_name
self.title = title
self.serial_port = nil
self.recent_weights = {} -- 存储最近6次重量
self.stable_weight = -1.0
self.is_running = true
self.epsilon = 0.001 -- 浮点数精度容忍度
-- 初始化GUI
self:init_ui()
-- 启动串口线程
self:start_serial_thread()
return self
end
-- 初始化GUI界面
function ScaleDialog:init_ui()
-- 创建主窗口
self.window = Gtk.Window {
title = self.title,
default_width = 300,
default_height = 200,
resizable = false,
window_position = Gtk.WindowPosition.CENTER
}
-- 主布局(垂直盒子)
local vbox = Gtk.Box {
orientation = Gtk.Orientation.VERTICAL,
spacing = 20,
margin = 20
}
-- 重量显示区域(水平盒子)
local weight_hbox = Gtk.Box {
orientation = Gtk.Orientation.HORIZONTAL,
spacing = 10
}
local weight_label = Gtk.Label { label = "当前重量:" }
self.current_weight_label = Gtk.Label { label = "-- kg" }
weight_hbox:pack_start(weight_label, false, false, 0)
weight_hbox:pack_start(self.current_weight_label, false, false, 0)
-- 状态标签
self.status_label = Gtk.Label {
label = "等待数据稳定(连续6次相同值)..."
}
-- 取消按钮
local cancel_btn = Gtk.Button { label = "取消" }
cancel_btn:on_clicked(function() self:close() end)
-- 组装布局
vbox:pack_start(weight_hbox, false, false, 0)
vbox:pack_start(self.status_label, false, false, 0)
vbox:pack_end(cancel_btn, false, false, 10) -- 靠右显示
self.window:add(vbox)
-- 窗口关闭事件
self.window:on_destroy(function() self:close() end)
end
-- 启动串口处理线程
function ScaleDialog:start_serial_thread()
-- Lua 中使用协程模拟线程(或使用 lthread 库,此处用原生协程)
coroutine.wrap(function()
self:init_serial()
end)()
end
-- 初始化串口
function ScaleDialog:init_serial()
-- 配置串口参数
local port = serial:new()
local ok, err = port:open(self.port_name, {
baudrate = 9600,
databits = 8,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
timeout = 500 -- 读取超时(毫秒)
})
if not ok then
-- 跨线程更新UI(Gtk需在主线程更新)
Gtk.main_do_event(Gtk.Event { type = Gtk.EventType.NOTHING })
self:update_ui(function()
Gtk.MessageDialog {
parent = self.window,
message_type = Gtk.MessageType.ERROR,
buttons = Gtk.ButtonsType.OK,
text = string.format("串口 %s 打开失败", self.port_name),
secondary_text = err
}:run()
self.window:destroy()
end)
return
end
self.serial_port = port
-- 更新状态
self:update_ui(function()
self.status_label.label = string.format("串口 %s 已打开,等待数据...", self.port_name)
end)
-- 循环读取串口数据
while self.is_running do
local data, err = port:read(1024) -- 读取最多1024字节
if err then
self:update_ui(function()
Gtk.MessageDialog {
parent = self.window,
message_type = Gtk.MessageType.ERROR,
buttons = Gtk.ButtonsType.OK,
text = "数据读取错误",
secondary_text = err
}:run()
end)
break
end
if data and #data > 0 then
local weight = self:try_parse_weight(data:trim())
if weight then
-- 更新当前重量显示
self:update_ui(function()
self.current_weight_label.label = string.format("%.3f kg", weight)
end)
-- 检查是否稳定
self:check_stable_weight(weight)
end
end
-- 短暂延迟,避免CPU占用过高
os.execute("sleep 0.05") -- 50毫秒
end
end
-- 解析重量数据(提取字符串中的数字部分)
function ScaleDialog:try_parse_weight(data)
-- 匹配整数、小数(支持正负号)
local numeric_part = data:match("[-+]?%d+%.?%d*")
if numeric_part then
return tonumber(numeric_part)
end
return nil
end
-- 检查是否连续6次相同重量
function ScaleDialog:check_stable_weight(current_weight)
-- 保留最近6次数据
table.insert(self.recent_weights, current_weight)
if #self.recent_weights > 6 then
table.remove(self.recent_weights, 1)
end
-- 检查是否稳定
if #self.recent_weights == 6 and self:all_equal() then
self.stable_weight = current_weight
self.is_running = false
-- 关闭串口
if self.serial_port then
self.serial_port:close()
end
-- 更新状态并延迟关闭窗口
self:update_ui(function()
self.status_label.label = string.format("数据稳定:%.3f kg,自动确认中...", self.stable_weight)
-- 延迟1秒关闭
GLib.timeout_add_seconds(1, function()
self.window:destroy()
return false -- 只执行一次
end)
end)
end
end
-- 检查列表中所有元素是否相等(考虑浮点数精度)
function ScaleDialog:all_equal()
local first = self.recent_weights[1]
for _, w in ipairs(self.recent_weights) do
if math.abs(w - first) > self.epsilon then
return false
end
end
return true
end
-- 跨线程更新UI(Gtk要求UI操作在主线程)
function ScaleDialog:update_ui(callback)
GLib.idle_add(GLib.PRIORITY_DEFAULT, function()
callback()
return false -- 只执行一次
end)
end
-- 关闭窗口及资源清理
function ScaleDialog:close()
self.is_running = false
if self.serial_port then
self.serial_port:close()
end
if self.window then
self.window:destroy()
end
end
-- 显示对话框并返回稳定重量
function ScaleDialog:show()
self.window:show_all()
Gtk.main()
return self.stable_weight
end
-- 主程序入口
local function main()
-- 初始化Gtk
Gtk.init()
-- 示例:传入串口号"COM3"和标题"生鲜电子秤采集"
local dialog = ScaleDialog.new("COM3", "生鲜电子秤采集")
local stable_weight = dialog:show()
-- 显示结果
if stable_weight ~= -1 then
local msg_dialog = Gtk.MessageDialog {
message_type = Gtk.MessageType.INFO,
buttons = Gtk.ButtonsType.OK,
text = "采集完成",
secondary_text = string.format("最终稳定重量:%.3f kg", stable_weight)
}
msg_dialog:run()
msg_dialog:destroy()
end
end
-- 运行主程序
main()
实现说明
1. 依赖库
lgi:Lua 绑定的 GTK 库,用于构建跨平台 GUI 界面(支持 Windows/macOS/Linux)luaserial:Lua 的串口通信库,处理串口读写- 安装命令(需先安装 Lua 环境和 LuaRocks 包管理器):
bash
运行
luarocks install lgi # 安装 GTK 绑定 luarocks install luaserial # 安装串口库
2. 功能特点
- 完全对齐原 C# 功能:串口通信、数据解析、连续 6 次稳定判定、GUI 界面、取消功能
- 跨平台支持:依托 GTK 和 luaserial,可在 Windows/macOS/Linux 运行
- 线程安全:使用 GTK 的
GLib.idle_add实现跨线程 UI 更新,避免卡顿 - 浮点数精度处理:使用
epsilon = 0.001容忍微小误差,避免因硬件抖动导致误判
3. 关键细节
- 串口配置:波特率 9600、无校验、8 数据位、1 停止位(与原 C# 一致,可根据设备调整)
- 数据解析:使用 Lua 正则提取数字部分,支持
123、123.45、-123.45等格式 - 稳定判定:保留最近 6 次重量,全部在误差范围内则判定为稳定
- 资源清理:关闭窗口或取消时自动关闭串口,避免资源泄漏
4. 运行方法
- 安装依赖(Lua 5.1+/5.2+/5.3+ 均可)
- 保存代码为
scale_dialog.lua - 根据实际电子秤串口号修改
main函数中的"COM3"(Linux 为/dev/ttyUSB0,macOS 为/dev/cu.usbserial-*) - 运行命令:
bash
运行
lua scale_dialog.lua
5. 注意事项
- GTK 环境:Windows 系统需提前安装 GTK 运行时(可通过 GTK 官网 下载)
- 串口权限:Linux/macOS 需给串口添加读写权限(如
sudo chmod 666 /dev/ttyUSB0) - 数据协议适配:若电子秤输出格式特殊(如带前缀、校验位),需修改
try_parse_weight函数的正则表达式
该实现充分利用了 Lua 的简洁性和 GTK 的跨平台特性,同时保持了原程序的核心功能和用户体验,适合快速部署和轻量场景使用。
Lua 在电子秤读取中的独特优势、应用场景与初学者指南
Lua 作为一种轻量级、高效且灵活的编程语言,在电子秤读取领域展现出显著的优势。结合上述代码,我们可以深入探讨 Lua 在电子秤数据读取方面的重要意义、广泛应用场景以及初学者的学习路径。
Lua 电子秤读取的优势与重要意义
- 轻量级与高效性:Lua 语言本身轻量级,占用资源少,这使得它在资源有限的环境中,如一些嵌入式系统连接的电子秤,能够高效运行。它的执行速度快,对于电子秤实时数据的读取和处理能够迅速响应,确保数据的及时获取和分析,满足电子秤读取应用对实时性的要求。
- 灵活性与可扩展性:Lua 的语法简洁灵活,易于学习和掌握。它可以方便地与其他语言或库进行集成,在电子秤读取场景中,通过
require语句引入lgi库用于创建图形界面,以及serial库进行串口通信,这种强大的扩展性使得开发者能够根据具体需求快速定制和扩展功能。例如,可轻松添加数据存储、网络传输等功能模块。 - 良好的跨平台性:Lua 能够在多种操作系统上运行,无论是 Windows、Linux 还是其他类 Unix 系统,都能很好地适配。这意味着基于 Lua 开发的电子秤读取程序可以在不同的操作系统环境下部署,适应各种实际应用场景,提高了程序的通用性和适用性。
10 个应用场景
- 小型零售店铺:在小型超市、便利店中,电子秤结合 Lua 程序,可快速准确地获取商品重量,自动计算价格并打印小票。由于 Lua 的轻量级特性,可在低配置的收银设备上高效运行,提升结算效率,减少人工计算错误。
- 餐饮后厨管理:餐厅后厨利用电子秤控制食材用量,Lua 程序读取电子秤数据,帮助厨师按照食谱精确配料,保证菜品口味的一致性。同时,通过记录食材使用量,实现食材库存的精细化管理,避免浪费,优化成本控制。
- 工业生产质量检测:在工业生产线上,电子秤用于对零部件进行称重检测。Lua 程序读取重量数据,实时判断零部件是否符合质量标准,及时发现生产过程中的质量问题。例如,在汽车零部件制造中,通过精确称重确保产品质量的稳定性,提高生产效率。
- 物流包裹处理:物流行业使用电子秤称量包裹重量,Lua 程序读取数据后与物流管理系统集成,自动计算运费、生成运单信息,并更新库存状态。这有助于优化物流流程,提高物流运营的智能化水平,提升客户服务质量。
- 药品生产与质量控制:在制药行业,药品的重量精度至关重要。Lua 读取电子秤数据,对药品原料、中间体和成品进行精确称重,严格控制药品剂量,保障药品质量和安全性,确保符合药品生产的严格规范和质量标准。
- 珠宝鉴定与交易:珠宝行业对重量精度要求极高,高精度电子秤结合 Lua 程序读取重量数据,为珠宝鉴定和定价提供准确依据。确保珠宝交易的公平公正,维护买卖双方的利益,促进珠宝行业的健康发展。
- 科研实验数据采集:在科研实验室中,电子秤用于精确称量化学试剂、生物样本等。Lua 程序读取重量数据并自动记录,同时可进行数据分析和处理,提高实验数据的准确性和记录效率,为科研工作提供可靠的数据支持。
- 农业农产品收购分级:在农产品收购环节,电子秤称重结合 Lua 程序,快速统计农产品重量,并根据重量和质量进行分级定价。这实现了农产品收购的自动化和规范化管理,提高了收购效率,保障了农民和收购商的利益。
- 环保废弃物回收计量:环保回收企业利用电子秤对回收的废弃物进行称重,Lua 程序读取数据,统计回收量,为废弃物处理和资源再利用提供数据支持。同时,便于环保监管部门对废弃物回收情况进行监控和管理,推动环保事业的发展。
- 医疗保健体重监测:在医院、健身房等场所,电子秤用于测量人体体重。Lua 程序读取重量数据,与医疗信息系统或健身管理系统集成,方便医生或教练跟踪患者或会员的体重变化,提供个性化的健康建议和治疗方案,助力人们的健康管理。
初学者如何利用
- 学习基础知识:
- Lua 基础语法:初学者需要掌握 Lua 的基本语法,包括变量声明、数据类型(如数字、字符串、表等)、控制结构(如
if - then - else、for循环等)、函数定义与调用以及表的使用。理解 Lua 的面向对象编程概念(通过元表和元方法实现),这对于理解和扩展给定的电子秤读取程序至关重要。 - 串口通信知识:了解串口通信的基本原理,包括波特率、数据位、停止位、奇偶校验等概念。学习如何使用
serial库(如代码中的serial:new()、port:open()等方法)进行串口配置和数据读取,掌握串口通信的基本流程和操作。 - 图形界面编程:由于代码使用
lgi库结合 Gtk 创建图形界面,初学者要学习 Gtk 的基本组件和布局方式,如Gtk.Window、Gtk.Box、Gtk.Label、Gtk.Button等组件的使用,以及如何通过信号连接(如on_clicked、on_destroy等)实现用户交互。同时,要理解如何在 Lua 中利用GLib.idle_add等方法进行跨线程 UI 更新,因为 Gtk 要求 UI 操作在主线程执行。
- Lua 基础语法:初学者需要掌握 Lua 的基本语法,包括变量声明、数据类型(如数字、字符串、表等)、控制结构(如
- 深入剖析代码:
- 运行与观察:将代码复制到 Lua 开发环境中运行,确保安装了所需的库(
lgi和serial)。观察程序的运行效果,了解电子秤数据的读取、显示、稳定性判断以及用户交互等功能的实现过程,熟悉程序的整体运行逻辑。 - 代码解读:仔细研读代码,分析每个函数和模块的功能。例如,理解
ScaleDialog.new函数如何初始化应用的各种参数和界面,init_serial函数怎样配置和打开串口并进行数据读取,try_parse_weight函数如何解析重量数据,以及check_stable_weight函数如何判断数据稳定性。逐步跟踪代码执行流程,掌握数据在各个模块之间的传递和处理方式。
- 运行与观察:将代码复制到 Lua 开发环境中运行,确保安装了所需的库(
- 实践与创新:
- 简单修改与测试:尝试对代码进行简单修改,如调整串口参数(波特率、串口号等),观察数据读取的变化;或者修改界面布局,使界面更加美观易用。通过修改和测试,加深对代码和相关概念的理解。例如,改变
init_serial函数中的串口配置参数,查看程序对不同串口设置的响应。 - 功能扩展与优化:根据实际需求对程序进行功能扩展,比如添加数据存储功能,将读取到的重量数据保存到文件或数据库中;或者优化代码性能,减少 CPU 占用。在实践过程中,不断积累经验,提升自己的编程能力和解决实际问题的能力。例如,使用 Lua 的文件操作函数将重量数据保存到本地文件,实现数据的持久化存储。
- 简单修改与测试:尝试对代码进行简单修改,如调整串口参数(波特率、串口号等),观察数据读取的变化;或者修改界面布局,使界面更加美观易用。通过修改和测试,加深对代码和相关概念的理解。例如,改变
阿雪技术观
让我们积极投身于技术共享的浪潮中,不仅仅是作为受益者,更要成为贡献者。无论是分享自己的代码、撰写技术博客,还是参与开源项目的维护和改进,每一个小小的举动都可能成为推动技术进步的巨大力量
Embrace open source and sharing, witness the miracle of technological progress, and enjoy the happy times of humanity! Let's actively join the wave of technology sharing. Not only as beneficiaries, but also as contributors. Whether sharing our own code, writing technical blogs, or participating in the maintenance and improvement of open source projects, every small action may become a huge force driving technological progress.
1万+

被折叠的 条评论
为什么被折叠?



