Source: hocker.js

/*
 * Copyright (C) 2022 Velometrik GmbH
 * <http://www.velometrik.de/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact: 
 * support@velometrik.de
 * 
 * Sitzknochenvermessung
 *    
 */

/* global variable. */
var sitzknochenabstand;
var conn;
var url_smartcube = $("#url_smartcube").val();
var sc ="smartcube119.local"

/* It loads an image and draws a cross on it */
class Hockerbild {

    /**
     *  A constructor for the class `Hockerbild`.
     *  `canvas.width / 28` = Pixel pro cm.
     *  @param {CANVAS} canvas - HTML-CANVAS-Objekt für die Anzeige
     */
    constructor(canvas) {
        this.PPCM = canvas.width/28;
        this.MARKSIZE = 1 * this.PPCM;
        this.canvas = canvas;
        this.ctx = canvas.getContext("2d");
        this.ctx.lineWidth = 3;
        this.ctx.strokeStyle = "white";
        this.image = new Image();
        this.image.ctx = this.ctx;
        this.image.hb = this;
        this.sp1 = {
            x: 0,
            y: 0
        };
        this.sp2 = {
            x: 0,
            y: 0
        };
        this.image.onload = function() {
            this.ctx.drawImage(this, 0, 0, 777, 444);
            this.hb.mark(this.hb.sp1.x, this.hb.sp1.y);
            this.hb.mark(this.hb.sp2.x, this.hb.sp2.y);
        };
    }

    /**
     * Druckbild holen und danach Markierungen anbringen,
     * sofern diese sich nicht im Nullpunkt befinden.
     * Der Nullpunkt ist jeweils 0.5cm vom linken und vom unteren Bildrand entfernt.
     * 
     * @param {URL} src - Url des Druckbildes
     * @param {number} x1 - X-Koodinate der 1. Markierung
     * @param {number} y1 - Y-Koodinate der 1. Markierung
     * @param {number} x2 - X-Koodinate der 2. Markierung
     * @param {number} y2 - Y-Koodinate der 2. Markierung
     */
    load(src, x1, y1, x2, y2) {
        this.sp1.x = x1;
        this.sp1.y = y1;
        this.sp2.x = x2;
        this.sp2.y = y2;
        this.image.src = src;
    }

    /**
     * It draws a cross on the image.
     * @param {number} x - X-Koordinate der Markierung
     * @param {number} y - Y-Koordinate der Markierung
     */
    mark(x, y) {
        if (x > 0 && y > 0) {
            x += 0.5;
            y += 0.5;
            this.ctx.beginPath();
            this.ctx.moveTo(x * this.PPCM - this.MARKSIZE / 2, this.canvas.height - y * this.PPCM);
            this.ctx.lineTo(x * this.PPCM + this.MARKSIZE / 2, this.canvas.height - y * this.PPCM);
            this.ctx.moveTo(x * this.PPCM, this.canvas.height - y * this.PPCM - this.MARKSIZE / 2);
            this.ctx.lineTo(x * this.PPCM, this.canvas.height - y * this.PPCM + this.MARKSIZE / 2);
            this.ctx.stroke();
        }
    }
}

/* It creates a new instance of the class `Hockerbild` and assigns it to the variable `hockerbild`. */
var hockerbild = new Hockerbild(document.getElementById("hockerbild"));

/* It creates a new XMLHttpRequest object. */
var httpStoredbld = new XMLHttpRequest();

/* A callback function that is called when the server responds to the request. */
httpStoredbld.onreadystatechange = function() {
    if (httpStoredbld.readyState == 4) {
        if (httpStoredbld.status == 200) {
            console.log("httpStoredbld response: ", httpStoredbld.responseText);
            /* Assigning the response text to the variable `response`. */
            var response = httpStoredbld.responseText;
            if (response.charAt(0) == '{') { // JSON-Objekt
                response = JSON.parse(response);
                if (response.email != null) {
                    alert("Das Druckbild wurde unter " + response.email + " gespeichert.");
                }
            } else {
                alert(response);
            }
        } else {
            alert("HTTP Fehler " + httpStoredbld.status + " beim Hochladen des Druckbildes.");
        }
    }
}

/**
 * It loads the image from the server and displays it in the canvas
 * @param {object} msg - the message received from the server
 */
function hockerbildAktualisieren(msg) {

    /* A workaround to prevent the browser from using the cached image. */
    var url = "http://" + url_smartcube + "/hockerbild?rnd=" + (new Date).getTime();

    if (msg === undefined) {
        hockerbild.load(url, 0, 0, 0, 0);

    } else if (msg.wsevent == "hockerbild" && msg.v_sum > 0) {
        hockerbild.load(url, 0, 0, 0, 0);
        document.getElementById("sitzknochenabstand").value = "0.0 cm";

    } else if (msg.wsevent == "sitzknochenabstand") {
        // Markierungen übernehmen
        hockerbild.load(url, msg.schwerpunkt1_x, msg.schwerpunkt1_y, msg.schwerpunkt2_x, msg.schwerpunkt2_y);
        // Sitzknochenabstand anzeigen (Formatieren mit ##.#)
        var fmtska = msg.sitzknochenabstand < 10 ? "" : "";

        fmtska += msg.sitzknochenabstand.toFixed(1);
        document.getElementById("sitzknochenabstand").value = fmtska + " cm";

        /* A global variable that stores the distance between the sitzknochen. */
        sitzknochenabstand = msg.sitzknochenabstand;
    }
}

