View difference between Paste ID: n4QW8bfr and gRxBRrAD
SHOW: | | - or go back to the newest paste.
1-
-- Adaptive Learning Turbine Controller (CC:Tweaked)
1+
local args = {...};
2-
-- Nominal flow iteratively learns from every cycle, not just stable ones.
2+
3
local screen = "monitor_16"
4-
local reactorName       = "BigReactors-Reactor_0"
4+
local RsInSide = "right"
5-
local turbineName       = "BigReactors-Turbine_0"
5+
local RsOutSide = "top"
6-
local monitorName       = "monitor_0"
6+
7-
local FLOW_PERSIST_FILE = "nominal_flow.txt"
7+
local Nreactor1="BigReactors-Reactor_0"
8
local Nreactor1 = "BigReactors-Reactor_0"
9-
local RPM_TARGET, RPM_TOLERANCE = 1800, 5
9+
local Nreactor2 = "BigReactors-Reactor_2"
10-
local FLOW_TUNE_LOWER, FLOW_TUNE_UPPER = 100, 2000
10+
local Nreactor3 = "BigReactors-Reactor_3"
11-
local MAX_TUNE_ITER = 10
11+
local Nreactor4 = "BigReactors-Reactor_4"
12-
local BTN_X, BTN_Y, BTN_W, BTN_H = 120, 2, 7, 3
12+
local Nreactor5 = "BigReactors-Reactor_5"
13-
local CTRLBAR_X = 126
13+
local Nreactor6 = "BigReactors-Reactor_6"
14-
local RPMBAR_X, RPMBAR_Y = 2, 11
14+
local Nreactor7 = "BigReactors-Reactor_7"
15-
local RPMBAR_W = 100
15+
local Nreactor8 = "BigReactors-Reactor_8"
16
17-
local FLOW_BUFFER      = 0.10   -- 10% buffer above nominal during acceleration
17+
local Nturbine1 = "BigReactors-Turbine_0"
18-
local DECEL_FRACTION   = 0.25   -- 25% of nominal during deceleration
18+
local Nturbine2 = "BigReactors-Turbine_1"
19-
local FLOW_LEARN_STEP  = 2      -- increment/decrement per tick during learning
19+
local Nturbine3 = "BigReactors-Turbine_2"
20
local Nturbine4 = "BigReactors-Turbine_3"
21-
-- Peripheral checks
21+
local Nturbine5 = "BigReactors-Turbine_4"
22-
local reactor = peripheral.wrap(reactorName)
22+
local Nturbine6 = "BigReactors-Turbine_5"
23-
if not reactor then error("Reactor '"..reactorName.."' not found") end
23+
local Nturbine7 = "BigReactors-Turbine_6"
24-
local turbine = peripheral.wrap(turbineName)
24+
local Nturbine8 = "BigReactors-Turbine_7"
25-
if not turbine then error("Turbine '"..turbineName.."' not found") end
25+
26-
local monitor = peripheral.wrap(monitorName) or peripheral.find("monitor")
26+
	rednet.open("back");
27-
if not monitor then error("Monitor not found") end
27+
	reactor1 = peripheral.wrap(Nreactor1);
28
	reactor2 = peripheral.wrap(Nreactor2);
29-
monitor.setTextScale(0.5)
29+
	reactor3 = peripheral.wrap(Nreactor3);
30-
monitor.setBackgroundColor(colors.black)
30+
	reactor4 = peripheral.wrap(Nreactor4);
31-
monitor.setTextColor(colors.white)
31+
	reactor5 = peripheral.wrap(Nreactor5);
32-
monitor.clear()
32+
	reactor6 = peripheral.wrap(Nreactor6);
33
	reactor7 = peripheral.wrap(Nreactor7);
34-
-- Persistent flow helpers
34+
	reactor8 = peripheral.wrap(Nreactor8);
35-
local function readFlow()
35+
36-
  if fs.exists(FLOW_PERSIST_FILE) then
36+
	turbine1 = peripheral.wrap(Nturbine1);
37-
    local f = fs.open(FLOW_PERSIST_FILE, "r")
37+
	turbine2 = peripheral.wrap(Nturbine2);
38-
    local v = tonumber(f.readAll())
38+
	turbine3 = peripheral.wrap(Nturbine3);
39-
    f.close()
