Grafana实例-Gauge仪表盘面板

Gauge仪表盘面板

  • 主要用于直观展示某个数值在定义的最小和最大范围内的位置。

Grafana8.2.7 + InfluxDB1.x + ECharts2.2.4 对运行时间进行全天的占比计算

OEE = 正常运行时间 / 24h

一、基础查询

sql 复制代码
SELECT "valueInt" FROM "prod_process_values" WHERE ("topic" = 'CT/OESL/AS/CHN/QDA/HCS/Extrusion_Area/ERS104_Extrusion_Line/MachineState') 
AND $timeFilter

二、ECharts 面板 JS 计算OEE

  • 分母固定为24h
  • 最后状态若为运行,则最后运行时间为:now - 状态起始时间
javascript 复制代码
// console.log("=== 完整 Grafana 数据结构 ===", data);
// console.log("=== 顶层 series 数组 ===", data?.series ?? []);
// console.log("=== 查询状态 ===", data?.state);
// console.log("=== 系列总数 ===", Array.isArray(data?.series) ? data.series.length : 0);

/**
 * 工具:安全判断有效非空数组
 * @param {*} arr
 * @returns {boolean}
 */
function isSafeArray(arr) {
  return Array.isArray(arr) && arr.length > 0;
}

/**
 * 按 refId 匹配,返回对应单条系列对象
 * @param {Object|null|undefined} data Grafana 根数据
 * @param {string} refId 查询标识 A/B/C
 * @returns {Object|null} 单个series对象
 */
function getSeriesByRefId(data, refId) {
  if (!data || !isSafeArray(data.series)) return null;
  return data.series.find(item => item.refId === refId) || null;
}

/**
 * 根据字段固定下标提取数值数组
 * 约定:fields[0] = Time,fields[1] = valueInt
 * @param {Object|null} singleSeries 单个series对象
 * @param {number} fieldIndex 0=Time / 1=valueInt
 * @returns {Array} 数据数组,无数据返回[]
 */
function getFieldByIndex(singleSeries, fieldIndex) {
  // 校验series和fields数组
  if (!singleSeries || !isSafeArray(singleSeries.fields)) return [];
  // 下标越界直接返回空
  if (fieldIndex < 0 || fieldIndex >= singleSeries.fields.length) return [];

  const targetField = singleSeries.fields[fieldIndex];
  if (!targetField?.values) return [];

  // 兼容 Grafana 两种存储格式:buffer / 直接values数组
  const valueList = targetField.values.buffer ?? targetField.values;
  return isSafeArray(valueList) ? valueList : [];
}

// ====================== 业务提取逻辑 ======================
const targetSeriesItem = getSeriesByRefId(data, "A");
const timeArr = getFieldByIndex(targetSeriesItem, 0);
const valueArr = getFieldByIndex(targetSeriesItem, 1);
console.log("时间数组 Time(fields[0]):", timeArr);
console.log("状态数组 valueInt(fields[1]):", valueArr);

// ====================== 精准计算运行时长(修改:最后一段用实时now) ======================
let runTotalMs = 0;
const dataLen = timeArr.length;
const segmentLog = [];

// 【修改】最后一段统一使用浏览器实时当前时间戳,不再用面板timeRange.to
const realNowTs = Date.now();
console.log("浏览器实时当前时间戳 realNowTs:", realNowTs, new Date(realNowTs).toLocaleString());

if (dataLen >= 2) {
  // 逐段计算两个点位之间时长
  for (let i = 0; i < dataLen - 1; i++) {
    const s = valueArr[i];
    const tStart = timeArr[i];
    const tEnd = timeArr[i + 1];
    const dur = tEnd - tStart;
    segmentLog.push({
      idx: i,
      state: s,
      start: new Date(tStart).toLocaleString(),
      end: new Date(tEnd).toLocaleString(),
      durationMs: dur
    });
    // 仅状态1累加时长
    if (s === 1) {
      runTotalMs += dur;
    }
  }

  // 处理最后一个点位:当前持续状态,结束时间取实时now
  const lastState = valueArr[dataLen - 1];
  const lastTs = timeArr[dataLen - 1];
  const lastDur = realNowTs - lastTs;
  segmentLog.push({
    idx: "last",
    state: lastState,
    start: new Date(lastTs).toLocaleString(),
    end: new Date(realNowTs).toLocaleString(),
    durationMs: lastDur,
    note: "最后一段到浏览器实时当前时间now"
  });
  if (lastState === 1) {
    runTotalMs += lastDur;
  }
}

// 打印每一段明细,方便核对时长
console.log("分段明细日志:", segmentLog);
console.log("累计运行总毫秒 runTotalMs =", runTotalMs);

// 辅助换算
const runHours = (runTotalMs / (1000 * 60 * 60)).toFixed(3);
const dayTotalMs = 24 * 60 * 60 * 1000;
const oeeRate = runTotalMs / dayTotalMs;
const oeePercent = (oeeRate * 100).toFixed(2);

console.log("累计运行小时:", runHours, "h");
console.log("24h运行占比OEE:", oeePercent, "%");

// ====================== ECharts2.2.4 仪表盘配置 ======================
return {
  tooltip: {
    formatter: "{b}: {c}%"
  },
  series: [
    {
      type: 'gauge',
      min: 0,
      max: 100,
      splitNumber: 10,
      axisLine: {
        lineStyle: {
          width: 10,
          color: [
            [0.3, '#ff4444'],
            [0.7, '#ffbb33'],
            [1, '#00C851']
          ]
        }
      },
      axisTick: {
        length: 15,
        lineStyle: {
          color: 'auto'
        }
      },
      splitLine: {
        length: 20,
        lineStyle: {
          color: 'auto'
        }
      },
      pointer: {
        width: 5
      },
      title: {
        show: true,
        offsetCenter: [0, '70%'],
        textStyle: {
          fontSize: 16
        }
      },
      detail: {
        formatter: '{value} %',
        fontSize: 24,
        offsetCenter: [0, '40%']
      },
      data: [
        {
          value: Number(oeePercent),
          name: ''
        }
      ]
    }
  ]
};
相关推荐
微三云 - 廖会灵 (私域系统开发)1 天前
电商系统监控告警体系从0到1:Prometheus+Grafana+AlertManager的全链路可观测性实践
oracle·grafana·prometheus
维C°1 天前
Grafana-Text面板
grafana
少陽君3 天前
Docker 部署 Loki + Promtail + Grafana 日志监控栈
docker·容器·grafana
摇滚侠6 天前
Prometheus+Grafana+睿象云的监控告警系统 01-07
grafana·prometheus
time展天17 天前
abbix、Prometheus、Grafana、Nightingale,四个监控如何选型?
grafana·prometheus
Elastic 中国社区官方博客9 天前
使用 Elasticsearch 作为 Grafana 的直接替代 Prometheus 后端
大数据·数据库·sql·elasticsearch·搜索引擎·grafana·prometheus
查拉图斯特拉面条10 天前
使用 k6 编写登录与业务场景脚本并进行性能测试及 Grafana 可视化实战
grafana
spider_xcxc10 天前
Prometheus + Grafana流程搭建总结
grafana·prometheus·云计算运维
c_zyer10 天前
一站式可观测新选择:OpenObserve 深度对标 ELK / Grafana+Prometheus / Netdata,附Docker一键部署实战
elk·grafana·prometheus·netdata·openobserve