Доброго времени суток! 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: Простите, но описано всё будет пошагово.
ШАГ2: Теперь надо, чтобы загружаемая страница отображала область SVG: И загружала данные самостоятельно с интервалом времени. Это можно проконтролировать с помощью браузера (у меня 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: Приступаем к созданию координатной сетки, если нужна: Следует отметить, что для работы были описаны ряд функций в отдельный файл - язык не поворачивается назвать библиотекой. Файл "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: На текущем шаге определяем масштабы параметров ЧАСТОТА и НАПРЯЖЕНИЕ, нанесение значений на сетку, отображение параметров в виде графиков из массивов, отображение текущих значений в левом верхнем углу холста. Код (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; } Архив: DataSVGtest.zip PS: А вот определение параметров по маркеру, по координатам курсора на холсте, надо или нет - не знаю.
ШАГ5: Скриншот сделать не мог, а фото с экрана что-то с чем-то. Когда наводится курсор мыши на полотно появляется на полотне окошко с данными и маркер (вертикальная линия) так же как тут: https://forum.amperka.ru/threads/Разговоры-на-технические-темы.22016/page-12#post-314348 Собственно архив: DataSVGtest.zip Ну вроде всё. PS: Основной смысл в неиспользовании программ на стороне пользователя - только браузер. Если кто испытал, будьте добры сообщить что за браузер и каков результат. У меня Firefox с ОС Didian. Я это к тому, что у меня получалось на разных машинах иметь разные результаты отображений. Правда к SVG это не относилось, но всё же! Спасибо!
Вот с танцами с бубном сделал скриншот: Дело в том, что уходом курсора из области холста макер и окно с данными исчезают. Курсор опять не отобразился на скриншоте. 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 Спасибо!