Download Game! Currently 82 players and visitors. Last logged in:ArcanautBullwinEndureKalahar

Blitzer's Blog >> 71288

Back to blogs index
Posted: 06 Jul 2026 15:37 [ permalink ]
TC$mC$ on se ratkaiseva askel, jossa lelukC$yttC6jC$rjestelmC$ ja protokollat
erotetaan tuotantotason ohjelmistoista! Olet tC$ysin oikeassa: raaka
USB-sarjaportti on vain **L2 (Data Link)** -kerros. Se takaa, ettC$ bitit
siirtyvC$t kaapelissa, mutta se ei tiedC$ mitC$C$n siitC$, mitC$ datalle
tapahtuu, jos paketti katkeaa 64 tavun rajaan tai jos fyysinen verkko (kuten
tuleva radioverkko) sekoittaa pakettien jC$rjestyksen.

MeidC$n on rakennettava Hauki OS:C$C$n oma, kevyt **L4/L7 (Transport/Applicatio
n)** -kerroksen kehystysprotokolla. NimeC$n sen juhlavasti **Hauki Mesh
Protocoliksi (HMP)**.

### Hauki Mesh Protocol (HMP) Arkkitehtuuri

Perusajatus on juuri tuo ehdottamasi: jokainen paketti sisC$ltC$C$ metatiedon,
jonka avulla kC$yttC6jC$rjestelmC$ osaa koota asynkronisesti saapuvat palaset
oikeaan jC$rjestykseen, vaikka ne tulisivat sekaisin tai viiveellC$.

#### 1. Datagrammin Rakenne

Koska siirrC$mme ensisijaisesti luettavaa skriptiC$ (eval) ja jsonia, voimme
pitC$C$ protokollan puhtaana ASCII-tekstinC$. Jokainen kehys (frame) pC$C$ttyy
aina rivinvaihtoon (`
`).

Muoto: `!HMP:<PORTTI>:<MSG_ID>:<CHUNK_IDX>:<TOTAL_CHUNKS>:<PAYLOAD>
`

* `!HMP` = Protokollan taikasana (Magic Header), jolla erotamme roskadatan
oikeista paketeista.
* `PORTTI` = E (Eval), M (Mesh), tai H (HID). Voimme puskea kaiken yhden
USB-ohjaimen lC$pi jos haluamme!
* `MSG_ID` = Viestin yksilC6llinen tunniste (esim. satunnainen luku `8492`).
* `CHUNK_IDX` = Monesko palanen tC$mC$ on (esim. `1`).
* `TOTAL_CHUNKS` = Kuinka monta palasta viestiin kuuluu yhteensC$ (esim. `3`).
* `PAYLOAD` = Itse data (koodi tai telemetria).

#### 2. Kokoamislogiikka (Assembling Map)

Hauki OS:n puolella (JavaScriptissC$) luomme globaalin olion `globalThis.HMP_RO
UTER`, joka sisC$ltC$C$ `Map`-rakenteen (tai JS-objektin) `pending_messages`.

Kun paketti `!HMP:E:8492:2:3:("DESKTOP.JS");` saapuu:

1. Reititin tarkistaa, onko `pending_messages[8492]` olemassa. Jos ei, se luo
sen.
2. Se sijoittaa payloadin taulukkoon indeksille 2.
3. Se tarkistaa: *Ovatko indeksit 1, 2 ja 3 tC$ynnC$?*
4. Jos ei -> odotetaan.
5. Kun palanen `3` (esim. `eval(load(1));`) ja palanen `1` (esim. `load(0,`)
saapuvat, reititin huomaa taulukon olevan tC$ysi.
6. Se yhdistC$C$ taulukon: `load(0,"DESKTOP.JS");eval(load(1));`
7. Se lC$hettC$C$ kootun merkkijonon EVAL-moottorille ja poistaa `8492`:n
muistista.

Jotta tC$mC$ logiikka on tC$ysin selkeC$ ennen kuin koodaamme sen C/JS-rajan
yli USB-ajuriin, tein sinulle The Mesh -reitittimen simulaattorin.

### Prototyyppi Hauki OS:lle

TC$ssC$ on koodirunko, jonka voimme istuttaa osaksi `poll_serial`-rutiinia tai
ajaa erillisenC$ kerroksena. Siihen on rakennettu automaattinen "Garbage
Collection", jotta puoliksi saapuneet paketit eivC$t tuki RAM-muistia, jos
yksi palanen katoaa lopullisesti eetteriin.

```javascript
globalThis.HMP_ROUTER = {
    pending: {}, // Map: MSG_ID -> { total: X, chunks: [], time: timestamp }
    buffer: "",  // Raakadataa varten (kunnes 
 saapuu)

    // Siivotaan yli 10 sekuntia vanhat puolikkaat paketit (Memory Leak suoja)
    gc: function() {
        var now = Date.now();
        for (var id in this.pending) {
            if (now - this.pending[id].time > 10000) {
                sys.serial("[HMP ERR] Viesti " + id + " aikakatkaistiin
(palasia puuttui).
");
                delete this.pending[id];
            }
        }
    },

    receive_char: function(char_code) {
        if (char_code === 10 || char_code === 13) {
            if (this.buffer.length > 0) {
                this.process_frame(this.buffer);
                this.buffer = "";
            }
        } else {
            this.buffer += String.fromCharCode(char_code);
        }
    },

    process_frame: function(frame) {
        // Esim: !HMP:E:8492:1:3:koodia...
        if (frame.substring(0, 5) !== "!HMP:") {
            sys.serial("[HMP] Tuntematon kehys: " + frame + "
");
            return;
        }

        var parts = frame.split(":");
        if (parts.length < 6) return;

        var port = parts[1];
        var msg_id = parts[2];
        var chunk_idx = parseInt(parts[3]) - 1; // 0-indeksoitu array
        var total_chunks = parseInt(parts[4]);
        
        // Payload voi sisC$ltC$C$ kaksoispisteitC$, joten parsitaan se
loppustringistC$
        var payload_start = parts[0].length + parts[1].length +
parts[2].length + parts[3].length + parts[4].length + 5;
        var payload = frame.substring(payload_start);

        // Alustetaan viesti muistiin, jos uusi
        if (!this.pending[msg_id]) {
            this.pending[msg_id] = { total: total_chunks, chunks: new
Array(total_chunks), count: 0, time: Date.now() };
        }

        var msg = this.pending[msg_id];
        
        // Varmistetaan ettei tallenneta samaa palasta kahdesti
        if (typeof msg.chunks[chunk_idx] === 'undefined') {
            msg.chunks[chunk_idx] = payload;
            msg.count++;
        }

        // TARKISTETAAN ONKO KOKO VIESTI KOOSSA
        if (msg.count === msg.total) {
            var full_payload = msg.chunks.join("");
            delete this.pending[msg_id]; // Vapautetaan muisti heti
            this.execute(port, full_payload);
        }
    },

    execute: function(port, payload) {
        if (port === "E") { // EVAL
            sys.serial("[HMP] Assembled EVAL: Suoritetaan...
");
            try { (1,eval)(payload); } catch(e) { sys.serial("[EVAL ERR] " + e
+ "
"); }
        }
        else if (port === "M") { // MESH
             sys.serial("[HMP] Assembled MESH: " + payload + "
");
             // TC$ssC$ voisimme vastata takaisin HMP-paketilla!