CCCPaste

Reattore Nuovo

-- ============================================================================
-- 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