39+
	turbine4 = peripheral.wrap(Nturbine4);
40-
    if v then return math.floor(v) end
40+
	turbine5 = peripheral.wrap(Nturbine5);
41
	turbine6 = peripheral.wrap(Nturbine6);
42
	turbine7 = peripheral.wrap(Nturbine7);
43-
local function writeFlow(v)
43+
	turbine8 = peripheral.wrap(Nturbine8);
44-
  local f = fs.open(FLOW_PERSIST_FILE, "w")
44+
45-
  f.write(tostring(v))
45+
	monitor = peripheral.wrap(screen);
46-
  f.close()
46+
	monitor.setBackgroundColor( colors.black );
47
	monitor.clear();
48
49-
-- Initial guess: binary search
49+
function UIprint( wd, x, y, text, color )
50-
local function tuneFlow()
50+
	local a, b = wd.getCursorPos();
51-
  local best, bestDelta = nil, math.huge
51+
	wd.setTextColor( color );
52-
  local lo, hi = FLOW_TUNE_LOWER, FLOW_TUNE_UPPER
52+
	wd.setCursorPos( x, y );
53-
  for _ = 1, MAX_TUNE_ITER do
53+
	wd.write(text);
54-
    local tf = math.floor((lo + hi) / 2)
54+
	monitor.setBackgroundColor( colors.black );
55-
    turbine.setFluidFlowRateMax(tf)
55+
	wd.setCursorPos( a, b );
56-
    os.sleep(2)
56+
57-
    local r1 = turbine.getRotorSpeed()
57+
58-
    os.sleep(1)
58+
function UIHeader( wd, x, y, wx, wy, text, clr, txtclr )
59-
    local r2 = turbine.getRotorSpeed()
59+
	local r = window.create( wd, x, y, wx, wy );
60-
    local d  = math.abs(r2 - r1)
60+
	px = x + (wx / 2) - (math.floor(string.len(text) / 2));
61-
    if     r2 > RPM_TARGET + RPM_TOLERANCE then hi = tf - 1
61+
	py = y + math.floor(wy/2);
62-
    elseif r2 < RPM_TARGET - RPM_TOLERANCE then lo = tf + 1
62+
		r.setBackgroundColor( clr );
63-
    else
63+
		r.clear();
64-
      if d < bestDelta then bestDelta, best = d, tf end
64+
		UIprint(wd, px, py, text, txtclr );
65-
      if d <= 1 then break end
65+
66
67
function UILaserButton( wd, xl, yl, wxl, wyl, text1, text2, clr1, clr2, txtclr1, txtclr2 )
68-
  if best then writeFlow(best); return best end
68+
	local r = window.create( wd, xl, yl, wxl, wyl );
69-
  return FLOW_TUNE_UPPER
69+
	pxl = xl + (wxl / 2) - (math.floor(string.len(text1) / 2));
70
	pyl = yl + 1 
71
	if redstone.getInput(RsInSide) then
72-
-- Draw ON/OFF button
72+
		r.setBackgroundColor( clr1 );
73-
local function drawButton(active)
73+
		r.clear();
74-
  local bg = active and colors.green or colors.red
74+
		UIprint(wd, pxl, pyl, text1, txtclr1 ); 
75-
  for y = BTN_Y, BTN_Y + BTN_H - 1 do
75+
76-
    monitor.setBackgroundColor(bg)
76+
	elseif not redstone.getInput(RsInSide) then
77-
    monitor.setCursorPos(BTN_X, y)
77+
		r.setBackgroundColor( clr2 );
78-
    monitor.write(string.rep(" ", BTN_W))
78+
		r.clear();
79
		UIprint(wd, pxl, pyl, text2, txtclr2 );
80-
  monitor.setBackgroundColor(bg)
80+
81-
  monitor.setTextColor(colors.white)
81+
	end 
82-
  monitor.setCursorPos(BTN_X + 2, BTN_Y + 1)
82+
83-
  monitor.write(active and "ON" or "OFF")
83+
84-
  monitor.setBackgroundColor(colors.black)
84+
function UIButton( wd, x, y, wx, wy, text1, text2, clr1, clr2, txtclr1, txtclr2, machine )
85-
  monitor.setTextColor(colors.white)
85+
	local r = window.create( wd, x, y, wx, wy );
