├── assets ├── 多联核电.jpg ├── 5联MOX核电.png └── mox99堆.jpg ├── coolantcellThread.lua ├── README.md ├── database.lua ├── config.lua ├── action.lua └── justStart.lua /assets/多联核电.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljhljll/GTNH-NuclearCooler/HEAD/assets/多联核电.jpg -------------------------------------------------------------------------------- /assets/5联MOX核电.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljhljll/GTNH-NuclearCooler/HEAD/assets/5联MOX核电.png -------------------------------------------------------------------------------- /assets/mox99堆.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljhljll/GTNH-NuclearCooler/HEAD/assets/mox99堆.jpg -------------------------------------------------------------------------------- /coolantcellThread.lua: -------------------------------------------------------------------------------- 1 | local action = require("action") 2 | local config = require("config") 3 | local component = require("component") 4 | local coroutine = require("coroutine") 5 | 6 | -- 获取电量锁存器信号 7 | local function getLatchRedstoneSingal() 8 | local latchRedstone = component.proxy(config.energyLatchRedstone) 9 | local singalTable = latchRedstone.getInput() 10 | for side, num in pairs(singalTable) do 11 | if num > 0 then 12 | return true 13 | end 14 | end 15 | return false 16 | end 17 | -- 运行核电堆 18 | local function runningReactorChamber(rc) 19 | while true do 20 | -- 是否开启耐久及物品名检测 21 | local canCheck = true 22 | -- 配置文件中如果开启全局电量检测(energyLatchRedstone==-1) 23 | -- 并且该项配置开启了电量控制(rc.energy == true) 24 | -- 则根据电量锁存器的状态开关核电 25 | if config.energyLatchRedstone ~= -1 and rc.energy then 26 | if not getLatchRedstoneSingal() then 27 | action.stopReactorChamberByRc(rc) 28 | canCheck = false 29 | end 30 | end 31 | if canCheck then 32 | local scheme = config.scheme[rc.scheme] 33 | action.checkReactorChamberDMG(rc, scheme) 34 | action.startReactorChamber(rc) 35 | end 36 | coroutine.yield() 37 | end 38 | end 39 | 40 | return { 41 | runningReactorChamber = runningReactorChamber 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GTNH-NuclearCooler 2 | 3 | 基本使用流程: 4 | 1. 准备一套OC(高级机箱、内存条、显示器、键盘、磁盘驱动器、红石IO端口2个、一些线缆、BIOS、高级处理器(cpu)、基础显卡、转运器、适配器等等 5 | 2. 装机后将lua文件放入存档中的opencomputers/{设备id}下 6 | 3. 编辑config.lua,释义已在文件中标注,使用oc中的分析器右键红石端口可以直接复制它的地址id 7 | 文件中的方向是可通过开F3等方式来获取(请以转运器为基准来区分方向) 8 | 4. 开机启动! 9 | 10 | 搭建的结构参考: 11 | 12 | ![多联核电](assets/多联核电.jpg) 13 | 14 | ![mox99%核电](assets/mox99堆.jpg) 15 | 16 | # 必看常见问题 17 | 1. 请不要使用windows记事本编辑器进行config.lua的修改,会导致程序无法使用 18 | 2. 默认的config.lua中包含了mox99%强冷堆、4连强冷铀堆的默认配置,请按照自己的组件地址进行修改 19 | 20 | 21 | - [x] 删去无用的散热代码 22 | - [x] 完成99%核电测试及处理 23 | - [x] 简化结构使其得以更少的组件管理更多的核电 24 | - [x] 自行控制反应堆是否参与电量控制 25 | - [x] 16套4连核电低tps运行30*24小时不停机稳定运行 26 | - [x] 半云使用协程优化核电堆检测逻辑 27 | - [x] 增加温控功能 28 | - [x] 优化核电状态打印 29 | - [x] 优化justStart管理协查的代码,增加协查重启机制 30 | 31 | # config.lua配置项 32 | 33 | 游戏中按F3+H开启显示物品拓展信息即可找到物品代码 34 | 35 | | 配置项名 | 取值 | 默认 | 说明 | 36 | | ------------------------ | --------- | ------------------------------ | ------------------------------------------------------------ | 37 | | `name` | `string` | 不填写则取核电仓地址值 | 用于日志打印时的核电堆名称标识 | 38 | | `scheme` | `string` | slyb | 可选`slyb`\|`mox`\|`yghhw` | 39 | | `thresholdHeat` | `number` | -1 | 开启预热功能,无需开启则填写-1或nil
示例: 99%=9900 | 40 | | `preheatItem` | `string` | gregtech:gt.reactorUraniumQuad | 用于预热的材料,对应四连铀棒的物品代码 | 41 | | `reactorChamberAddr` | `string` | | 核电仓地址(使用`调试器`shift+右键连接核电仓的`适配器`获取) | 42 | | `reactorChamberSide` | `number` | | 以`转运器`为基准,`核电仓`所对应的方向值 | 43 | | `switchRedstone` | `string` | | 控制该核电仓的`红石端口`地址 | 44 | | `reactorChamberSideToRS` | `number` | `reactorChamberSide`的取值 | 以`switchRedstone`对应的`红石端口`为基准,`核电仓`所对应的方向值 | 45 | | `transforAddr` | `string` | | 转运器地址 | 46 | | `inputSide` | `number` | | 以`转运器`为基准,输入原材料的箱子方向值 | 47 | | `outputSide` | `number` | | 以`转运器`为基准,输出低耐久冷却单元的箱子方向值 | 48 | | `changeItemOutputSide` | `number` | | 以`转运器`为基准,输出枯竭燃料棒方向值 | 49 | | `tempSide` | `number` | | 以`转运器`为基准,存放预加热的物品的箱子方向值 | 50 | | `energy` | `boolean` | nil | 是否参与电量控制
`true`:是
`nil\|false`:否 | 51 | | `aborted` | `boolean` | nil | 是否进行过热检测
`true`:是
`nil\|false`:否 | 52 | 53 | 54 | 55 | 方向取值: 56 | 57 | 底面: 0 对应方向:down、negy 58 | 59 | 顶面: 1 对应方向:up、posy 60 | 61 | 背面: 2 对应方向:north、negz 62 | 63 | 前面: 3 对应方向:south、posz、forward 64 | 65 | 右面: 4 对应方向:west、negx 66 | 67 | 左面: 5 对应方向:east、posx 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /database.lua: -------------------------------------------------------------------------------- 1 | local component = require("component") 2 | local config = require("config") 3 | 4 | -- 缓存全局变量 5 | local globalRedstoneSide = nil 6 | local globalRedstoneProxy = nil 7 | local reactorChambers = {} 8 | -- 缓存组件代理 9 | local componentCache = {} 10 | 11 | local function getGlobalRedstoneSide() 12 | return globalRedstoneSide 13 | end 14 | 15 | -- 获取组件代理(带缓存) 16 | local function getComponentProxy(address) 17 | if not componentCache[address] then 18 | componentCache[address] = component.proxy(address) 19 | end 20 | return componentCache[address] 21 | end 22 | 23 | -- 获取全局红石信号 24 | local function getGlobalRedstone() 25 | if not globalRedstoneProxy then 26 | globalRedstoneProxy = getComponentProxy(config.globalRedstone) 27 | end 28 | 29 | if getGlobalRedstoneSide() ~= nil then 30 | return globalRedstoneProxy.getInput(getGlobalRedstoneSide()) > 0 31 | end 32 | 33 | local signal = globalRedstoneProxy.getInput() 34 | for side, num in pairs(signal) do 35 | if num > 0 then 36 | globalRedstoneSide = side 37 | return true 38 | end 39 | end 40 | return false 41 | end 42 | 43 | 44 | -- 验证和规范化反应堆配置 45 | local function validateReactorConfig(config, index) 46 | local validated = { 47 | running = false, 48 | energy = config.energy ~= false, 49 | reactorChamberSideToRS = config.reactorChamberSideToRS or config.reactorChamberSide, 50 | name = config.name or config.reactorChamberAddr, 51 | aborted = false 52 | } 53 | 54 | -- 复制原始配置 55 | for k, v in pairs(config) do 56 | validated[k] = v 57 | end 58 | 59 | return validated 60 | end 61 | 62 | -- 硬件验证 63 | local function validateHardware(reactorConfig) 64 | local requiredComponents = { 65 | reactorChamberAddr = "反应堆仓", 66 | switchRedstone = "红石开关", 67 | transforAddr = "转运器" 68 | } 69 | 70 | for componentField, componentName in pairs(requiredComponents) do 71 | local address = reactorConfig[componentField] 72 | if not address or not getComponentProxy(address) then 73 | print(string.format("警告: 配置 %s 的 %s 组件无效", reactorConfig.name, componentName)) 74 | return false 75 | end 76 | end 77 | 78 | return true 79 | end 80 | 81 | local function scanAdaptor() 82 | local reactorChamberList = config.reactorChamberList 83 | print("读取到" .. #reactorChamberList .. "个核电配置") 84 | 85 | -- 清理旧数据 86 | for k in pairs(reactorChambers) do reactorChambers[k] = nil end 87 | componentCache = {} 88 | 89 | for i = 1, #reactorChamberList do 90 | local config = reactorChamberList[i] 91 | 92 | -- 验证硬件 93 | if not validateHardware(config) then 94 | print(string.format("跳过无效配置 %d: %s", i, config.name or "未命名")) 95 | goto continue 96 | end 97 | 98 | -- 验证和规范化配置 99 | reactorChambers[i] = validateReactorConfig(config, i) 100 | 101 | print(string.format("配置 %d 使用模式: %s 预热堆温: %s 电量控制: %s", 102 | i, 103 | reactorChambers[i].scheme, 104 | reactorChambers[i].thresholdHeat or "无", 105 | tostring(reactorChambers[i].energy))) 106 | 107 | ::continue:: 108 | end 109 | 110 | print(string.format("成功加载 %d 个有效反应堆配置", #reactorChambers)) 111 | end 112 | 113 | -- 清理缓存 114 | local function clearCache() 115 | componentCache = {} 116 | globalRedstoneProxy = nil 117 | globalRedstoneSide = nil 118 | end 119 | 120 | -- 获取反应堆状态统计 121 | local function getReactorStats() 122 | local stats = { 123 | total = #reactorChambers, 124 | running = 0, 125 | aborted = 0, 126 | withEnergyControl = 0 127 | } 128 | 129 | for _, reactor in ipairs(reactorChambers) do 130 | -- 正在运行的数量 131 | if reactor.running then 132 | stats.running = stats.running + 1 133 | end 134 | -- 因为温度过热而停机的数量 135 | if reactor.aborted then 136 | stats.aborted = stats.aborted + 1 137 | end 138 | -- 开启了电量控制的数量 139 | if reactor.energy then 140 | stats.withEnergyControl = stats.withEnergyControl + 1 141 | end 142 | end 143 | 144 | return stats 145 | end 146 | 147 | return { 148 | scanAdaptor = scanAdaptor, 149 | reactorChambers = reactorChambers, 150 | getGlobalRedstone = getGlobalRedstone, 151 | getComponentProxy = getComponentProxy, 152 | clearCache = clearCache, 153 | getReactorStats = getReactorStats 154 | } 155 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | -- 核电模式 3 | scheme = { 4 | -- 模式名(slyb) 5 | slyb = { 6 | -- 所使用到的资源 7 | resource = { 8 | { 9 | name = "gregtech:gt.360k_Helium_Coolantcell", 10 | changeName = -1, 11 | dmg = 90, 12 | count = 14, 13 | slot = { 3, 6, 9, 10, 15, 22, 26, 29, 33, 40, 45, 46, 49, 52 } 14 | }, 15 | { 16 | -- name = "gregtech:gt.reactorUraniumQuad", 17 | name = "gregtech:gt.rodUranium4", 18 | -- changeName = "IC2:reactorUraniumQuaddepleted", 19 | changeName = "gregtech:gt.depletedRodUranium4", 20 | dmg = -1, 21 | count = 40, 22 | slot = { 23 | 1, 2, 4, 5, 7, 8, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 36, 37, 24 | 38, 39, 41, 42, 43, 44, 47, 48, 50, 51, 53, 54 } 25 | } 26 | } 27 | }, 28 | mox = { 29 | resource = { 30 | { 31 | name = "gregtech:gt.360k_Helium_Coolantcell", 32 | changeName = -1, 33 | dmg = 90, 34 | count = 14, 35 | slot = { 3, 6, 9, 10, 15, 22, 26, 29, 33, 40, 45, 46, 49, 52 } 36 | }, 37 | { 38 | name = "gregtech:gt.reactorMOXQuad", 39 | changeName = "IC2:reactorMOXQuaddepleted", 40 | dmg = -1, 41 | count = 40, 42 | slot = { 43 | 1, 2, 4, 5, 7, 8, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 36, 37, 44 | 38, 39, 41, 42, 43, 44, 47, 48, 50, 51, 53, 54 } 45 | } 46 | } 47 | }, 48 | yghhw = { 49 | resource = { 50 | { 51 | name = "gregtech:gt.360k_Helium_Coolantcell", 52 | changeName = -1, 53 | dmg = 90, 54 | count = 9, 55 | slot = { 5, 9, 11, 25, 31, 37, 45, 47, 51 } 56 | }, 57 | { 58 | name = "gregtech:gt.Quad_Thoriumcell", 59 | changeName = "gregtech:gt.Quad_ThoriumcellDep", 60 | dmg = -1, 61 | count = 26, 62 | slot = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54 } 63 | }, 64 | { 65 | name = "gregtech:gt.glowstoneCell", 66 | changeName = "gregtech:gt.sunnariumCell", 67 | dmg = -1, 68 | count = 18, 69 | slot = { 1, 3, 7, 13, 15, 17, 19, 21, 23, 27, 29, 33, 35, 39, 41, 43, 49, 53 } 70 | } 71 | } 72 | } 73 | 74 | }, 75 | -- 是否开启控制台日志清理 76 | clearLog = true, 77 | -- 每n秒清理一次控制台日志 78 | cleatLogInterval = 30, 79 | --[[ 80 | 是否开启电量控制(提供一个红石端口,向该端口开启红石信号后开启核电) 81 | 填入电量控制红石端口的地址,默认不启用(-1) 82 | --]] 83 | energyLatchRedstone = "535435c8-1019-440a-8b22-b8bc1b6cf9a5", 84 | -- 全局红石开关地址(必填) 85 | globalRedstone = "00b0cb29-bc9a-488a-8605-22a31028ed95", 86 | --[[ 87 | 核电堆配置,可同时配置多台核电并在启动时自己选择开启哪几台,例如下边有2个核电配置,输入1 2即可开两台,只输入2则只启动第二个配置的核电 88 | 方向取值可开启f3来看当前的朝向: 89 | 底面: 0 对应方向:down、negy 90 | 顶面: 1 对应方向:up、posy 91 | 背面: 2 对应方向:north、negz 92 | 前面: 3 对应方向:south、posz、forward 93 | 右面: 4 对应方向:west、negx 94 | 左面: 5 对应方向:east、posx 95 | --]] 96 | reactorChamberList = { 97 | { 98 | name = "上堆", -- 核电堆命名(用于日志打印显示对应的名称) 99 | scheme = "slyb", -- 模式 100 | thresholdHeat = -1, -- 预热堆温 101 | preheatItem = "gregtech:gt.reactorUraniumQuad", -- 预热燃料 102 | reactorChamberAddr = "e044a135-4d27-4403-902e-b0d8d66b1c53", -- 核电仓地址 103 | reactorChamberSide = 1, -- 核电仓方向 104 | switchRedstone = "49b24209-57c8-445d-8e9e-473fec29f3de", -- 开关核电的红石端口地址 105 | reactorChamberSideToRS = 1, -- 核电仓相对于红石信号器的方向 旧版本更新的话默认填写nil即可会取{reactorChamberSide}的方向 106 | transforAddr = "4a9956c3-7f70-4b30-a0c8-dd6e25affabd", -- 转运器地址 107 | inputSide = 3, -- 输入原材料的箱子位置 108 | outputSide = 3, -- 输出低耐久冷却单元的箱子位置 109 | changeItemOutputSide = 3, -- 输出枯竭燃料棒 110 | tempSide = 3, -- 堆加热的东西的箱子位置 111 | energy = nil -- 是否参与电量控制,默认不填写或者为nil代表参与电量控制,只有在缺电时才会开启,设置为false则无视电量持续监测运行 112 | }, 113 | { 114 | name = "右堆", -- 核电堆命名(用于日志打印显示对应的名称) 115 | scheme = "slyb", -- 模式 116 | thresholdHeat = -1, -- 预热堆温 117 | preheatItem = "gregtech:gt.reactorUraniumQuad", -- 预热燃料 118 | reactorChamberAddr = "3ba763f9-5c55-4be9-8ccc-5f321f702b17", -- 核电仓地址 119 | reactorChamberSide = 4, -- 核电仓方向 120 | switchRedstone = "49b24209-57c8-445d-8e9e-473fec29f3de", -- 开关核电的红石端口地址 121 | reactorChamberSideToRS = 4, -- 核电仓相对于红石信号器的方向 旧版本更新的话默认填写nil即可会取{reactorChamberSide}的方向 122 | transforAddr = "4a9956c3-7f70-4b30-a0c8-dd6e25affabd", -- 转运器地址 123 | inputSide = 3, -- 输入原材料的箱子位置 124 | outputSide = 3, -- 输出低耐久冷却单元的箱子位置 125 | changeItemOutputSide = 3, -- 输出枯竭燃料棒 126 | tempSide = 3, -- 堆加热的东西的箱子位置 127 | energy = nil -- 是否参与电量控制,默认不填写或者为nil代表参与电量控制,只有在缺电时才会开启,设置为false则无视电量持续监测运行 128 | }, 129 | { 130 | name = "下堆", -- 核电堆命名(用于日志打印显示对应的名称) 131 | scheme = "slyb", -- 模式 132 | thresholdHeat = -1, -- 预热堆温 133 | preheatItem = "gregtech:gt.reactorUraniumQuad", -- 预热燃料 134 | reactorChamberAddr = "3a2fa58e-b862-4df5-ae99-b67e13fea876", -- 核电仓地址 135 | reactorChamberSide = 0, -- 核电仓方向 136 | switchRedstone = "49b24209-57c8-445d-8e9e-473fec29f3de", -- 开关核电的红石端口地址 137 | reactorChamberSideToRS = 0, -- 核电仓相对于红石信号器的方向 旧版本更新的话默认填写nil即可会取{reactorChamberSide}的方向 138 | transforAddr = "4a9956c3-7f70-4b30-a0c8-dd6e25affabd", -- 转运器地址 139 | inputSide = 3, -- 输入原材料的箱子位置 140 | outputSide = 3, -- 输出低耐久冷却单元的箱子位置 141 | changeItemOutputSide = 3, -- 输出枯竭燃料棒 142 | tempSide = 3, -- 堆加热的东西的箱子位置 143 | energy = nil -- 是否参与电量控制,默认不填写或者为nil代表参与电量控制,只有在缺电时才会开启,设置为false则无视电量持续监测运行 144 | }, 145 | { 146 | name = "左堆", -- 核电堆命名(用于日志打印显示对应的名称) 147 | scheme = "slyb", -- 模式 148 | thresholdHeat = -1, -- 预热堆温 149 | preheatItem = "gregtech:gt.reactorUraniumQuad", -- 预热燃料 150 | reactorChamberAddr = "9b49cef2-44e8-42b8-8ac8-e8e91223de01", -- 核电仓地址 151 | reactorChamberSide = 5, -- 核电仓方向 152 | switchRedstone = "49b24209-57c8-445d-8e9e-473fec29f3de", -- 开关核电的红石端口地址 153 | reactorChamberSideToRS = 5, -- 核电仓相对于红石信号器的方向 旧版本更新的话默认填写nil即可会取{reactorChamberSide}的方向 154 | transforAddr = "4a9956c3-7f70-4b30-a0c8-dd6e25affabd", -- 转运器地址 155 | inputSide = 3, -- 输入原材料的箱子位置 156 | outputSide = 3, -- 输出低耐久冷却单元的箱子位置 157 | changeItemOutputSide = 3, -- 输出枯竭燃料棒 158 | tempSide = 3, -- 堆加热的东西的箱子位置 159 | energy = nil -- 是否参与电量控制,默认不填写或者为nil代表参与电量控制,只有在缺电时才会开启,设置为false则无视电量持续监测运行 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /action.lua: -------------------------------------------------------------------------------- 1 | local component = require("component") 2 | local database = require("database") 3 | local config = require("config") 4 | local coroutine = require("coroutine") 5 | local computer = require("computer") 6 | 7 | local function coroutineSleep(time) 8 | local DDL = computer.uptime() + time 9 | while computer.uptime() < DDL do 10 | coroutine.yield() 11 | end 12 | end 13 | 14 | local function checkItemCount(runningTable) 15 | for i = 1, #runningTable, 1 do 16 | local rc = database.reactorChambers[runningTable[i]] 17 | local inputBox = component.proxy(rc.transforAddr).getAllStacks(rc.inputSide).getAll() 18 | local resource = config.scheme[rc.scheme].resource 19 | for j = 1, #resource, 1 do 20 | local num = 0 21 | for index, item in pairs(inputBox) do 22 | if item.name and item.name == resource[j].name then 23 | num = num + item.size 24 | end 25 | if num >= resource[j].count then break end 26 | end 27 | if num < resource[j].count then 28 | print(rc.name .. "所需的材料:" .. resource[j].name .. "小于" .. resource[j].count) 29 | os.exit(0) 30 | end 31 | end 32 | end 33 | end 34 | 35 | local function stopReactorChamberByRc(rc, isBlock) 36 | local redstone = component.proxy(rc.switchRedstone) 37 | if redstone.getOutput(rc.reactorChamberSideToRS) == 0 then 38 | return 39 | end 40 | rc.running = false 41 | -- setOutput为非直接调用,其正确输出对应的红石信号需要至少1tick时间 42 | redstone.setOutput(rc.reactorChamberSideToRS, 0) 43 | if isBlock then 44 | repeat 45 | coroutineSleep(0.5) 46 | local singal = redstone.getOutput(rc.reactorChamberSideToRS) 47 | until (singal == 0) 48 | end 49 | print(rc.name .. " is shutdown") 50 | end 51 | 52 | local function stopAllReactorChamber(isBlock) 53 | for i = 1, #database.reactorChambers, 1 do 54 | stopReactorChamberByRc(database.reactorChambers[i], isBlock) 55 | end 56 | end 57 | 58 | local function remove(transforAddr, sourceSide, slot, outpuSide) 59 | local transposer = component.proxy(transforAddr) 60 | repeat 61 | local removeCount = transposer.transferItem(sourceSide, outpuSide, 1, slot) 62 | if removeCount == 0 then 63 | print("箱子已满,无法输出物品") 64 | end 65 | coroutine.yield() 66 | until (removeCount > 0) 67 | end 68 | 69 | local function insert(transforAddr, sourceSide, targetSlot, outputSide, name, dmg) 70 | local transposer = component.proxy(transforAddr) 71 | while true do 72 | local sourceBox = transposer.getAllStacks(sourceSide).getAll() 73 | for index, item in pairs(sourceBox) do 74 | if item.name == name and (dmg == -1 or item.damage < dmg) then 75 | local insertCount = transposer.transferItem(sourceSide, outputSide, 1, index + 1, targetSlot) 76 | if insertCount > 0 then 77 | return 78 | end 79 | end 80 | end 81 | sourceBox = nil 82 | print("材料箱未找到物品:" .. name) 83 | coroutine.yield() 84 | end 85 | end 86 | 87 | local function startReactorChamber(rc, isBlock) 88 | if isBlock == nil then 89 | isBlock = true 90 | end 91 | 92 | local rcRedstone = component.proxy(rc.switchRedstone) 93 | if rcRedstone.getOutput(rc.reactorChamberSideToRS) > 0 then 94 | return 95 | end 96 | if rc.aborted then 97 | print(string.format( 98 | "%s was over-heated, it cannot start. You can manually cooldown it and then restart the program.", rc.name)) 99 | return 100 | end 101 | rc.running = true 102 | rcRedstone.setOutput(rc.reactorChamberSideToRS, 15) 103 | 104 | if isBlock then 105 | repeat 106 | coroutine.yield() 107 | local singal = rcRedstone.getOutput(rc.reactorChamberSideToRS) 108 | until (singal > 0) 109 | end 110 | 111 | print(rc.name .. " is running") 112 | end 113 | 114 | local function preheatInsert(transforAddr, sourceSide, targetSlot, outputSide, name, dmg) 115 | local transposer = component.proxy(transforAddr) 116 | while true do 117 | local sourceBox = transposer.getAllStacks(sourceSide).getAll() 118 | for index, item in pairs(sourceBox) do 119 | if item.name == name and (dmg == -1 or item.damage < dmg) then 120 | local insertCount = transposer.transferItem(sourceSide, outputSide, 1, index + 1, targetSlot) 121 | if insertCount > 0 then 122 | return 123 | end 124 | end 125 | end 126 | sourceBox = nil 127 | print("材料箱未找到物品:" .. name) 128 | os.sleep(0) 129 | end 130 | end 131 | 132 | local function preheatRemove(transforAddr, sourceSide, slot, outpuSide) 133 | local transposer = component.proxy(transforAddr) 134 | repeat 135 | local removeCount = transposer.transferItem(sourceSide, outpuSide, 1, slot) 136 | if removeCount == 0 then 137 | print("箱子已满,无法输出物品") 138 | end 139 | os.sleep(0) 140 | until (removeCount > 0) 141 | end 142 | 143 | local function preheatRc(rc) 144 | local rcComponent = component.proxy(rc.reactorChamberAddr) 145 | if rcComponent.getHeat() >= rc.thresholdHeat then return true end 146 | preheatInsert(rc.transforAddr, rc.tempSide, 1, rc.reactorChamberSide, rc.preheatItem, -1) 147 | startReactorChamber(rc, false) 148 | repeat 149 | local heat = rcComponent.getHeat() 150 | if not database.getGlobalRedstone() then 151 | break 152 | end 153 | until (heat >= rc.thresholdHeat) 154 | stopReactorChamberByRc(rc, false) 155 | preheatRemove(rc.transforAddr, rc.reactorChamberSide, 1, rc.tempSide) 156 | end 157 | 158 | -- 向核电仓中转移原材料 159 | local function insertItemsIntoReactorChamber(runningTable) 160 | checkItemCount(runningTable) 161 | for k = 1, #runningTable, 1 do -- 原先这里是for i = 1, #runningTable, 1 do,内促内循环还有一个循环变量i,容易出问题 162 | local rc = database.reactorChambers[runningTable[k]] 163 | local transposer = component.proxy(rc.transforAddr) 164 | local sourceBoxitemList = transposer.getAllStacks(rc.inputSide).getAll() 165 | local resource = config.scheme[rc.scheme].resource 166 | 167 | if rc.thresholdHeat ~= -1 then 168 | preheatRc(rc) 169 | if not database.getGlobalRedstone() then 170 | print("终止初始化") 171 | os.exit(0) 172 | end 173 | print(rc.name .. " 预热完成") 174 | end 175 | 176 | for i = 1, #resource do 177 | local nowIndex = 0 178 | -- pairs性能比#table性能差,由于此处需要多层循环遍历,故使用numeric for 179 | for j = 1, #resource[i].slot, 1 do 180 | while nowIndex < #sourceBoxitemList do 181 | local item = sourceBoxitemList[nowIndex] 182 | if item.name == resource[i].name then 183 | transposer.transferItem(rc.inputSide, rc.reactorChamberSide, 1, nowIndex + 1, resource[i].slot 184 | [j]) 185 | if transposer.getSlotStackSize(rc.inputSide, nowIndex + 1) == 0 then 186 | nowIndex = nowIndex + 1 187 | end 188 | break 189 | end 190 | nowIndex = nowIndex + 1 191 | end 192 | end 193 | end 194 | print(string.format("完成了对核反应堆 %s 的初次材料转移", rc.name)) 195 | end 196 | end 197 | 198 | ---移出{transforAddr}在{rcSide}方向上的第{targetSlot}的物品到{outputSide}容器中,并将耐久阈值小于{dmg}的{itemName}放入{targetSlot} 199 | ---@param transforAddr string 200 | ---@param sourceSide integer 201 | ---@param targetSlot integer 202 | ---@param outputSide integer 203 | ---@param itemName string 204 | ---@param dmg integer 205 | local function removeAndInsert(transforAddr, rcSide, sourceSide, targetSlot, outputSide, itemName, dmg) 206 | remove(transforAddr, rcSide, targetSlot, outputSide) 207 | insert(transforAddr, sourceSide, targetSlot, rcSide, itemName, dmg) 208 | end 209 | 210 | local function checkItemChangeName(cfgResource, rc) 211 | local transposer = component.proxy(rc.transforAddr) 212 | local rcBox = transposer.getAllStacks(rc.reactorChamberSide).getAll() 213 | for i = 1, #cfgResource.slot, 1 do 214 | local boxSlot = cfgResource.slot[i] 215 | -- 名称已变化 216 | if rcBox[boxSlot - 1].name ~= cfgResource.name 217 | and rcBox[boxSlot - 1].name == cfgResource.changeName then 218 | stopReactorChamberByRc(rc, true) 219 | removeAndInsert(rc.transforAddr, 220 | rc.reactorChamberSide, 221 | rc.inputSide, 222 | boxSlot, 223 | rc.changeItemOutputSide, 224 | cfgResource.name, -1) 225 | goto continue 226 | end 227 | -- 是否为空位 228 | if rcBox[boxSlot - 1].name == nil then 229 | stopReactorChamberByRc(rc, true) 230 | insert(rc.transforAddr, rc.inputSide, boxSlot, rc.reactorChamberSide, cfgResource.name, -1) 231 | end 232 | ::continue:: 233 | end 234 | end 235 | 236 | local function checkItemDmg(cfgResource, rc) 237 | local transposer = component.proxy(rc.transforAddr) 238 | local rcBox = transposer.getAllStacks(rc.reactorChamberSide).getAll() 239 | for i = 1, #cfgResource.slot, 1 do 240 | local boxSlot = cfgResource.slot[i] 241 | -- 耐久是否达到阈值 242 | if rcBox[boxSlot - 1].damage ~= nil then 243 | if rcBox[boxSlot - 1].damage >= cfgResource.dmg then 244 | stopReactorChamberByRc(rc, true) 245 | removeAndInsert(rc.transforAddr, rc.reactorChamberSide, rc.inputSide, boxSlot, rc.outputSide, 246 | cfgResource.name, cfgResource.dmg) 247 | goto continue 248 | end 249 | end 250 | -- 是否为空位 251 | if rcBox[boxSlot - 1].damage == nil then 252 | stopReactorChamberByRc(rc, true) 253 | insert(rc.transforAddr, rc.inputSide, boxSlot, rc.reactorChamberSide, cfgResource.name, -1) 254 | end 255 | ::continue:: 256 | end 257 | end 258 | 259 | local function checkReactorChamberDMG(rc, scheme) 260 | for i = 1, #scheme.resource do 261 | -- 检测耐久 262 | if scheme.resource[i].dmg ~= -1 then 263 | checkItemDmg(scheme.resource[i], rc) 264 | goto continue 265 | end 266 | -- 检测替换物 267 | if scheme.resource[i].changeName ~= -1 then 268 | checkItemChangeName(scheme.resource[i], rc) 269 | end 270 | ::continue:: 271 | end 272 | end 273 | 274 | return { 275 | coroutineSleep = coroutineSleep, 276 | checkItemCount = checkItemCount, 277 | insertItemsIntoReactorChamber = insertItemsIntoReactorChamber, 278 | stopAllReactorChamber = stopAllReactorChamber, 279 | checkReactorChamberDMG = checkReactorChamberDMG, 280 | startReactorChamber = startReactorChamber, 281 | stopReactorChamberByRc = stopReactorChamberByRc, 282 | preheatRc = preheatRc 283 | } 284 | -------------------------------------------------------------------------------- /justStart.lua: -------------------------------------------------------------------------------- 1 | local database = require("database") 2 | local action = require("action") 3 | local detection = require("coolantcellThread") 4 | local config = require("config") 5 | local coroutine = require("coroutine") 6 | local computer = require("computer") 7 | local component = require("component") 8 | 9 | -- 协程类型定义 10 | local COROUTINE_TYPES = { 11 | REACTOR = 0, 12 | LOGGER = 1, 13 | HEAT_MONITOR = 2 14 | } 15 | 16 | local function printResidentMessages() 17 | local time = computer.uptime() 18 | local diffSeconds = math.floor(time - database.startTimeStamp) 19 | local days = math.floor(diffSeconds / 86400) 20 | local hours = math.floor((diffSeconds % 86400) / 3600) 21 | local minutes = math.floor((diffSeconds % 3600) / 60) 22 | print(string.format("本核电站已安全运行 %d 天 %d 时 %d 分。道路千万条,安全第一条;核电不规范,回档两行泪。", days, hours, minutes)) 23 | end 24 | 25 | local function printOverHeated(rcTable) 26 | local outputLines = {} 27 | for i = 1, #rcTable do 28 | local rc = database.reactorChambers[rcTable[i]] 29 | if not rc then goto continue end 30 | 31 | local rcComponent = component.proxy(rc.reactorChamberAddr) 32 | if not rcComponent then goto continue end 33 | 34 | local heat = rcComponent.getHeat() 35 | if rc.aborted then 36 | table.insert(outputLines, 37 | string.format("The heat of %s is %d K, it is aborted due to over-heated", rc.name, heat)) 38 | else 39 | table.insert(outputLines, string.format("The heat of %s is %d K", rc.name, heat)) 40 | end 41 | ::continue:: 42 | end 43 | 44 | -- 一次性输出所有行,减少I/O操作 45 | for _, line in ipairs(outputLines) do 46 | print(line) 47 | end 48 | outputLines = nil -- 清理内存 49 | end 50 | 51 | -- 温度监控器,过热强制关机避免爆炸 52 | local function heatMonitor(rcTable) 53 | local checkInterval = 0.1 -- 检查间隔 54 | local lastCheck = 0 55 | 56 | while true do 57 | local currentTime = computer.uptime() 58 | if currentTime - lastCheck >= checkInterval then 59 | for i = 1, #rcTable do 60 | local rc = database.reactorChambers[rcTable[i]] 61 | if not rc or rc.scheme == "mox" or not rc.aborted then 62 | goto continue_reactor 63 | end 64 | 65 | local rcComponent = component.proxy(rc.reactorChamberAddr) 66 | if not rcComponent then goto continue_reactor end 67 | 68 | local heat = rcComponent.getHeat() 69 | local threshold = rc.thresholdHeat or 0 70 | 71 | if heat >= threshold + 100 or heat >= 9960 then 72 | print(string.format("警告: %s 温度过高 (%d K),执行紧急停堆!", rc.name, heat)) 73 | rc.aborted = true 74 | action.stopReactorChamberByRc(rc, false) 75 | end 76 | ::continue_reactor:: 77 | end 78 | lastCheck = currentTime 79 | end 80 | coroutine.yield() 81 | end 82 | end 83 | 84 | -- 清理控制台打印信息,防止内存溢出 85 | local function clearAndIntervalMessages(rcTable) 86 | local clearLogInterval = math.max(config.cleatLogInterval or 30, 5) 87 | 88 | while true do 89 | action.coroutineSleep(clearLogInterval) 90 | os.execute("cls") 91 | 92 | -- 批量处理输出,减少协程yield 93 | printResidentMessages() 94 | printOverHeated(rcTable) 95 | print(string.format("下一次清屏计划在 %d 秒后", clearLogInterval)) 96 | 97 | -- 只在最后yield一次 98 | coroutine.yield() 99 | end 100 | end 101 | 102 | -- 协程管理器 103 | local CoroutineManager = { 104 | creators = { 105 | [COROUTINE_TYPES.REACTOR] = function(rc) 106 | return function() detection.runningReactorChamber(rc) end 107 | end, 108 | [COROUTINE_TYPES.LOGGER] = function(rcTable) 109 | return function() clearAndIntervalMessages(rcTable) end 110 | end, 111 | [COROUTINE_TYPES.HEAT_MONITOR] = function(rcTable) 112 | return function() heatMonitor(rcTable) end 113 | end 114 | } 115 | } 116 | 117 | function CoroutineManager.restartCoroutine(coroutineData, rcTable) 118 | local creator = CoroutineManager.creators[coroutineData.type] 119 | if not creator then return nil end 120 | 121 | local newCoro = coroutine.create(creator(rcTable[coroutineData.index] or rcTable)) 122 | return newCoro 123 | end 124 | 125 | -- 处理协程错误并重启 126 | local function handleCoroutineError(coroutineData, rcTable) 127 | print(string.format("协程 %d (类型: %d) 发生错误,正在重启...", coroutineData.index, coroutineData.type)) 128 | 129 | -- 关闭旧协程 130 | if coroutine.status(coroutineData.coroutine) ~= "dead" then 131 | coroutineData.coroutine = nil 132 | end 133 | 134 | -- 创建新协程 135 | local newCoro = CoroutineManager.restartCoroutine(coroutineData, rcTable) 136 | if newCoro then 137 | coroutineData.coroutine = newCoro 138 | local success, err = coroutine.resume(newCoro) 139 | if not success then 140 | print(string.format("协程重启失败: %s", err)) 141 | return false 142 | end 143 | return true 144 | else 145 | print(string.format("无法重启协程: 未知的协程类型 %d", coroutineData.type)) 146 | return false 147 | end 148 | end 149 | 150 | -- 主协程调度器 151 | local function runCoroutineScheduler(coroutines, rcTable) 152 | local running = true 153 | 154 | while running do 155 | -- 检查全局开关 156 | if not database.getGlobalRedstone() then 157 | print("检测到全局开关关闭,准备安全停机...") 158 | running = false 159 | break 160 | end 161 | 162 | -- 调度所有协程 163 | for i = 1, #coroutines do 164 | local coroData = coroutines[i] 165 | if coroutine.status(coroData.coroutine) ~= "dead" then 166 | local success, err = coroutine.resume(coroData.coroutine) 167 | if not success then 168 | handleCoroutineError(coroData, rcTable) 169 | end 170 | end 171 | end 172 | 173 | os.sleep(0) -- 最高效的调度间隔 174 | end 175 | 176 | return running 177 | end 178 | 179 | -- 安全停机流程 180 | local function emergencyShutdown(rcTable) 181 | print("开始执行紧急停机程序...") 182 | 183 | local shutdownCoroutines = {} 184 | for i = 1, #rcTable do 185 | shutdownCoroutines[i] = coroutine.create(function() 186 | local rc = database.reactorChambers[rcTable[i]] 187 | if rc then 188 | action.stopReactorChamberByRc(rc, true) 189 | end 190 | end) 191 | coroutine.resume(shutdownCoroutines[i]) 192 | end 193 | 194 | -- 等待所有反应堆停机 195 | local shutdownTimeout = computer.uptime() + 30 -- 30秒超时 196 | while computer.uptime() < shutdownTimeout do 197 | local stoppedCount = 0 198 | for i = 1, #shutdownCoroutines do 199 | local status = coroutine.status(shutdownCoroutines[i]) 200 | if status == "dead" then 201 | stoppedCount = stoppedCount + 1 202 | else 203 | coroutine.resume(shutdownCoroutines[i]) 204 | end 205 | end 206 | 207 | if stoppedCount == #rcTable then 208 | print("所有反应堆已安全停机") 209 | return true 210 | end 211 | 212 | os.sleep(0.1) 213 | end 214 | 215 | print("警告:停机超时,部分反应堆可能未完全关闭") 216 | return false 217 | end 218 | 219 | local function reactorChamberStart(rcTable) 220 | os.execute("cls") 221 | print(string.format("启动 %d 个核反应堆控制程序...", #rcTable)) 222 | 223 | local coroutines = {} 224 | 225 | -- 创建反应堆监控协程 226 | for i = 1, #rcTable do 227 | local rc = database.reactorChambers[rcTable[i]] 228 | coroutines[i] = { 229 | coroutine = coroutine.create(function() detection.runningReactorChamber(rc) end), 230 | type = COROUTINE_TYPES.REACTOR, 231 | index = i 232 | } 233 | end 234 | 235 | -- 创建日志协程 236 | coroutines[#coroutines + 1] = { 237 | coroutine = coroutine.create(function() clearAndIntervalMessages(rcTable) end), 238 | type = COROUTINE_TYPES.LOGGER, 239 | index = #coroutines + 1 240 | } 241 | 242 | -- 创建温度监控协程 243 | coroutines[#coroutines + 1] = { 244 | coroutine = coroutine.create(function() heatMonitor(rcTable) end), 245 | type = COROUTINE_TYPES.HEAT_MONITOR, 246 | index = #coroutines + 1 247 | } 248 | 249 | -- 运行主调度器 250 | local running = runCoroutineScheduler(coroutines, rcTable) 251 | 252 | -- 清理协程资源 253 | for _, coroData in ipairs(coroutines) do 254 | if coroutine.status(coroData.coroutine) ~= "dead" then 255 | coroData.coroutine = nil 256 | end 257 | end 258 | coroutines = nil 259 | 260 | -- 执行停机 261 | if not running then 262 | emergencyShutdown(rcTable) 263 | end 264 | 265 | print("核反应堆控制系统已关闭") 266 | end 267 | 268 | -- 验证用户输入的反应堆配置 269 | local function validateReactorConfig(choices) 270 | local validChoices = {} 271 | local maxConfig = #config.reactorChamberList 272 | 273 | for _, choice in ipairs(choices) do 274 | if choice >= 1 and choice <= maxConfig then 275 | table.insert(validChoices, choice) 276 | else 277 | print(string.format("警告: 配置 %d 无效,跳过 (有效范围: 1-%d)", choice, maxConfig)) 278 | end 279 | end 280 | 281 | if #validChoices == 0 then 282 | print("错误: 没有有效的反应堆配置") 283 | return nil 284 | end 285 | 286 | return validChoices 287 | end 288 | -- 主入口 289 | local function justStart() 290 | print("=== GTNH 核反应堆控制系统 ===") 291 | print("(0) 直接启动 (1) 配置启动 (-1) 退出") 292 | 293 | local model = io.read() 294 | if model == "-1" then 295 | print("退出系统") 296 | return 297 | end 298 | 299 | if model ~= "0" and model ~= "1" then 300 | print("无效选择,退出系统") 301 | return 302 | end 303 | 304 | print("请输入要启动的反应堆配置编号 (1-" .. #config.reactorChamberList .. "),用空格分隔:") 305 | local input = io.read() 306 | 307 | -- 解析用户输入 308 | local choices = {} 309 | for num in input:gmatch("%d+") do 310 | table.insert(choices, tonumber(num)) 311 | end 312 | 313 | -- 验证配置 314 | local runningTable = validateReactorConfig(choices) 315 | if not runningTable then 316 | return 317 | end 318 | 319 | print(string.format("准备启动以下反应堆: %s", table.concat(runningTable, ", "))) 320 | 321 | database.startTimeStamp = computer.uptime() 322 | 323 | if model == "1" then 324 | print("执行初始材料装载...") 325 | local success, err = pcall(action.insertItemsIntoReactorChamber, runningTable) 326 | if not success then 327 | print(string.format("材料装载失败: %s", err)) 328 | return 329 | end 330 | print("材料装载完成") 331 | end 332 | 333 | reactorChamberStart(runningTable) 334 | end 335 | 336 | -- 系统初始化和安全检查 337 | local function systemInit() 338 | print("系统初始化中...") 339 | 340 | -- 检查全局开关状态 341 | if not database.getGlobalRedstone() then 342 | print("错误: 未开启全局安全开关") 343 | action.stopAllReactorChamber(false) 344 | os.exit(1) 345 | end 346 | 347 | -- 扫描硬件适配器 348 | local success, err = pcall(database.scanAdaptor) 349 | if not success then 350 | print(string.format("硬件扫描失败: %s", err)) 351 | os.exit(1) 352 | end 353 | 354 | print("系统初始化完成") 355 | print(string.format("发现 %d 个反应堆配置", #database.reactorChambers)) 356 | end 357 | 358 | -- 主程序入口 359 | local function main() 360 | -- 系统初始化 361 | systemInit() 362 | 363 | -- 确保所有反应堆处于关闭状态 364 | action.stopAllReactorChamber(false) 365 | 366 | -- 启动用户界面 367 | justStart() 368 | end 369 | 370 | -- 启动主程序 371 | main() 372 | --------------------------------------------------------------------------------