Protection contre la surcharge
Lire le stress et la capacité d'un réseau Create, ralentir un Contrôleur de vitesse de rotation avant la surcharge, donner l'alarme et dessiner une barre de stress avec son historique.
Quand un réseau Create consomme plus de stress que ses générateurs n'en fournissent, tout s'arrête dessus : les broyeurs, la presse, les tapis, même les ordinateurs qu'il fait tourner. Cela arrive quand vous ajoutez une machine de trop, ou quand une contraption démarre et que sa charge rejoint le réseau d'un coup. On construit alors en général une grosse marge de sécurité, qui gaspille des roues à eau.
Ce projet confie la marge à un ordinateur. Un Contrôleur de vitesse de rotation alimente les machines ; l'ordinateur lit le stress et la capacité du réseau à travers lui, et quand le réseau approche de sa limite, il baisse la vitesse du contrôleur. Les machines derrière le contrôleur consomment en proportion de leur vitesse : une chaîne plus lente consomme moins. Quand la place revient, l'ordinateur remonte la vitesse jusqu'à celle que vous avez demandée. Une lampe s'allume tant que le réseau est près de sa limite, et l'écran dessine une barre de stress et l'historique de la dernière minute.
Le projet montre comment piloter un bloc cinétique de Create (Blocs cinétiques de Create), et comment dessiner un graphique en direct avec gfx, avec l'astuce qui rend un graphique défilant bon marché : gfx.scroll.
Ce qu'il vous faut
- Un ordinateur qui dessine : un Ordinateur à transistors ou plus récent (
gfxn'existe pas sur le Calculateur à tubes). Un Micro-ordinateur affiche les couleurs des zones ; l'Ordinateur à transistors et le Mini-ordinateur montrent tout dans une seule couleur. - Un Contrôleur de vitesse de rotation entre vos générateurs et les machines, contre un côté de l'ordinateur. À partir du Mini-ordinateur, il peut aussi être sur un Câble de données : il s'appelle alors
kinetic@x,y,z. - Une lampe à redstone (ou une cloche) sur l'ordinateur : l'alarme.
- L'ordinateur a aussi besoin de sa propre rotation, comme tout ordinateur : un arbre dans son dos, venu d'un autre réseau (une roue à eau à lui). Un ordinateur entraîné par le réseau qu'il protège se fige avec lui pendant une surcharge, et ne pourrait plus le relancer.
Vu de dessus (la grande roue dentée du contrôleur est posée dessus, et entraîne les machines) :
+------------+
roues à eau ========== | Contrôleur | ========= l'arbre continue
+------------+
| Ordinateur | -> écran lampe sur l'ordinateur
===> +------------+
arbre dans le dos de l'ordinateurLe contrôleur touche le flanc de l'ordinateur. Réglez REGULATEUR sur ce côté, "left" ou "right" vu en face de l'écran.
Comment Create compte le stress
Chaque machine a un impact sur le stress par tr/min : une presse mécanique à 64 tr/min consomme deux fois plus qu'à 32 tr/min. Chaque générateur donne une capacité (une roue à eau qui tourne à sa vitesse donne une quantité fixe). Les deux se comptent en unités de stress (us). Le réseau est en surcharge quand la somme des impacts dépasse la somme des capacités.
Le contrôleur fait partie du même réseau que les machines qu'il entraîne : kinetic.stress() et kinetic.capacity() appelés sur lui lisent les totaux du réseau entier, les générateurs et chaque machine avant et après lui. Et comme les machines derrière lui consomment en proportion de sa vitesse, kinetic.set_speed() agit directement sur la charge.
| méthode | renvoie |
|---|---|
regulateur.stress() | le stress consommé par le réseau, en SU |
regulateur.capacity() | le stress que les générateurs peuvent fournir, en SU (0 sans rotation) |
regulateur.overstressed() | true quand le réseau est en surcharge (tout est arrêté) |
regulateur.set_speed(tr_min) | règle la vitesse visée : un nombre entier (le contrôleur va de -256 à 256 tr/min) |
Deux détails comptent. set_speed veut un nombre entier : set_speed(90.5) arrête le programme avec bad argument #1 to 'set_speed' (number has no integer representation). Et chaque nouvelle vitesse reconstruit le réseau cinétique : le programme ne l'appelle donc que quand la vitesse change vraiment.
Le programme, étape par étape
1. Les réglages
local REGULATEUR = "left" -- le Contrôleur de vitesse de rotation : un côté ou "kinetic@x,y,z"
local ALARME = "top" -- la lampe : allumée tant que le réseau est près de sa limite
local SENS = 1 -- 1 ou -1 : le sens de rotation des machines
local VITESSE_MIN = 16 -- la protection ne descend jamais plus bas
local PAS = 8 -- tr/min changés à chaque vérification
local HAUT = 0.90 -- au-dessus de 90 % de la capacité : ralentir
local BAS = 0.75 -- en dessous de 75 % : réaccélérer
local PERIODE = 0.5 -- secondes entre deux vérifications
local cible = 128 -- la vitesse voulue ; les touches + et - la changentEntre BAS et HAUT, la protection garde la vitesse telle quelle. Sans cette bande, elle accélérerait et ralentirait à chaque vérification autour d'une seule limite.
2. Lire le réseau
local regulateur = nil
local function lire_reseau()
return {
contrainte = regulateur.stress(),
capacite = regulateur.capacity(),
surcharge = regulateur.overstressed(),
}
endEn Brass, une fonction renvoie une seule valeur : les trois mesures reviennent donc dans une table. Le programme l'appelle à travers pcall : si le contrôleur est cassé ou si son chunk se décharge, la lecture échoue par une erreur au lieu d'arrêter le programme.
3. Choisir la vitesse
Chaque vérification calcule charge, la part de la capacité utilisée, et choisit une nouvelle vitesse :
- surcharge : directement à
VITESSE_MIN, pour relancer le réseau au plus vite ; - **au-dessus de
HAUT** : unPASplus lent (si elle est déjà àVITESSE_MIN, la protection ne peut plus rien : « AU MINIMUM ») ; - cible baissée avec la touche
-: jusqu'à la cible tout de suite ; - **en dessous de
BAS** et plus lent que la cible : unPASplus vite ; - sinon : maintenir.
local vitesse = VITESSE_MIN -- la vitesse réglée par la protection, sans le sens
local contrainte, capacite, charge = 0, 0, 0
local etat = "DÉMARRAGE"
local function appliquer(nouvelle)
if nouvelle == vitesse then return end
local r = pcall(regulateur.set_speed, nouvelle * SENS)
if r.ok then vitesse = nouvelle else etat = "PAS DE CONTRÔLEUR" end
end
local function verifier()
local r = pcall(lire_reseau)
if not r.ok then
etat = "PAS DE CONTRÔLEUR"
charge = 0
rs.set(ALARME, true)
return
end
contrainte = r.value.contrainte
capacite = r.value.capacite
if capacite <= 0 then
charge = 0
etat = "SANS ROTATION"
rs.set(ALARME, false)
return
end
charge = contrainte / capacite
local voulue = vitesse
if r.value.surcharge then
voulue = VITESSE_MIN
etat = "SURCHARGE"
elseif charge > HAUT then
voulue = math.max(VITESSE_MIN, vitesse - PAS)
if voulue == vitesse then etat = "AU MINIMUM" else etat = "RALENTIT" end
elseif vitesse > cible then
voulue = cible
etat = "OK"
elseif charge < BAS and vitesse < cible then
voulue = math.min(cible, vitesse + PAS)
etat = "ACCÉLÈRE"
elseif vitesse < cible then
etat = "MAINTIEN"
else
etat = "OK"
end
appliquer(voulue)
rs.set(ALARME, r.value.surcharge or charge > HAUT)
endSans rotation (capacite vaut 0), il n'y a rien à protéger : la protection attend, et ne divise pas par zéro.
4. La barre de stress
Le dessin utilise la couche de pixels de l'écran : 306 x 171 pixels sur un Micro-ordinateur, 6 x 9 par caractère. La barre occupe la largeur de l'écran sous la troisième ligne de texte. Deux traits blancs marquent BAS et HAUT, avec leur pourcentage écrit dans la petite police 3 x 5 de gfx.text.
local BX, BY, BL, BH = 7, 30, 294, 14 -- la barre : x, y, largeur, hauteur en pixels
local function couleur_zone(c)
if c >= HAUT then return "red" end
if c >= BAS then return "yellow" end
return "lime"
end
local function dessiner_barre(c)
gfx.rect(BX, BY, BL, BH, "gray", true)
local l = math.floor(math.min(1, c) * BL + 0.5)
if l > 0 then gfx.rect(BX, BY, l, BH, couleur_zone(c), true) end
for _, niveau in ipairs({BAS, HAUT}) do
local x = BX + math.floor(niveau * BL + 0.5)
gfx.line(x, BY - 2, x, BY + BH + 1, "white")
gfx.text(x - 3, BY + BH + 4, tostring(math.floor(niveau * 100 + 0.5)), "light_gray")
end
endgfx.rect(x, y, l, h, couleur, true) remplit le rectangle ; dessiner le fond gris d'abord puis la partie colorée par dessus est plus simple que de dessiner deux morceaux côte à côte.
5. Le graphique de l'historique, avec gfx.scroll
Redessiner tout un graphique à chaque vérification coûterait une boucle sur toutes les mesures. À la place, le programme décale l'image du graphique de trois pixels vers la gauche avec gfx.scroll, et ne dessine que la nouvelle colonne au bord droit. Chaque colonne reçoit aussi un pixel de chaque ligne de seuil, ce qui les dessine en pointillés qui défilent avec les données.
local GX, GY, GL, GH = 7, 93, 294, 66 -- la zone du graphique, en pixels
local COL = 3 -- largeur d'une mesure
local function y_niveau(c)
return GY + GH - math.floor(math.min(1, c) * GH + 0.5)
end
local function colonne_graphe(c)
gfx.scroll(-COL, 0, GX, GY, GL, GH)
local x = GX + GL - COL
gfx.rect(x, GY, COL, GH, "black", true)
local h = GY + GH - y_niveau(c)
if h > 0 then gfx.rect(x, GY + GH - h, COL, h, couleur_zone(c), true) end
gfx.pixel(x + 1, y_niveau(HAUT), "red")
gfx.pixel(x + 1, y_niveau(BAS), "yellow")
endAvec 294 pixels et 3 pixels par mesure, le graphique garde 98 mesures : 49 secondes à une vérification par demi-seconde.
6. Le texte et l'écran
Le texte passe par-dessus le dessin : term.clear efface les caractères mais laisse les pixels, si bien que le programme peut réécrire toutes les lignes à chaque vérification sans toucher au graphique.
local COULEURS_ETAT = {
["DÉMARRAGE"] = "light_gray", ["OK"] = "lime", ["ACCÉLÈRE"] = "light_blue",
["MAINTIEN"] = "light_gray", ["RALENTIT"] = "yellow", ["AU MINIMUM"] = "red",
["SURCHARGE"] = "red", ["SANS ROTATION"] = "gray", ["PAS DE CONTRÔLEUR"] = "red",
}
local function dessiner()
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-49s ", "PROTECTION CONTRE LA SURCHARGE"))
term.set_bg(term.colors.black)
term.set_cursor(1, 3)
term.write(string.format(" Stress %d / %d us", math.floor(contrainte + 0.5), math.floor(capacite + 0.5)))
term.set_cursor(44, 3)
term.set_fg(term.colors[couleur_zone(charge)])
term.write(string.format("%5d %%", math.floor(charge * 100 + 0.5)))
dessiner_barre(charge)
term.set_cursor(1, 7)
term.set_fg(term.colors.white)
term.write(string.format(" Vitesse %d tr/min (cible %d tr/min)", vitesse * SENS, cible * SENS))
term.set_cursor(1, 8)
term.write(" État ")
term.set_fg(term.colors[COULEURS_ETAT[etat]])
term.write(etat)
term.set_cursor(1, 10)
term.set_fg(term.colors.gray)
term.write(" Charge, dernières " .. math.floor(GL / COL * PERIODE) .. " s")
term.set_cursor(1, 19)
term.write(" + et - changent la vitesse visée")
endterm.colors[couleur_zone(charge)] transforme un nom de couleur en nombre, comme l'attend term.set_fg : term.colors.red et term.colors["red"] sont la même chose. %% dans string.format écrit un signe pour cent.
7. Le démarrage et la boucle principale
Au démarrage, la protection prend le contrôleur et le règle sur VITESSE_MIN : un démarrage en douceur, les machines accélèrent pas à pas au lieu de tout d'un coup, et la protection voit la charge monter. Si le bloc de ce côté est cinétique sans être un Contrôleur de vitesse de rotation, set_speed échoue et le programme s'arrête avec un message clair.
local function preparer()
regulateur = peripheral.wrap(REGULATEUR)
if regulateur == nil or regulateur.set_speed == nil then
error("pas de Contrôleur de vitesse de rotation sur '" .. REGULATEUR .. "'")
end
local r = pcall(regulateur.set_speed, VITESSE_MIN * SENS)
if not r.ok then error(r.error) end
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
gfx.rect(GX - 1, GY - 1, GL + 2, GH + 2, "gray")
end
preparer()
local minuteur = os.start_timer(PERIODE)
while true do
local e = os.pull_event()
if e.name == "timer" and e.id == minuteur then
verifier()
colonne_graphe(charge)
dessiner()
minuteur = os.start_timer(PERIODE)
elseif e.name == "char" and e.char == "+" then
cible = math.min(256, cible + PAS)
dessiner()
elseif e.name == "char" and e.char == "-" then
cible = math.max(VITESSE_MIN, cible - PAS)
dessiner()
end
endL'évènement char porte le caractère tapé dans le terminal : les touches marchent tant que le terminal est ouvert.
Le programme complet
-- Protection contre la surcharge : lit le stress d'un réseau Create à
-- travers un Contrôleur de vitesse de rotation, le ralentit avant la
-- surcharge, le réaccélère quand la place revient, et dessine une barre et son
-- historique.
local REGULATEUR = "left" -- le Contrôleur de vitesse de rotation : un côté ou "kinetic@x,y,z"
local ALARME = "top" -- la lampe : allumée tant que le réseau est près de sa limite
local SENS = 1 -- 1 ou -1 : le sens de rotation des machines
local VITESSE_MIN = 16 -- la protection ne descend jamais plus bas
local PAS = 8 -- tr/min changés à chaque vérification
local HAUT = 0.90 -- au-dessus de 90 % de la capacité : ralentir
local BAS = 0.75 -- en dessous de 75 % : réaccélérer
local PERIODE = 0.5 -- secondes entre deux vérifications
local cible = 128 -- la vitesse voulue ; les touches + et - la changent
local BX, BY, BL, BH = 7, 30, 294, 14 -- la barre, en pixels
local GX, GY, GL, GH = 7, 93, 294, 66 -- la zone du graphique, en pixels
local COL = 3 -- largeur d'une mesure
local COULEURS_ETAT = {
["DÉMARRAGE"] = "light_gray", ["OK"] = "lime", ["ACCÉLÈRE"] = "light_blue",
["MAINTIEN"] = "light_gray", ["RALENTIT"] = "yellow", ["AU MINIMUM"] = "red",
["SURCHARGE"] = "red", ["SANS ROTATION"] = "gray", ["PAS DE CONTRÔLEUR"] = "red",
}
local regulateur = nil
local vitesse = VITESSE_MIN -- la vitesse réglée par la protection, sans le sens
local contrainte, capacite, charge = 0, 0, 0
local etat = "DÉMARRAGE"
-- Lire et décider ------------------------------------------------------------------
local function lire_reseau()
return {
contrainte = regulateur.stress(),
capacite = regulateur.capacity(),
surcharge = regulateur.overstressed(),
}
end
local function appliquer(nouvelle)
if nouvelle == vitesse then return end
local r = pcall(regulateur.set_speed, nouvelle * SENS)
if r.ok then vitesse = nouvelle else etat = "PAS DE CONTRÔLEUR" end
end
local function verifier()
local r = pcall(lire_reseau)
if not r.ok then
etat = "PAS DE CONTRÔLEUR"
charge = 0
rs.set(ALARME, true)
return
end
contrainte = r.value.contrainte
capacite = r.value.capacite
if capacite <= 0 then
charge = 0
etat = "SANS ROTATION"
rs.set(ALARME, false)
return
end
charge = contrainte / capacite
local voulue = vitesse
if r.value.surcharge then
voulue = VITESSE_MIN
etat = "SURCHARGE"
elseif charge > HAUT then
voulue = math.max(VITESSE_MIN, vitesse - PAS)
if voulue == vitesse then etat = "AU MINIMUM" else etat = "RALENTIT" end
elseif vitesse > cible then
voulue = cible
etat = "OK"
elseif charge < BAS and vitesse < cible then
voulue = math.min(cible, vitesse + PAS)
etat = "ACCÉLÈRE"
elseif vitesse < cible then
etat = "MAINTIEN"
else
etat = "OK"
end
appliquer(voulue)
rs.set(ALARME, r.value.surcharge or charge > HAUT)
end
-- Dessin ----------------------------------------------------------------------------
local function couleur_zone(c)
if c >= HAUT then return "red" end
if c >= BAS then return "yellow" end
return "lime"
end
local function dessiner_barre(c)
gfx.rect(BX, BY, BL, BH, "gray", true)
local l = math.floor(math.min(1, c) * BL + 0.5)
if l > 0 then gfx.rect(BX, BY, l, BH, couleur_zone(c), true) end
for _, niveau in ipairs({BAS, HAUT}) do
local x = BX + math.floor(niveau * BL + 0.5)
gfx.line(x, BY - 2, x, BY + BH + 1, "white")
gfx.text(x - 3, BY + BH + 4, tostring(math.floor(niveau * 100 + 0.5)), "light_gray")
end
end
local function y_niveau(c)
return GY + GH - math.floor(math.min(1, c) * GH + 0.5)
end
local function colonne_graphe(c)
gfx.scroll(-COL, 0, GX, GY, GL, GH)
local x = GX + GL - COL
gfx.rect(x, GY, COL, GH, "black", true)
local h = GY + GH - y_niveau(c)
if h > 0 then gfx.rect(x, GY + GH - h, COL, h, couleur_zone(c), true) end
gfx.pixel(x + 1, y_niveau(HAUT), "red")
gfx.pixel(x + 1, y_niveau(BAS), "yellow")
end
local function dessiner()
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-49s ", "PROTECTION CONTRE LA SURCHARGE"))
term.set_bg(term.colors.black)
term.set_cursor(1, 3)
term.write(string.format(" Stress %d / %d us", math.floor(contrainte + 0.5), math.floor(capacite + 0.5)))
term.set_cursor(44, 3)
term.set_fg(term.colors[couleur_zone(charge)])
term.write(string.format("%5d %%", math.floor(charge * 100 + 0.5)))
dessiner_barre(charge)
term.set_cursor(1, 7)
term.set_fg(term.colors.white)
term.write(string.format(" Vitesse %d tr/min (cible %d tr/min)", vitesse * SENS, cible * SENS))
term.set_cursor(1, 8)
term.write(" État ")
term.set_fg(term.colors[COULEURS_ETAT[etat]])
term.write(etat)
term.set_cursor(1, 10)
term.set_fg(term.colors.gray)
term.write(" Charge, dernières " .. math.floor(GL / COL * PERIODE) .. " s")
term.set_cursor(1, 19)
term.write(" + et - changent la vitesse visée")
end
-- Démarrage et boucle principale -------------------------------------------------
local function preparer()
regulateur = peripheral.wrap(REGULATEUR)
if regulateur == nil or regulateur.set_speed == nil then
error("pas de Contrôleur de vitesse de rotation sur '" .. REGULATEUR .. "'")
end
local r = pcall(regulateur.set_speed, VITESSE_MIN * SENS)
if not r.ok then error(r.error) end
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
gfx.rect(GX - 1, GY - 1, GL + 2, GH + 2, "gray")
end
preparer()
local minuteur = os.start_timer(PERIODE)
while true do
local e = os.pull_event()
if e.name == "timer" and e.id == minuteur then
verifier()
colonne_graphe(charge)
dessiner()
minuteur = os.start_timer(PERIODE)
elseif e.name == "char" and e.char == "+" then
cible = math.min(256, cible + PAS)
dessiner()
elseif e.name == "char" and e.char == "-" then
cible = math.max(VITESSE_MIN, cible - PAS)
dessiner()
end
endÀ quoi ressemble l'écran
Le code de dessin du programme, nourri avec 98 mesures inventées au lieu d'un vrai réseau : la charge monte, la protection la tient sous 90 %, une grosse contraption démarre et met le réseau en surcharge un instant, la protection tombe à VITESSE_MIN puis remonte. Dernière mesure : 1792 SU utilisées sur 2048, la protection tient 96 tr/min au lieu de 128.
local HAUT, BAS, PERIODE, SENS = 0.90, 0.75, 0.5, 1
local BX, BY, BL, BH = 7, 30, 294, 14
local GX, GY, GL, GH = 7, 93, 294, 66
local COL = 3
local COULEURS_ETAT = {
["DÉMARRAGE"] = "light_gray", ["OK"] = "lime", ["ACCÉLÈRE"] = "light_blue",
["MAINTIEN"] = "light_gray", ["RALENTIT"] = "yellow", ["AU MINIMUM"] = "red",
["SURCHARGE"] = "red", ["SANS ROTATION"] = "gray", ["PAS DE CONTRÔLEUR"] = "red",
}
local cible, vitesse = 128, 96
local contrainte, capacite, charge = 1792, 2048, 0.875
local etat = "MAINTIEN"
local function couleur_zone(c)
if c >= HAUT then return "red" end
if c >= BAS then return "yellow" end
return "lime"
end
local function dessiner_barre(c)
gfx.rect(BX, BY, BL, BH, "gray", true)
local l = math.floor(math.min(1, c) * BL + 0.5)
if l > 0 then gfx.rect(BX, BY, l, BH, couleur_zone(c), true) end
for _, niveau in ipairs({BAS, HAUT}) do
local x = BX + math.floor(niveau * BL + 0.5)
gfx.line(x, BY - 2, x, BY + BH + 1, "white")
gfx.text(x - 3, BY + BH + 4, tostring(math.floor(niveau * 100 + 0.5)), "light_gray")
end
end
local function y_niveau(c)
return GY + GH - math.floor(math.min(1, c) * GH + 0.5)
end
local function colonne_graphe(c)
gfx.scroll(-COL, 0, GX, GY, GL, GH)
local x = GX + GL - COL
gfx.rect(x, GY, COL, GH, "black", true)
local h = GY + GH - y_niveau(c)
if h > 0 then gfx.rect(x, GY + GH - h, COL, h, couleur_zone(c), true) end
gfx.pixel(x + 1, y_niveau(HAUT), "red")
gfx.pixel(x + 1, y_niveau(BAS), "yellow")
end
local function dessiner()
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.set_fg(term.colors.white)
term.write(string.format(" %-49s ", "PROTECTION CONTRE LA SURCHARGE"))
term.set_bg(term.colors.black)
term.set_cursor(1, 3)
term.write(string.format(" Stress %d / %d us", math.floor(contrainte + 0.5), math.floor(capacite + 0.5)))
term.set_cursor(44, 3)
term.set_fg(term.colors[couleur_zone(charge)])
term.write(string.format("%5d %%", math.floor(charge * 100 + 0.5)))
dessiner_barre(charge)
term.set_cursor(1, 7)
term.set_fg(term.colors.white)
term.write(string.format(" Vitesse %d tr/min (cible %d tr/min)", vitesse * SENS, cible * SENS))
term.set_cursor(1, 8)
term.write(" État ")
term.set_fg(term.colors[COULEURS_ETAT[etat]])
term.write(etat)
term.set_cursor(1, 10)
term.set_fg(term.colors.gray)
term.write(" Charge, dernières " .. math.floor(GL / COL * PERIODE) .. " s")
term.set_cursor(1, 19)
term.write(" + et - changent la vitesse visée")
end
-- mesures inventées : une charge qui monte, tenue sous 90 %, puis une surcharge
local function mesure(i)
if i <= 30 then return 0.35 + i * 0.017 end
if i <= 55 then return 0.84 + 0.04 * math.sin(i * 0.7) end
if i <= 58 then return 1.08 end
if i <= 62 then return 0.30 + (i - 58) * 0.05 end
return math.min(0.875, 0.50 + (i - 62) * 0.02)
end
gfx.clear()
gfx.rect(GX - 1, GY - 1, GL + 2, GH + 2, "gray")
for i = 1, 98 do
colonne_graphe(mesure(i))
end
dessiner()
L'essayer
- Alimentez un Contrôleur de vitesse de rotation avec quelques roues à eau, et mettez une grosse charge derrière : plusieurs presses mécaniques, une rangée de roues de concassage ou un grand mur de ventilateurs. Posez l'ordinateur contre le contrôleur, la lampe dessus.
- Lancez le programme : la vitesse part de 16 tr/min et monte de 8 chaque demi-seconde. Regardez la barre grandir.
- Si la charge est assez lourde, la montée s'arrête avant que la barre n'atteigne la zone rouge : l'état affiche
MAINTIEN, et la vitesse reste sous 128. - Ajoutez une presse pendant que ça tourne : la barre bondit, la lampe s'allume, l'état affiche
RALENTIT, et la vitesse baisse jusqu'à ce que la barre repasse sous 90 %. - Cassez une roue à eau : la capacité chute, le réseau peut être en surcharge un instant ; la protection tombe au minimum puis remonte jusqu'à la vitesse que les roues restantes peuvent porter.
- Tapez
-plusieurs fois dans le terminal : la cible baisse, et le contrôleur suit aussitôt.
Variantes
- Une cloche ou une Liaison de redstone. Remplacez
rs.set(ALARME, ...)parlink.set("minecraft:redstone", "minecraft:bell", ...)pour donner l'alarme n'importe où dans la base. Ne la sonnez que quand l'état passe àSURCHARGE, comme la cloche deAlarme de stock et panneau d'affichage. - Plusieurs chaînes, une protection. Prenez plusieurs contrôleurs, chacun avec sa priorité. Quand le réseau manque, ralentissez d'abord la chaîne la moins importante (le générateur de pierre avant le fer).
- Un journal. Ajoutez l'heure et la charge à un fichier chaque minute, pour savoir quand le réseau manque (
Enregistreur de données et graphique). - Un écran à distance. Envoyez
chargeetetatau tableau de bord de l'usine avecnet.broadcast(Tableau de bord d'usine). - Un pas plus malin. Le stress des machines derrière le contrôleur croît avec la vitesse : la vitesse qui ramène la charge à, disons, 85 % vaut environ
vitesse * 0.85 / charge. Utilisez-la au lieu dePASpour réagir plus vite, arrondie avecmath.floorpuisqueset_speedveut un nombre entier.