From 3b0874618ac08906b82a0ec93e9398fe9ac36c10 Mon Sep 17 00:00:00 2001 From: xingluo Date: Sun, 16 Nov 2025 18:57:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.lua | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++++ panel.lua | 222 +++++++++++++++++++++++++++ panel1.lua | 122 +++++++++++++++ 3 files changed, 779 insertions(+) create mode 100644 main.lua create mode 100644 panel.lua create mode 100644 panel1.lua diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..b4eb23e --- /dev/null +++ b/main.lua @@ -0,0 +1,435 @@ +-- ME空间原件控制器 +-- 功能: 管理ME空间原件的存储和切换 +--printUtf8 = load(http.get("https://git.liulikeji.cn/xingluo/ComputerCraft-Utf8/raw/branch/main/utf8ptrint.lua").readAll())() + +--function print(a) +-- printUtf8(a,colors.white,colors.lightGray) +--end + +-- 全局变量 +CONFIG_FILE = "disk_cfg" +CHECK_INTERVAL = 2 -- 检测间隔(秒) +REDSTONE_PULSE_INTERVAL = 0.1 -- 红石脉冲间隔(秒) +MAX_WAIT_CYCLES = 30 -- 最大等待循环次数 + +peripherals = nil +config = nil + +-- 初始化外围设备 +local function initPeripherals() + -- 左侧: 红网网卡 + local left = peripheral.getType("left") + if left ~= "modem" then + error("左侧没有找到红网网卡!") + end + + -- 从配置读取监听端口 + local listenPort = config.rednet_ports and config.rednet_ports.listen or 101 + peripheral.wrap("left").open(listenPort) -- 打开监听端口 + + -- 右侧: 扬声器 + local right = peripheral.getType("right") + if right ~= "speaker" then + print("警告: 右侧没有找到扬声器") + end + + -- 前方: 空间IO端口 + local front = peripheral.getType("front") + if front ~= "ae2:spatial_io_port" then + error("前方没有找到空间IO端口!") + end + + return { + modem = peripheral.wrap("left"), + speaker = peripheral.wrap("right"), + spatial_io = peripheral.wrap("front") + } +end + +-- 读取配置文件 +local function readConfig() + if not fs.exists(CONFIG_FILE) then + -- 创建默认配置文件 + local defaultConfig = { + current_disk = nil, + current_slot = nil, + disks = {}, -- 格式: {["显示名称"] = 槽位编号} + rednet_ports = { + listen = 101, -- 监听端口 + status = 100, -- 状态发送端口 + response = 102 -- 响应发送端口 + } + } + + local file = fs.open(CONFIG_FILE, "w") + file.write(textutils.serialize(defaultConfig)) + file.close() + + print("已创建默认配置文件,请手动编辑 " .. CONFIG_FILE .. " 配置原件映射") + return defaultConfig + end + + local file = fs.open(CONFIG_FILE, "r") + local content = file.readAll() + file.close() + + local loadedConfig = textutils.unserialize(content) or { + current_disk = nil, + current_slot = nil, + disks = {} + } + + -- 确保端口配置存在 + if not loadedConfig.rednet_ports then + loadedConfig.rednet_ports = { + listen = 101, + status = 100, + response = 102 + } + else + -- 设置默认值 + loadedConfig.rednet_ports.listen = loadedConfig.rednet_ports.listen or 101 + loadedConfig.rednet_ports.status = loadedConfig.rednet_ports.status or 100 + loadedConfig.rednet_ports.response = loadedConfig.rednet_ports.response or 102 + end + + return loadedConfig +end + +-- 写入配置文件 +-- 写入配置文件 +local function writeConfig() + -- 创建按槽位排序的disks表 + local sortedDisks = {} + + -- 先将disks转换为数组形式进行排序 + local tempArray = {} + for name, slot in pairs(config.disks) do + table.insert(tempArray, {name = name, slot = slot}) + end + + -- 按槽位排序 + table.sort(tempArray, function(a, b) + return a.slot < b.slot + end) + + -- 重新构建有序的disks表 + for _, item in ipairs(tempArray) do + sortedDisks[item.name] = item.slot + end + + -- 创建临时配置,使用排序后的disks + local configToSave = { + rednet_ports = config.rednet_ports, + current_disk = config.current_disk, + current_slot = config.current_slot, + disks = sortedDisks + } + + local file = fs.open(CONFIG_FILE, "w") + file.write(textutils.serialize(configToSave)) + file.close() +end + +-- 播放声音提示 +local function playSound(soundType) + if peripherals.speaker then + pcall(function() + if soundType == "success" then + peripherals.speaker.playSound("block.note_block.pling", 1.0, 2.0) + elseif soundType == "working" then + for i=1,3 do + peripherals.speaker.playSound("block.note_block.bass", 3, 1) + sleep(1) + end + elseif soundType == "error" then + peripherals.speaker.playSound("block.note_block.bass", 1.0, 0.5) + end + end) + end +end + +-- 发送红石脉冲 +local function sendRedstonePulse() + redstone.setOutput("front", true) + sleep(0.1) + redstone.setOutput("front", false) +end + +-- 发送状态信息到红网 +local function sendStatusUpdate() + -- 从配置读取端口 + local statusPort = config.rednet_ports.status + local listenPort = config.rednet_ports.listen + + -- 通过红网发送状态 + peripherals.modem.transmit(statusPort, listenPort, { + type = "status_update", + current_disk = config.current_disk, + disks = config.disks, + timestamp = os.time() + }) + + print("状态已发送到端口 " .. statusPort .. ": 当前原件=" .. (config.current_disk or "无")) +end + +-- 检查空间IO端口状态 +local function checkSpatialIOState() + local slots = peripherals.spatial_io.list() + local state = { + slot1 = slots[1], -- 第一格: 未放出/已收回 + slot2 = slots[2] -- 第二格: 已放出 + } + return state +end + +-- 验证槽位是否有物品 +local function validateSlotHasItem(slot) + local item = turtle.getItemDetail(slot) + return item ~= nil +end + +-- 收回当前原件 +local function retractCurrentDisk() + if not config.current_disk or not config.current_slot then + return true -- 没有当前原件,直接返回成功 + end + + print("开始收回原件: " .. config.current_disk) + + -- 切换到当前原件槽位 + turtle.select(config.current_slot) + + -- 尝试从空间IO端口拿取 + if turtle.suck() then + print("235 原件已取回") + end + + -- 将原件放入空间IO端口 + if not turtle.drop() then + error("无法放入空间IO端口") + end + + print("通过红石信号收回原件...") + + local waitCycles = 0 + while waitCycles < MAX_WAIT_CYCLES do + -- 发送红石脉冲 + sendRedstonePulse() + + -- 检查空间IO端口状态 + local state = checkSpatialIOState() + + if state.slot2 and not state.slot1 then + -- 原件已回到第2格,尝试拿取 + if turtle.suck() then + print("原件收回成功") + return true + end + end + + sleep(REDSTONE_PULSE_INTERVAL) + waitCycles = waitCycles + 1 + end + + print("收回原件超时!") + return false +end + +-- 放出目标原件 +local function deployTargetDisk(targetDisk) + local targetSlot = config.disks[targetDisk] + if not targetSlot then + error("未找到原件: " .. targetDisk) + end + + -- 验证槽位 + if not validateSlotHasItem(targetSlot) then + error("槽位 " .. targetSlot .. " 没有物品,无法放出") + end + + print("开始放出原件: " .. targetDisk) + + -- 切换到目标槽位 + turtle.select(targetSlot) + + -- 将原件放入空间IO端口 + if not turtle.drop() then + error("无法放入空间IO端口") + end + + -- 通过红石信号激活放出 + print("通过红石信号放出原件...") + + local waitCycles = 0 + while waitCycles < MAX_WAIT_CYCLES do + -- 发送红石脉冲 + sendRedstonePulse() + + -- 检查空间IO端口状态 + local state = checkSpatialIOState() + + if state.slot2 and not state.slot1 then + -- 原件已移动到第二格,放出成功 + print("原件放出成功") + return true + end + + sleep(REDSTONE_PULSE_INTERVAL) + waitCycles = waitCycles + 1 + end + + print("放出原件超时!") + return false +end + +-- 切换到指定原件 +local function switchToDisk(targetDisk) + print("收到切换命令: " .. targetDisk) + + -- 检查是否已经是目标原件 + if config.current_disk == targetDisk then + print("已经是目标原件,无需切换") + playSound("success") + return true + end + + -- 检查目标原件是否存在 + if not config.disks[targetDisk] then + print("错误: 未配置原件 '" .. targetDisk .. "'") + playSound("error") + return false + end + + playSound("working") + + -- 收回当前原件 + if not retractCurrentDisk() then + playSound("error") + return false + end + + -- 放出目标原件 + if not deployTargetDisk(targetDisk) then + playSound("error") + return false + end + + -- 更新配置文件 + config.current_disk = targetDisk + config.current_slot = config.disks[targetDisk] + writeConfig() + + playSound("success") + print("切换完成: " .. targetDisk) + sendStatusUpdate() -- 发送状态更新 + return true +end + +-- 处理红网消息 +local function handleRednetMessage(message) + local responsePort = config.rednet_ports.response + local listenPort = config.rednet_ports.listen + + if message.type == "switch_disk" and message.disk_name then + local success = switchToDisk(message.disk_name) + + -- 发送响应 + peripherals.modem.transmit(responsePort, listenPort, { + type = "switch_response", + disk_name = message.disk_name, + success = success, + timestamp = os.time() + }) + + elseif message.type == "status_request" then + -- 发送状态信息 + peripherals.modem.transmit(responsePort, listenPort, { + type = "status_response", + current_disk = config.current_disk, + disks = config.disks, + timestamp = os.time() + }) + elseif message.type == "reload_config" then + -- 重新加载配置 + config = readConfig() + peripherals.modem.transmit(responsePort, listenPort, { + type = "reload_response", + success = true, + timestamp = os.time() + }) + print("配置已重新加载") + end +end + +-- 主循环 - 状态发送 +local function main() + print("ME空间原件控制器启动中...") + + + print("1 外围设备初始化完成") + print("2 当前原件: " .. (config.current_disk or "无")) + print("3 配置的原件: ") + for name, slot in pairs(config.disks) do + print(" " .. name .. " -> 槽位 " .. slot) + end + print("4 红网端口配置: ") + print(" 监听端口: " .. config.rednet_ports.listen) + print(" 状态端口: " .. config.rednet_ports.status) + print(" 响应端口: " .. config.rednet_ports.response) + + -- 初始状态发送 + sendStatusUpdate() + + while true do + sleep(CHECK_INTERVAL) + sendStatusUpdate() + end +end + +-- 主循环 - 消息监听 +local function main1() + local listenPort = config.rednet_ports.listen + + while true do + -- 检查红网消息 + local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + if channel == listenPort and type(message) == "table" then + handleRednetMessage(message) + end + end +end + +-- 初始化检查 +if not turtle then + error("这个脚本需要在海龟上运行!") +end + +-- 检查配置文件是否存在,如果不存在则创建并提示 +if not fs.exists(CONFIG_FILE) then + config = readConfig() -- 这会创建默认配置文件 + print("请编辑 " .. CONFIG_FILE .. " 文件配置原件映射,格式如下:") + print("{") + print(' current_disk = "原件A",') + print(' current_slot = 1,') + print(' disks = {') + print(' ["原件A"] = 1,') + print(' ["原件B"] = 2,') + print(' ["原件C"] = 3') + print(" },") + print(' rednet_ports = {') + print(' listen = 101, -- 监听端口') + print(' status = 100, -- 状态发送端口') + print(' response = 102 -- 响应发送端口') + print(" }") + print("}") + print("配置完成后重新启动脚本") + return +end + + +config = readConfig() +peripherals = initPeripherals() + +-- 启动程序 +parallel.waitForAny(main, main1) \ No newline at end of file diff --git a/panel.lua b/panel.lua new file mode 100644 index 0000000..014be71 --- /dev/null +++ b/panel.lua @@ -0,0 +1,222 @@ +-- ME空间原件控制器测试客户端 +-- 在另一台计算机上运行,用于测试和控制海龟 +printUtf8 = load(http.get("https://git.liulikeji.cn/xingluo/ComputerCraft-Utf8/raw/branch/main/utf8ptrint.lua").readAll())() +basalt = require("basalt") + +-- 配置文件常量 +local CLIENT_CONFIG_FILE = "disk_client_cfg" + +-- 默认端口配置 +local DEFAULT_PORTS = { + control = 101, -- 控制信道(发送切换命令) + response = 102, -- 响应接收信道 + status = 100 -- 状态接收信道(原INVENTORY_CHANNEL) +} + +-- 读取客户端配置文件 +local function readClientConfig() + if not fs.exists(CLIENT_CONFIG_FILE) then + -- 创建默认配置文件 + local defaultConfig = { + rednet_ports = DEFAULT_PORTS + } + + local file = fs.open(CLIENT_CONFIG_FILE, "w") + file.write(textutils.serialize(defaultConfig)) + file.close() + + print("已创建客户端默认配置文件: " .. CLIENT_CONFIG_FILE) + return defaultConfig + end + + local file = fs.open(CLIENT_CONFIG_FILE, "r") + local content = file.readAll() + file.close() + + local config = textutils.unserialize(content) or {rednet_ports = DEFAULT_PORTS} + + -- 确保端口配置完整 + if not config.rednet_ports then + config.rednet_ports = DEFAULT_PORTS + else + -- 设置默认值 + config.rednet_ports.control = config.rednet_ports.control or DEFAULT_PORTS.control + config.rednet_ports.response = config.rednet_ports.response or DEFAULT_PORTS.response + config.rednet_ports.status = config.rednet_ports.status or DEFAULT_PORTS.status + end + + return config +end + +-- 读取配置 +local config = readClientConfig() + +-- 初始化红网 +local function initRednet() + local sides = {"left", "right", "front", "back", "top", "bottom"} + local modem = nil + + for _, side in ipairs(sides) do + if peripheral.getType(side) == "modem" then + modem = peripheral.wrap(side) + -- 打开所有需要的端口 + modem.open(config.rednet_ports.control) + modem.open(config.rednet_ports.response) + modem.open(config.rednet_ports.status) + print("找到网卡在: " .. side) + print("端口配置:") + print(" 控制端口: " .. config.rednet_ports.control) + print(" 响应端口: " .. config.rednet_ports.response) + print(" 状态端口: " .. config.rednet_ports.status) + break + end + end + + if not modem then + error("未找到可用的网卡!") + end + + return modem +end + +local modem = initRednet() + +-- 发送切换命令 +local function sendSwitchCommand(diskName) + local message = { + type = "switch_disk", + disk_name = diskName + } + + modem.transmit(config.rednet_ports.control, config.rednet_ports.control, message) + print("已发送切换命令: " .. diskName) +end + +disks = {} + +local mainf = basalt.createFrame() +local main = { + panel = mainf:addFrame():setPosition(1, 1):setSize("parent.w", "parent.h"):setBackground(colors.white), +} + +UI = {} + +-- 生成界面 +function addFrameS(disks) + SY = {} + local SYI = 1 + local h = 2 + for name,slot in pairs(disks) do + UI[name] = {} + UI[name]["Back"] = main["panel"]:addFrame():setPosition(1, h):setSize("parent.w", 3):setBackground(colors.gray) + UI[name]["XZ"] = UI[name]["Back"]:addPane():setPosition(1, 1):setSize(1, 3):setBackground(false, "\149", colors.gray) + UI[name]["Name"] = UI[name]["Back"]:addProgram():setPosition(2, 1):setSize(35, 4):execute(function() + term.setBackgroundColor(colors.gray) + term.clear() + printUtf8(name, colors.white, colors.gray) + end):injectEvent("char", false, "w"):disable() + SY[SYI] = name + SYI = SYI + 1 + h = h+4 + end +end + +-- 辅助函数:比较两个表是否相等 +local function areTablesEqual(t1, t2) + if t1 == t2 then return true end + if type(t1) ~= "table" or type(t2) ~= "table" then return false end + + for k, v in pairs(t1) do + if t2[k] ~= v then + return false + end + end + + for k, v in pairs(t2) do + if t1[k] == nil then + return false + end + end + + return true +end + +-- 处理红网消息 +local function handleRednetMessage(message) + if message.type == "status_update" then + -- 只有当两个表数值不一样时才执行 + if not areTablesEqual(message.disks, disks) then + disks = message.disks + addFrameS(disks) + end + + for name,slot in pairs(disks) do + if name == message.current_disk then + UI[name]["Back"]:setBackground(colors.red) + else + UI[name]["Back"]:setBackground(colors.gray) + end + end + end +end + +DSY = 1 +on = false + +-- 主循环 - 消息监听 +local function main1() + while true do + -- 检查红网消息 + local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + + -- 检查是否是状态端口或响应端口的消息 + if (channel == config.rednet_ports.status or channel == config.rednet_ports.response) and type(message) == "table" then + handleRednetMessage(message) + if not on then + if SY and SY[DSY] then + UI[SY[DSY]]["XZ"]:setBackground(false, "\149", colors.lime) + end + end + end + end +end + +local function main2() + while true do + local event, key, is_held = os.pullEvent("key") + if keys.getName(key) == "down" then + if SY and DSY ~= #SY then + DSY = DSY + 1 + end + + for k,v in pairs(SY or {}) do + if k == DSY then + UI[v]["XZ"]:setBackground(false, "\149", colors.lime) + else + UI[v]["XZ"]:setBackground(false, "\149", colors.gray) + end + end + sleep(0.5) + elseif keys.getName(key) == "up" then + if DSY > 1 then + DSY = DSY -1 + end + + for k,v in pairs(SY or {}) do + if k == DSY then + UI[v]["XZ"]:setBackground(false, "\149", colors.lime) + else + UI[v]["XZ"]:setBackground(false, "\149", colors.gray) + end + end + sleep(0.5) + elseif keys.getName(key) == "enter" then + if SY and SY[DSY] then + sendSwitchCommand(SY[DSY]) + end + end + end +end + + +parallel.waitForAny(main1, main2, basalt.autoUpdate) \ No newline at end of file diff --git a/panel1.lua b/panel1.lua new file mode 100644 index 0000000..ded3d41 --- /dev/null +++ b/panel1.lua @@ -0,0 +1,122 @@ +printUtf8 = load(http.get("https://git.liulikeji.cn/xingluo/ComputerCraft-Utf8/raw/branch/main/utf8ptrint.lua").readAll())() + +f = fs.open("config","rb") + + + + + + + +-- 简易空间控制端 +-- 功能: 通过配置文件读取参数,检测按键并发送切换命令 + +-- 配置文件名称 +local CONFIG_FILE = "config" + +-- 读取配置文件 +local function readConfig() + if not fs.exists(CONFIG_FILE) then + -- 创建默认配置文件 + local defaultConfig = { + rednet_ports = { + control = 101, -- 控制信道 + response = 102 -- 响应信道 + }, + name = "默认空间" -- 空间名称 + } + + local file = fs.open(CONFIG_FILE, "w") + file.write(textutils.serialize(defaultConfig)) + file.close() + + print("已创建默认配置文件,请编辑 " .. CONFIG_FILE) + return defaultConfig + end + + local file = fs.open(CONFIG_FILE, "r") + local content = file.readAll() + file.close() + + local config = textutils.unserialize(content) + if not config then + error("配置文件格式错误!") + end + + -- 验证必要字段 + if not config.name then + error("配置文件中缺少 'name' 字段") + end + + if not config.rednet_ports then + config.rednet_ports = { + control = 101, + response = 102 + } + else + config.rednet_ports.control = config.rednet_ports.control or 101 + config.rednet_ports.response = config.rednet_ports.response or 102 + end + + return config +end + +-- 初始化红网 +local function initRednet() + local sides = {"left", "right", "front", "back", "top", "bottom"} + local modem = nil + + for _, side in ipairs(sides) do + if peripheral.getType(side) == "modem" then + modem = peripheral.wrap(side) + print("找到网卡在: " .. side) + break + end + end + + if not modem then + error("未找到可用的网卡!") + end + + return modem +end + +-- 发送切换命令 +local function sendSwitchCommand(diskName, modem, controlPort) + local message = { + type = "switch_disk", + disk_name = diskName + } + + modem.transmit(controlPort, controlPort, message) +end + +-- 主程序 +local function main() + -- 读取配置 + local config = readConfig() + + -- 初始化红网 + local modem = initRednet() + + -- 显示提示信息 + term.clear() + term.setCursorPos(1, 1) + + printUtf8("当前空间: "..config.name,colors.white,colors.black) + printUtf8("如果你被困在此空间中,请按下任意键",colors.white,colors.black) + printUtf8("我将放出此空间",colors.white,colors.black) + + + -- 检测按键 + while true do + local event, key = os.pullEvent("key") + + -- 发送切换命令 + sendSwitchCommand(config.name, modem, config.rednet_ports.control) + end + +end + +-- 运行程序 +main() \ No newline at end of file