86
		px = x + 1;	
87
	py = y + math.floor(wy/2);
88-
-- Draw left panel stats
88+
	if machine.getActive() then
89-
local function drawStats(s, nominal)
89+
		r.setBackgroundColor( clr1 );
90-
  monitor.setBackgroundColor(colors.black)
90+
		r.clear();
91-
  monitor.setTextColor(colors.white)
91+
		UIprint(wd, px, py, text1, txtclr1 ); 
92-
  monitor.setCursorPos(2,2); monitor.write("Turbine: #0")
92+
	else
93-
  monitor.setCursorPos(2,3); monitor.write("Status: " .. (s.active and "ONLINE" or "OFF"))
93+
		r.setBackgroundColor( clr2 );
94-
  monitor.setCursorPos(2,4); monitor.write(("RPM: %6.1f"):format(s.rpm))
94+
		r.clear();
95-
  monitor.setCursorPos(2,5); monitor.write("Flow: " .. s.flow .. " mB/t")
95+
		UIprint(wd, px, py, text2, txtclr2 );
96-
  monitor.setCursorPos(2,6); monitor.write("Nom : " .. nominal .. " mB/t")
96+
	end 
97-
  monitor.setCursorPos(2,7); monitor.write(("Drift: %+.1f rpm"):format(s.drift))
97+
	
98-
  monitor.setCursorPos(2,8); monitor.write("Coil: " .. (s.inductor and "ENGAGED" or "OFF"))
98+
99-
  monitor.setCursorPos(2,9); monitor.write("Vent: " .. s.msg)
99+
100
function UIFrame( wd, x, y, wx, wy, clr )
101
        local f = window.create( wd, x, y, wx, wy );
102-
-- Draw capped RPM bar (max 100 chars)
102+
              f.setBackgroundColor( clr );
103-
local function drawRPMgraph(s)
103+
              f.clear();
104-
  local pct = math.min(1, s.rpm / (RPM_TARGET + 50))
104+
105-
  local filled = math.floor(pct * RPMBAR_W)
105+
106-
  local fill = string.rep("█", filled)
106+
function Setmachine( machine )
107-
  local empty = string.rep("░", RPMBAR_W - filled)
107+
	if machine.getActive() then
108-
  monitor.setCursorPos(RPMBAR_X, RPMBAR_Y)
108+
		machine.setActive(false);
109-
  monitor.write("[" .. fill .. empty .. "] ")
109+
	else
110-
  monitor.write(("%6.1f/%d RPM"):format(s.rpm, RPM_TARGET + 50))
110+
		machine.setActive(true);
111
	end 
112
end
113-
-- Draw vertical control-rod bar (getControlRodLevel(0))
113+
114-
local function drawControlRod()
114+
function UIPos( xPos, yPos, xMin, yMin, xWidth, yWidth, Machine)
115-
  local raw = reactor.getControlRodLevel(0) or 0
115+
  if xPos >= xMin then
116-
  local frac = raw / 100
116+
    if xPos <= (xMin - 1 + xWidth) then
117-
  local height = math.floor(frac * 52)
117+
      if yPos >= yMin then
118-
  for y = 2, 53 do
118+
        if yPos <= (yMin + yWidth - 1) then
119-
    monitor.setCursorPos(CTRLBAR_X, y)
119+
          Setmachine( Machine );
120-
    monitor.setBackgroundColor(colors.gray)
120+
        end
121-
    monitor.write(y > 53 - height and "█" or " ")
121+
      end
122
    end
123-
  monitor.setCursorPos(CTRLBAR_X, 54)
123+
124-
  monitor.setBackgroundColor(colors.black)
124+
125-
  monitor.setTextColor(colors.white)
125+
126-
  monitor.write(string.format("%3d%%", raw))
126+
127
--Setup--
128
129-
-- Handle monitor touch
129+
local mw, mh = monitor.getSize()
130-
local function handleTouch(x,y)
130+
UIHeader( monitor, 1, 1, mw, 1, "REACTOR MAIN SWITCHES", colors.gray, colors.red);
131-
  if x >= BTN_X and x < BTN_X + BTN_W
131+
UIFrame( monitor, 1, 2, mw, 1, colors.gray);
132-
  and y >= BTN_Y and y < BTN_Y + BTN_H then
