286 lines
9.4 KiB
Lua
286 lines
9.4 KiB
Lua
--------------------------------------------------------------------
|
|
-- lib/materiel.lua : resolution des peripheriques et adaptateurs.
|
|
-- Tous les formats sont VERIFIES en jeu (inventaire du 17/07/2026).
|
|
--
|
|
-- Directs : altitude_sensor (top), 2 Create_DisplayLink (faces dans
|
|
-- conf.ECRANS), ender modem (gps), moteur diesel en bottom pilote
|
|
-- par redstone analogique (15=arret, 14=mini, 0=maxi).
|
|
-- Reseau : RSC (roles calibres), velocity_sensor x3 (axes/directions
|
|
-- calibres), gimbal_sensor (axes calibres), optical_sensor,
|
|
-- Create_Stressometer, redstone_relay (joystick calibre),
|
|
-- linked_typewriter, monitor, 2 cuves create:fluid_tank.
|
|
--------------------------------------------------------------------
|
|
local Materiel = {}
|
|
|
|
local function trouver(type_, obligatoire, journal, note)
|
|
local p = peripheral.find(type_)
|
|
if not p and obligatoire then
|
|
error("peripherique manquant: " .. type_, 0)
|
|
end
|
|
if not p and journal then
|
|
journal.alerte(type_ .. " introuvable" .. (note and (": " .. note) or ""))
|
|
end
|
|
return p
|
|
end
|
|
|
|
function Materiel.initialiser(conf, etat, journal)
|
|
local m = {}
|
|
m.rpm = {} -- role -> { v, tChange } (pour l'affichage)
|
|
local posCache = nil, nil
|
|
local posDate = 0
|
|
|
|
m.altitude = trouver("altitude_sensor", true)
|
|
m.gimbal = trouver("gimbal_sensor", true)
|
|
m.optical = trouver("optical_sensor", true)
|
|
m.moniteur = trouver("monitor", true)
|
|
m.relay = trouver("redstone_relay", false, journal, "joystick inactif")
|
|
m.stresso = trouver("Create_Stressometer", false, journal,
|
|
"detection moteur et surcharge inactives")
|
|
m.typewriter = trouver("linked_typewriter", false, journal,
|
|
"consigne au moniteur uniquement")
|
|
|
|
-- les deux display links, identifies par leur face (conf.ECRANS)
|
|
local function ecran(face, usage)
|
|
local p = peripheral.wrap(face)
|
|
if not p or peripheral.getType(face) ~= "Create_DisplayLink" then
|
|
error(("Create_DisplayLink attendu en '%s' (%s), voir conf.ECRANS")
|
|
:format(face, usage), 0)
|
|
end
|
|
return p
|
|
end
|
|
m.ecranConduite = ecran(conf.ECRANS.conduite, "conduite")
|
|
m.ecranCarburant = ecran(conf.ECRANS.carburant, "carburant")
|
|
|
|
-- rotation speed controllers et velocity sensors: les affectations
|
|
-- (etat.roles / etat.velocite) sont rechargeables A CHAUD apres
|
|
-- une recalibration via m.rafraichirAffectations()
|
|
m.rsc, m.velocite = {}, {}
|
|
for _, nom in ipairs(peripheral.getNames()) do
|
|
local type_ = peripheral.getType(nom)
|
|
if type_ == "Create_RotationSpeedController" then
|
|
m.rsc[nom] = peripheral.wrap(nom)
|
|
elseif type_ == "velocity_sensor" then
|
|
m.velocite[nom] = peripheral.wrap(nom)
|
|
end
|
|
end
|
|
|
|
function m.rafraichirAffectations()
|
|
m.rscParRole, m.veloParAxe = {}, {}
|
|
for nom, p in pairs(m.rsc) do
|
|
local role = etat.roles[nom]
|
|
if role then m.rscParRole[role] = p end
|
|
end
|
|
for nom, p in pairs(m.velocite) do
|
|
local aff = etat.velocite[nom]
|
|
if aff then m.veloParAxe[aff.axe] = { p = p, signe = aff.signe } end
|
|
end
|
|
end
|
|
m.rafraichirAffectations()
|
|
|
|
-- cuves de carburant (create:fluid_tank; exclut le moteur diesel,
|
|
-- qui est fluid_storage mais pas fluid_tank)
|
|
m.cuves = {}
|
|
for _, nom in ipairs(peripheral.getNames()) do
|
|
if peripheral.hasType(nom, "create:fluid_tank") then
|
|
table.insert(m.cuves, peripheral.wrap(nom))
|
|
end
|
|
end
|
|
if #m.cuves == 0 then
|
|
journal.alerte("aucune cuve create:fluid_tank: suivi carburant inactif")
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- LECTURES
|
|
------------------------------------------------------------------
|
|
function m.lireAltitude()
|
|
return m.altitude.getHeight()
|
|
end
|
|
|
|
-- angles bruts du gimbal (table de 2)
|
|
function m.lireAnglesBruts()
|
|
local a = m.gimbal.getAngles()
|
|
return a[1] or 0, a[2] or 0
|
|
end
|
|
|
|
-- assiette NORMALISEE via la calibration gimbal :
|
|
-- tangage negatif = nez bas, roulis negatif = penche a gauche
|
|
function m.lireAssiette()
|
|
local g = etat.gimbal
|
|
if not g then return m.lireAnglesBruts() end
|
|
local a = m.gimbal.getAngles()
|
|
return (a[g.indexTangage] or 0) * g.signeTangage,
|
|
(a[g.indexRoulis] or 0) * g.signeRoulis
|
|
end
|
|
|
|
function m.lireDistanceSol()
|
|
return m.optical.getDistance()
|
|
end
|
|
|
|
function m.auSol()
|
|
return m.lireDistanceSol() <= conf.DIST_SOL
|
|
end
|
|
|
|
-- sol detecte ET posable (pas d'eau/lave: garde-fou ocean)
|
|
function m.solPosable()
|
|
if not m.optical.hasHit() then return false end
|
|
local bloc = m.optical.getBlock()
|
|
return not conf.BLOCS_INTERDITS[bloc]
|
|
end
|
|
|
|
function m.horizontal()
|
|
local t, r = m.lireAssiette()
|
|
return math.abs(t) <= conf.ANGLE_MAX and math.abs(r) <= conf.ANGLE_MAX
|
|
end
|
|
|
|
-- vitesse le long d'un axe calibre ("avant"|"lateral"|"vertical")
|
|
-- signe: positif = avant / gauche / haut
|
|
function m.lireVitesseAxe(axe)
|
|
local c = m.veloParAxe[axe]
|
|
if not c then return 0 end
|
|
return (c.p.getVelocity() or 0) * c.signe
|
|
end
|
|
|
|
function m.vitesseHorizontale()
|
|
local va = m.lireVitesseAxe("avant")
|
|
local vl = m.lireVitesseAxe("lateral")
|
|
return math.sqrt(va * va + vl * vl)
|
|
end
|
|
|
|
-- joystick calibre: avance (devant-derriere), virage (gauche-droite)
|
|
-- dans -1..1; pas de zone morte (joystick de mod, zero franc)
|
|
function m.lireJoystick()
|
|
local j = etat.joystick
|
|
if not (m.relay and j) then return 0, 0 end
|
|
local function lire(direction)
|
|
local face = j[direction]
|
|
if not face then return 0 end
|
|
return m.relay.getAnalogInput(face) / 15
|
|
end
|
|
return lire("devant") - lire("derriere"),
|
|
lire("gauche") - lire("droite")
|
|
end
|
|
|
|
function m.lireFacesRelay()
|
|
if not m.relay then return {} end
|
|
local lectures = {}
|
|
for _, face in ipairs({ "top", "bottom", "left", "right",
|
|
"front", "back" }) do
|
|
lectures[face] = m.relay.getAnalogInput(face)
|
|
end
|
|
return lectures
|
|
end
|
|
|
|
-- typewriter: table des codes de touches actuellement pressees
|
|
function m.lireTouches()
|
|
if not m.typewriter then return {} end
|
|
return m.typewriter.getPressedKeyCodes() or {}
|
|
end
|
|
|
|
-- stressometer (formats verifies): ratio 0..1, ou nil
|
|
function m.lireStress()
|
|
if not m.stresso then return nil end
|
|
local cap = m.stresso.getStressCapacity()
|
|
if not cap or cap <= 0 then return nil end
|
|
return m.stresso.getStress() / cap
|
|
end
|
|
|
|
-- valeurs brutes (su) pour l'affichage: stress, capacite
|
|
function m.lireStressBrut()
|
|
if not m.stresso then return nil end
|
|
return m.stresso.getStress(), m.stresso.getStressCapacity()
|
|
end
|
|
|
|
-- capacite > 0 <=> le moteur tourne (verifie: capacite 0 a l'arret)
|
|
function m.moteurTourne()
|
|
if not m.stresso then return true end -- indetectable: on suppose oui
|
|
return (m.stresso.getStressCapacity() or 0) > 0
|
|
end
|
|
|
|
-- carburant: quantite totale (mB) et capacite totale
|
|
function m.lireCarburant()
|
|
if #m.cuves == 0 then return nil end
|
|
local quantite, capacite = 0, 0
|
|
for _, cuve in ipairs(m.cuves) do
|
|
for _, t in ipairs(cuve.tanks() or {}) do
|
|
quantite = quantite + (t.amount or 0)
|
|
capacite = capacite + (t.capacity or conf.CARBURANT.CAPACITE_TANK)
|
|
end
|
|
end
|
|
if capacite <= 0 then capacite = conf.CARBURANT.CAPACITE_TANK * #m.cuves end
|
|
return quantite, capacite
|
|
end
|
|
|
|
-- ACTUALISATION GPS (BLOQUANTE, jusqu'a 2 s): a appeler depuis une
|
|
-- tache dediee uniquement
|
|
function m.actualiserPosition()
|
|
local x, y, z = gps.locate(2)
|
|
if x then
|
|
posCache = { x = x + conf.OFFSET_X, y = y + conf.OFFSET_Y,
|
|
z = z + conf.OFFSET_Z }
|
|
posDate = os.clock()
|
|
end
|
|
end
|
|
|
|
-- position GPS en CACHE (non bloquante); nil si absente ou > 8 s
|
|
function m.lirePosition()
|
|
if not posCache or os.clock() - posDate > 8 then return nil end
|
|
return posCache.x, posCache.y, posCache.z
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- SORTIES
|
|
------------------------------------------------------------------
|
|
function m.reglerMoteur(niveau) -- 0 (arret) .. 15 (maxi)
|
|
niveau = math.max(0, math.min(15, math.floor(niveau + 0.5)))
|
|
redstone.setAnalogOutput("bottom", 15 - niveau)
|
|
end
|
|
|
|
function m.reglerRsc(role, vitesse)
|
|
local p = m.rscParRole[role]
|
|
if not p then return end
|
|
vitesse = math.floor(vitesse + 0.5)
|
|
local suivi = m.rpm[role]
|
|
if not suivi or suivi.v ~= vitesse then
|
|
m.rpm[role] = { v = vitesse, tChange = os.clock() }
|
|
end
|
|
p.setTargetSpeed(vitesse)
|
|
end
|
|
|
|
function m.toutArreter()
|
|
for _, p in pairs(m.rsc) do p.setTargetSpeed(0) end
|
|
for role, suivi in pairs(m.rpm) do
|
|
if suivi.v ~= 0 then m.rpm[role] = { v = 0, tChange = os.clock() } end
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- DIAGNOSTIC DES CALIBRATIONS
|
|
------------------------------------------------------------------
|
|
function m.helicesManquantes()
|
|
local manquantes = {}
|
|
for _, role in ipairs({ "lb", "rb", "lf", "rf" }) do
|
|
if not m.rscParRole[role] then table.insert(manquantes, role) end
|
|
end
|
|
return manquantes
|
|
end
|
|
|
|
function m.propulseursManquants()
|
|
local manquants = {}
|
|
for _, role in ipairs({ "prop_l", "prop_r" }) do
|
|
if not m.rscParRole[role] then table.insert(manquants, role) end
|
|
end
|
|
return manquants
|
|
end
|
|
|
|
function m.axesManquants()
|
|
local manquants = {}
|
|
for _, axe in ipairs({ "avant", "lateral", "vertical" }) do
|
|
if not m.veloParAxe[axe] then table.insert(manquants, axe) end
|
|
end
|
|
return manquants
|
|
end
|
|
|
|
return m
|
|
end
|
|
|
|
return Materiel
|