-- ============================================================================
-- CONTROLLO REATTORE IC2 CON LOGICA GATE INTEGRATA
-- ============================================================================
-- Questo programma gestisce automaticamente il reattore IC2 per l'arricchimento
-- del materiale, con logica di controllo integrata che sostituisce i gate hardware.
--
-- LOGICA GATE INTEGRATA (non servono più gate fisici):
-- - RS Latch: Gestisce la commutazione tra modalità normale e rifornimento
-- con isteresi per evitare oscillazioni (soglie: 10s per Set, 15s per Reset)
-- - XNOR: Garantisce che arancione e bianco non siano mai attivi contemporaneamente
--
-- COLORI BUNDLED REDSTONE:
-- - Arancione (2): Modalità normale - Input/output materiale da arricchire ATTIVI
-- - Bianco (1): Modalità rifornimento - Input/output cellule uranio ATTIVI
-- - Nessuno (0): Sistema fermo/errore
-- ============================================================================
-- Funzione per verificare che il peripheral sia valido
function getPeripheral()
local success, s = pcall(function()
return peripheral.wrap("bottom")
end)
if not success or not s then
return nil, "Peripheral non disponibile"
end
return s, nil
end
-- Funzione per ottenere il monitor (prova più lati)
function getMonitor()
local sides = {"top", "left", "right", "front", "back"}
for _, side in ipairs(sides) do
local success, monitor = pcall(function()
local m = peripheral.wrap(side)
if m and m.setCursorPos then
return m, side
end
return nil, nil
end)
if success and monitor then
return monitor, side, nil
end
end
return nil, nil, "Monitor non trovato"
end
-- Funzione per aggiornare il monitor con i dati del reattore
function updateMonitor(monitor, uuid, state, title, tab, isRefueling, errorMsg)
if not monitor then
return false
end
local success, err = pcall(function()
-- Ottieni dimensioni del monitor
local width, height = monitor.getSize()
if not width or not height then
-- Fallback se getSize non disponibile
width, height = 26, 9
end
-- Configura il monitor
monitor.setTextScale(0.5) -- Testo più piccolo per monitor 2x3
monitor.clear()
monitor.setCursorPos(1, 1)
-- Header
monitor.setTextColor(colors.white)
local header = "=== REATTORE IC2 ==="
if string.len(header) > width then
header = string.sub(header, 1, width)
end
monitor.write(header)
monitor.setCursorPos(1, 2)
monitor.setTextColor(colors.gray)
monitor.write(string.rep("=", width))
local line = 3
-- Mostra errori se presenti
if errorMsg then
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.red)
monitor.write("ERRORE:")
line = line + 1
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.orange)
-- Tronca messaggio se troppo lungo
local msg = string.sub(tostring(errorMsg), 1, width - 1)
monitor.write(msg)
line = line + 2
elseif tab and tab.timeLeft then
-- Stato del reattore
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.cyan)
monitor.write("Stato: ")
if isRefueling then
monitor.setTextColor(colors.yellow)
monitor.write("RIFORNIMENTO")
else
monitor.setTextColor(colors.green)
monitor.write("ATTIVO")
end
line = line + 1
-- Tempo rimanente
local hours = math.floor(tab.timeLeft / 3600)
local minutes = math.floor((tab.timeLeft % 3600) / 60)
local seconds = tab.timeLeft % 60
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.lightBlue)
monitor.write("Tempo:")
line = line + 1
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.white)
local timeStr
if hours > 0 then
timeStr = string.format("%02d:%02d:%02d", hours, minutes, seconds)
else
timeStr = string.format("%02d:%02d", minutes, seconds)
end
monitor.write(timeStr)
line = line + 1
-- Barra di avanzamento (adattata alla larghezza)
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.gray)
monitor.write("[")
local barWidth = math.min(width - 4, 18) -- Lascia spazio per [ e ]
local timePercent = math.min(tab.timeLeft / 3600, 1) * 100 -- Percentuale basata su 1 ora
local filled = math.floor((timePercent / 100) * barWidth)
if timePercent > 50 then
monitor.setTextColor(colors.green)
elseif timePercent > 20 then
monitor.setTextColor(colors.yellow)
else
monitor.setTextColor(colors.red)
end
for i = 1, barWidth do
if i <= filled then
monitor.write("=")
else
monitor.setTextColor(colors.gray)
monitor.write("-")
end
end
monitor.setTextColor(colors.gray)
monitor.write("]")
line = line + 1
-- Avviso se tempo basso
if tab.timeLeft < 60 and not isRefueling then
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.red)
monitor.write("! ATTENZIONE !")
if line < height - 1 then
line = line + 1
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.orange)
local warning = "Uranio in esaurimento"
if string.len(warning) > width then
warning = string.sub(warning, 1, width)
end
monitor.write(warning)
end
end
else
-- Nessun dato disponibile
monitor.setCursorPos(1, line)
monitor.setTextColor(colors.orange)
monitor.write("Dati non disponibili")
end
-- Footer
if line < height then
monitor.setCursorPos(1, height)
monitor.setTextColor(colors.gray)
monitor.write(string.rep("=", width))
end
return true
end)
if not success then
return false
end
return true
end
-- Funzione per ottenere lo stato del reattore in modo sicuro
function getReactorState(s)
if not s then
return nil, nil, nil, nil, "Peripheral non valido"
end
local success, uuid, state, title, tab = pcall(function()
return s.get(1)
end)
if not success then
return nil, nil, nil, nil, "Errore nella lettura del reattore: " .. tostring(uuid)
end
if not tab or type(tab) ~= "table" then
return uuid, state, title, nil, "Dati reattore non validi"
end
if tab.timeLeft == nil or type(tab.timeLeft) ~= "number" then
return uuid, state, title, tab, "timeLeft non valido"
end
return uuid, state, title, tab, nil
end
-- Costanti per la logica RS Latch con isteresi (sostituisce i gate hardware)
local REFUEL_THRESHOLD_LOW = 10 -- Soglia per attivare rifornimento (Set)
local REFUEL_THRESHOLD_HIGH = 15 -- Soglia per disattivare rifornimento (Reset) - isteresi
-- Costanti per i colori bundled redstone
local COLOR_ORANGE = 2 -- Arancione: modalità normale (input/output materiale da arricchire)
local COLOR_WHITE = 1 -- Bianco: modalità rifornimento (input/output cellule uranio)
local COLOR_NONE = 0 -- Nessun colore: sistema fermo
-- Funzione che implementa la logica RS Latch integrata
-- Gestisce la commutazione tra arancione (normale) e bianco (rifornimento) con isteresi
-- Sostituisce il gate RS Latch hardware
function updateReactorMode(timeLeft, currentMode)
-- currentMode può essere: "normal" (arancione), "refuel" (bianco), o nil (inizializzazione)
if not timeLeft or timeLeft < 0 then
return COLOR_NONE, "error" -- Errore: dati non validi
end
-- Logica RS Latch con isteresi:
-- - Set (rifornimento): quando timeLeft < REFUEL_THRESHOLD_LOW
-- - Reset (normale): quando timeLeft >= REFUEL_THRESHOLD_HIGH
-- - Isteresi: tra LOW e HIGH mantiene lo stato corrente (evita oscillazioni)
if timeLeft < REFUEL_THRESHOLD_LOW then
-- Set: attiva modalità rifornimento (bianco)
return COLOR_WHITE, "refuel"
elseif timeLeft >= REFUEL_THRESHOLD_HIGH then
-- Reset: attiva modalità normale (arancione)
return COLOR_ORANGE, "normal"
else
-- Zona di isteresi (tra LOW e HIGH): mantieni lo stato corrente
if currentMode == "refuel" then
return COLOR_WHITE, "refuel"
else
-- Se non c'è uno stato corrente, usa il valore più vicino alla soglia
-- Se siamo appena scesi sotto HIGH, rimani in normale
-- Se siamo appena saliti sopra LOW, vai in rifornimento
-- Per sicurezza, se non c'è stato, considera normale
return COLOR_ORANGE, "normal"
end
end
end
-- Funzione che implementa la logica XNOR integrata
-- Garantisce che arancione e bianco non siano mai attivi contemporaneamente
-- (già gestito automaticamente da setBundledOutput, ma questa funzione verifica la logica)
function validateOutput(outputValue)
-- Verifica che l'output sia valido (solo arancione, bianco o nessuno)
if outputValue == COLOR_ORANGE or outputValue == COLOR_WHITE or outputValue == COLOR_NONE then
return true, outputValue
else
-- Output non valido: spegni tutto per sicurezza
return false, COLOR_NONE
end
end
-- Funzione di compatibilità per bundled redstone (ComputerCraft 1.5 vs versioni più recenti)
local function setBundledOutputCompat(side, color)
-- Prova prima con rs (versioni recenti)
if rs and rs.setBundledOutput then
return rs.setBundledOutput(side, color)
-- Fallback a redstone (ComputerCraft 1.5 per Minecraft 1.4.7)
elseif redstone and redstone.setBundledOutput then
return redstone.setBundledOutput(side, color)
else
error("API bundled redstone non disponibile")
end
end
-- Funzione per impostare output redstone in modo sicuro con logica gate integrata
function setOutputSafely(outputValue, errorMsg)
-- Valida l'output (logica XNOR: solo un colore alla volta)
local isValid, safeValue = validateOutput(outputValue)
if not isValid then
print("ATTENZIONE: Output non valido, uso valore sicuro")
outputValue = safeValue
end
local success, err = pcall(function()
setBundledOutputCompat("back", outputValue)
end)
if not success then
print("ERRORE: Impossibile impostare output redstone: " .. tostring(err))
if errorMsg then
print(errorMsg)
end
return false
end
return true
end
function clear()
term.clear()
term.setCursorPos(1, 1)
end
clear()
-- Rilevamento versione ComputerCraft e API disponibile
local ccVersion = "sconosciuta"
local bundledAPI = "nessuna"
if rs and rs.setBundledOutput then
bundledAPI = "rs.setBundledOutput (ComputerCraft recente)"
ccVersion = "recente"
elseif redstone and redstone.setBundledOutput then
bundledAPI = "redstone.setBundledOutput (ComputerCraft 1.5 - MC 1.4.7)"
ccVersion = "1.5"
else
bundledAPI = "NON DISPONIBILE"
print("ATTENZIONE: API bundled redstone non trovata!")
print("Il programma potrebbe non funzionare correttamente")
end
print("Versione ComputerCraft rilevata: " .. ccVersion)
print("API bundled redstone: " .. bundledAPI)
print("")
-- Inizializzazione sicura
local s, err = getPeripheral()
if not s then
print("ERRORE CRITICO: " .. err)
print("Premere un tasto per riprovare...")
os.pullEvent("key")
os.reboot()
end
-- Inizializzazione monitor
local monitor, monitorSide, monitorErr = getMonitor()
if monitor then
print("Monitor trovato sul lato: " .. monitorSide)
else
print("ATTENZIONE: " .. (monitorErr or "Monitor non trovato"))
print("Il programma continuera' senza monitor")
end
-- Lettura iniziale dello stato
local uuid, state, title, tab, err = getReactorState(s)
if err then
print("ERRORE: " .. err)
print("Verificare la connessione del reattore")
if monitor then
updateMonitor(monitor, nil, nil, nil, nil, false, err)
end
print("Premere un tasto per continuare...")
os.pullEvent("key")
end
if tab and tab.timeLeft == 0 then
print("Caricare il reattore e premere un tasto")
if monitor then
updateMonitor(monitor, uuid, state, title, tab, false, "Reattore vuoto - caricare uranio")
end
os.pullEvent("key")
clear()
end
print("Pronto")
print("Sistema di sicurezza attivo")
print("Logica gate integrata (XNOR + RS Latch) attiva")
-- Variabili per il controllo di sicurezza e logica gate integrata
local lastTimeLeft = nil
local consecutiveErrors = 0
local maxErrors = 5
local currentReactorMode = nil -- "normal", "refuel", o nil (inizializzazione)
local lastOutputValue = COLOR_NONE -- Traccia l'ultimo output per evitare cambi inutili
-- Inizializzazione stato iniziale basato sul timeLeft corrente
if tab and tab.timeLeft then
local initOutput, initMode = updateReactorMode(tab.timeLeft, nil)
currentReactorMode = initMode
lastOutputValue = initOutput
-- Imposta l'output iniziale
if initMode == "normal" then
setOutputSafely(COLOR_ORANGE, "Impossibile impostare modalità iniziale normale")
print("Stato iniziale: MODALITA' NORMALE (arancione)")
elseif initMode == "refuel" then
setOutputSafely(COLOR_WHITE, "Impossibile impostare modalità iniziale rifornimento")
print("Stato iniziale: MODALITA' RIFORNIMENTO (bianco)")
else
setOutputSafely(COLOR_NONE, "Impossibile impostare stato iniziale")
print("Stato iniziale: ERRORE")
end
else
-- Se non ci sono dati, inizia in modalità normale
currentReactorMode = "normal"
lastOutputValue = COLOR_ORANGE
setOutputSafely(COLOR_ORANGE, "Impossibile impostare stato iniziale")
print("Stato iniziale: MODALITA' NORMALE (dati non disponibili)")
end
if monitor then
local isRefueling = (currentReactorMode == "refuel")
updateMonitor(monitor, uuid, state, title, tab, isRefueling, nil)
end
while true do
clear()
print("Caricamento automatico materiale da arricchire in funzione")
-- Verifica periodica della connessione del peripheral
if consecutiveErrors > 3 then
print("Riconnessione al peripheral...")
s, err = getPeripheral()
if not s then
print("ERRORE: " .. err)
print("Attesa 5 secondi prima di riprovare...")
sleep(5)
consecutiveErrors = 0
else
consecutiveErrors = 0
print("Riconnesso con successo")
end
end
-- Verifica e riconnessione monitor se necessario
if not monitor or not pcall(function() monitor.getSize() end) then
monitor, monitorSide, monitorErr = getMonitor()
if monitor then
print("Monitor riconnesso sul lato: " .. monitorSide)
end
end
-- Lettura sicura dello stato
uuid, state, title, tab, err = getReactorState(s)
if err then
consecutiveErrors = consecutiveErrors + 1
print("ERRORE: " .. err)
print("Tentativo " .. consecutiveErrors .. "/" .. maxErrors)
-- Aggiorna monitor con errore (mantieni lo stato corrente)
if monitor then
local isRefuelingMonitor = (currentReactorMode == "refuel")
updateMonitor(monitor, uuid, state, title, tab, isRefuelingMonitor, err)
end
if consecutiveErrors >= maxErrors then
print("ERRORE CRITICO: Troppi errori consecutivi")
print("Arresto sistema per sicurezza")
-- In caso di errore critico, spegni tutto (COLOR_NONE)
setOutputSafely(COLOR_NONE, "Sistema fermato per sicurezza")
currentReactorMode = "error"
lastOutputValue = COLOR_NONE
if monitor then
updateMonitor(monitor, uuid, state, title, tab, false, "SISTEMA FERMATO - Troppi errori")
end
print("Premere un tasto per riprovare...")
os.pullEvent("key")
consecutiveErrors = 0
-- Ripristina lo stato iniziale dopo l'errore
currentReactorMode = nil
else
sleep(2)
end
else
consecutiveErrors = 0
-- Validazione dei dati
if tab and tab.timeLeft then
-- Verifica coerenza dei dati (timeLeft non dovrebbe aumentare)
if lastTimeLeft ~= nil and tab.timeLeft > lastTimeLeft + 5 then
print("ATTENZIONE: Rilevato incremento anomalo del tempo")
print("Possibile errore di lettura, attesa...")
sleep(3)
else
lastTimeLeft = tab.timeLeft
end
local hours = math.floor(tab.timeLeft / 3600)
local minutes = math.floor((tab.timeLeft % 3600) / 60)
local seconds = tab.timeLeft % 60
print("Tempo rimanente: " .. hours .. " ore, " .. minutes .. " minuti, " .. seconds .. " secondi")
-- LOGICA GATE INTEGRATA (sostituisce XNOR e RS Latch hardware)
-- Determina la modalità del reattore basandosi sul timeLeft
local outputValue, newMode = updateReactorMode(tab.timeLeft, currentReactorMode)
-- Aggiorna lo stato solo se è cambiato
if newMode ~= currentReactorMode then
local oldMode = currentReactorMode
currentReactorMode = newMode
if newMode == "refuel" then
clear()
print("ATTENZIONE: Uranio in esaurimento")
print("Modalità RIFORNIMENTO attivata (bianco)")
print("Input/output materiale da arricchire: DISATTIVATI")
print("Input/output cellule uranio: ATTIVI")
elseif newMode == "normal" then
if oldMode == "refuel" then
print("Rifornimento completato")
end
print("Modalità NORMALE attivata (arancione)")
print("Input/output materiale da arricchire: ATTIVI")
print("Input/output cellule uranio: DISATTIVATI")
end
end
-- Imposta l'output solo se è cambiato (ottimizzazione)
if outputValue ~= lastOutputValue then
local modeName = "normale"
if newMode == "refuel" then
modeName = "rifornimento"
elseif newMode == "error" then
modeName = "errore"
end
setOutputSafely(outputValue, "Impossibile impostare modalità " .. modeName)
lastOutputValue = outputValue
if outputValue == COLOR_ORANGE then
print("Output: ARANCIONE (normale)")
elseif outputValue == COLOR_WHITE then
print("Output: BIANCO (rifornimento)")
elseif outputValue == COLOR_NONE then
print("Output: SPENTO (errore)")
end
end
-- Aggiorna monitor con dati attuali
local isRefueling = (newMode == "refuel")
if monitor then
updateMonitor(monitor, uuid, state, title, tab, isRefueling, nil)
end
else
print("ERRORE: Dati reattore non validi")
consecutiveErrors = consecutiveErrors + 1
end
end
sleep(1)
end