132+
UIHeader( monitor, 1, 2, 11, 1, "Block", colors.gray, colors.white);
133-
    reactor.setActive(not reactor.getActive())
133+
UIHeader( monitor, 11, 2, 10, 1, "Status", colors.gray, colors.white);
134
UIHeader( monitor, 20, 2, 12, 1, "Turbine", colors.gray, colors.white);
135
136
UIprint( monitor, 2, 4, "Block 1-A:", colors.lightGray );
137-
-- Initialization: get persisted or tuned nominal flow
137+
UIprint( monitor, 2, 5, "Block 1-B:", colors.lightGray );
138-
local nominalFlow = readFlow() or tuneFlow()
138+
UIprint( monitor, 2, 6, "Block 2-A:", colors.lightGray );
139-
local learnedFlow = nominalFlow
139+
UIprint( monitor, 2, 7, "Block 2-B:", colors.lightGray );
140-
local lastRPM     = turbine.getRotorSpeed()
140+
UIprint( monitor, 2, 8, "Block 3-A:", colors.lightGray );
141
UIprint( monitor, 2, 9, "Block 3-B:", colors.lightGray );
142-
-- MAIN LOOP
142+
UIprint( monitor, 2, 10, "Block 4-A:", colors.lightGray );
143
UIprint( monitor, 2, 11, "Block 4-B:", colors.lightGray );
144-
  monitor.clear()
144+
145-
  local s = {
145+
UIButton( monitor, 12, 4, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor1 );
146-
    active = reactor.getActive(),
146+
UIButton( monitor, 12, 5, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor2 );
147-
    rpm = turbine.getRotorSpeed()
147+
UIButton( monitor, 12, 6, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor3 );
148-
  }
148+
UIButton( monitor, 12, 7, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor4 );
149-
  s.drift = math.abs(s.rpm - lastRPM)
149+
UIButton( monitor, 12, 8, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor5 );
150-
  lastRPM = s.rpm
150+
UIButton( monitor, 12, 9, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor6 );
151
UIButton( monitor, 12, 10, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor7 );
152-
  local mode = "STABLE"
152+
UIButton( monitor, 12, 11, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor8 );
153-
  local flow, msg, inductor
153+
154
UIButton( monitor, 22, 4, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine1 );
155-
  -- Main adaptive control logic
155+
UIButton( monitor, 22, 5, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine2 );
156-
  if s.rpm < RPM_TARGET - RPM_TOLERANCE then
156+
UIButton( monitor, 22, 6, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine3 );
157-
    flow = math.floor(learnedFlow * (1 + FLOW_BUFFER))
157+
UIButton( monitor, 22, 7, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine4 );
158-
    mode, msg, inductor = "ACCEL", "ACCEL", false
158+
UIButton( monitor, 22, 8, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine5 );
159-
    turbine.setVentOverflow()
159+
UIButton( monitor, 22, 9, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine6 );
160-
    -- Learn: We are too slow, increase flow a bit
160+
UIButton( monitor, 22, 10, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine7 );
161-
    learnedFlow = math.min(FLOW_TUNE_UPPER, learnedFlow + FLOW_LEARN_STEP)
161+
UIButton( monitor, 22, 11, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine8 );
162-
    writeFlow(learnedFlow)
162+
163-
  elseif s.rpm > RPM_TARGET + RPM_TOLERANCE then
163+
164-
    flow = math.max(1, math.floor(learnedFlow * DECEL_FRACTION))
164+
-- RUN --
165-
    mode, msg, inductor = "DECCEL", "DECCEL", true
165+
166-
    turbine.setVentAll()
166+
167-
    -- Learn: We are too fast, decrease flow a bit
167+
  event, side, xPos, yPos = os.pullEvent("monitor_touch");
168-
    learnedFlow = math.max(FLOW_TUNE_LOWER, learnedFlow - FLOW_LEARN_STEP)
168+
    if event == "monitor_touch" then
169-
    writeFlow(learnedFlow)
169+
170-
  else
170+
	UIPos( xPos, yPos, 12, 4, 9, 1, reactor1 );
171-
    -- In window, flow ramps down from buffer to learnedFlow as we approach center
171+
	UIButton( monitor, 12, 4, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor1 );
172-
    local dist = math.abs(s.rpm - RPM_TARGET)
