Intra Forum
    • Категории
    • Последние
    • Метки
    • Популярные
    • Зарегистрироваться
    • Войти
    1. Главная
    2. grinsva
    3. Сообщения
    G
    Не в сети
    • Профиль
    • Подписки 0
    • Подписчики 0
    • Темы 5
    • Сообщения 13
    • Группы 0

    Сообщения

    Последние Лучшие сообщения Спорные
    • RE: Пример ГРАФИКА на HTML виджете.

      В папку \anyfiles поместить 2 файла с библиотекой

      1. chart.js - код для него можно скачать по ссылке https://cdn.jsdelivr.net/npm/chart.js и вставить в файл
      2. chartjs - plugin-zoom.js - код для него можно скачать по ссылке https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom и вставить в файл
      написал в HTML виджеты
      G
      grinsva
    • RE: Пример ГРАФИКА на HTML виджете.

      Добавил в версию при отсутствии интернета или доступа к внешним ресурсам загружать библиотеки с папки \anyfiles

      <style type="text/css">
        /* ===== Стили для контейнера и canvas ===== */
        #${uuid} {
          height: 100%;
          width: 100%;
          position: relative;
        }
        #${uuid} canvas {
          width: 100% !important;
          height: 100% !important;
          display: block;
        }
      </style>
      
      <div id="${uuid}">
        <canvas id="myChart"></canvas>
        <button id="exportCsvBtn" style="position: absolute; top: 10px; right: 10px; z-index: 20; padding: 6px 12px; background: #5889A5; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-size: 13px;">
          ⬇ CSV
        </button>
      </div>
      
      <script type="text/javascript">
        // ===== Глобальные переменные =====
        let updateTimeout = null;
        let isRendering = false;
        let chartInstance = null;
        let crosshair = null;
        let isSubscribed = false;
      
        // ===== Экспорт данных в CSV (разделитель ";" с BOM) =====
        function exportChartDataToCSV() {
          if (!chartInstance) {
            console.warn('График ещё не создан');
            return;
          }
      
          const datasets = chartInstance.data.datasets;
          if (!datasets || datasets.length === 0) {
            console.warn('Нет данных для экспорта');
            return;
          }
      
          const xValues = new Set();
          datasets.forEach(ds => {
            ds.data.forEach(point => {
              if (point.x !== undefined && point.x !== null) {
                xValues.add(point.x);
              }
            });
          });
      
          const sortedX = Array.from(xValues).sort((a, b) => a - b);
      
          const headers = ['Масса, кг'];
          datasets.forEach(ds => {
            headers.push(ds.label || 'Без названия');
          });
      
          const rows = [];
          sortedX.forEach(x => {
            const row = [x];
            datasets.forEach(ds => {
              const point = ds.data.find(p => p.x === x);
              row.push(point && point.y !== undefined && point.y !== null ? point.y : '');
            });
            rows.push(row);
          });
      
          const csvContent = [
            headers.join(';'),
            ...rows.map(row => row.join(';'))
          ].join('\n');
      
          const bom = '\uFEFF';
          const blob = new Blob([bom + csvContent], { type: 'text/csv;charset=utf-8;' });
          const link = document.createElement('a');
          link.href = URL.createObjectURL(blob);
          link.download = `chart_data_${new Date().toISOString().slice(0,10)}.csv`;
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
          URL.revokeObjectURL(link.href);
          console.log('CSV файл успешно скачан (разделитель ";", с BOM)');
        }
      
        // ===== КАСТОМНОЕ ПЕРЕКРЕСТИЕ (с поддержкой изменения размера окна) =====
        function enableCrosshair(chart) {
          if (!chart) return;
      
          // Удаляем старое перекрестие, если есть
          if (crosshair) {
            if (crosshair.vLine) {
              crosshair.vLine.remove();
              crosshair.hLine.remove();
              crosshair.label.remove();
            }
            if (crosshair.resizeHandler) {
              window.removeEventListener('resize', crosshair.resizeHandler);
            }
            if (crosshair.resizeObserver) {
              crosshair.resizeObserver.disconnect();
            }
            crosshair = null;
          }
      
          const canvas = chart.canvas;
          if (!canvas) {
            console.warn('Canvas отсутствует');
            return;
          }
          const container = canvas.parentNode;
          if (!container) {
            console.warn('Контейнер canvas не найден');
            return;
          }
          container.style.position = 'relative';
      
          // Создаём элементы перекрестия
          const vLine = document.createElement('div');
          vLine.style.cssText = `
            position: absolute; top: 0; left: 0;
            height: 100%;
            border-left: 1px dashed rgba(88, 137, 165, 0.7);
            pointer-events: none; display: none;
            z-index: 10;
          `;
          container.appendChild(vLine);
      
          const hLine = document.createElement('div');
          hLine.style.cssText = `
            position: absolute; top: 0; left: 0;
            width: 100%;
            border-top: 1px dashed rgba(88, 137, 165, 0.7);
            pointer-events: none; display: none;
            z-index: 10;
          `;
          container.appendChild(hLine);
      
          const label = document.createElement('div');
          label.style.cssText = `
            position: absolute;
            background: rgba(235,235,235,0.85);
            padding: 2px 8px; border-radius: 4px;
            font-size: 12px; pointer-events: none;
            display: none; white-space: nowrap;
            border: 1px solid #ccc;
            z-index: 11;
          `;
          container.appendChild(label);
      
          crosshair = { vLine, hLine, label };
      
          // Храним относительные координаты (в долях от размера canvas)
          let lastXPercent = -1;
          let lastYPercent = -1;
          let isMouseInside = false;
      
          function updateCrosshairPosition(x, y) {
            if (!crosshair || !crosshair.vLine) return;
      
            const rect = canvas.getBoundingClientRect();
            if (x < 0 || y < 0 || x > rect.width || y > rect.height || !isMouseInside) {
              crosshair.vLine.style.display = 'none';
              crosshair.hLine.style.display = 'none';
              crosshair.label.style.display = 'none';
              return;
            }
            crosshair.vLine.style.display = 'block';
            crosshair.vLine.style.left = x + 'px';
            crosshair.hLine.style.display = 'block';
            crosshair.hLine.style.top = y + 'px';
      
            const xScale = chart.scales.x;
            const yScale = chart.scales.y;
            if (xScale && yScale) {
              const xVal = xScale.getValueForPixel(x);
              const yVal = yScale.getValueForPixel(y);
              if (xVal !== undefined && yVal !== undefined) {
                crosshair.label.style.display = 'block';
                crosshair.label.textContent = `X: ${xVal.toFixed(1)} кг, Y: ${yVal.toFixed(2)} МПа`;
                let lx = x + 10, ly = y - 10;
                if (lx + 200 > rect.width) lx = x - 200;
                if (ly < 0) ly = 0;
                crosshair.label.style.left = lx + 'px';
                crosshair.label.style.top = ly + 'px';
              } else {
                crosshair.label.style.display = 'none';
              }
            }
          }
      
          function onMouseMove(e) {
            const rect = canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            // Сохраняем относительные координаты
            lastXPercent = x / rect.width;
            lastYPercent = y / rect.height;
            isMouseInside = true;
            updateCrosshairPosition(x, y);
          }
      
          function onMouseLeave() {
            isMouseInside = false;
            if (crosshair) {
              crosshair.vLine.style.display = 'none';
              crosshair.hLine.style.display = 'none';
              crosshair.label.style.display = 'none';
            }
          }
      
          // Обработчик изменения размера окна или контейнера
          function onResize() {
            if (isMouseInside && lastXPercent >= 0 && lastYPercent >= 0 && crosshair) {
              // Обновляем размеры canvas
              chart.resize();
              // Даём время на пересчёт размеров
              setTimeout(() => {
                const rect = canvas.getBoundingClientRect();
                const x = lastXPercent * rect.width;
                const y = lastYPercent * rect.height;
                updateCrosshairPosition(x, y);
              }, 30);
            }
          }
      
          canvas.addEventListener('mousemove', onMouseMove);
          canvas.addEventListener('mouseleave', onMouseLeave);
          window.addEventListener('resize', onResize);
      
          // Наблюдатель за изменением размера контейнера (для случаев, когда окно не меняется, а контейнер — да)
          const resizeObserver = new ResizeObserver(() => {
            onResize();
          });
          resizeObserver.observe(container);
      
          // Сохраняем ссылки для очистки
          chart._crosshairHandlers = { onMouseMove, onMouseLeave, onResize };
          crosshair.resizeHandler = onResize;
          crosshair.resizeObserver = resizeObserver;
        }
      
        // ===== ОСНОВНАЯ ФУНКЦИЯ (debounce) =====
        function drawChart(canvasId) {
          if (isRendering) return;
          if (updateTimeout) {
            clearTimeout(updateTimeout);
            updateTimeout = null;
          }
          updateTimeout = setTimeout(() => {
            updateTimeout = null;
            performRender(canvasId);
          }, 150);
        }
      
        // ===== РЕАЛЬНЫЙ РЕНДЕРИНГ =====
        function performRender(canvasId) {
          if (isRendering) return;
          isRendering = true;
      
          try {
            const canvas = document.getElementById(canvasId);
            if (!canvas) { console.warn('Canvas not found'); return; }
            const context = canvas.getContext('2d');
            if (!context) { console.warn('Canvas context missing'); return; }
      
            const dataObj = window.ihapi.deviceValue('local676', 'report_graf_line_points');
            if (!dataObj || !dataObj.items || dataObj.items.length === 0) {
              console.warn('Нет данных');
              return;
            }
      
            const scaleXVal = window.ihapi.deviceValue('local638', 'scale_X');
            const scaleYVal = window.ihapi.deviceValue('local637', 'scale_Y');
            const isScaleX = scaleXVal == true || scaleXVal == 'true';
            const isScaleY = scaleYVal == true || scaleYVal == 'true';
            let mode = 'xy';
            if (isScaleX && !isScaleY) mode = 'x';
            else if (!isScaleX && isScaleY) mode = 'y';
            else if (!isScaleX && !isScaleY) mode = 'none';
      
            let zoomConfig = {
              pan: { enabled: false },
              zoom: { wheel: { enabled: false } }
            };
            if (mode !== 'none') {
              zoomConfig = {
                pan: { enabled: true, mode: mode },
                zoom: { wheel: { enabled: true }, mode: mode },
                limits: {
                  x: { min: dataObj.start, max: dataObj.end },
                  y: { min: dataObj.min, max: dataObj.max }
                }
              };
            }
      
            const datasets = [];
            for (const item of dataObj.items) {
              if (!item.visible) continue;
              const points = item.points.map(p => ({
                x: parseFloat(p.x),
                y: parseFloat(p.y)
              }));
              datasets.push({
                label: item.legend || 'Без названия',
                data: points,
                fill: false,
                borderColor: item.lineColor || '#000000',
                borderWidth: parseInt(item.lineWidth) || 2,
                tension: 0.0,
                pointRadius: 1.0,
                pointStyle: 'circle',
              });
            }
            if (datasets.length === 0) {
              console.warn('Нет видимых линий');
              return;
            }
      
            if (chartInstance) {
              // === ОБНОВЛЕНИЕ ===
              chartInstance.data.datasets = datasets;
              chartInstance.options.scales.x.min = dataObj.start;
              chartInstance.options.scales.x.max = dataObj.end;
              chartInstance.options.scales.y.min = dataObj.min;
              chartInstance.options.scales.y.max = dataObj.max;
              chartInstance.options.plugins.zoom = zoomConfig;
              chartInstance.update();
              console.log('График обновлён (zoom: ' + mode + ')');
            } else {
              // === ПЕРВОЕ СОЗДАНИЕ ГРАФИКА ===
              const config = {
                type: 'line',
                data: { datasets },
                options: {
                  responsive: true,
                  maintainAspectRatio: false,
                  animation: false,
                  scales: {
                    x: {
                      type: 'linear',
                      min: dataObj.start,
                      max: dataObj.end,
                      ticks: {
                        autoSkip: true,
                        maxRotation: 0,
                        callback: (val, index, ticks) =>
                          index === 0 || index === ticks.length - 1 ? null : `${Number(val.toFixed(3))} кг`,
                        color: '#5889A5',
                        font: { size: 12 },
                        maxTicksLimit: 20
                      },
                      grid: { display: true, color: 'rgba(0,0,0,0.1)', lineWidth: 1 },
                      border: { color: '#5889A540', lineWidth: 1 },
                      title: {
                        display: true,
                        text: 'Количество вяжущего (цемент + МД)',
                        color: '#5C7C8F',
                        font: { size: 14 }
                      }
                    },
                    y: {
                      min: dataObj.min,
                      max: dataObj.max,
                      ticks: {
                        callback: (val, index, ticks) =>
                          index === 0 || index === ticks.length - 1 ? null : Number(val.toFixed(3)),
                        color: '#5889A5',
                        font: { size: 12 },
                        maxTicksLimit: 20
                      },
                      grid: { display: true, color: 'rgba(0,0,0,0.1)', lineWidth: 1 },
                      border: { color: '#5889A540', lineWidth: 1 },
                      title: {
                        display: true,
                        text: 'Прочность, МПа',
                        color: '#5C7C8F',
                        font: { size: 14 }
                      }
                    }
                  },
                  plugins: {
                    zoom: zoomConfig,
                    tooltip: {
                      mode: 'nearest',
                      axis: 'x',
                      intersect: false,
                      backgroundColor: '#F0F0F0',
                      titleColor: '#444444',
                      titleFont: { size: 13, weight: 'normal' },
                      bodyColor: '#444444',
                      borderColor: '#CCCCCC',
                      borderWidth: 1,
                      cornerRadius: 2,
                      padding: 5,
                      usePointStyle: true,
                      boxWidth: 5,
                      boxHeight: 5,
                      callbacks: {
                        title: function(tooltipItems) {
                          const x = tooltipItems[0].parsed.x;
                          return `масса: ${x.toFixed(1)} кг`;
                        },
                        label: function(context) {
                          let label = context.dataset.label || '';
                          if (label) label += ': ';
                          if (context.parsed.y !== null) {
                            label += context.parsed.y.toFixed(2) + ' МПа';
                          }
                          return label;
                        },
                        labelPointStyle: function(context) {
                          return { pointStyle: 'circle' };
                        },
                        labelColor: function(context) {
                          return {
                            borderColor: context.dataset.borderColor,
                            backgroundColor: context.dataset.borderColor,
                            borderWidth: 0,
                            borderRadius: 2
                          };
                        }
                      }
                    },
                    legend: {
                      labels: {
                        boxWidth: 7,
                        boxHeight: 7,
                        usePointStyle: true,
                        font: { size: 13 }
                      }
                    }
                  }
                }
              };
      
              chartInstance = new Chart(context, config);
      
              // Принудительная установка размеров
              const container = canvas.parentNode;
              container.style.height = '100%';
              container.style.width = '100%';
              container.style.overflow = 'hidden';
              canvas.style.height = '100%';
              canvas.style.width = '100%';
              canvas.style.display = 'block';
              chartInstance.resize();
      
              // Вызываем enableCrosshair только один раз
              enableCrosshair(chartInstance);
              console.log('График успешно создан (zoom: ' + mode + ')');
      
              if (!isSubscribed) {
                setTimeout(initSubscriptions, 100);
              }
            }
          } catch (err) {
            console.error('Ошибка при рендеринге:', err);
          } finally {
            isRendering = false;
          }
        }
      
        // ===== ЗАГРУЗКА ПЛАГИНА ZOOM =====
        function loadZoomPlugin() {
        // Проверяем, не загружен ли уже плагин (чтобы избежать дублирования)
        if (document.getElementById('chartjs-plugin-zoom')) {
          drawChart('myChart');
          return;
        }
      
        const pluginScript = document.createElement('script');
        pluginScript.id = 'chartjs-plugin-zoom';
        pluginScript.src = 'https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom';
        document.head.appendChild(pluginScript);
      
        pluginScript.addEventListener('load', function () {
          console.log('Плагин zoom загружен');
          drawChart('myChart');
        });
      
        pluginScript.addEventListener('error', function () {
          console.warn('Ошибка загрузки плагина zoom с CDN, пробуем локальную версию');
          const localPlugin = document.createElement('script');
          localPlugin.id = 'chartjs-plugin-zoom';
          localPlugin.src = '/files/chartjs-plugin-zoom.js'; // путь к локальной копии
          document.head.appendChild(localPlugin);
      
          localPlugin.addEventListener('load', function () {
            console.log('Плагин zoom загружен локально');
            drawChart('myChart');
          });
          localPlugin.addEventListener('error', function () {
            console.error('Не удалось загрузить плагин zoom');
          });
        });
      }
      
        // ===== ЗАГРУЗКА ОСНОВНОГО СКРИПТА =====
       function loadScripts() {
        const chartScript = document.getElementById('chart-js');
        if (!chartScript) {
          const script = document.createElement('script');
          script.id = 'chart-js';
          console.log('Текущий хост:', window.location.hostname);
          // Сначала пробуем загрузить с CDN
          script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
          document.head.appendChild(script);
      
          script.addEventListener('load', function () {
            loadZoomPlugin();
          });
      
          script.addEventListener('error', function () {
            console.warn('Ошибка загрузки Chart.js с CDN, пробуем локальную версию');
            // Если CDN недоступен, загружаем локальный файл
            const localScript = document.createElement('script');
            localScript.id = 'chart-js'; // тот же ID, чтобы не дублировать
            localScript.src = '/files/chart.js'; // путь к локальной копии
            document.head.appendChild(localScript);
      
            localScript.addEventListener('load', function () {
              loadZoomPlugin();
            });
            localScript.addEventListener('error', function () {
              console.error('Не удалось загрузить Chart.js ни с CDN, ни локально');
            });
          });
        } else {
          if (!document.getElementById('chartjs-plugin-zoom')) {
            loadZoomPlugin();
          } else {
            drawChart('myChart');
          }
        }
      }
      
        // ===== Подписка на изменения локальных переменных =====
        let prevScaleX = null;
        let prevScaleY = null;
        let prevRecipeData = null;
        let prevFlags = null;
      
        function initSubscriptions() {
          if (isSubscribed) {
            console.warn('Подписка уже создана, пропускаем');
            return;
          }
      
          const myUuid = '${uuid}';
          const subscribeList = [
            'local638_scale_X',
            'local637_scale_Y',
            'local644_new_rec_table_12_3',
            'local669_report_enabledFlags'
          ];
      
          console.log('Вызов initSubscriptions');
      
          prevScaleX = window.ihapi.deviceValue('local638', 'scale_X');
          prevScaleY = window.ihapi.deviceValue('local637', 'scale_Y');
          prevRecipeData = window.ihapi.deviceValue('local644', 'new_rec_table_12_3');
          prevFlags = window.ihapi.deviceValue('local669', 'report_enabledFlags');
      
          window.ihapi.deviceSub(myUuid, subscribeList);
      
          window.ihapi.addEventListener(myUuid, 'data', function(event) {
            const newData = event;
            let hasChanged = false;
      
            subscribeList.forEach(key => {
              if (newData[key] !== undefined) {
                const currentVal = newData[key];
                if (key === 'local638_scale_X' && currentVal !== prevScaleX) {
                  prevScaleX = currentVal;
                  hasChanged = true;
                } else if (key === 'local637_scale_Y' && currentVal !== prevScaleY) {
                  prevScaleY = currentVal;
                  hasChanged = true;
                } else if (key === 'local644_new_rec_table_12_3' &&
                           JSON.stringify(currentVal) !== JSON.stringify(prevRecipeData)) {
                  prevRecipeData = currentVal;
                  hasChanged = true;
                } else if (key === 'local669_report_enabledFlags') {
                  prevFlags = currentVal;
                  hasChanged = true;
                }
              }
            });
      
            if (hasChanged) {
              console.log('Обнаружено изменение локальной переменной, обновляем график');
              drawChart('myChart');
            }
          });
      
          window.ihapi.addEventListener(myUuid, 'destroy', function() {
            window.ihapi.deviceUnsub(myUuid, subscribeList);
            console.log('Отписка от локальных переменных выполнена');
          });
      
          isSubscribed = true;
        }
      
        // ===== ИНИЦИАЛИЗАЦИЯ =====
        console.log('START');
        const scriptExists = document.getElementById('chart-js');
      
        const exportBtn = document.getElementById('exportCsvBtn');
        if (exportBtn) {
          exportBtn.addEventListener('click', exportChartDataToCSV);
        }
      
        if (!scriptExists) {
          loadScripts();
        } else {
          if (!document.getElementById('chartjs-plugin-zoom')) {
            loadZoomPlugin();
          } else {
            drawChart('myChart');
            setTimeout(initSubscriptions, 500);
          }
        }
      </script>
      
      написал в HTML виджеты
      G
      grinsva
    • Пример ГРАФИКА на HTML виджете.

      Была задача реализовать ГРАФИК максимально приближенный по функционалу к Chart_MultiLine_GL. Т.е. возможность масштабирования по осям, всплывающая подсказка, перья.
      Главное условие, что по осям можно произвольные свои переменные вставлять и гибко настраивать визуал.

      Пример как все выглядит:
      cf7fd5a4-793a-43f7-8daf-9a8b26d5c5a0.jpg

      Процесс:

      • создаем контейнер
      • вставляем HTML виджет
      • вставляем код.

      Созданы несколько локальных переменных:

      • переменная с результирующим объектом из точек;

      • 2 переменные для включения/отключения масштабирования по осям;
        738f69e1-103c-4beb-937c-28c009faf55a-image.jpeg

      • переменная с массивом типа [1,1,1,1] для включения отображения или скрытия каких то графиков, т.к. при использовании встроенного функционала при обновлении графика все кривые вновь появляются и надо их снова отключать.
        350b1a56-d6bd-4edd-afc6-9a378a2a0edd-image.jpeg

      Изменение настроечных переменных график отслеживает по подписке.

      Входящие данные для графиков формируются в скрипте визуализации(можно делать каким угодно образом, не обязательно так). В моем случае я формирую такой же объект для графика как в стандартном обработчике и с ним работаю.

      Итоговый код (комментарии есть, если не понятно закидываем в ИИ):

      написал в HTML виджеты
      G
      grinsva
    • RE: Привязка локальных переменных для Графиков в HTML

      Доброго дня.
      Для подписки правильно ли я формирую названия переменных для метода? А то что то не реагируют на действия кнопок.

      const subscribeList = [ // <-- НОВОЕ
        'local638_scale_X', // <-- НОВОЕ
        'local637_scale_Y', // <-- НОВОЕ
        'local644_new_rec_table_12_3', // <-- НОВОЕ
        'local670_report_enabledFlags' // <-- НОВОЕ
      ]; // <-- НОВОЕ
      
      // Получаем начальные значения через deviceValue // <-- НОВОЕ
      prevScaleX = window.ihapi.deviceValue('local638', 'scale_X'); // <-- НОВОЕ
      prevScaleY = window.ihapi.deviceValue('local637', 'scale_Y'); // <-- НОВОЕ
      prevRecipeData = window.ihapi.deviceValue('local644', 'new_rec_table_12_3'); // <-- НОВОЕ
      prevFlags = window.ihapi.deviceValue('local670', 'report_enabledFlags'); // <-- НОВОЕ
      
      // Подписываемся на изменения // <-- НОВОЕ
      window.ihapi.deviceSub(myUuid, subscribeList); // <-- НОВОЕ
      
      написал в HTML виджеты
      G
      grinsva
    • RE: Привязка локальных переменных для Графиков в HTML

      Вопрос решился неправильно локальную переменную указал. в Коде 674, а в реальности 676 должно быть

      написал в HTML виджеты
      G
      grinsva
    • Привязка локальных переменных для Графиков в HTML

      Как правильно привязывать. По документации что то не получается

      Код:
      <style type="text/css"> </style>

      <div id= "${uuid}">'
      <canvas id="myChart"></canvas>
      </div>

      <script type="text/javascript">

      function drawChart(canvasId) {
      const ctx = document.getElementById(canvasId); // .getContext('3d')
      if (!ctx) {
      console.log('canvas contex missing');

      }
       
       let x_arr = [];
       let y_arr = [];
       const points = window.ihapi.deviceValue('local674', 'report_graf_line_points') ;
      
        console.log( 'points' );
        console.log( points );
       points.sort((a, b) => a.x - b.x);
       
       for ( const item of points){
                     x_arr.push(item.x)
                     y_arr.push(item.y)
       }
       
      
       
      
      const labels = x_arr ; //  [2, 4, 6, 8, 10, 12, 14]; ось X
      const data = {
        labels: labels,
        datasets: [{
          label: 'dataset1',
          data: y_arr, //ось Y  [65, 59, 80, 81, 56, 55, 40]
          fill: false,
          borderColor: 'rgb(75, 192, 192)',
          tension: 0.1
        } 
        ]
      }
      
      const myChart = new Chart(ctx, {  
        type: 'line',
        data: data
      });
      

      }

      function loadScript(scriptId) {
      const script = document.createElement('script');
      script.id = scriptId;
      script.src = '/files/chart.min.js'; // /files/chart.min.js https://cdn.jsdelivr.net/npm/chart.js https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.min.js
      // script.async = true;
      document.head.appendChild(script);

      script.addEventListener('load', function() {
        drawChart('myChart');
      });
      
      script.addEventListener('error', function() {
        console.error('/files/chart.min.js loading failed');
        return { err: '/files/chart.min.js loading failed' }
      });
      

      }

      const scriptExists = document.getElementById('chart-js');
      if (!scriptExists) {
      loadScript('chart-js');
      console.log('loadScript( chart-js )');
      } else {
      drawChart('myChart');
      console.log('drawChart( myChart )');
      }

      </script>

      e226fd33-dbb8-4d6d-8c6f-eb574e0be08e-image.jpeg

      В консоли:
      65444dfd-3e44-4df1-93b9-4950328df309-image.jpeg

      написал в HTML виджеты
      G
      grinsva
    • RE: Корявое изображение в Графиках CHART Multiline GL при зумировании по оси Х

      @maksimvershinin у меня так и так через него время получает. Но это не помогало. Пусть решение и затычка каких то проблем, но раз иного нет, то может кому то и поможет.
      NULL, как я понял, обрывает соединительную линию между точками.
      Этим я и воспользовался. для того что красиво смотрелось.

      написал в IntraSCADA
      G
      grinsva
    • RE: Корявое изображение в Графиках CHART Multiline GL при зумировании по оси Х

      grinsva said:

      Когда включаю Зумирование по Х появляются соединения начальных точек с конечными.
      Сперва на обнаружил на версии 5,18 - обновил. То же самое. Без зума по Х - нормально

      9f55bb45-c154-46ec-96c8-850b2d1fd1b4-image.jpeg

      42fec02d-6b0d-404d-84c4-fa62abad882d-image.jpeg

      Эту проблему я исправил - задав последние точки равными NULL

      написал в IntraSCADA
      G
      grinsva
    • RE: Корявое изображение в Графиках CHART Multiline GL при зумировании по оси Х

      Еще когда по p2p подключаюсь и интервал времени больше месяца, то по шкале Х не отображаются дата-время
      da7a1a82-08fc-478b-b78c-f891f20d81f9-image.jpeg

      Время только показывается.
      ce0794e5-ea14-4314-b51c-c4f82cabcd29-image.jpeg

      А когда из сети сервера смотрю. То нормально показывается - только язык Английский
      93831e0d-42e9-4878-8f28-b9302aaa5068-image.jpeg

      написал в IntraSCADA
      G
      grinsva
    • Корявое изображение в Графиках CHART Multiline GL при зумировании по оси Х

      Когда включаю Зумирование по Х появляются соединения начальных точек с конечными.
      Сперва на обнаружил на версии 5,18 - обновил. То же самое. Без зума по Х - нормально

      9f55bb45-c154-46ec-96c8-850b2d1fd1b4-image.jpeg

      42fec02d-6b0d-404d-84c4-fa62abad882d-image.jpeg

      написал в IntraSCADA
      G
      grinsva
    • Появиться ли возможность обновить виджет REPORT из скрипта визуализации?

      Сейчас появилась возможность из кнопки обновлять через Команду элемента REFRESH виджет REPOPRT. Но это не то что моем случае подходит. Мне нужно чтоб из скрипта была такая возможность. Для того, чтобы когда я из дерева выбираю ветку для нее автоматически обновлялся отчет. На данный момент только через диалог можно так делать.

      1fd186d4-a356-4f8e-8ccd-567cf4b6e528-image.jpeg

      написал в IntraSCADA
      G
      grinsva
    • RE: Изменение атрибутов сохранения в БД для устройства из скрипта/сценария

      44b65fb0-ccf2-43f5-8f9d-87ddde56778a-image.jpeg

      Насколько нормально с точки зрения ресурсозатратности, если для всех устройств одного типа я включу в типе сохранение в БД. А сохранением для каждого устройства буду через метку управлять SAVE_DB_SET в обработчике, как на картинке? Равнозначно ли это если я для каждого отдельного устройства в атрибутах настраиваю данную опцию. Или для системы это все равно, что все устройства разом сохраняют?

      написал в IntraSCADA
      G
      grinsva
    • Изменение атрибутов сохранения в БД для устройства из скрипта/сценария

      Жду обновлений по этой теме (Изменение атрибутов сохранения в БД для устройства из скрипта/сценария)

      написал в IntraSCADA
      G
      grinsva