主题
data — 共享数据对象
模块名:data
创建一个「值容器」(内部类型 lpchar),可以装字符串、数字或布尔值,并支持算术、比较、连接等运算符重载,用起来接近普通变量。
它的主要用途是给串口协议子设备当数据落点:com_modbus / com_fx / com_dlt645 的采集通道可以把值直接写进 data 对象,脚本再从对象里读,避免占用 PLC 寄存器。也可以用来在不同代码块之间共享一份可变数据。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
data.creat([value]) | 创建一个数据对象,可带初值 |
对象方法
| 方法 | 作用 |
|---|---|
d:set | 写入新值 |
d:get_num | 按数字读出 |
d:get_bool | 按布尔读出 |
d:get_string | 按原始字节串读出 |
get_num / get_bool / get_string 在库表里也注册了,data.get_num(d) 与 d:get_num() 等价。set 只能用冒号写法。
支持的运算符
对象重载了这些元方法,可以直接参与表达式:
| 运算 | 元方法 |
|---|---|
+ - * / % ^ | __add __sub __mul __div __mod __pow |
取负 -d | __unm |
连接 .. | __concat |
长度 #d | __len |
比较 == < <= | __eq __lt __le |
tostring(d) / print(d) | __tostring |
本模块不注册常量。
data.creat
lua
data.creat([value]) --> data创建一个数据对象。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | value | string | number | boolean | nil | 否 | — | 初值。string 按字节串存;number 按浮点存;true→1、false→0;nil 或 table 等其它类型 → 0 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | 数据对象。必须用变量接住,否则可能被 GC 回收 |
示例
lua
local temp = data.creat(0) -- 数值容器
local state = data.creat(0) -- 通信状态容器
local name = data.creat("LPPLC") -- 字符串容器实用例程
lua
modbus_state=data.creat(0)--连接状态标志对象
dianlu=data.creat(0)--电流对象
--zongliangh=data.creat(0)--总量x100对象
--zongliangl=data.creat(0)--总量x1对象
meter=com_modbus.creat(com_modbus.RTU,3,modbus_state)--建立MODBUS子设备,数字是设备地址
--meter:set_32_abcd(com_modbus.)--32位数编码方式
--meter:set_float_abcd(com_modbus.ABCD)
meter:set_time_recv(2000)--接收时间
meter:add_channel(400001,com_modbus.S16,dianlu,com_modbus.READ)--加入采集通道,数字是寄存器D的地址,400001是要采集的MODBUS设备寄存器
--meter:add_channel(400001,com_modbus.S16,zongliangh,com_modbus.READ)--加入采集通道
--meter:add_channel(400001,com_modbus.FLOAT,zongliangh,com_modbus.READ)--加入采集通道
--meter:add_channel(400004,com_modbus.FLOAT,zongliangl,com_modbus.READ)--加入采集通道
--meter:add_channel(400006,com_modbus.FLOAT,liuliang,com_modbus.READ)--加入采集通道
serial=com.setup(1,9600,com.NONE,8,1)--建立串口父设备,前面 1是串口号
serial:add_device(meter)--加入子设备
serial:set_time_lunxun(2000)--设定轮询时间
serial:set_timeout(2000)--接收超时时间
serial:start()--启动串口数据采集
ds1=timer.start(5000,function()
print("dianlu:",dianlu)--plc.d(100))
plc.d(120,dianlu)
end
)把上面的地址、主题、引脚替换成你项目里的即可。
d:set
lua
d:set(value)写入新值。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | value | data | string | number | boolean | nil | 是 | — | 传另一个 data 对象则拷贝它的内容;其它类型规则同 data.creat |
返回值
无返回值。
示例
lua
temp:set(25.6)
name:set("workshop-A")
temp:set(other_data) -- 从另一个对象拷贝说明
- 传 table 或 lightuserdata 时什么也不做(保持原值),不报错。
d:get_num
lua
d:get_num() --> number按数字读出当前值。
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| number | 值恰好是整数时返回 integer,否则返回浮点数 |
示例
lua
print(temp:get_num())d:get_bool
lua
d:get_bool() --> boolean按布尔读出当前值。
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| boolean | 内部整数值为 0 时返回 false,否则 true |
d:get_string
lua
d:get_string() --> string按原始字节串读出当前值。
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| string | 内部存储的原始字节,可含 \0 |
说明
- 用数字初始化的对象再
get_string得到的是内部二进制表示,不是可读数字。需要可读文本请用tostring(d)。
data.start —— 本模块没有这个函数
data 是纯值容器,不需要也没有任何"启动"动作:creat 出来的那一刻就能读写。个别流传的 API 清单会把 start 挂在 data 名下——那是误归属,运行时调用只会报错:
lua
local d = data.creat(0)
data.start() --> 报错:attempt to call field 'start' (a nil value)参数
start——data模块的库表里只有creat/get_bool/get_num/get_string四个键,对象元表里只有本页「对象方法」列出的那些,没有start。真正带start的是各通信模块(如mc_server.start、modbus的dev:start),和data无关。
返回值
以 data.start() 调用一律报错,没有返回值。
示例
lua
-- 正确:data 对象创建即可用,挂到采集通道上就是它的"生效"方式
v = data.creat(0)
dev:add_channel(400001, com_modbus.FLOAT, v, com_modbus.READ) -- 之后由 com 轮询写入常见用法
给 Modbus 采集通道当落点
lua
local state = data.creat(0) -- 通信状态
local temp = data.creat(0) -- 温度值
local dev = com_modbus.creat(com_modbus.RTU, 1, state)
dev:add_channel(400001, com_modbus.FLOAT, temp, com_modbus.READ)
local port = com.setup(1, 9600)
port:add_device(dev)
port:set_time_lunxun(1000)
port:start()
timer.start(2000, function()
print("温度:", temp:get_num(), "状态:", state:get_num())
end)直接参与运算
lua
local a = data.creat(10)
local b = data.creat(3)
print(a + b) -- 13
print(a > b) -- true
print(tostring(a)) -- 10注意事项
- 对象必须用变量长期持有,被 GC 回收后采集通道会写到已释放的对象上。
- 内部只有一份值,没有类型标签:用数字写进去、用
get_string读出来得到的是二进制,不是文本。读写用同一族方法。 - 名字里的「共享」指的是多处代码共享同一份值,不是同步原语,本模块不提供锁。事件回调本身是串行执行的,通常也不需要锁;并发模型见 并发模型概述。
- 需要掉电保存请用
fdb或ini;data仅保存在内存中。
例程
统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:给 Modbus 采集当数据落点(不占 D 区)
适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 / 上位机 | 依赖模块:data com_modbus com | 难度:★☆☆
场景:温控表浮点温度要拿来算 PID,不想占用 PLC D 寄存器,直接落进 data 对象。
完整代码(可直接下载运行):
lua
local state = data.creat(0) -- 通信状态
local temp = data.creat(0) -- 温度值
local dev = com_modbus.creat(com_modbus.RTU, 1, state)
dev:add_channel(400001, com_modbus.FLOAT, temp, com_modbus.READ)
local port = com.setup(1, 9600)
port:add_device(dev)
port:set_time_lunxun(1000)
port:start()
timer.start(2000, function()
print("温度:", temp:get_num(), "状态:", state:get_num())
end)要点:data 是纯值容器,creat 出来即用,没有 start;可直接作为采集通道的 target。
验证方法:下载重启,日志每 2 秒打印温度值。
注意事项:对象必须用变量长期持有(被 GC 回收后采集通道写到已释放对象);没有 data.start 之类函数,调用会报错。
例程 2:跨代码块共享 + 直接运算
适用:同上 | 依赖模块:data | 难度:★☆☆
场景:多个定时器/回调共享一个计数,并对其直接做数值运算。
完整代码(可直接下载运行):
lua
local count = data.creat(0)
timer.start(1000, function()
count:set(count:get_num() + 1)
print("计数:", count:get_num())
end)要点:data 重载了 + - * / 等运算符,可直接参与表达式;set 用冒号写法。
验证方法:每秒计数 +1。
注意事项:set 只能用冒号;传 table/lightuserdata 时 set 静默忽略不报错。
例程 3:注意事项——get_string 与 get_num 的区别
适用:同上 | 依赖模块:data | 难度:★☆☆
场景:用数字初始化后想打印可读文本,却拿到乱码。
完整代码(可直接下载运行):
lua
local d = data.creat(25.6)
print(d:get_string()) -- 内部二进制,乱码
print(tostring(d)) -- "25.6" 正确可读
local s = data.creat("LPPLC")
print(s:get_string()) -- "LPPLC"要点:内部只有一份值、没有类型标签;数字初始化再 get_string 得到二进制。
验证方法:tostring(d) 输出 25.6。
注意事项:需要可读文本用 tostring(d) 而非 get_string()。