40-clients/ucepsa/edge-oee-demo/sql/050_reports_oee_functions.sql

273 lines
7.5 KiB
PL/PgSQL

CREATE OR REPLACE FUNCTION mv_reports_ucepsa_demo.oee_kpis(
p_from timestamptz,
p_to timestamptz,
p_machine_id text DEFAULT 'CORT-01',
p_asset text DEFAULT 'revpi_oee_node_01'
)
RETURNS TABLE (
oee_pct numeric,
availability_pct numeric,
performance_pct numeric,
quality_pct numeric
)
LANGUAGE sql
STABLE
AS $$
WITH dashboard_bounds AS (
SELECT
p_from AS dashboard_from,
LEAST(p_to, now()) AS dashboard_to
),
telemetry_span AS (
SELECT
MIN(ingest_ts) AS first_ts,
MAX(ingest_ts) AS last_ts
FROM mv_hot.edge_oee_live_telemetry
WHERE asset = p_asset
AND ingest_ts >= (SELECT dashboard_from FROM dashboard_bounds)
AND ingest_ts <= (SELECT dashboard_to FROM dashboard_bounds)
),
bounds AS (
SELECT
CASE
WHEN first_ts IS NULL THEN dashboard_from
ELSE GREATEST(dashboard_from, first_ts)
END AS t_from,
CASE
WHEN last_ts IS NULL THEN dashboard_to
ELSE LEAST(dashboard_to, last_ts)
END AS t_to
FROM dashboard_bounds
CROSS JOIN telemetry_span
),
target AS (
SELECT
COALESCE((
SELECT mt.target_cycle_rate_ppm
FROM mv_hot.edge_oee_machine_targets mt
WHERE mt.machine_id = p_machine_id
LIMIT 1
), 10.00)::numeric AS target_cycle_rate_ppm,
COALESCE((
SELECT mt.quality_pct_default
FROM mv_hot.edge_oee_machine_targets mt
WHERE mt.machine_id = p_machine_id
LIMIT 1
), 100.00)::numeric AS quality_pct_default
),
planned AS (
SELECT
GREATEST(EXTRACT(EPOCH FROM (t_to - t_from))::numeric, 0) AS planned_s
FROM bounds
),
stops AS (
SELECT
COALESCE(
SUM(
GREATEST(
EXTRACT(EPOCH FROM (
LEAST(COALESCE(s.ended_at, b.t_to), b.t_to)
-
GREATEST(
CASE
WHEN s.started_at > s.created_at + interval '5 seconds'
THEN s.created_at
ELSE s.started_at
END,
b.t_from
)
))::numeric,
0
)
) FILTER (
WHERE s.started_at < b.t_to
AND COALESCE(s.ended_at, now()) > b.t_from
),
0
)::numeric AS stop_s
FROM mv_hot.edge_oee_machine_stops s
CROSS JOIN bounds b
WHERE s.machine_id = p_machine_id
),
cycles AS (
SELECT
COALESCE(
GREATEST(
MAX(cycle_pulse_count) - MIN(cycle_pulse_count),
0
),
0
)::numeric AS actual_cycles
FROM mv_hot.edge_oee_live_telemetry l
CROSS JOIN bounds b
WHERE l.asset = p_asset
AND l.ingest_ts >= b.t_from
AND l.ingest_ts <= b.t_to
AND l.cycle_pulse_count IS NOT NULL
),
calc AS (
SELECT
p.planned_s,
st.stop_s,
GREATEST(p.planned_s - st.stop_s, 0)::numeric AS running_s,
c.actual_cycles,
t.target_cycle_rate_ppm,
t.quality_pct_default,
CASE
WHEN p.planned_s > 0
THEN ROUND(((GREATEST(p.planned_s - st.stop_s, 0) / p.planned_s) * 100)::numeric, 2)
ELSE 0
END AS availability_pct,
CASE
WHEN GREATEST(p.planned_s - st.stop_s, 0) > 0
AND t.target_cycle_rate_ppm > 0
THEN ROUND(
LEAST(
100::numeric,
(
c.actual_cycles
/
NULLIF(
t.target_cycle_rate_ppm * (GREATEST(p.planned_s - st.stop_s, 0) / 60.0),
0
)
) * 100
)::numeric,
2
)
ELSE 0
END AS performance_pct,
t.quality_pct_default AS quality_pct
FROM planned p
CROSS JOIN stops st
CROSS JOIN cycles c
CROSS JOIN target t
)
SELECT
ROUND(((calc.availability_pct * calc.performance_pct * calc.quality_pct) / 10000.0)::numeric, 2) AS oee_pct,
calc.availability_pct,
calc.performance_pct,
calc.quality_pct
FROM calc;
$$;
CREATE OR REPLACE FUNCTION mv_reports_ucepsa_demo.oee_operational_data(
p_from timestamptz,
p_to timestamptz,
p_machine_id text DEFAULT 'CORT-01',
p_asset text DEFAULT 'revpi_oee_node_01'
)
RETURNS TABLE (
running_min numeric,
stop_min numeric,
actual_cycles numeric
)
LANGUAGE sql
STABLE
AS $$
WITH dashboard_bounds AS (
SELECT
p_from AS dashboard_from,
LEAST(p_to, now()) AS dashboard_to
),
telemetry_span AS (
SELECT
MIN(ingest_ts) AS first_ts,
MAX(ingest_ts) AS last_ts
FROM mv_hot.edge_oee_live_telemetry
WHERE asset = p_asset
AND ingest_ts >= (SELECT dashboard_from FROM dashboard_bounds)
AND ingest_ts <= (SELECT dashboard_to FROM dashboard_bounds)
),
bounds AS (
SELECT
CASE
WHEN first_ts IS NULL THEN dashboard_from
ELSE GREATEST(dashboard_from, first_ts)
END AS t_from,
CASE
WHEN last_ts IS NULL THEN dashboard_to
ELSE LEAST(dashboard_to, last_ts)
END AS t_to
FROM dashboard_bounds
CROSS JOIN telemetry_span
),
planned AS (
SELECT
GREATEST(EXTRACT(EPOCH FROM (t_to - t_from))::numeric, 0) AS planned_s
FROM bounds
),
stops AS (
SELECT
COALESCE(
SUM(
GREATEST(
EXTRACT(EPOCH FROM (
LEAST(COALESCE(s.ended_at, b.t_to), b.t_to)
-
GREATEST(
CASE
WHEN s.started_at > s.created_at + interval '5 seconds'
THEN s.created_at
ELSE s.started_at
END,
b.t_from
)
))::numeric,
0
)
) FILTER (
WHERE s.started_at < b.t_to
AND COALESCE(s.ended_at, now()) > b.t_from
),
0
)::numeric AS stop_s
FROM mv_hot.edge_oee_machine_stops s
CROSS JOIN bounds b
WHERE s.machine_id = p_machine_id
),
cycles AS (
SELECT
COALESCE(
GREATEST(
MAX(cycle_pulse_count) - MIN(cycle_pulse_count),
0
),
0
)::numeric AS actual_cycles
FROM mv_hot.edge_oee_live_telemetry l
CROSS JOIN bounds b
WHERE l.asset = p_asset
AND l.ingest_ts >= b.t_from
AND l.ingest_ts <= b.t_to
AND l.cycle_pulse_count IS NOT NULL
),
calc AS (
SELECT
p.planned_s,
st.stop_s,
GREATEST(p.planned_s - st.stop_s, 0)::numeric AS running_s,
c.actual_cycles
FROM planned p
CROSS JOIN stops st
CROSS JOIN cycles c
)
SELECT
ROUND((running_s / 60.0)::numeric, 2) AS running_min,
ROUND((stop_s / 60.0)::numeric, 2) AS stop_min,
actual_cycles
FROM calc;
$$;
GRANT EXECUTE ON FUNCTION mv_reports_ucepsa_demo.oee_kpis(timestamptz, timestamptz, text, text)
TO grafana_ucepsa_ro;
GRANT EXECUTE ON FUNCTION mv_reports_ucepsa_demo.oee_operational_data(timestamptz, timestamptz, text, text)
TO grafana_ucepsa_ro;