A Hub automation scriptek aszinkron JavaScriptként futnak. Emiatt a várakozó műveleteknél await használható, miközben a UI és a script Pause / Resume / Abort működése megmarad.
async function runTrain() {
dcc.setLoco(18, 30, "forward");
await delay(1000);
dcc.setLoco(18, 0, "forward");
}
await runTrain();Nem blokkoló várakozás.
await delay(1000);Több aszinkron művelet párhuzamos futtatása.
await Promise.all([
task1(),
task2(),
]);Ideiglenes információ megjelenítése a futó automation kártyán.
setInfo("Mozdony indul a B1 blokkba");Üzenet törlése:
setInfo("");Logolás a böngésző konzoljába az automation nevével.
log("P101 elérte a következő pontot", 33);A scriptet futtató automation elem biztonságos, read-only kontextusa.
log(element.id, element.name);true, amíg az Automation Control normál futási módban van.
Tipikus ciklus:
while (isRunning()) {
await runSequence();
}Ha közben Finishing módba kapcsolunk, az aktuális runSequence() befejeződik, de új ciklus már nem indul.
true, ha a globális Finishing mód aktív.
if (isFinishing()) {
return;
}async function runSequence() {
setInfo("A -> B");
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true);
setInfo("B -> A");
dcc.setLoco(18, 30, "reverse");
await dcc.waitForSensor(20, true);
}
while (isRunning()) {
await runSequence();
}
dcc.setLoco(18, 0, "forward");
setInfo("Befejezve");A switchMan() egy váltócsoportot atomikusan, all-or-none módon foglal le.
1. kéri a teljes váltócsoportot
2. ha bármelyik foglalt -> vár
3. ha mind szabad -> egyszerre lefoglalja az összeset
4. lefut a callback
5. a callback végén a foglalást automatikusan feloldja
A callback normál befejezése, exception vagy kontrollált abort esetén a finally ág megpróbálja feloldani a foglalást.
await switchMan([10, 11], async sw => {
setInfo("Váltókörzet beállítása");
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
setInfo("Indulás");
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true);
});A callback vége után a 10 és 11 váltó automatikusan felszabadul.
Csak az adott SwitchMan scope által lefoglalt váltó állítható.
await sw.setTurnout(10, true);A closed logikai állapot:
true // CLOSED
false // THROWNEz hibát ad, ha a scope például csak [10, 11] váltót birtokolja:
await sw.setTurnout(25, true);await switchMan([10, 11], async sw => {
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true);
});
await dcc.waitForSensor(40, true);
await switchMan([20, 21, 22], async sw => {
await sw.setTurnout(20, false);
await sw.setTurnout(21, true);
await sw.setTurnout(22, true);
await dcc.waitForSensor(55, true);
});
dcc.setLoco(18, 0, "forward");P101:
await switchMan([10, 11], async sw => {
// ...
});P202:
await switchMan([11, 12], async sw => {
// ...
});Ha P101 birtokolja a 10, 11 csoportot, akkor P202 vár. Amikor P101 elengedi a csoportot, P202 atomikusan megkaphatja a 11, 12 váltókat.
Várakozás közben a script információ például:
Váltókörzetre vár: 11 (P101)
Alapértelmezésben a SwitchMan korlátlan ideig vár.
await switchMan([10, 11], async sw => {
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
}, 30000);Ez legfeljebb 30 másodpercig vár.
await switchMan([10, 11], async sw => {
// ...
}, 0);A 0 azonnali timeoutot jelent, ha a teljes csoport nem szabad.
A megadható timeout jelenleg legfeljebb 600000 ms (10 perc).
async function runCycle() {
await switchMan([10, 11], async sw => {
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true);
});
await switchMan([20, 21], async sw => {
await sw.setTurnout(20, false);
await sw.setTurnout(21, true);
await dcc.waitForSensor(55, true);
});
}
while (isRunning()) {
await runCycle();
}
dcc.setLoco(18, 0, "forward");Finishing közben a már megkezdett ciklus befejezhető, de új ciklus nem indul.
- A teljes váltócsoport foglalása atomikus.
- Egy script egymásba ágyazott, egymást átfedő SwitchMan scope-jai tiltottak.
- Például
[10, 11]belsejében új[11, 12]scope hibát ad, mert önmagára várna. - SwitchMan callbacken belül a lefoglalt váltókhoz
await sw.setTurnout(...)használata ajánlott. - A normál
dcc.setTurnout(...)nem kapja meg a SwitchMan scope tulajdonjogát. - A backend lock a végső authoritative védelem.
- A WebUI piros villogó váltójelzése csak vizuális reprezentáció; a valódi lock a backendben él.
A név alapján beállít egy RouteButtont. A route lépései között alapból 250 ms várakozás van.
await setRoute("Bejárat 1");Egyedi késleltetés a route lépései között.
await setRoute("Bejárat 1", 100);SwitchMan által lefoglalt váltók beállításához a SwitchMan scope-on belül inkább
sw.setTurnout()használható. A normál route-parancsot a backend lock visszautasíthatja, ha zárolt váltót próbál állítani.
Mozdony sebesség és irány.
- cím:
1..10239 - sebesség:
0..126 - irány:
"forward"vagy"reverse"
dcc.setLoco(18, 30, "forward");Megállítás:
dcc.setLoco(18, 0, "forward");F0–F28 funkció.
dcc.setLocoFunction(18, 0, true);
dcc.setLocoFunction(18, 2, false);Fővágány táp.
dcc.setPower(true);
dcc.setPower(false);Programozóvágány táp.
dcc.setProgrammingPower(true);Azonnali emergency stop.
dcc.emergencyStop();A script API szempontjából a szenzor egységes szenzor. Nem számít, hogy S88, <Q> vagy más backend forrás adja az állapotát.
A lokálisan cache-elt szenzorállapot.
const occupied = dcc.getSensor(33);
if (occupied) {
log("33 foglalt");
}Szenzor runtime állapot beállítása.
dcc.setSensor(33, true);Event-driven várakozás egy szenzorállapotra.
Timeout nélkül korlátlan ideig vár:
await dcc.waitForSensor(33, true);10 másodperces timeout:
await dcc.waitForSensor(33, true, 10000);Felszabadulásra várás:
await dcc.waitForSensor(33, false);A megadott wait timeout jelenleg 1..600000 ms.
A normál turnout API a Layoutban konfigurált logikai váltóállapotokat használja.
Aktuális logikai állapot.
const closed = dcc.getTurnout(20);if (dcc.isClosed(20)) {
log("CLOSED");
}if (dcc.isThrown(20)) {
log("THROWN");
}dcc.setTurnout(20, true); // CLOSED
dcc.setTurnout(20, false); // THROWNdcc.setClosed(20);dcc.setThrown(20);await dcc.waitForTurnout(20, true);Timeouttal:
await dcc.waitForTurnout(20, true, 10000);await dcc.waitForClosed(20);await dcc.waitForThrown(20);A magas szintű signal API a Layoutban konfigurált logikai jelzőállapotokat használja.
const state = dcc.getSignalState(100);
log(state);if (dcc.isSignalState(100, "Slow")) {
log("Lassú menet");
}dcc.isRed(100);
dcc.isGreen(100);
dcc.isYellow(100);
dcc.isWhite(100);dcc.setSignalState(100, "Slow");dcc.setRed(100);
dcc.setGreen(100);
dcc.setYellow(100);
dcc.setWhite(100);await dcc.waitForSignalState(100, "Slow");Timeouttal:
await dcc.waitForSignalState(100, "Slow", 10000);await dcc.waitForRed(100);
await dcc.waitForGreen(100);
await dcc.waitForYellow(100);
await dcc.waitForWhite(100);Mindegyik kaphat opcionális timeoutMs paramétert.
Alacsony szintű explicit DCC extended signal aspect.
- jelzőcím:
1..2048 - aspect:
0..255
dcc.setSignalAspect(100, 16);Ha van Layoutban konfigurált logikai jelzőállapot, új scriptben általában a setSignalState() / setRed() / setGreen() stb. használata jobb.
A blokk megadható névvel vagy numerikus Layout ID-val.
A blokkban lévő DCC mozdonycímet adja vissza, vagy 0-t.
const loco = dcc.getBlock("A1");
if (loco === 18) {
log("A1-ben a 18-as mozdony van");
}dcc.setBlock("A2", 18);dcc.clearBlock("A2");Minden runtime blokk-hozzárendelés törlése.
dcc.resetBlocks();A blokk felé tartóként megjelölt mozdony címét adja vissza, vagy 0-t.
const target = dcc.getBlockTargetLoco("A2");dcc.setBlockTargetLoco("A2", 18);dcc.clearBlockTargetLoco("A2");Alacsony szintű basic DCC accessory setter.
dcc.setAccessory(100, true);Nyers DCC-EX parancs küldése.
dcc.sendRaw("<s>");Új scriptnél, ha van hozzá magasabb szintű dcc.* API, inkább azt érdemes használni.
Az emulált SD kártyáról játszik le hangfájlt. A Flow editor fájlválasztója teljes virtuális útvonalat ad, például /sd/audio/mav_szignal.mp3.
Nem blokkoló lejátszás — a script azonnal továbbfut:
playAudio("/sd/audio/mav_szignal.mp3");A playAudio() Promise-t ad vissza, ezért await-tel megvárható a lejátszás vége:
await playAudio("/sd/audio/mav_szignal.mp3");A régi rövid forma továbbra is kompatibilis: a playAudio("mav_szignal") hívás a /sd/audio/mav_szignal.mp3 fájlt játssza le. Az awaitelt hívás a hang befejezésekor folytatódik. Ha az execution közben Abortot kap, a hozzá tartozó aktív audio is leáll.
setInfo("P101 indulásra kész");
await switchMan([10, 11], async sw => {
setInfo("P101 váltókörzet beállítása");
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
dcc.setGreen(100);
dcc.setBlockTargetLoco("B2", 18);
setInfo("P101 indul");
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true, 30000);
dcc.setLoco(18, 0, "forward");
dcc.setRed(100);
dcc.clearBlockTargetLoco("B2");
dcc.setBlock("B2", 18);
setInfo("P101 megérkezett B2-be");
});async function AtoB() {
await switchMan([10, 11], async sw => {
setInfo("A -> B váltókörzet");
await sw.setTurnout(10, true);
await sw.setTurnout(11, false);
dcc.setLoco(18, 30, "forward");
await dcc.waitForSensor(33, true);
dcc.setLoco(18, 0, "forward");
});
}
async function BtoA() {
await switchMan([20, 21], async sw => {
setInfo("B -> A váltókörzet");
await sw.setTurnout(20, false);
await sw.setTurnout(21, true);
dcc.setLoco(18, 30, "reverse");
await dcc.waitForSensor(20, true);
dcc.setLoco(18, 0, "reverse");
});
}
while (isRunning()) {
await AtoB();
if (isFinishing()) {
break;
}
await BtoA();
}
setInfo("P101 befejezve");async function name()
await delay(ms)
await Promise.all([...])
isFinishing()
isRunning()
log(value, ...)
setInfo(message)
playAudio(source)
await playAudio(source)
await switchMan([turnouts], async sw => { ... })
await switchMan([turnouts], async sw => { ... }, timeoutMs)
await sw.setTurnout(address, closed)
await setRoute(name)
await setRoute(name, delayMs)
await dispatcher([blocks], async (loco, dir) => { ... }, options?)
await smartDispatcher([blocks], async (loco, dir, run) => { ... }, options?)
startTask(name, taskFunction)
isTaskRunning(name)
getTaskState(name)
dcc.clearBlock(blockName)
dcc.clearBlockTargetLoco(blockName)
dcc.getBlock(blockName)
dcc.getBlockTargetLoco(blockName)
dcc.resetBlocks()
dcc.setBlock(blockName, locoAddress)
dcc.setBlockTargetLoco(blockName, locoAddress)
dcc.getSensor(address)
dcc.setSensor(address, on)
dcc.waitForSensor(address, on, timeoutMs?)
dcc.getTurnout(address)
dcc.isClosed(address)
dcc.isThrown(address)
dcc.setClosed(address)
dcc.setThrown(address)
dcc.setTurnout(address, closed)
dcc.waitForClosed(address, timeoutMs?)
dcc.waitForThrown(address, timeoutMs?)
dcc.waitForTurnout(address, closed, timeoutMs?)
dcc.getSignalState(address)
dcc.isGreen(address)
dcc.isRed(address)
dcc.isSignalState(address, stateName)
dcc.isWhite(address)
dcc.isYellow(address)
dcc.setGreen(address)
dcc.setRed(address)
dcc.setSignalAspect(address, aspect)
dcc.setSignalState(address, stateName)
dcc.setWhite(address)
dcc.setYellow(address)
dcc.waitForGreen(address, timeoutMs?)
dcc.waitForRed(address, timeoutMs?)
dcc.waitForSignalState(address, stateName, timeoutMs?)
dcc.waitForWhite(address, timeoutMs?)
dcc.waitForYellow(address, timeoutMs?)
dcc.emergencyStop()
dcc.sendRaw("<DCC-EX command>")
dcc.setAccessory(address, active)
dcc.setLoco(address, speed, "forward|reverse")
dcc.setLocoFunction(address, function, active)
dcc.setPower(on)
dcc.setProgrammingPower(on)
Ezek ténylegesen léteznek a backend ScriptRuntime-ban, de nem részei az ajánlott Quick Help API-nak.
Alacsony szintű turnout parancs, amely nem a Layout logikai turnout konfigurációját használja.
dcc.setTurnoutRaw(20, true);Új scriptekben általában:
dcc.setTurnout(20, true);vagy SwitchMan scope-ban:
await sw.setTurnout(20, true);használata ajánlott.
Alacsonyabb szintű blokk-hozzárendelési API. Normál automation scriptben inkább a setBlock() / clearBlock() használata javasolt.
A runtime kompatibilitás miatt még léteznek:
dcc.power(on)
dcc.programmingPower(on)
dcc.loco(address, speed, direction)
dcc.locoFunction(address, function, active)
dcc.turnout(address, closed)
dcc.sensor(address, on)
dcc.accessory(address, active)
dcc.signal(address, aspect)
dcc.raw(command)
Új scriptekben ezek helyett az explicit neveket használd:
dcc.setPower(...)
dcc.setProgrammingPower(...)
dcc.setLoco(...)
dcc.setLocoFunction(...)
dcc.setTurnout(...)
dcc.setSensor(...)
dcc.setAccessory(...)
dcc.setSignalAspect(...)
dcc.sendRaw(...)
- Szenzorra, váltóra vagy jelzőállapotra várásnál használj
await-et. - A
waitFor*()timeout elhagyása általában korlátlan várakozást jelent. - Váltókörzetek konfliktusvédelméhez használj
switchMan()-t. - SwitchMan scope-ban a lefoglalt váltókat
await sw.setTurnout()-tal állítsd. - Ciklikus automationnél a külső ciklust
isRunning()/isFinishing()alapján vezesd. - Ha van magas szintű logikai API, azt részesítsd előnyben a raw DCC parancsokkal szemben.
- Az S88 /
<Q>/ egyéb érzékelőforrás a script számára ugyanaz a szenzor API. - Az emergency stop közvetlenül elérhető:
dcc.emergencyStop(). - Dispatcher használat előtt a route gráfot generálni és a Layouttal együtt menteni kell.
- Ismétlődő, párhuzamos Dispatcher próbálkozásokhoz használd a
startTask()single-flight Task Managert.
A dispatcher() a Layout mentett route gráfjából választ ki pontosan egy fizikai útvonalat, lefoglalja az érintett blokkokat, szükség esetén SwitchMan segítségével beállítja és zárolja a váltókat, majd a tényleges vonatmozgást a script callbackjére bízza.
const result = await dispatcher(
["A1", "B1", "C1"],
async (loco, dir) => {
dcc.setLoco(loco, 20, dir);
await dcc.waitForSensor(1020, true);
dcc.setLoco(loco, 0, dir);
}
);A callback paraméterei:
loco: a kiinduló blokkban lévő mozdony DCC címe;dir: a gráfból meghatározott"forward"vagy"reverse"menetirány.
Lehetséges eredmény státuszok:
completed
empty
blocked
A megadott blokkoknak nem kell a teljes fizikai blokkútvonalat tartalmazniuk.
Ha a teljes útvonal:
A1 -> B2 -> C3 -> D4
akkor ez is érvényes:
await dispatcher(["A1", "B2", "D4"], async (loco, dir) => {
// ...
});A checkpointoknak sorrendben kell szerepelniük a teljes útvonalban. Az első és utolsó blokk mindig a kért útvonal kezdő- és végpontja.
Ha nincs találat:
dispatcher_route_not_found
Ha több fizikai útvonal is illeszkedik:
dispatcher_route_ambiguous
A Dispatcher ilyenkor szándékosan nem választ önkényesen. Adj meg további köztes checkpoint blokkot, amíg pontosan egy útvonal marad.
A kiinduló blokk:
- tartalmazhat foglaltságot, hiszen ott áll az induló vonat;
- tényleges mozdony-hozzárendeléséből kapja a Dispatcher a
lococímet; - nem tartalmazhat másik target loco jelölést.
A kiinduló blokk után minden útvonalblokk csak akkor szabad, ha egyszerre:
actual loco == 0
target loco == 0
occupancy sensor == false
A Dispatcher indulás előtt minden következő blokkra beállítja a mozdony target jelölését, majd újra ellenőrzi, hogy a foglalás közben nem változott-e az állapot.
Az egymást átfedő Dispatcher útvonalakat a Windows backend DispatcherRuntime sorosítja. A script Dispatcher és a Movement ugyanazt a blokk/szegmens/váltó authority-t használja, ezért külön böngészős lockkal nem kerülhetik meg egymást.
Ha az útvonal váltóállításokat igényel, a Dispatcher automatikusan SwitchMan foglalást használ.
A gráfban tárolt váltóállásokat beállítja, majd a teljes Dispatcher callback futása alatt megtartja a SwitchMan scope-ot.
Ha a váltókörzet pillanatnyilag foglalt, az adott Dispatcher-próbálkozás blocked státusszal visszatér, így a Task Manager / scheduler később újra próbálhatja.
Ha a callback sikeresen befejeződik:
- a célblokk előtti útvonalblokkok actual + target állapota törlődik;
- a célblokk target jelölése törlődik;
- a célblokk actual mozdony-hozzárendelése a Dispatcher mozdonyára áll.
Ha a callback hibával vagy Aborttal megszakad, a Dispatcher nem állítja azt, hogy a vonat megérkezett. Csak a saját maga által létrehozott target jelöléseket oldja fel.
await dispatcher(
["A1", "B1", "C1"],
async (loco, dir) => {
// ...
},
{
timeoutMs: 30000,
setDelayMs: 250,
blockPollMs: 250,
onEmpty: async dir => {
log("A1 üres", dir);
},
onBlocked: async (loco, dir, conflicts) => {
log("Útvonal blokkolt", loco, dir, conflicts);
},
}
);timeoutMs: a backend route-resource authority várakozás timeoutja; elhagyva nincs explicit Dispatcher timeout, maximum600000 ms;setDelayMs: váltóállítások közötti késleltetés, alapból250 ms;blockPollMs: a backend route-resource authority újrapróbálási ideje, alapból250 ms, tartománya25..5000 ms;onEmpty(dir): akkor fut, ha a kiinduló blokkban nincs mozdony;onBlocked(loco, dir, conflicts): blokk- vagy váltókonfliktus esetén fut.
Ha onEmpty nincs megadva, az üres kiinduló blokk dispatcher_empty_source hibát dob.
A startTask() arra való, hogy egy automation scheduler több független async feladatot tudjon párhuzamosan futtatni anélkül, hogy ugyanazt a feladatot egyszerre többször elindítaná.
startTask("A1_TO_C1", A1toC1);A hívás azonnal visszatér; a feladat háttérben fut tovább.
Ugyanazzal a névvel egyszerre csak egy példány futhat. Ha már fut:
started = false
reason = "already-running"
Finishing módban új feladat nem indul:
started = false
reason = "finishing"
A már futó feladatok befejezhetik az aktuális menetüket.
if (isTaskRunning("A1_TO_C1")) {
log("A1_TO_C1 még fut");
}const state = getTaskState("A1_TO_C1");
log(state);A state többek között tartalmazza:
name
running
status
runCount
startedAt
finishedAt
lastResult
lastError
A Task Manager a háttérfeladat hibáját eltárolja és logolja, így abból nem lesz kezeletlen Promise rejection.
const SENSOR_A1 = 1001;
const SENSOR_B1 = 1006;
const SENSOR_B2 = 1007;
const SENSOR_C1 = 1020;
const SPEED = 20;
const HORN_FUNCTION = 2;
async function horn(loco) {
dcc.setLocoFunction(loco, HORN_FUNCTION, true);
await delay(700);
dcc.setLocoFunction(loco, HORN_FUNCTION, false);
}
async function A1toC1() {
await dispatcher(
["A1", "B1", "C1"],
async (loco, dir) => {
setInfo(`Loco ${loco}: A1 -> B1 -> C1`);
dcc.setLoco(loco, SPEED, dir);
await dcc.waitForSensor(SENSOR_B1, true);
log(`Loco ${loco}: B1`);
await horn(loco);
dcc.setLoco(loco, 25, dir);
await dcc.waitForSensor(SENSOR_C1, true);
log(`Loco ${loco}: C1`);
dcc.setLoco(loco, 0, dir);
},
{
onEmpty: async () => {
log("A1 -> C1: nincs mozdony A1-ben");
},
onBlocked: async (loco, dir, conflicts) => {
log("A1 -> C1: blokkolt", loco, conflicts);
}
}
);
}
async function C1toA1() {
await dispatcher(
["C1", "B2", "A1"],
async (loco, dir) => {
setInfo(`Loco ${loco}: C1 -> B2 -> A1`);
dcc.setLoco(loco, SPEED, dir);
await dcc.waitForSensor(SENSOR_B2, true);
log(`Loco ${loco}: B2`);
await horn(loco);
dcc.setLoco(loco, 25, dir);
await dcc.waitForSensor(SENSOR_A1, true);
log(`Loco ${loco}: A1`);
dcc.setLoco(loco, 0, dir);
},
{
onEmpty: async () => {
log("C1 -> A1: nincs mozdony C1-ben");
},
onBlocked: async (loco, dir, conflicts) => {
log("C1 -> A1: blokkolt", loco, conflicts);
}
}
);
}
while (isRunning()) {
startTask("A1_TO_C1", A1toC1);
startTask("C1_TO_A1", C1toA1);
await delay(500);
}
setInfo("Finishing - nincs új dispatcher");A scheduler 500 ms-onként megpróbálja elindítani mindkét menetet. A startTask() miatt ugyanaz a menet nem indul el újra, amíg az előző példánya fut. Ha egy Dispatcher próbálkozás empty vagy blocked státusszal gyorsan befejeződik, egy későbbi scheduler kör újra próbálhatja.
A `smartDispatcher()` a stabil `dispatcher()` mellett külön implementáció. A normál Dispatcher továbbra is a teljes útvonalat foglalja le előre; a SmartDispatcher ezzel szemben mindig csak a jelenlegi és a következő blokkot tartja.
A SmartDispatcher automatikusan:
- ellenőrzi a következő blokk `actual`, `target` és occupancy állapotát;
- a backend DispatcherRuntime-ban lefoglalja a jelenlegi→következő blokk authority-t, a közös blokk/szegmens erőforrásokkal együtt;
- beállítja a következő blokk `target loco` jelölését;
- csak az aktuális blokkátmenethez szükséges váltókat foglalja és állítja;
- clearance hiányában megállítja a mozdonyt;
- clearance megjelenésekor visszaállítja a script által kért sebességet;
- biztos megérkezés után felszabadítja az előző blokkot és az előző átmenet váltózárait;
- a célblokkba érkezéskor automatikusan megállítja a mozdonyt.
```js await smartDispatcher( ["A1", "B1", "C1"],
async (loco, dir, run) => { run.setSpeed(20);
await run.waitForBlock("B1");
await horn(loco);
run.setSpeed(25);
await run.waitForBlock("C1");
} ); ```
A SmartDispatcher alatt a normál menethez a `run.setSpeed()` használata javasolt a közvetlen `dcc.setLoco()` helyett. A SmartDispatcher csak így tudja eltárolni a kívánt sebességet és clearance után automatikusan visszaállítani.
Egy blokkhoz explicit szenzorfeltételek adhatók meg:
```js await smartDispatcher( [ "A1",
{
block: "B1",
arrivedWhen: [
{ sensor: 1000, state: true },
{ sensor: 999, state: false }
]
},
{
block: "C1",
arrivedWhen: [
{ sensor: 1010, state: true },
{ sensor: 1000, state: false }
]
}
],
async (loco, dir, run) => { run.setSpeed(20);
await run.waitForBlock("B1");
await horn(loco);
await run.waitForBlock("C1");
} ); ```
A példában B1 akkor számít biztosan elfoglaltnak, amikor:
```text sensor 1000 == true sensor 999 == false ```
Ez azt jelenti, hogy a vonat már érzékelhető B1-ben, és az előző szakaszt teljesen elhagyta. Csak ekkor szabadítja fel a SmartDispatcher az előző blokkot.
Ha egy blokkhoz nincs explicit `arrivedWhen`, az alapértelmezett feltétel:
```text aktuális blokk occupancy sensor == true előző blokk occupancy sensor == false ```
Ha két egymást követő blokk ugyanazt az occupancy szenzort használja, explicit `arrivedWhen` szükséges.
A SmartDispatcher a háttérben automatikusan próbálja megszerezni a következő blokkot.
A következő blokk csak akkor használható, ha:
```text actual loco == 0 target loco == 0 occupancy == false backend Dispatcher blokk/szegmens authority megszerezhető szükséges váltózárak megszerezhetők szükséges váltóállások beállíthatók ```
Ha ez nem teljesül, a mozdony fizikai sebessége:
```text 0 ```
de a `run.setSpeed()` által kért sebesség megmarad.
Például:
```js run.setSpeed(30); ```
Ha a következő blokk foglalt:
```text desired speed = 30 physical speed = 0 ```
Amikor a blokk szabaddá válik és a váltók is lefoglalhatók:
```text desired speed = 30 physical speed = 30 ```
Ehhez a scriptnek nem kell külön újraindító logikát írnia.
```js await run.waitForBlock("B1"); ```
Azt jelenti, hogy a B1 blokkhoz tartozó `arrivedWhen` feltételek már teljesültek, tehát a vonat biztosan megérkezett a blokkba.
```js await run.waitForClearance("C1"); ```
Azt jelenti, hogy C1 már le van foglalva a vonat számára és a hozzá vezető váltók is rendelkezésre állnak.
A `waitForClearance()` nem szükséges a SmartDispatcher működéséhez. A clearance kezelése automatikus; ez a függvény akkor hasznos, ha a scriptednek külön meg kell várnia vagy tudnia kell, hogy már szabad az út.
A SmartDispatcher blokkkezelése mellett a normál szenzor API továbbra is használható:
```js await run.waitForBlock("B1");
await dcc.waitForSensor(1234, true);
await horn(loco); ```
Így a blokkoktól független tetszőleges ponton lehet például kürtölni, lassítani vagy más műveletet végezni.
```text run.setSpeed(speed) run.stop() await run.waitForBlock(blockName) await run.waitForClearance(blockName) run.getCurrentBlock() run.getNextBlock() run.getRoute() run.getDesiredSpeed() ```
A sebességtartomány `0..126`.
```js await smartDispatcher( ["A1", "B1", "C1"], async (loco, dir, run) => { run.setSpeed(20); await run.waitForBlock("C1"); }, { setDelayMs: 250, blockPollMs: 100,
onEmpty: async dir => {
log("A1 üres", dir);
},
onBlocked: async (loco, dir, conflicts) => {
log("SmartDispatcher vár", loco, conflicts);
}
} ); ```
- `setDelayMs`: egymást követő váltóállítások közötti késleltetés;
- `blockPollMs`: a blokk- és érkezési feltételek ellenőrzési periódusa, `25..5000 ms`;
- `onEmpty(dir)`: üres induló blokk;
- `onBlocked(loco, dir, conflicts)`: akkor fut, amikor az adott következő blokkra első alkalommal várni kell.
A SmartDispatcher a célblokkba történő biztos megérkezéskor automatikusan `0` sebességre állítja a mozdonyt.