172+
173-
    local frac = dist / RPM_TOLERANCE
173+
	UIPos( xPos, yPos, 12, 5, 9, 1, reactor2 );
174-
    local adj = 1 + (FLOW_BUFFER * frac)  -- e.g. 1.10 at window edge, 1.00 at center
174+
	UIButton( monitor, 12, 5, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor2 );
175-
    flow = math.max(1, math.floor(learnedFlow * adj))
175+
176-
    msg, inductor = "STABLE", true
176+
	UIPos( xPos, yPos, 12, 6, 9, 1, reactor3 );
177-
    turbine.setVentOverflow()
177+
	UIButton( monitor, 12, 6, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor3 );
178-
    -- Optionally: reinforce learnedFlow with what actually works
178+
179-
    -- Only update if we are very close to center
179+
	UIPos( xPos, yPos, 12, 7, 9, 1, reactor4 );
180-
    if dist <= 1 then
180+
	UIButton( monitor, 12, 7, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor4 );
181-
      learnedFlow = flow
181+
182-
      writeFlow(learnedFlow)
182+
	UIPos( xPos, yPos, 12, 8, 9, 1, reactor5 );
183
	UIButton( monitor, 12, 8, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor5 );
184
185
	UIPos( xPos, yPos, 12, 9, 9, 1, reactor6 );
186-
  -- Apply controls
186+
	UIButton( monitor, 12, 9, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor6 );
187-
  turbine.setFluidFlowRateMax(flow)
187+
188-
  turbine.setInductorEngaged(inductor)
188+
	UIPos( xPos, yPos, 12, 10, 9, 1, reactor7 );
189-
  reactor.setAllControlRodLevels(100 - math.floor(flow / learnedFlow * 100))
189+
	UIButton( monitor, 12, 10, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor7 );
190
191-
  -- For display
191+
	UIPos( xPos, yPos, 12, 11, 9, 1, reactor8 );
192-
  s.flow = flow
192+
	UIButton( monitor, 12, 11, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, reactor8 );
193-
  s.msg = msg
193+
194-
  s.inductor = inductor
194+
-- Turbines
195
196-
  -- Draw UI
196+
	UIPos( xPos, yPos, 22, 4, 9, 1, turbine1 );
197-
  drawButton(s.active)
197+
	UIButton( monitor, 22, 4, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine1 );
198-
  drawStats(s, learnedFlow)
198+
199-
  drawRPMgraph(s)
199+
	UIPos( xPos, yPos, 22, 5, 9, 1, turbine2 );
200-
  drawControlRod()
200+
	UIButton( monitor, 22, 5, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine2 );
201
202-
  -- Footer
202+
	UIPos( xPos, yPos, 22, 6, 9, 1, turbine3 );
203-
  monitor.setBackgroundColor(colors.black)
203+
	UIButton( monitor, 22, 6, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine3 );
204-
  monitor.setTextColor(colors.lightGray)
204+
205-
  monitor.setCursorPos(2,54)
205+
	UIPos( xPos, yPos, 22, 7, 9, 1, turbine4 );
206-
  monitor.write("Tap ON/OFF | Adaptive learning | Flow file auto-update | Rod sync | 1s updates")
206+
	UIButton( monitor, 22, 7, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine4 );
207
208-
  -- Wait for touch or 1-second timer
208+
	UIPos( xPos, yPos, 22, 8, 9, 1, turbine5 );
209-
  local timer = os.startTimer(1)
209+
	UIButton( monitor, 22, 8, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine5 );
210-
  repeat
210+
211-
    local ev, _, x, y = os.pullEvent()
211+
	UIPos( xPos, yPos, 22, 9, 9, 1, turbine6 );
212-
    if ev == "monitor_touch" then handleTouch(x,y) end
212+
	UIButton( monitor, 22, 9, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine6 );
213-
  until ev == "timer"
213+
214
	UIPos( xPos, yPos, 22, 10, 9, 1, turbine7 );
215-
215+
	UIButton( monitor, 22, 10, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine7 );
216
217
	UIPos( xPos, yPos, 22, 11, 9, 1, turbine8 );
218
	UIButton( monitor, 22, 11, 9, 1, "online", "offline", colors.lime, colors.orange, colors.lime, colors.orange, turbine8 );
219
220
    end
221
end