Apache+php--->browser+js+svg

Тема в разделе "Raspberry Pi", создана пользователем Igor68, 4 июл 2024.

  1. Igor68

    Igor68 Гуру

    Доброго времени суток!
    PS: Пишу сие от жары, что устал немного, ну... и в надежде что это кому-то поможет. Сам через такое прошел не так давно. Всё рассматриваемое реализация того что описано:
    https://forum.amperka.ru/threads/Разговоры-на-технические-темы.22016/page-12#post-314348
    А именно построение графиков в окне браузера, отображение данных и прочее.

    ШАГ 1:
    Начинается всё с получения данных от сервера... WEB сервера конечно. Точнее Вашего локального WEB сервера на устройстве (предположительно МАЛИНЫ, но можно и другого). Предполагается что на этом устройстве крутится ещё что-то полезное, данные которого надо отображать в браузере.
    Сначала получение данных просто обращаясь к скрипту на этом сервере из браузера. А почему не напрямую, а через скрипт? Да потому что скрипт, в моём случае PHP, но можно Питон и прочее может выполнять что-то еще - к примеру обращаться к базе данных например MySQL.
    Обратившись к файлу MyPHP.php из распакованного архива:
    DataSVGtest.zip - это архив всей страницы WEB сервера (в моём случае Apache).
    Вы должны получить данные в формате JSON в окне браузера.
    Это содержимое файла "data/abpupowerauto.json" - это набор данных, которые надо отобразить:
    -- массив "ДАТА и ВРЕМЯ" в формате UTC
    -- массив "НАПРЯЖЕНИЕ"
    -- массив "ЧАСТОТА"
    эти параметры приведены как пример от реальных измерений. Следует отметить, что данные в реальности постоянно перезаписываются и накопитель в виде NAND в связи с недолговечностью непригоден. В нашем случае это просто статический пример пример.

    PS: Простите, но описано всё будет пошагово.
     

    Вложения:

    • DataSVGtest.zip
      Размер файла:
      4,8 КБ
      Просмотров:
      17
    Последнее редактирование: 4 июл 2024
  2. Igor68

    Igor68 Гуру

    ШАГ2:
    Теперь надо, чтобы загружаемая страница отображала область SVG:
    [​IMG]
    И загружала данные самостоятельно с интервалом времени. Это можно проконтролировать с помощью браузера (у меня Firefox)
    МЕНЮ: Инструменты-->Инструменты браузера-->Инструменты вэб-разработчика. в Опции "Отладчик" в файле "index.js" в функции:
    Код (Javascript):
    //Запросы серверу
    var dbI;
    function LoadIndex() {
        var str;
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.onreadystatechange = function () {
        if(xmlHttp.readyState == 4) {
            if(xmlHttp.status == 200) {
                str = xmlHttp.responseText;
                //document.getElementById("test").innerHTML = str;
                dbI = eval('(' + str +')');
                ParseIndex();
                }
            }
        }
        xmlHttp.open("GET", "MyPHP.php?dtime=" + new Date().getTime(), true);
        xmlHttp.send();
    }
    Сделать точку останова в строке с содержимым ParseIndex(); и при остановке навести курсор
    на значение dbI в предыдущей строке.
    должны быть определены:
    Код (Text):
    F220: (800)  [...]
    U220: (800)  [...]
    dtime: (800)  [...]
    Значит мы получаем с интервалом:
    Код (Javascript):
    const tloop = 1000;    //таймаут ms цикла данных
    данные от нашего сервера:
    --частота 800 значений
    --напряжение 800 значений
    --дата и время 800 значений
    Учтите, что ваша программа (если не этот пример) на сервере должна перезаписывать/изменять массивы в файле "abpupowerauto.json". Это только пример.

    Архив страницы на сервере:
    DataSVGtest.zip
     

    Вложения:

  3. Igor68

    Igor68 Гуру

    Итак данные принимаем, заготовлена ВЭБ страница.
    ШАГ3:
    Приступаем к созданию координатной сетки, если нужна:
    [​IMG]
    Следует отметить, что для работы были описаны ряд функций в отдельный файл - язык не поворачивается назвать библиотекой.
    Файл "graphSVG.js" содержит функции для работы:
    -- Инициализация "холста"
    -- Фиксация шаблона и работа с ним
    -- Рисование ЛИНИИ и ПРЯМОУГОЛЬНИКА на холсте
    -- Вывод текстовой строки на холст
    -- Получение координат курсора мыши на холсте
    Можно конечно всё это делать и своими функциями, но уж больно скучно.
    Следует иметь в виду что в данном примере реализовано два цикла:
    --Цикл данных (получение от сервера):
    Код (Javascript):
    //цикл данных
    function MyLoop() {
        LoadIndex();
         setTimeout(MyLoop, tloop);
    }
    --Цикл графики:
    Код (Javascript):
    //цикл графики
    function LoopGraph() {
        ClearGraphSVG(); //обновляем холст из шаблона, а не рисуем каждый раз
       
        setTimeout(LoopGraph, tgraph);
    }
    Но стартовая функция не только запускает эти циклы, и инициализирует параметры холста:
    Код (Javascript):
    unction MyStart() {
        //
        InitGrarhSVG("MySVG", Xmax, Ymax); //инициализируем холст
        GrawGrid();    //рисуем сетку
        SetPatternSVG(); //принимаем весь холст с сеткой как шаблон
        //активация "циклов"
        MyLoop();
        LoopGraph();
    }
     
    Архив с примером:
    DataSVGtest.zip
     

    Вложения:

  4. Igor68

    Igor68 Гуру

    Ну теперь всё готово для отображения данных. Приступим...
    ШАГ4:
    На текущем шаге определяем масштабы параметров ЧАСТОТА и НАПРЯЖЕНИЕ, нанесение значений на сетку,
    отображение параметров в виде графиков из массивов, отображение текущих значений в левом верхнем углу холста.
    Код (Javascript):
    //цикл графики
    function LoopGraph() {
        ClearGraphSVG(); //обновляем холст из шаблона, а не рисуем каждый раз
        //
        if(DataFlag == true) {
            var cnt = 0;
            var sx = 1;
            var x1 = 0;
            //график (из отрезков) на холст
            while(cnt < 799) {
                var x2 = x1 + sx;
                //в левом верхнем углу последнее измерение - реального времени
                if(cnt == 0) {
                    DrawText("rU220t", 5, 15, '12px', "rgb(255,0,0)", U220[cnt] + "V");
                }
                DrawLine("U220" + cnt, x1, 201 - ((U220[cnt] - 230) * GU220s), x2, 201 - ((U220[cnt + 1] - 230) * GU220s), "1px", "rgb(255,0,0)" );
                x1 = x2;
                cnt++;
            }
            var x1 = 0;
            cnt = 0;
            //график (из отрезков) на холст
            while(cnt < 799) {
                var x2 = x1 + sx;
                //в левом верхнем углу последнее измерение - реального времени
                if(cnt == 0) {
                    DrawText("rF220t", 5, 27, '12px', "rgb(0,255,0)", F220[cnt] + "Hz");
                }
                DrawLine("F220" + cnt, x1, 201 - ((F220[cnt] - 50) * GF220s), x2, 201 - ((F220[cnt + 1] - 50) * GF220s), "1px", "rgb(0,255,0)" );
                x1 = x2;
                cnt++;
            }
            //параметры (масштаб) на сетке
            cnt = 1;
            while(cnt < 4) {
                var uf = Math.round((startscale + (cnt * scale)) * Math.pow(10,2)) / Math.pow(10,2);
                DrawText("U220t" + cnt, 105, 401 - (cnt * 100) - 5, '12px', "rgb(255,0,0)", uf + "V");
                var df = Math.round((fstartscale + (cnt * fscale)) * Math.pow(10,2)) / Math.pow(10,2);
                DrawText("F220t" + cnt, 705, 401 - (cnt * 100) - 5, '12px', "rgb(0,255,0)", df + "Hz");
                cnt++;
            }
            var uf = Math.round((startscale + (cnt * scale)) * Math.pow(10,2)) / Math.pow(10,2);
            DrawText("U220t" + cnt, 105, 401 - (cnt * 100) + 15, '12px', "rgb(255,0,0)", uf + "V");
            var df = Math.round((fstartscale + (cnt * fscale)) * Math.pow(10,2)) / Math.pow(10,2);
            DrawText("F220t" + cnt, 705, 401 - (cnt * 100) + 15, '12px', "rgb(0,255,0)", df + "Hz");
            //ДАТА и ВРЕМЯ на сетке
            cnt = 0;
            while(cnt < 8) {
                var Dstr = timeConverter(dtime[cnt * 100] * 1000, 255);
                var Tstr = timeConverter(dtime[cnt * 100] * 1000, 0);
                DrawText("Date" + cnt, (cnt * 100) + 5, 401 - 5, '12px', "white", Dstr);
                DrawText("Time" + cnt, (cnt * 100) + 5, 401 - 17, '12px', "white", Tstr);
                cnt++;
            }
        }
     
        setTimeout(LoopGraph, tgraph);
    }
    А парсинг в цикле данных - определение масштаба шкалы и т.п.
    Код (Javascript):
    function ParseIndex() {
        var cnt = 0;
        U220max = 0;
        U220min = 999;
        F220max = 0;
        F220min = 999;
        //определяем максимумы и минимумы параметров и заполнение массивов
        while(cnt < 800) {
            U220[cnt] = parseFloat(dbI.U220[cnt]);
            if(U220max < U220[cnt]) U220max = U220[cnt];
            if(U220min > U220[cnt]) U220min = U220[cnt];
            F220[cnt] = parseFloat(dbI.F220[cnt]);
            if(F220max < F220[cnt]) F220max = F220[cnt];
            if(F220min > F220[cnt]) F220min = F220[cnt];
            dtime[cnt] = parseInt(dbI.dtime[cnt], 10);
            cnt++;
        }
        //напряжение - масштаб шкалы
        if((Math.abs(U220max - 230) < 4) && (Math.abs(U220min - 230) < 4)) {
            GU220s = 50;    //200px / 4v
            scale = 2;
            startscale = 226;
        } else    if((Math.abs(U220max - 230) < 10) && (Math.abs(U220min - 230) < 10)) {
            GU220s = 20;    //200px / 10v
            scale = 5;
            startscale = 220;
        } else if((Math.abs(U220max - 230) < 20) && (Math.abs(U220min - 230) < 20)) {
            GU220s = 10;    //200px / 20v
            scale = 10;
            startscale = 210;
        } else if((Math.abs(U220max - 230) < 40) && (Math.abs(U220min - 230) < 40)) {
            GU220s = 5;        //200px / 40v
            scale = 20;
            startscale = 190;
        } else if((Math.abs(U220max - 230) < 100) && (Math.abs(U220min - 230) < 100)) {
            GU220s = 2;        //200px / 100v
            scale = 50;
            startscale = 130;
        } else if((Math.abs(U220max - 230) < 200) && (Math.abs(U220min - 230) < 200)) {
            GU220s = 1;        //200px / 200v
            scale = 100;
            startscale = 30;
        } /*else if((Math.abs(U220max - 230) < 230) && (Math.abs(U220min - 230) < 230)) {
            GU220s = 1;
            scale = 115;
            startscale = 0;
        }*/

        //частота - масштаб шкалы
        if((Math.abs(F220max - 50) < 0.01) && (Math.abs(F220min - 50) < 0.01)) {
            GF220s = 10000;
            fscale = 0.01;
            fstartscale = 49.98;
        } else if((Math.abs(F220max - 50) < 0.1) && (Math.abs(F220min - 50) < 0.1)) {
            GF220s = 1000;
            fscale = 0.1;
            fstartscale = 49.8;
        } else if ((Math.abs(F220max - 50) < 2) && (Math.abs(F220min - 50) < 2)) {
            GF220s = 100; // 200px / 2Hz
            fscale = 1;
            fstartscale = 48; //49.8;
        } else if ((Math.abs(F220max - 50) < 5) && (Math.abs(F220min - 50) < 5)) {
            GF220s = 40; //200px / 5Hz
            fscale = 2.5;
            fstartscale = 45;
        }   else { //if ((Math.abs(F220max - 50) < 10) && (Math.abs(F220min - 50) < 10)) {
            GF220s = 20; //200px / 10Hz
            fscale = 10;
            fstartscale = 30;
        }
        //
        DataFlag = true;
    }
    [​IMG]
    Архив:
    DataSVGtest.zip

    PS: А вот определение параметров по маркеру, по координатам курсора на холсте, надо или нет - не знаю.
     

    Вложения:

  5. Igor68

    Igor68 Гуру

    ШАГ5:
    Скриншот сделать не мог, а фото с экрана что-то с чем-то. Когда наводится курсор мыши на полотно появляется на полотне окошко с данными и маркер (вертикальная линия) так же как тут:
    https://forum.amperka.ru/threads/Разговоры-на-технические-темы.22016/page-12#post-314348
    Собственно архив:
    DataSVGtest.zip


    Ну вроде всё.

    PS: Основной смысл в неиспользовании программ на стороне пользователя - только браузер.
    Если кто испытал, будьте добры сообщить что за браузер и каков результат. У меня Firefox с ОС Didian. Я это к тому, что у меня получалось на разных машинах иметь разные результаты отображений. Правда к SVG это не относилось, но всё же!
    Спасибо!
     

    Вложения:

    • DataSVGtest.zip
      Размер файла:
      12,7 КБ
      Просмотров:
      15
    Последнее редактирование: 5 июл 2024
  6. Igor68

    Igor68 Гуру

    Вот с танцами с бубном сделал скриншот:
    [​IMG]
    Дело в том, что уходом курсора из области холста макер и окно с данными исчезают. Курсор опять не отобразился на скриншоте.

    PS: Ребята скажите а у Вас сработало. Мне интересно про разные браузеры. У меня Firefox 115.12.0esr (64-разрядный) и ОС 5.10.0-30-amd64 #1 SMP Debian 5.10.218-1 (2024-06-01) x86_64 GNU/Linux
    Спасибо!
     

    Вложения:

    Последнее редактирование: 6 июл 2024
  7. Igor68

    Igor68 Гуру

    ...итак: Никто не сказал как страница отображается у Вас в браузере:(
    Может буквы уплыли и ещё что-то.
    Ну да ладно.
    Допустим всё отображается нормально. А почему в JSON? Да потому, что все браузеры их понимают их и JS браузера спокойно их парсит:
    Код (Javascript):
     dbI = eval('(' + str +')'); //парсинг строки "str" в "dbI"
    Формат JSON можно посмотреть:
    https://habr.com/ru/articles/554274/
    Данные в примере, что непрерывно перезаписываются в файл "abpupowerauto.json", получаем от устройства
    https://owen.ru/uploads/re_mu110-6u_1833.pdf
    По Modbus RTU. Эти данные записываются в буферы (массивы) таким образом, что в начало массивов записываются новые денные, когда перед записью все массивы "сдвигаются" вправо. В цикле Массивы перезаписываются в файл.
    Для этих операций я применил программу на Си, которая запускается на устройстве с ВЭБ сервером.
    Функция записи файла JSON:
    Код (C++):
    //вывод в файл JSON массива мониторинга питания автоматики
    int    ffmjson(void) {
        int    cnt = sz;
        //
        while(cnt > 0) {
            Um[cnt] = Um[cnt-1];
            Fm[cnt] = Fm[cnt-1];
            dtime[cnt] = dtime[cnt-1];
            cnt--;
        }
        //
        FILE * file;
        file = fopen((const char*)(&namefile[0]),"wt");
        if(file < 0)
            return -1;
        //
        fprintf(file, "{\n"); //начало JSON
        // ПАРАМЕТРЫ СТАТУСА СОЕДИНЕНИЙ - Это для MODMUS RTU
        fprintf(file, "\"constat_F1_RdInIO\" : \"%02X\",\n", f1data.constat_F1_RdInIO);
        fprintf(file, "\"constat_F1_RdOutIO\" : \"%02X\",\n", f1data.constat_F1_RdOutIO);
        fprintf(file, "\"constat_F1_WrOutIO\" : \"%02X\",\n", f1data.constat_F1_WrOutIO);
        fprintf(file, "\"constat_Upower\" : \"%02X\",\n", f1data.constat_Upower);
        // ДАТА и ВРЕМЯ в UTC - Массив в JSON
        cnt = 0;
        fprintf(file, "\"dtime\" : [");
        while(cnt < (sz - 1)) {
            fprintf(file, "\"%lli\",", dtime[cnt]);
            cnt++;
        }
        fprintf(file, "\"%lli\"],\n", dtime[cnt]);
        // НАПРЯЖЕНИЕ - Массив в JSON
        cnt = 0;
        fprintf(file, "\"U220\" : [");
        while(cnt < (sz - 1)) {
            fprintf(file, "\"%0.2f\",", Um[cnt]);
            cnt++;
        }
        fprintf(file, "\"%0.2f\"],\n", Um[cnt]);
        // ЧАСТОТА - Массив в JSON
        cnt = 0;
        fprintf(file, "\"F220\" : [");
        while(cnt < (sz - 1)) {
            fprintf(file, "\"%0.2f\",", Fm[cnt]);
            cnt++;
        }
        fprintf(file, "\"%0.2f\"]\n", Fm[cnt]);
        fprintf(file, "}\n"); //окончание JSON
        //
        fclose(file);
        return 0;
    }
     
    Далее этот файл читается простым PHP скриптом при обращении ВЭБ страницы к
    ВЭБ серверу:
    PHP:
    <?php
            $cmd = "cat data/abpupowerauto.json";
            $data = shell_exec($cmd);
            echo $data;
    ?>
     
     
  8. Igor68

    Igor68 Гуру

    Доброго времени суток! Хотел бы извиниться за мою невнимательность в вызове (точнее построение функции JS):
    Код (Javascript):
    //цикл графики
    function LoopGraph() {
        //ClearGraphSVG(); //обновляем холст из шаблона, а не рисуем каждый раз
        //
        if(DataFlag == true) {
            var cnt = 0;
            var sx = 1;
            var x1 = 0;
            ClearGraphSVG(); //обновляем холст из шаблона, а не рисуем каждый раз
            //график (из отрезков) на холст
            while(cnt < 799) {
                var x2 = x1 + sx;
                //в левом верхнем углу последнее измерение - реального времени
                if(cnt == 0) {
                    DrawText("rU220t", 5, 15, '12px', "rgb(255,0,0)", U220[cnt] + "V");
                }
                DrawLine("U220" + cnt, x1, 201 - ((U220[cnt] - 230) * GU220s), x2, 201 - ((U220[cnt + 1] - 230) * GU220s), "1px", "rgb(255,0,0)" );
                x1 = x2;
                cnt++;
            }
            var x1 = 0;
            cnt = 0;
            //график (из отрезков) на холст
            while(cnt < 799) {
                var x2 = x1 + sx;
                //в левом верхнем углу последнее измерение - реального времени
                if(cnt == 0) {
                    DrawText("rF220t", 5, 27, '12px', "rgb(0,255,0)", F220[cnt] + "Hz");
                }
                DrawLine("F220" + cnt, x1, 201 - ((F220[cnt] - 50) * GF220s), x2, 201 - ((F220[cnt + 1] - 50) * GF220s), "1px", "rgb(0,255,0)" );
                x1 = x2;
                cnt++;
            }
            //параметры (масштаб) на сетке
            cnt = 1;
            while(cnt < 4) {
                var uf = Math.round((startscale + (cnt * scale)) * Math.pow(10,2)) / Math.pow(10,2);
                DrawText("U220t" + cnt, 105, 401 - (cnt * 100) - 5, '12px', "rgb(255,0,0)", uf + "V");
                var df = Math.round((fstartscale + (cnt * fscale)) * Math.pow(10,2)) / Math.pow(10,2);
                DrawText("F220t" + cnt, 705, 401 - (cnt * 100) - 5, '12px', "rgb(0,255,0)", df + "Hz");
                cnt++;
            }
            var uf = Math.round((startscale + (cnt * scale)) * Math.pow(10,2)) / Math.pow(10,2);
            DrawText("U220t" + cnt, 105, 401 - (cnt * 100) + 15, '12px', "rgb(255,0,0)", uf + "V");
            var df = Math.round((fstartscale + (cnt * fscale)) * Math.pow(10,2)) / Math.pow(10,2);
            DrawText("F220t" + cnt, 705, 401 - (cnt * 100) + 15, '12px', "rgb(0,255,0)", df + "Hz");
            //ДАТА и ВРЕМЯ на сетке
            cnt = 0;
            while(cnt < 8) {
                var Dstr = timeConverter(dtime[cnt * 100] * 1000, 255);
                var Tstr = timeConverter(dtime[cnt * 100] * 1000, 0);
                DrawText("Date" + cnt, (cnt * 100) + 5, 401 - 5, '12px', "white", Dstr);
                DrawText("Time" + cnt, (cnt * 100) + 5, 401 - 17, '12px', "white", Tstr);
                cnt++;
            }
            //маркер
            /*var mp = GetSVGPoint(); //волучаем параметры курсора на холсте
            if(mp.MouseInBorders) {
                var Xmark = mp.x;
                var Ymark = mp.y;
                var Xmark0;
                var Ymark0;
                var width0 = 75;
                var height0 = 50;
                var Xmark1;
                var Ymark1;
                DrawLine("marker", Xmark, 0, Xmark, Ymax, "1px", "rgb(255,255,255)"); //отображаем маркер
                //координаты для отображения
                if(Xmark < (Xmax / 2)) {
                    Xmark0 = Xmark + 25;
                    Xmark1 = Xmark0;
                } else {
                    Xmark0 = Xmark - 25;
                    Xmark1 = Xmark0 - width0;
                }
                if(Ymark < (Ymax / 2)) {
                    Ymark0 = Ymark + 25;
                    Ymark1 = Ymark0;
                } else {
                    Ymark0 = Ymark - 25;
                    Ymark1 = Ymark0 - height0;
                }
                //окно для отображения
                DrawLine("marker1", Xmark, Ymark, Xmark0, Ymark0, "1px", "rgb(255,255,255)");
                DrawRectangle("infbox", Xmark1, Ymark1, width0, height0, "rgb(255,255,255)", "rgb(10,10,10)", "0.7");
                //отображение параметров в окне
                var Dstr = timeConverter(dtime[Xmark] * 1000, 255);
                var Tstr = timeConverter(dtime[Xmark] * 1000, 0);
                DrawText("DateMark", Xmark1 + 5, Ymark1 + 10, '12px', "white", Dstr);
                DrawText("TimeMark", Xmark1 + 5, Ymark1 + 10 + 12, '12px', "white", Tstr);
                DrawText("Umark", Xmark1 + 5, Ymark1 + 10 + 24, '12px', "rgb(255,0,0)", U220[Xmark] + "V");
                DrawText("Fmark", Xmark1 + 5, Ymark1 + 10 + 36, '12px', "rgb(0,255,0)", F220[Xmark] + "Hz");
               
            } */

            DataFlag = false;
        }
       
        setTimeout(LoopGraph, tgraph);
    }
     
    Ошибка в том, что она перегружает браузер. Слишком часто перерисовывает, то что не требует изменений.
    PS: Я закоментировал узкие места. В другом рабочем проекте такого конечно такого нет. Но это просто пример, а не готовое решение. И Да! Тут ошибка, но именно в перегрузке браузера и ни в чём более.