Files
blackbird/bot4cj/src/r.cj
T
2026-07-28 12:44:07 +08:00

1072 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -------- 机器人构成模块
* -------- INFO(20260514-qing): 该模块确定机器人结构,为解耦考虑应该从解析出的矩阵对象中构建
* -------- TODO(20260527-qing): 使脚本中的数值亦可从运行时确定
* -------- */
package bot4cj
import std.io.*
import std.time.*
import std.random.*
import std.collection.*
import std.convert.*
import stdx.encoding.json.*
import bot4cj.hd.*
//import bot4cj.hd.extern.*
import bot4cj.math.*
import ctype4cj.*
import spi4cj.*
import lua4cj.*
/* ---------------- 硬件变量基类 ----------------
* -------- 接收3个lambda表达式,在回调函数中定义行为()
* -------- 通过JsonValue这种通用的数据结构进行传值
*
* -------- TODO*(20260517-qing): 因为析构器(终结器)的调用时间不确定,需要手动构造RAII机制
* -------- TODO(20260519-qing): 考虑将@Action矩阵对象置为硬件变量的一种
* -------- TODO*(20260529-qing): 增加微调器机制,通过不同的微调器对@Action进行微调
* ---------------------------------------------- */
open class MatrixVars_Hardware {
/* ---------------- 主初始化器 ----------------
* -------- 输入一个回调函数,用json参数能够为多种硬件提供同一接口
* -------- 在@Action矩阵中的每一列都是一个输入参数
* -------------------------------------------- */
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit>,
free!: Option<() -> Unit> ) {
this.things = things
this.rise = rise
this.free = free
}
/* --------------------------------
* --------------------------------
* -------- 事件回调,接收和返回JsonValue
* -------- 这是整个硬件的主要事件 */
private let things: (JsonValue) -> JsonValue
/* -------- 召集事件,不接受参数
* -------- 即硬件获取资源 */
private let rise: Option<() -> Unit>
/* -------- 遣返事件,不接受参数
* -------- 即硬件释放资源*/
private let free: Option<() -> Unit>
/* -------------------------------- *
* -------------------------------- */
/* -------- 事件发生器
* -------- TODO(20260612-qing): 使用某种方式记录变量是否经过了初始化
* --------
* -------- 该方法会调用硬件,需要传入1个JsonValue参数
* -------- 它返回1个JsonValue值 */
public func play(input: JsonValue): JsonValue {
return things(input)
}
/* -------- 伪初始化器
* -------- 该方法会打开硬件,什么也不传入和返回 */
public func build() {
if (let Some(callback) <- rise) {
callback()
}
}
/* -------- 伪解构器
* -------- 该方法会关闭硬件,什么也不传入和返回 */
public func destroy() {
if (let Some(callback) <- free) {
callback()
}
}
/* ----------------- */
/* ----------------- */
/* -------- 获得项目值
* -------- 如果矩阵单元的原始值是占位符且不为标题行,不为首行
* -------- 就递归地获取上一行的值 */
public static func getMt(obj: MatrixObject, row: Int64, col: Int64): String
{
let raw = obj[row, col]
if (raw == "-" && row > 1) {
return getMt(obj, row - 1, col)
}
return raw
}
/* -------- 解析项目值
* -------- 先获取矩阵单元的项目值(解析过占位符的值)
* -------- 然后解析为需要的格式
* -------- */
public static func parseMt<T>(obj: MatrixObject, row: Int64, col: Int64): T where T <: Parsable<T>
{
/* ---- 先获取矩阵单元的原始值 ---- */
let raw = obj[row, col]
/* ---- 原始值为占位符且(非首行的)非标题行 ---- */
if (raw == "-" && row > 1) {
return parseMt<T>(obj, row-1, col)
}
/* ---- 原始值为占位符且为(非标题行的)首行 ---- */
else if (raw == "-" && !(row > 1)) {
return T.parse("0") }
/* ---- 原始值并非占位符 ---- */
// else if (!raw == "-")
return T.parse(raw)
}
/* ----------------- */
/* ----------------- */
}
/* ---- 用于注册到Lua的I2C操作C语言函数 ----
*
*
* ---- 打开一个I2C设备 ---- */
@C
func Lua_openI2c(L: lua_State): C_Int {
unsafe {
let filename: CString = Lua_checkstring(L, 1) /* 第一个参数 */
let fd = openI2cDev(filename)
let val: lua_Integer = Int64(fd) /* lua_Integer */
Lua_pushinteger(L, val)
return 1 /* 返回1,表示有1个返回值 */
}
}
/* ---- 关闭I2C设备 ---- */
@C
func Lua_closeI2c(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
closeI2cDev(fd)
return 0
}
}
/* ---- 设置I2C地址 ---- */
@C
func Lua_setI2cAddr(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let addr = UInt8(Lua_checkinteger(L, 2))
setI2cAddr(fd, addr)
return 0
}
}
/* ---- 写I2C寄存器 ---- */
@C
func Lua_writeI2cReg(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let reg = UInt8(Lua_checkinteger(L, 2))
let val = UInt8(Lua_checkinteger(L, 3))
writeI2cReg(fd, reg, val)
return 0
}
}
/* ---- 读I2C寄存器 ---- */
@C
func Lua_readI2cReg(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let reg = UInt8(Lua_checkinteger(L, 2))
let res = Int64(readI2cReg(fd, reg)) /* lua_Integer */
Lua_pushinteger(L, res)
return 1
}
}
/* ---- 毫秒级延时 ---- */
@C
func Lua_milliSleep(L: lua_State): C_Int {
unsafe {
let n = Int64(Lua_checkinteger(L, 1))
sleep(Duration.millisecond * n)
return 0
}
}
/* ---- 打开一个SPI设备 ---- */
@C
func Lua_openSPI(L: lua_State): C_Int {
unsafe {
let filename: CString = Lua_checkstring(L, 1) /* 第一个参数 */
let fd = open_spi(filename)
let val: lua_Integer = Int64(fd) /* lua_Integer */
Lua_pushinteger(L, val)
return 1 /* 返回1,表示有1个返回值 */
}
}
/* ---- 关闭一个SPI设备 ---- */
@C
func Lua_closeSPI(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
close_spi(fd)
return 0
}
}
/* ---- 设置一个SPI设备 ---- */
@C
func Lua_setupSPI(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let mode = UInt8(Lua_checkinteger(L, 2))
let bits = UInt8(Lua_checkinteger(L, 3))
let speed = UInt32(Lua_checkinteger(L, 4))
let delay = UInt16(Lua_checkinteger(L, 5))
let res: lua_Integer = Int64(setup_spi(fd, mode, bits, speed, delay)) /* lua_Integer */
Lua_pushinteger(L, res)
return 1
}
}
/* ---- 写SPI设备 ---- */
@C
func Lua_writeSPI(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let val = UInt8(Lua_checkinteger(L, 2))
let res: lua_Integer = Int64(write_spi(fd, val)) /* lua_Integer */
Lua_pushinteger(L, res)
return 1
}
}
/* ---- 读SPI设备 ---- */
@C
func Lua_readSPI(L: lua_State): C_Int {
unsafe {
let fd = Int32(Lua_checkinteger(L, 1)) /* C_Int */
let res: lua_Integer = Int64(read_spi(fd)) /* lua_Integer */
Lua_pushinteger(L, res)
return 1
}
}
/* ---- Lua状态机 - 硬件变量
* ----
* ---- TODO(20260612-qing): 提前创建Lua状态机,利用闭包进行引用储存
* ---- 在rise中申请的资源将在free中被释放。
* ----
* ---- 我们将Lua状态机做成类成员存储,在原本的召集函数中进行初始化
* ---- 在解构函数中释放资源。
* ----
* ---- */
class MtVars_Lua <: MatrixVars_Hardware {
/* ---- 构造函数 => 传入事件函数,召集函数以及解构函数
* ----
* ---- */
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None )
{
super(things: things, rise: rise, free: free)
}
/* ---- 工厂函数 => 运行指定Lua脚本,传入矩阵中的所有参数(元数据)
* ----
* ---- */
public static func Factory(luapath: String, metadata: JsonValue): MtVars_Lua
{
/* ---- LUA状态机对象 ---- */
let L: lua_State = unsafe {Lua_init()}
unsafe {
if (Lua_check(L) == LUA_ERR) {
throw IllegalMemoryException("Lua_check()")
}
}
let mtVars_Lua = MtVars_Lua(
/* ---- 主要事件:调用该硬件的驱动脚本
* ---- 合并两张表(json_obj),一张来自创建该硬件时的设置(元数据)
* ---- 另一张是调用该硬件事件时传入的参数
* ----
* ---- 其中前者是一些诸如I2C地址、SPI速率的硬件参数
* ---- 后者是一些类似舵机旋转角度的实时参数
* ---- */
things: { json: JsonValue =>
/* ---- 从元数据(json_obj)进行深拷贝 ---- */
let obj = JsonValue.fromStr(metadata.toJsonString()).asObject()
obj.put("mode", JsonString("call"))
/* --- 合并两张(json_obj)表(元数据和参数) --- */
let fields: HashMap<String, JsonValue> = json.asObject().getFields()
for (k in fields.keys()) {
let val = fields.get(k).getOrThrow()
obj.put(k, val)
}
println("Lua scripts call parameters: ${obj.toString()}")
let res = call_lua(L, obj)
println("Lua script call result: ${res.toString()}")
return res
},
/* ---- 召集方法:提供元数据(json_obj)
* ---- 并对该硬件进行初始化
* ---- */
rise: { =>
/* ---- 在这里构建Lua状态机进行构建 ----
* ---- 使用专用的异常 ---- */
if (build_lua(L, luapath) == false) {
throw Exception("build_lua")
}
/* ---- 从元数据进行深拷贝 ---- */
let obj = JsonValue.fromStr(metadata.toJsonString()).asObject()
/* ---- 插入模式指令 ---- */
obj.put("mode", JsonString("build"))
println("Lua scripts build parameters: ${obj.toString()}")
let res = call_lua(L, obj)
println("Lua script build result: ${res.toString()}")
},
/* ---- 解构方法:传入元数据(json_obj|json_data)
* ---- 然后使用Lua状态机对设备进行关闭(或者重置)
* ---- */
free: { =>
/* ---- 从元数据进行深拷贝 ---- */
let obj = JsonValue.fromStr(metadata.toJsonString()).asObject()
/* ---- 插入模式指令 ---- */
obj.put("mode", JsonString("destroy"))
println("Lua scripts destroy parameters: ${obj.toString()}")
let res = call_lua(L, obj)
println("Lua script destroy result: ${res.toString()}")
/* ---- 在这里写就Lua状态机的解构 ---- */
destroy_lua(L)
}
)
/* ---- 返回构造的硬件变量
* ---- */
return mtVars_Lua
}
/* ---- 构造Lua状态机 ----
* ---- NOTE(20260618-qing): 构建完毕会运行一次 */
private static func build_lua(L: lua_State, luapath: String): Bool {
/* ---- 申请资源
* ---- */
unsafe {
let filename_c = LibC.mallocCString(luapath)
if (filename_c.isEmpty()) {
throw IllegalMemoryException("LibC.mallocCString()")
}
/* ---- 加载Lua库、注册Lua函数
* ---- */
Lua_loadlib(L)
/* ---- TH ---- */
/* ---- milli_sleep() ---- */
var cstr = LibC.mallocCString("milli_sleep")
Lua_register(L, cstr, Lua_milliSleep)
LibC.free(cstr)
/* ---- I2C设备 ---- */
/* ---- i2c_open() ---- */
cstr = LibC.mallocCString("i2c_open")
Lua_register(L, cstr, Lua_openI2c)
LibC.free(cstr)
/* ---- i2c_close() ---- */
cstr = LibC.mallocCString("i2c_close")
Lua_register(L, cstr, Lua_closeI2c)
LibC.free(cstr)
/* ---- i2c_set_addr() ---- */
cstr = LibC.mallocCString("i2c_set_addr")
Lua_register(L, cstr, Lua_setI2cAddr)
LibC.free(cstr)
/* ---- i2c_write_reg() ---- */
cstr = LibC.mallocCString("i2c_write_reg")
Lua_register(L, cstr, Lua_writeI2cReg)
LibC.free(cstr)
/* ---- i2c_read_reg() ---- */
cstr = LibC.mallocCString("i2c_read_reg")
Lua_register(L, cstr, Lua_readI2cReg)
LibC.free(cstr)
/* ---- SPI ----*/
/* ---- spi_open() ---- */
cstr = LibC.mallocCString("spi_open")
Lua_register(L, cstr, Lua_openSPI)
LibC.free(cstr)
/* ---- spi_close() ---- */
cstr = LibC.mallocCString("spi_close")
Lua_register(L, cstr, Lua_closeSPI)
LibC.free(cstr)
/* ---- spi_setsup() ---- */
cstr = LibC.mallocCString("spi_setup")
Lua_register(L, cstr, Lua_setupSPI)
LibC.free(cstr)
/* ---- spi_write() ---- */
cstr = LibC.mallocCString("spi_write")
Lua_register(L, cstr, Lua_writeSPI)
LibC.free(cstr)
/* ---- spi_read() ---- */
cstr = LibC.mallocCString("spi_read")
Lua_register(L, cstr, Lua_readSPI)
LibC.free(cstr)
/* ---- 设置Lua状态机
* ---- */
var status = Lua_loadfilex(L, filename_c, CPointer<C_Char>())
LibC.free(filename_c)
if (status == LUA_OK) {
var status = Lua_pcallk(L, 0, LUA_MULTRET, 0, 0, CPointer<Unit>())
println("lua build status: ${status}")
if (status == LUA_OK) {
return true
}
}
/* ---- 从栈顶获取错误信息
* ---- */
let error_msg = Lua_tostring(L, -1);
println(error_msg.toString());
return false
}
}
/* ---- 释放LUA状态机
* ---- NOTE(20260618-qing): 销毁之后该变量即废弃 */
private static func destroy_lua(L: lua_State) {
unsafe { Lua_free(L) }
}
/* ---- 调用Lua脚本的方法
* ---- NOTE(20260618-qing): 每次调用之后会重置栈指针 */
private static func call_lua(L: lua_State, json: JsonValue): JsonValue {
let obj = json.asObject()
let ret = JsonObject()
unsafe {
/* ---- 获取栈顶指针 ----*/
let top = Lua_gettop(L)
/* ---- ---- */
let mode = obj.get("mode").getOrThrow().asString().getValue()
let cstr = LibC.mallocCString(mode)
Lua_getglobal(L, cstr)
LibC.free(cstr)
/* ----在Lua状态机里 创建Lua表
* ---- */
Lua_createtable(L, 0, 0)
/* ---- 向Lua表中推入数据
* ---- */
let fields: HashMap<String, JsonValue> = obj.getFields()
for (k in fields.keys()) {
let val = fields.get(k).getOrThrow()
if (val is JsonString) {
let v = val.asString().getValue()
let sk = LibC.mallocCString(k)
let sv = LibC.mallocCString(v)
Lua_pushstring(L, sv)
Lua_setfield(L, -2, sk)
LibC.free(sk)
LibC.free(sv)
}
else if (val is JsonInt) {
let v = val.asInt().getValue()
let sk = LibC.mallocCString(k)
let sv = Int64(v)
Lua_pushinteger(L, sv)
Lua_setfield(L, -2, sk)
LibC.free(sk)
}
}
var status = Lua_pcallk(L, 1, LUA_MULTRET, 0, 0, CPointer<Unit>())
println("lua status: ${status}")
if (status == LUA_OK) {
if (Lua_type(L, -1) == LUA_TTABLE) {
/* ---- 栈顶是表
* ---- 压入 nil 作为遍历的起始键
* ---- */
Lua_pushnil(L)
while(Lua_next(L, -2) != 0) {
/* ---- 此时栈:[表, 键, 值] ----
* ---- 检查键值是否为空指针 ----
* ---- FIXME(20260531-qing) ---- */
let key = Lua_tostring(L, -2)
let val = Lua_tostring(L, -1)
if (key.isEmpty() || val.isEmpty()) {
throw NoneValueException("Lua_tostring()")
}
println("${key}: ${val}")
ret.put(key.toString(), JsonString(val.toString()))
Lua_pop(L, 1)
}
}
//Lua_pop(L, 1)
}
else if (status != LUA_OK) {
let err = Lua_tostring(L, -1)
if (!err.isEmpty()) {
println("Lua 错误详情: ${err.toString()}")
}
//Lua_pop(L, 1)
}
/* ---- 恢复Lua状态机 ----*/
Lua_settop(L, top)
//Lua_pop(L, 1)
}
return ret
}
}
/* -------- 别名变量
* -------- 这种变量用于给某个参数取一个更简单的别名
* -------- 它的事件返回(JsonString)将作为新的矩阵项目值
* --------
* -------- */
class MtVars_Alias <: MatrixVars_Hardware {
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None )
{ super(things: things, rise: rise, free: free) }
public static func Factory(AliasDefs: MatrixObject, i: Int64,
_: HashMap<String, MatrixVars_Hardware>): MtVars_Alias
{
let idxName = AliasDefs.indexOfCol(0, "name")
let idxAlias= AliasDefs.indexOfCol(0, "alias")
let name = AliasDefs[i, idxName]
let alias= getMt(AliasDefs, i, idxAlias)
println("Create a Alias vars: ${name}, witch means ${alias}")
let mtvars_alias = MtVars_Alias(things: {_: JsonValue =>
let str = JsonString(alias) /* 直接返回JsonString(alias)*/
return str
})
return mtvars_alias
}
}
/* -------- I2C变量类 这种硬件变量是特殊且固定的
* -------- TODO(20260617-qing): 等LUA状态机被闭包保存之后,将其优化为Lua驱动
* -------- */
class MtVars_I2C <: MatrixVars_Hardware {
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None )
{ super(things: things, rise: rise, free: free) }
/* @I2C
* name path;
* i2c-0 /dev/i2c-0;
* @
*/
public static func Factory( /* 工厂模式,根据矩阵对象构造 */
I2CDefs: MatrixObject, i: Int64, _: HashMap<String, MatrixVars_Hardware>): MtVars_I2C
{
let idxName = I2CDefs.indexOfCol(0, "name")
let idxPath = I2CDefs.indexOfCol(0, "path")
let name = I2CDefs[i, idxName]
let path = getMt(I2CDefs, i, idxPath)
println("Create a I2C hard-Var: ${name},\tpath: ${path}") /* ---- debug ---- */
let i2c = I2C_Orig(path) /* 在这里创建传统I2C类对象,通过闭包保存在MtVars_I2C里 */
let mtvars_i2c = MtVars_I2C(
things: { json: JsonValue =>
let fd = i2c.getFd()
let ret = JsonObject()
ret.put("fd",JsonInt(Int64(fd)))
return ret
},
free: {=>
println("I2C HVar: ${name} is closing now")
i2c._close()
})
return mtvars_i2c
}
}
/* -------- MPU6050_BIND硬件变量
* -------- INFO(20260612-qing): 自从引入了Lua状态机扩展硬件,该硬件变量不知道何去何从。
* -------- TODO(20260612-qing): 在Lua脚本中定义好BIND函数后,在pthread线程中反复调用
* -------- */
class MtVars_MPU6050_BIND <: MatrixVars_Hardware {
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None) {
super(things: things, rise: rise, free: free) }
/* 工厂函数,通过矩阵对象构建硬件变量
* @MPU6050_BIND
* name I2C addr;
* mpu6050-0 i2c-1 0x69;
*/
public static func Factory (
MPU6050_BIND_Defs: MatrixObject, i: Int64,
map: HashMap<String, MatrixVars_Hardware>)//: MtVars_MPU6050_BIND
{
let idxName = MPU6050_BIND_Defs.indexOfCol(0, "name")
let idxI2c = MPU6050_BIND_Defs.indexOfCol(0, "I2C")
let idxAddr = MPU6050_BIND_Defs.indexOfCol(0, "addr")
let name = MPU6050_BIND_Defs[i, idxName]
let i2c = getMt(MPU6050_BIND_Defs, i, idxI2c)
let addr = getMt(MPU6050_BIND_Defs, i, idxAddr)
println("Create a MPU6050_BIND HVar: ${name}, use i2c interf: ${i2c}, i2c addr: ${addr}")
let i2c_HardVar = map[i2c]
let param = JsonObject()
param.put("mode", JsonString("get_fd"))
let ret = i2c_HardVar.play(param)
let fd = Int32(ret.asObject().get("fd").getOrThrow().asInt().getValue())
let _addr = UInt8.parse(addr)
let mpu6050 = MPU6050(fd, _addr)
let mtvars_mpu6050bind = MtVars_MPU6050_BIND(
things: {json: JsonValue =>
let ret = JsonObject()
ret.put("status", JsonString("OK"))
return ret
},
rise: {=>
println("MPU6050_BIND HVar: ${name} is starting now")
mpu6050.setup()
mpu6050.startDaemon()
},
free: {=>
println("MPU6050_BIND HVar: ${name} is closing now")
mpu6050.stopDaemon()
mpu6050.waitDaemon()
}
)
return mtvars_mpu6050bind
}
}
/* -------- 舵机变量类
* --------
* -------- TODO(20260515-qing): 现在普通的舵机类也是传入回调函数的,
* -------- 但是我们考虑到该硬件变量调用别的硬件变量,所以还是要专门做一个舵机硬件变量
* --------
* -------- TODO(20260612-qing): 根据Lua硬件脚本的返回结果,调用别的硬件变量
* -------- */
class MtVars_Servo <: MatrixVars_Hardware {
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None )
{ super(things: things, rise: rise, free: free) }
/*
* @Servo
* name max min offset controller channel;
* s1 90 - - pca9685-0 0;
* @
*/
public static func Factory( /* 工厂模式:使用矩阵对象构造 */
ServoDefs: MatrixObject, i: Int64, map: HashMap<String, MatrixVars_Hardware>)//: MtVars_Servo
{
let idxName = ServoDefs.indexOfCol(0, "name")
let idxMax = ServoDefs.indexOfCol(0, "max")
let idxMin = ServoDefs.indexOfCol(0, "min")
let idxOffset = ServoDefs.indexOfCol(0,"offset")
let idxCtrl = ServoDefs.indexOfCol(0, "controller")
let idxChannel = ServoDefs.indexOfCol(0, "channel")
let name = ServoDefs[i, idxName]
let max = parseMt<Float32>(ServoDefs, i, idxMax)
let min = parseMt<Float32> (ServoDefs,i, idxMin)
let offset = parseMt<Float32>(ServoDefs, i, idxOffset)
let ctrl = getMt(ServoDefs, i, idxCtrl)
let channel = parseMt<UInt8>(ServoDefs, i, idxChannel)
print("Create a Servo hard-Var: ${name}, max: ${max}, min: ${min}, offset: ${offset}")
println(", controller: ${ctrl}, channel: ${channel}")
let controller = map[ctrl] /* like pca9685 */
let servo = Servo(max: max, min: min, offset: offset,
callback_setAngle: {angle: Float32 =>
let obj = JsonObject()
obj.put("channel", JsonInt(Int64(channel)))
//obj.put("angle", JsonFloat(Float64(angle)))
obj.put("angle", JsonInt(Int64(angle)))
controller.play(obj)
})
/*
* {"angle": 180.0}
*/
let mtvars_servo = MtVars_Servo(things: {json=>
let angle = json.asObject().get("angle").getOrThrow().asFloat().getValue()
servo.setAngle(Float32(angle))
let ret = JsonObject()
ret.put("status", JsonString("OK"))
return ret
})
return mtvars_servo
}
}
/* -------- 扬声器类
* --------
* -------- 使用工厂函数
* --------
* -------- TODO: 输入扬声器设备名,扬声器又是一个复杂的功能
* --------
*/
class MtVars_Speaker <: MatrixVars_Hardware {
public init(things!: (JsonValue) -> JsonValue,
rise!: Option<() -> Unit> = None,
free!: Option<() -> Unit> = None )
{ super(things: things, rise: rise, free: free) }
/* --------| 工厂函数,根据矩阵对象进行构造
* --------| @Speaker
* --------| name channels sample_rate volumn max_f min_f;
* --------| spk 2 48000 0.6 5000 -;
* --------| @
*/
public static func Factory(
SpeakerDefs: MatrixObject, i: Int64, map: HashMap<String, MatrixVars_Hardware>): MtVars_Speaker {
let idxName = SpeakerDefs.indexOfCol(0, "name")
let idxChannels = SpeakerDefs.indexOfCol(0, "channels")
let idxSampleRate = SpeakerDefs.indexOfCol(0, "sample_rate")
let idxVolumn = SpeakerDefs.indexOfCol(0, "volumn")
let idxMax_f = SpeakerDefs.indexOfCol(0, "max_f")
let idxMin_f = SpeakerDefs.indexOfCol(0, "min_f")
let name = SpeakerDefs[i, idxName]
let channels = parseMt<Int32>(SpeakerDefs, i, idxChannels)
let sample_rate = parseMt<Int32>(SpeakerDefs, i, idxSampleRate)
let volume = parseMt<Float64>(SpeakerDefs, i, idxVolumn)
let max_f = parseMt<Float32>(SpeakerDefs, i, idxMax_f)
let min_f = parseMt<Float32>(SpeakerDefs, i, idxMin_f)
println("Create a Speaker: ${name}, channels: ${channels}, sample_rate: ${sample_rate}, volume: $(volume), max_f: ${max_f}, min_f: ${min_f}")
let spk = Spk(channels: channels, sample_rate: sample_rate)
let mtvar_spk = MtVars_Speaker(things: {json => // {"frequency": 1000.0, "delay": 400}
let json_obj = json.asObject()
var freq = Float32(json_obj.get("frequency").getOrThrow().asFloat().getValue())
if (freq > max_f) { freq = max_f }
if (freq < min_f) { freq = min_f }
let delay = Int32(json_obj.get("delay").getOrThrow().asFloat().getValue())
spk.play(freq, volume, delay)
let ret = JsonObject() /* ---- FIXME: 处理异常情况 */
ret.put("status", JsonString("OK"))
return ret
})
return mtvar_spk
}
}
/* -------- 机器人结构类
* --------
* -------- 机器人结构是动态的,从脚本中解析的
* -------- TODO(20260514-qing): 调整为输入MatrixObject
* -------- TODO(20260515-qing): 需要重写一遍硬件类(done)
* -------- TODO(20260518-qing): 使用神经网络RNN直接根据动作+传感器脚本生成下一步动作
* -------- TODO(20260527-qing): 增加硬件模块行为脚本
* -------- TODO*(20260527-qing): 为@Action表演矩阵对象增加位置指示器,以增量行动
* -------- */
class RobotStruct {
/* -------- 存放解析结果和创建的变量
* -------- */
private let C = LinkedList<MatrixObject>()
private let M = HashMap<String, MatrixVars_Hardware>()
/* -------- 使用矩阵脚本驱动机器人
* --------
* -------- 不行就将矩阵脚本转化为bot脚本吧
* -------- */
public init(input: InputStream) {
readMatrix(input)
createHardVars()
}
/* -------- 激活所有硬件变量
* -------- */
public func active() {
for (item in M.values()) {
item.build()
}
}
/* -------- 消除所有硬件变量
* --------
* -------- INFO(20260517-qing): 它需要以倒序方式进行清除
* -------- */
public func deactive() {
let items = M.values().toArray()
for (i in items.size-1..=0:-1) {
items[i].destroy()
}
}
/* -------- 执行@Action矩阵中的内容
* -------- */
public func run() {
run_drl_action()
}
/* -------- 储存整个矩阵变量表
* --------
* -------- NOTE(20260517-qing): 使用完毕要关闭输出流
* -------- */
public func save(out: OutputStream) {
let ms = MatrixSaver(out)
ms.save(C)
}
/* -------- 阅读矩阵脚本
* --------
* -------- TODO(20260504-qing): 使用矩阵脚本存储神经网络模型
* -------- */
private func readMatrix(file: InputStream){
let sr = StringReader<InputStream>(file)
let parser = MatrixParser(sr)
while (true) {
if (let Some(O) <- parser.parse()) {
let n = O.getName();
println(n)
C.addLast(O) }
else { break }
}
}
/* -------- 创建硬件变量字典
* --------
* -------- TODO(20260515-qing): 解耦合
* -------- FIXME(20260518-qing): 在插入哈希表之前如果有已经存在的键值,要释放它的资源
* -------- */
private func createHardVars()
{
for (matrix in C) {
let MtName = matrix.getName()
/* ---- 【特殊的】I2C硬件
* ---- */
if (MtName == "I2C") {
let idxName = matrix.indexOfCol(0,"name")
for (i in 1..matrix.size) {
let name = matrix[i, idxName]
M[name] = MtVars_I2C.Factory(matrix, i, M)
}
}
/* ---- 【特殊的】Alias别名硬件
* ---- */
else if (MtName == "Alias") {
let idxName = matrix.indexOfCol(0, "name")
for (i in 1..matrix.size) {
let name = matrix[i, idxName]
M[name] = MtVars_Alias.Factory(matrix, i, M)
}
}
/* ---- 【特殊的】Servo舵机硬件
* ---- */
else if (MtName == "Servo") {
let idxName = matrix.indexOfCol(0, "name")
for (i in 1..matrix.size) {
let name = matrix[i, idxName]
M[name] = MtVars_Servo.Factory(matrix, i, M)
}
}
/* ---- [特殊的] Action矩阵
* ---- */
else if (MtName == "Action") {
;
}
/* ---- [特殊的] Speaker扬声器硬件
* ---- */
else if (MtName == "Speaker") {
let idxName = matrix.indexOfCol(0, "name")
for (i in 1..matrix.size) {
let name = matrix[i, idxName]
M[name] = MtVars_Speaker.Factory(matrix, i, M)
}
}
/* ---- 【通用的】Lua脚本硬件
* ---- */
else if (matrix.size > 0) {
let idxName = matrix.indexOfCol(0, "name")
for (i in 1..matrix.size ) {
let obj = JsonObject()
let name = matrix[i, idxName]
for (j in 0..matrix.col) {
let colName = matrix[0, j]
let value = MatrixVars_Hardware.getMt(matrix, i, j)
/* ---- ----
* ---- FIXME: 加入点号别名的检索逻辑 ----
* ---- ---- */
if (let Some(mt) <- M.get(value)) {
/* ---- ----
* ---- 存在该名称的变量,传入空对象参数,并求取返回值
* ---- ---- */
let json = JsonObject()
let res = mt.play(json).asObject()
let fields: HashMap<String, JsonValue> = res.getFields()
for (k in fields.keys()) {
let val = fields.get(k).getOrThrow()
obj.put(k, val)
}
}
else {
obj.put(colName, JsonString(value))
}
}
/* ---- fixme: 这里的脚本路径是写死的,随后我们需要设计一个机制,从环境变量中查找
* ---- */
M[name] = MtVars_Lua.Factory("./modules/${MtName}.lua", obj)
}
}
}
}
/* -------- 在矩阵对象的容器中寻找目标
* -------- */
private static func findMt(ll: LinkedList<MatrixObject>, name: String): Option<MatrixObject>
{
for (item in ll) {
var target = item.getName()
target = target.trimAscii() // ???
if (name == target) {
return item }
}
return None
}
/* -------- 使用drl微调动作,使用RNN生成动作流
* -------- */
private func run_drl_action() {
let ActionDefs = findMt(C, "Action").getOrThrow() /* MatrixObject */
for (i in 1..ActionDefs.size) {
let set =JsonObject() /* 每个硬件变量的参数集合 */
for (j in 0..ActionDefs.col) {
parse_action_matrixObject(set, ActionDefs, i, j)
}
/* -------- 执行一行 -------- */
let keys = set.getFields().keys()
for (name_dev in keys) {
let hv = M[name_dev]
let params: JsonValue = set.get(name_dev).getOrThrow()
let _: JsonValue = hv.play(params)
}
}
}
/* -------- 解析@Action矩阵对象 -------- */
private func parse_action_matrixObject(set: JsonObject, ActionDefs: MatrixObject,
i: Int64, j: Int64) {
let name_HV = ActionDefs[0, j] /* 获取硬件变量名 FIXME: 该名称不得使用占位符 且不重复 */
let arr = name_HV.split(".")
if (arr.size != 2) {
throw IllegalArgumentException("Action column is not valid") }
let name_dev = arr[0]
var name_param = arr[1]
var val_fp64 = MatrixVars_Hardware.parseMt<Float64>(ActionDefs, i, j)
let AliasHV = M.get(name_param) /* 可能的参数别名转换 */
if (let Some(aliasHV) <- AliasHV) {
let ret = aliasHV.play(JsonObject())
let alias = ret.asString().getValue()
name_param = alias
}
var set_params: JsonObject /* 设置或者取得属于该硬件变量的参数 */
if (let Some(obj_) <- set.get(name_dev)) {
set_params = obj_.asObject() }
else {
set_params = JsonObject()
set.put(name_dev, set_params) }
set_params.put(name_param, JsonFloat(val_fp64)) /* 设置一个该硬件变量的参数 */
}
}