/* -------- 机器人构成模块 * -------- 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(obj: MatrixObject, row: Int64, col: Int64): T where T <: Parsable { /* ---- 先获取矩阵单元的原始值 ---- */ let raw = obj[row, col] /* ---- 原始值为占位符且(非首行的)非标题行 ---- */ if (raw == "-" && row > 1) { return parseMt(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 = 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()) LibC.free(filename_c) if (status == LUA_OK) { var status = Lua_pcallk(L, 0, LUA_MULTRET, 0, 0, CPointer()) 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 = 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()) 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): 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): 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)//: 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)//: 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(ServoDefs, i, idxMax) let min = parseMt (ServoDefs,i, idxMin) let offset = parseMt(ServoDefs, i, idxOffset) let ctrl = getMt(ServoDefs, i, idxCtrl) let channel = parseMt(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): 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(SpeakerDefs, i, idxChannels) let sample_rate = parseMt(SpeakerDefs, i, idxSampleRate) let volume = parseMt(SpeakerDefs, i, idxVolumn) let max_f = parseMt(SpeakerDefs, i, idxMax_f) let min_f = parseMt(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() private let M = HashMap() /* -------- 使用矩阵脚本驱动机器人 * -------- * -------- 不行就将矩阵脚本转化为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(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 = 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, name: String): Option { 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(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)) /* 设置一个该硬件变量的参数 */ } }