/**
 * It sends a request to the server to store the current image
 * @returns The image data.
 */
function btnHochladenOnclick() {
    var email = document.getElementById("email").value;
    var url = "http://" + url_smartcube + "/storedbld"

    if (email == "") {
        if (!confirm("Das Druckbild wird anonym gespeichert."))
            return;
    } else {
        url += "?email=" + encodeURI(email);
    }
    httpStoredbld.open("GET", url, true);
    httpStoredbld.send();
}


/**
 * It receives a message from the server, parses it, and then calls the function
 * `hockerbildAktualisieren()` with the message as an argument
 * @param {object} wsmessage - The message received from the server.
 * @returns the value of the variable msg.
 */
function handleWSMessage(wsmessage) {
    var msg;

    try {
        msg = JSON.parse(wsmessage);
    } catch (error) {
        console.log("Kein gültiger JSON-String: \"" + wsmessage + "\"");
        return;
    }
    if (msg.wsevent === "hockerbild") {
        hockerbildAktualisieren(msg);
    } else if (msg.wsevent === "sattelbild") {
        // ignorieren
    } else if (msg.wsevent === "sitzknochenabstand") {
        hockerbildAktualisieren(msg);
    } else if (msg.wsevent === "plugged") {
        if (msg.type === "MT_STD") {
            /* Calling the function `hockerbildAktualisieren()` without any arguments. */
            hockerbildAktualisieren();
            document.getElementById("message").innerHTML = "<i class=\"fa fa-circle\" style=\"color: var(--primary);\" title=\"angeschlossen\"></i> SENSOR";
        }
    } else if (msg.wsevent == "unplugged") {
        if (msg.type == "MT_STD") {
            document.getElementById("message").innerHTML = "<i class=\"fa fa-circle\" style=\"color: var(--primary);\" title=\"Sensor wurde entfernt\"></i> SENSOR";
        }
    } else {
        document.getElementById("message").innerHTML = wsmessage;
    }
}


/**
 * It tries to establish a WebSocket connection to the server
 * @param {URL} ws_url - The URL of the WebSocket server.
 */
function connect(ws_url) {
    if (window["WebSocket"]) {

        /* It checks if the connection is open and closes it. */
        if (typeof conn == "object") {
            if (conn.readyState == WebSocket.OPEN)
                conn.close();
        }
        try {
            /* It creates a new WebSocket object and assigns it to the variable `conn`. */
            conn = new WebSocket(ws_url, "dummy");
        } catch (error) {
            console.error(error);
            document.getElementById("statusinfo").innerHTML = "Wenn das 'mal kein Fehler ist.";
        }
        switch (conn.readyState) {
            case WebSocket.CONNECTING:
                document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle\" title=\"wird verbunden\"></i> WEBSOCKET";
                conn.onopen = function(evt) {
                    document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle\" style=\"color: var(--primary);\" title=\"verbunden\"></i> SmartCube connected";
                    console.log('Websocketverbindung hergestellt!');
                    conn.onclose = function(evt) {
                        document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle-o\" title=\"Verbindung wurde geschlossen\"></i> no connection";
                        document.getElementById("message").value = "";
                    };
                };
                conn.onclose = function(evt) {
                    document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle-o\" title=\"Verbindung konnte nicht hergestellt werden\"></i> WEBSOCKET";
                };
                break;
            case WebSocket.OPEN: // Verbindung ist hergestellt und bereit darüber zu kommunizieren.
                document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle\" style=\"color: var(--primary);\" title=\"verbunden\"></i> WEBSOCKET";
                break;
                // case WebSocket.CLOSING: // Verbindung ist im Prozess des Schließens.
            case WebSocket.CLOSED: // Die Verbindung konnte nicht hergestellt werden.
                document.getElementById("statusinfo").innerHTML = "<i class=\"fa fa-circle-o\" title=\"Verbindung konnte nicht hergestellt werden\"></i> WEBSOCKET";
                break;
        }
        /* Assigning a callback function to the `onmessage` property of the `conn` object. */
        conn.onmessage = function(evt) {
            handleWSMessage(evt.data);
            console.log(evt.data);
        };
    } else {
        document.getElementById("statusinfo").innerHTML = "WebSocket ist in diesem Browser nicht verfügbar";
    }
}

/* Calling the function `connect()` with the argument `"ws://" + window.location.hostname + ":81/messages"`. */
/*connect("ws://" + window.location.hostname + ":" + window.location.port + "/messages");*/
connect("ws://" + url_smartcube + "/messages");