@dazitech/cli 3.0.9 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/dist/clis/dazi-app.js +2 -2
  3. package/dist/clis/dazi-flow.js +2 -2
  4. package/dist/clis/dazi-onto.js +324 -41
  5. package/dist/clis/dazi.js +407 -185
  6. package/dist/docs/flow/flow-project-guide.md +1 -1
  7. package/dist/docs/guides/cli-reference.md +16 -3
  8. package/dist/docs/guides/troubleshooting.md +3 -3
  9. package/dist/docs/index.json +3 -15
  10. package/dist/docs/onto/dazi_script_sdk_reference.md +246 -244
  11. package/dist/docs/onto/dazi_script_seed_data_guide.md +1 -1
  12. package/dist/docs/onto/function-guide.md +123 -123
  13. package/dist/docs/onto//346/234/254/344/275/223/345/210/206/347/261/273/350/247/204/345/210/222/344/270/216SDK/346/211/251/345/261/225/346/226/271/346/241/210.md +169 -168
  14. package/dist/docs/onto//346/234/254/344/275/223/345/221/275/345/220/215/350/247/204/350/214/203_/347/211/251/347/220/206/350/241/250Cube/344/270/216/345/257/271/350/261/241.md +403 -402
  15. package/dist/docs/onto//346/234/254/344/275/223/345/274/200/345/217/221/344/274/230/345/214/226/346/200/273/347/273/223.md +257 -0
  16. package/dist/docs/onto//346/234/254/344/275/223/350/204/232/346/234/254/347/274/226/345/206/231/346/214/207/345/215/227.md +339 -311
  17. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +305 -281
  18. package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/345/270/270/350/247/201/351/224/231/350/257/257/345/244/204/347/220/206.md +297 -0
  19. package/dist/examples/index.json +222 -6
  20. package/dist/examples/onto/README.md +34 -36
  21. package/dist/examples/onto/_templates/onto_preflight.ps1 +84 -0
  22. package/dist/examples/onto/index.json +53 -0
  23. package/dist/examples/onto/index.yaml +29 -0
  24. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/README.md +23 -0
  25. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +12 -11
  26. package/dist/{docs/onto → examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/plans}//350/247/204/345/210/222/347/244/272/344/276/213_/345/210/251/346/266/246/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +37 -35
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_category_mount.py +85 -0
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +3 -66
  29. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/README.md +24 -0
  30. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_availability_analysis.py +84 -0
  31. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_downtime_breakdown.py +119 -0
  32. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_energy_intensity.py +98 -0
  33. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_get_summary.py +125 -0
  34. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_maintenance_compliance.py +77 -0
  35. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_mom_analysis.py +118 -0
  36. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_oee_analysis.py +126 -0
  37. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_plan_vs_actual.py +105 -0
  38. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_top_fault_equipment.py +104 -0
  39. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_unit_comparison.py +120 -0
  40. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/equip_ops_fn_yoy_analysis.py +115 -0
  41. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/save_test_arguments.ps1 +42 -0
  42. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.availability_analysis.json +7 -0
  43. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.downtime_breakdown.json +8 -0
  44. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.energy_intensity.json +8 -0
  45. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.get_summary.json +7 -0
  46. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.maintenance_compliance.json +7 -0
  47. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.mom_analysis.json +8 -0
  48. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.oee_analysis.json +8 -0
  49. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.plan_vs_actual.json +8 -0
  50. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.top_fault_equipment.json +8 -0
  51. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.unit_comparison.json +8 -0
  52. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/functions/test_arguments/equip_ops.fn.yoy_analysis.json +8 -0
  53. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/plans//345/214/226/345/267/245/350/256/276/345/244/207/350/277/220/350/220/245/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +735 -0
  54. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/setup/equip_ops_category_mount.py +106 -0
  55. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/setup/equip_ops_ontology_init.py +1077 -0
  56. package/dist/examples/onto//350/256/276/345/244/207/350/277/220/350/220/245/setup/equip_ops_seed_data.py +552 -0
  57. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/README.md +23 -0
  58. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +13 -12
  59. package/dist/{docs/onto → examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/plans}//350/247/204/345/210/222/347/244/272/344/276/213_/344/272/247/345/223/201/351/224/200/345/224/256/346/234/254/344/275/223/350/247/204/345/210/222/346/226/271/346/241/210.md +34 -34
  60. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_category_mount.py +82 -0
  61. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +3 -54
  62. package/dist/prompts/index.json +8 -1
  63. package/dist/prompts/onto/function-design.md +73 -73
  64. package/dist/prompts/onto/planning-design.md +226 -0
  65. package/dist/prompts/onto/script-publish-run.md +231 -208
  66. package/package.json +1 -1
  67. package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/347/272/240/351/224/231_/345/225/206/345/212/241/346/210/220/346/234/254/346/226/271/346/241/210/345/274/200/345/217/221/350/277/207/347/250/213.md +0 -213
@@ -0,0 +1,24 @@
1
+ # 设备运营 · 化工设备运营分析
2
+
3
+ ## 基本信息
4
+
5
+ | 字段 | 内容 |
6
+ | --- | --- |
7
+ | 数据空间 | 分类测试01 |
8
+ | 数据空间 ID | `space_cate_test01` |
9
+ | 示例 ID | `equip-ops`(见 `../index.yaml` / `index.json`) |
10
+
11
+ ## 目录
12
+
13
+ | 路径 | 说明 |
14
+ | --- | --- |
15
+ | [plans/化工设备运营分析本体方案.md](./plans/化工设备运营分析本体方案.md) | **规划正文**(OEE、停机等) |
16
+ | `setup/` | `equip_ops_ontology_init.py`、`equip_ops_seed_data.py`、`equip_ops_category_mount.py` |
17
+ | `functions/` | 11 个 `equip_ops_fn_*.py`、`test_arguments/` |
18
+
19
+ ## 使用
20
+
21
+ - **规划阶段**:推荐阅读(含 Cube + 冗余字段策略 + 11 函数清单)
22
+ - **实施阶段**:init 不含 `apply_registry`;见 `equip_ops_category_mount.py`
23
+
24
+ 规范指南:`资源/docs/onto/本体规划指南.md`
@@ -0,0 +1,84 @@
1
+ """可用率与停机结构分析 equip_ops.fn.availability_analysis
2
+
3
+ 参数:start_date, end_date, plant_id(可选)
4
+ 返回:planned_downtime_hours, unplanned_downtime_hours, runtime_hours, availability, load_rate
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_availability_analysis.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.availability_analysis
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
14
+ "object_type_code": "EquipmentAnalysis",
15
+ }
16
+
17
+
18
+ def _build_ops_where(start_date, end_date, plant_id=None):
19
+ clauses = []
20
+ if start_date and end_date:
21
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
22
+ if plant_id:
23
+ clauses.append(f"plant_id = '{plant_id}'")
24
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
25
+
26
+
27
+ def _ontology_fn_body(p):
28
+ params = dict(p.get_params() or {})
29
+ start_date = params.get("start_date", "")
30
+ end_date = params.get("end_date", "")
31
+ plant_id = params.get("plant_id") or None
32
+ where_clause = _build_ops_where(start_date, end_date, plant_id)
33
+
34
+ sql = f"""
35
+ SELECT
36
+ sum(planned_downtime_min) AS sum_planned_downtime_min,
37
+ sum(unplanned_downtime_min) AS sum_unplanned_downtime_min,
38
+ sum(runtime_min) AS sum_runtime_min,
39
+ sum(calendar_minutes - planned_downtime_min) AS sum_available_min
40
+ FROM fact_equipment_daily_ops
41
+ {where_clause}
42
+ """
43
+
44
+ rows = p.sql.query(sql)
45
+ row = rows[0] if rows else {}
46
+
47
+ sum_runtime = float(row.get("sum_runtime_min") or 0)
48
+ sum_available = float(row.get("sum_available_min") or 0)
49
+ availability = sum_runtime / sum_available if sum_available > 0 else 0.0
50
+ load_rate = availability
51
+
52
+ data = [{
53
+ "planned_downtime_hours": round(float(row.get("sum_planned_downtime_min") or 0) / 60.0, 2),
54
+ "unplanned_downtime_hours": round(
55
+ float(row.get("sum_unplanned_downtime_min") or 0) / 60.0, 2
56
+ ),
57
+ "runtime_hours": round(sum_runtime / 60.0, 2),
58
+ "availability": round(availability, 4),
59
+ "load_rate": round(load_rate, 4),
60
+ }]
61
+
62
+ return p.function_result(
63
+ columns=[
64
+ "planned_downtime_hours", "unplanned_downtime_hours",
65
+ "runtime_hours", "availability", "load_rate",
66
+ ],
67
+ data=data,
68
+ row_count=1,
69
+ )
70
+
71
+
72
+ def main():
73
+ s = space.get(ctx.space_id or "")
74
+ _Ports = type(
75
+ "_Ports",
76
+ (),
77
+ {
78
+ "get_params": lambda self: dict(ctx.params or {}),
79
+ "function_result": lambda self, **kw: onto.function_result(**kw),
80
+ },
81
+ )
82
+ p = _Ports()
83
+ p.sql = s.sql
84
+ return _ontology_fn_body(p)
@@ -0,0 +1,119 @@
1
+ """停机原因结构分析 equip_ops.fn.downtime_breakdown
2
+
3
+ 参数:start_date, end_date, reason_level=2, plant_id(可选)
4
+ JOIN dim_downtime_reason,返回 reason_code, reason_name, reason_category,
5
+ is_planned, downtime_hours, event_count, share_pct
6
+
7
+ 发布:
8
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_downtime_breakdown.py \
9
+ --space space_cate_test01 --register-function-id equip_ops.fn.downtime_breakdown
10
+ """
11
+
12
+ TEST_ARGUMENTS = {
13
+ "v": 1,
14
+ "arguments": {
15
+ "start_date": "2025-01-01",
16
+ "end_date": "2026-06-30",
17
+ "reason_level": 2,
18
+ },
19
+ "object_type_code": "EquipmentAnalysis",
20
+ }
21
+
22
+
23
+ def _build_where(start_date, end_date, plant_id=None):
24
+ clauses = []
25
+ if start_date and end_date:
26
+ clauses.append(
27
+ f"e.start_time >= '{start_date} 00:00:00' AND e.start_time <= '{end_date} 23:59:59'"
28
+ )
29
+ if plant_id:
30
+ clauses.append(f"e.plant_id = '{plant_id}'")
31
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
32
+
33
+
34
+ def _ontology_fn_body(p):
35
+ params = dict(p.get_params() or {})
36
+ start_date = params.get("start_date", "")
37
+ end_date = params.get("end_date", "")
38
+ reason_level = int(params.get("reason_level", 2) or 2)
39
+ plant_id = params.get("plant_id") or None
40
+ where_clause = _build_where(start_date, end_date, plant_id)
41
+
42
+ if reason_level <= 1:
43
+ group_cols = "dr.reason_category"
44
+ select_cols = """
45
+ dr.reason_category AS reason_code,
46
+ dr.reason_category AS reason_name,
47
+ dr.reason_category AS reason_category,
48
+ max(dr.is_planned) AS is_planned
49
+ """
50
+ else:
51
+ group_cols = "dr.reason_code, dr.reason_name, dr.reason_category, dr.is_planned"
52
+ select_cols = """
53
+ dr.reason_code AS reason_code,
54
+ dr.reason_name AS reason_name,
55
+ dr.reason_category AS reason_category,
56
+ dr.is_planned AS is_planned
57
+ """
58
+
59
+ sql = f"""
60
+ SELECT
61
+ {select_cols},
62
+ sum(e.duration_min) AS downtime_min,
63
+ count() AS event_count
64
+ FROM fact_downtime_event AS e
65
+ INNER JOIN dim_downtime_reason AS dr ON e.reason_id = dr.reason_id
66
+ {where_clause}
67
+ GROUP BY {group_cols}
68
+ ORDER BY downtime_min DESC
69
+ """
70
+
71
+ result = p.sql.query(sql)
72
+ if not result:
73
+ return p.function_result(
74
+ columns=[
75
+ "reason_code", "reason_name", "reason_category",
76
+ "is_planned", "downtime_hours", "event_count", "share_pct",
77
+ ],
78
+ data=[],
79
+ row_count=0,
80
+ )
81
+
82
+ total_min = sum(float(r.get("downtime_min") or 0) for r in result)
83
+ data = []
84
+ for row in result:
85
+ downtime_min = float(row.get("downtime_min") or 0)
86
+ share_pct = downtime_min / total_min if total_min > 0 else 0.0
87
+ data.append({
88
+ "reason_code": str(row.get("reason_code") or ""),
89
+ "reason_name": str(row.get("reason_name") or ""),
90
+ "reason_category": str(row.get("reason_category") or ""),
91
+ "is_planned": int(row.get("is_planned") or 0),
92
+ "downtime_hours": round(downtime_min / 60.0, 2),
93
+ "event_count": int(row.get("event_count") or 0),
94
+ "share_pct": round(share_pct, 4),
95
+ })
96
+
97
+ return p.function_result(
98
+ columns=[
99
+ "reason_code", "reason_name", "reason_category",
100
+ "is_planned", "downtime_hours", "event_count", "share_pct",
101
+ ],
102
+ data=data,
103
+ row_count=len(data),
104
+ )
105
+
106
+
107
+ def main():
108
+ s = space.get(ctx.space_id or "")
109
+ _Ports = type(
110
+ "_Ports",
111
+ (),
112
+ {
113
+ "get_params": lambda self: dict(ctx.params or {}),
114
+ "function_result": lambda self, **kw: onto.function_result(**kw),
115
+ },
116
+ )
117
+ p = _Ports()
118
+ p.sql = s.sql
119
+ return _ontology_fn_body(p)
@@ -0,0 +1,98 @@
1
+ """能耗强度分析 equip_ops.fn.energy_intensity
2
+
3
+ 参数:start_date, end_date, group_by=plant|unit|equipment, plant_id(可选)
4
+ 返回:group_id, group_name, energy_total, output_qty, energy_per_output
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_energy_intensity.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.energy_intensity
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {
14
+ "start_date": "2025-01-01",
15
+ "end_date": "2026-06-30",
16
+ "group_by": "plant",
17
+ },
18
+ "object_type_code": "EquipmentAnalysis",
19
+ }
20
+
21
+ _GROUP_BY_MAP = {
22
+ "plant": ("plant_id", "any(plant_name)"),
23
+ "unit": ("unit_id", "any(unit_name)"),
24
+ "equipment": ("equipment_id", "any(equipment_name)"),
25
+ }
26
+
27
+
28
+ def _build_ops_where(start_date, end_date, plant_id=None):
29
+ clauses = []
30
+ if start_date and end_date:
31
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
32
+ if plant_id:
33
+ clauses.append(f"plant_id = '{plant_id}'")
34
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
35
+
36
+
37
+ def _ontology_fn_body(p):
38
+ params = dict(p.get_params() or {})
39
+ start_date = params.get("start_date", "")
40
+ end_date = params.get("end_date", "")
41
+ group_by = params.get("group_by", "plant")
42
+ plant_id = params.get("plant_id") or None
43
+ group_id_col, group_name_expr = _GROUP_BY_MAP.get(group_by, _GROUP_BY_MAP["plant"])
44
+ where_clause = _build_ops_where(start_date, end_date, plant_id)
45
+
46
+ sql = f"""
47
+ SELECT
48
+ {group_id_col} AS group_id,
49
+ {group_name_expr} AS group_name,
50
+ sum(energy_consumption) AS energy_total,
51
+ sum(actual_output_qty) AS output_qty
52
+ FROM fact_equipment_daily_ops
53
+ {where_clause}
54
+ GROUP BY {group_id_col}
55
+ ORDER BY energy_total DESC
56
+ """
57
+
58
+ result = p.sql.query(sql)
59
+ if not result:
60
+ return p.function_result(
61
+ columns=["group_id", "group_name", "energy_total", "output_qty", "energy_per_output"],
62
+ data=[],
63
+ row_count=0,
64
+ )
65
+
66
+ data = []
67
+ for row in result:
68
+ energy_total = float(row.get("energy_total") or 0)
69
+ output_qty = float(row.get("output_qty") or 0)
70
+ energy_per_output = energy_total / output_qty if output_qty > 0 else 0.0
71
+ data.append({
72
+ "group_id": str(row.get("group_id") or ""),
73
+ "group_name": str(row.get("group_name") or ""),
74
+ "energy_total": round(energy_total, 2),
75
+ "output_qty": round(output_qty, 2),
76
+ "energy_per_output": round(energy_per_output, 4),
77
+ })
78
+
79
+ return p.function_result(
80
+ columns=["group_id", "group_name", "energy_total", "output_qty", "energy_per_output"],
81
+ data=data,
82
+ row_count=len(data),
83
+ )
84
+
85
+
86
+ def main():
87
+ s = space.get(ctx.space_id or "")
88
+ _Ports = type(
89
+ "_Ports",
90
+ (),
91
+ {
92
+ "get_params": lambda self: dict(ctx.params or {}),
93
+ "function_result": lambda self, **kw: onto.function_result(**kw),
94
+ },
95
+ )
96
+ p = _Ports()
97
+ p.sql = s.sql
98
+ return _ontology_fn_body(p)
@@ -0,0 +1,125 @@
1
+ """设备运营总览 equip_ops.fn.get_summary
2
+
3
+ 参数:start_date, end_date, plant_id(可选)
4
+ 返回:equipment_count, avg_availability, avg_oee, total_runtime_hours,
5
+ total_unplanned_downtime_hours, total_output_qty, total_energy,
6
+ energy_per_output, downtime_event_count
7
+
8
+ 发布:
9
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_get_summary.py \
10
+ --space space_cate_test01 --register-function-id equip_ops.fn.get_summary
11
+ """
12
+
13
+ TEST_ARGUMENTS = {
14
+ "v": 1,
15
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
16
+ "object_type_code": "EquipmentAnalysis",
17
+ }
18
+
19
+
20
+ def _build_ops_where(start_date, end_date, plant_id=None):
21
+ clauses = []
22
+ if start_date and end_date:
23
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
24
+ if plant_id:
25
+ clauses.append(f"plant_id = '{plant_id}'")
26
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
27
+
28
+
29
+ def _build_downtime_where(start_date, end_date, plant_id=None):
30
+ clauses = []
31
+ if start_date and end_date:
32
+ clauses.append(
33
+ f"toDate(start_time) >= '{start_date}' AND toDate(start_time) <= '{end_date}'"
34
+ )
35
+ if plant_id:
36
+ clauses.append(f"plant_id = '{plant_id}'")
37
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
38
+
39
+
40
+ def _calc_oee_from_sums(row):
41
+ sum_runtime = float(row.get("sum_runtime_min") or 0)
42
+ sum_available = float(row.get("sum_available_min") or 0)
43
+ sum_actual = float(row.get("sum_actual_output") or 0)
44
+ sum_qualified = float(row.get("sum_qualified_output") or 0)
45
+ sum_theoretical = float(row.get("sum_theoretical_output") or 0)
46
+ availability = sum_runtime / sum_available if sum_available > 0 else 0.0
47
+ quality = sum_qualified / sum_actual if sum_actual > 0 else 0.0
48
+ performance = sum_actual / sum_theoretical if sum_theoretical > 0 else 0.0
49
+ oee = availability * performance * quality
50
+ return round(availability, 4), round(oee, 4)
51
+
52
+
53
+ def _ontology_fn_body(p):
54
+ params = dict(p.get_params() or {})
55
+ start_date = params.get("start_date", "")
56
+ end_date = params.get("end_date", "")
57
+ plant_id = params.get("plant_id") or None
58
+ where_clause = _build_ops_where(start_date, end_date, plant_id)
59
+
60
+ sql = f"""
61
+ SELECT
62
+ uniq(equipment_id) AS equipment_count,
63
+ sum(runtime_min) AS sum_runtime_min,
64
+ sum(calendar_minutes - planned_downtime_min) AS sum_available_min,
65
+ sum(actual_output_qty) AS sum_actual_output,
66
+ sum(qualified_output_qty) AS sum_qualified_output,
67
+ sumIf(runtime_min * ideal_cycle_rate, runtime_min > 0) AS sum_theoretical_output,
68
+ sum(unplanned_downtime_min) AS sum_unplanned_downtime_min,
69
+ sum(energy_consumption) AS sum_energy
70
+ FROM fact_equipment_daily_ops
71
+ {where_clause}
72
+ """
73
+
74
+ rows = p.sql.query(sql)
75
+ row = rows[0] if rows else {}
76
+ avg_availability, avg_oee = _calc_oee_from_sums(row)
77
+
78
+ sum_runtime = float(row.get("sum_runtime_min") or 0)
79
+ sum_actual = float(row.get("sum_actual_output") or 0)
80
+ sum_energy = float(row.get("sum_energy") or 0)
81
+ energy_per_output = sum_energy / sum_actual if sum_actual > 0 else 0.0
82
+
83
+ dt_where = _build_downtime_where(start_date, end_date, plant_id)
84
+ dt_sql = f"SELECT count() AS event_count FROM fact_downtime_event {dt_where}"
85
+ dt_rows = p.sql.query(dt_sql)
86
+ downtime_event_count = int((dt_rows[0] if dt_rows else {}).get("event_count") or 0)
87
+
88
+ data = [{
89
+ "equipment_count": int(row.get("equipment_count") or 0),
90
+ "avg_availability": avg_availability,
91
+ "avg_oee": avg_oee,
92
+ "total_runtime_hours": round(sum_runtime / 60.0, 2),
93
+ "total_unplanned_downtime_hours": round(
94
+ float(row.get("sum_unplanned_downtime_min") or 0) / 60.0, 2
95
+ ),
96
+ "total_output_qty": round(sum_actual, 2),
97
+ "total_energy": round(sum_energy, 2),
98
+ "energy_per_output": round(energy_per_output, 4),
99
+ "downtime_event_count": downtime_event_count,
100
+ }]
101
+
102
+ return p.function_result(
103
+ columns=[
104
+ "equipment_count", "avg_availability", "avg_oee", "total_runtime_hours",
105
+ "total_unplanned_downtime_hours", "total_output_qty", "total_energy",
106
+ "energy_per_output", "downtime_event_count",
107
+ ],
108
+ data=data,
109
+ row_count=1,
110
+ )
111
+
112
+
113
+ def main():
114
+ s = space.get(ctx.space_id or "")
115
+ _Ports = type(
116
+ "_Ports",
117
+ (),
118
+ {
119
+ "get_params": lambda self: dict(ctx.params or {}),
120
+ "function_result": lambda self, **kw: onto.function_result(**kw),
121
+ },
122
+ )
123
+ p = _Ports()
124
+ p.sql = s.sql
125
+ return _ontology_fn_body(p)
@@ -0,0 +1,77 @@
1
+ """维保达成分析 equip_ops.fn.maintenance_compliance
2
+
3
+ 参数:start_date, end_date, plant_id(可选)
4
+ 返回:schedule_rate, total_maint, on_schedule_count, overdue_count, total_cost
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_maintenance_compliance.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.maintenance_compliance
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30"},
14
+ "object_type_code": "EquipmentAnalysis",
15
+ }
16
+
17
+
18
+ def _build_where(start_date, end_date, plant_id=None):
19
+ clauses = []
20
+ if start_date and end_date:
21
+ clauses.append(f"plan_date >= '{start_date}' AND plan_date <= '{end_date}'")
22
+ if plant_id:
23
+ clauses.append(f"plant_id = '{plant_id}'")
24
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
25
+
26
+
27
+ def _ontology_fn_body(p):
28
+ params = dict(p.get_params() or {})
29
+ start_date = params.get("start_date", "")
30
+ end_date = params.get("end_date", "")
31
+ plant_id = params.get("plant_id") or None
32
+ where_clause = _build_where(start_date, end_date, plant_id)
33
+
34
+ sql = f"""
35
+ SELECT
36
+ count() AS total_maint,
37
+ sum(is_on_schedule) AS on_schedule_count,
38
+ countIf(status = '逾期') AS overdue_count,
39
+ sum(actual_cost) AS total_cost
40
+ FROM fact_maintenance_record
41
+ {where_clause}
42
+ """
43
+
44
+ rows = p.sql.query(sql)
45
+ row = rows[0] if rows else {}
46
+ total_maint = int(row.get("total_maint") or 0)
47
+ on_schedule_count = int(row.get("on_schedule_count") or 0)
48
+ schedule_rate = on_schedule_count / total_maint if total_maint > 0 else 0.0
49
+
50
+ data = [{
51
+ "schedule_rate": round(schedule_rate, 4),
52
+ "total_maint": total_maint,
53
+ "on_schedule_count": on_schedule_count,
54
+ "overdue_count": int(row.get("overdue_count") or 0),
55
+ "total_cost": round(float(row.get("total_cost") or 0), 2),
56
+ }]
57
+
58
+ return p.function_result(
59
+ columns=["schedule_rate", "total_maint", "on_schedule_count", "overdue_count", "total_cost"],
60
+ data=data,
61
+ row_count=1,
62
+ )
63
+
64
+
65
+ def main():
66
+ s = space.get(ctx.space_id or "")
67
+ _Ports = type(
68
+ "_Ports",
69
+ (),
70
+ {
71
+ "get_params": lambda self: dict(ctx.params or {}),
72
+ "function_result": lambda self, **kw: onto.function_result(**kw),
73
+ },
74
+ )
75
+ p = _Ports()
76
+ p.sql = s.sql
77
+ return _ontology_fn_body(p)
@@ -0,0 +1,118 @@
1
+ """环比分析 equip_ops.fn.mom_analysis
2
+
3
+ 参数:start_date, end_date, metric=oee|availability|output, unit_id(可选)
4
+ 对比上一同等长度期间,返回 current / previous / mom
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_mom_analysis.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.mom_analysis
9
+ """
10
+
11
+ from datetime import datetime, timedelta
12
+
13
+ TEST_ARGUMENTS = {
14
+ "v": 1,
15
+ "arguments": {
16
+ "start_date": "2025-01-01",
17
+ "end_date": "2026-06-30",
18
+ "metric": "availability",
19
+ },
20
+ "object_type_code": "EquipmentAnalysis",
21
+ }
22
+
23
+
24
+ def _build_ops_where(start_date, end_date, unit_id=None):
25
+ clauses = []
26
+ if start_date and end_date:
27
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
28
+ if unit_id:
29
+ clauses.append(f"unit_id = '{unit_id}'")
30
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
31
+
32
+
33
+ def _query_period_metrics(p, start_date, end_date, unit_id):
34
+ where_clause = _build_ops_where(start_date, end_date, unit_id)
35
+ sql = f"""
36
+ SELECT
37
+ sum(runtime_min) AS sum_runtime_min,
38
+ sum(calendar_minutes - planned_downtime_min) AS sum_available_min,
39
+ sum(actual_output_qty) AS sum_actual_output,
40
+ sum(qualified_output_qty) AS sum_qualified_output,
41
+ sumIf(runtime_min * ideal_cycle_rate, runtime_min > 0) AS sum_theoretical_output
42
+ FROM fact_equipment_daily_ops
43
+ {where_clause}
44
+ """
45
+ rows = p.sql.query(sql)
46
+ row = rows[0] if rows else {}
47
+ sum_runtime = float(row.get("sum_runtime_min") or 0)
48
+ sum_available = float(row.get("sum_available_min") or 0)
49
+ sum_actual = float(row.get("sum_actual_output") or 0)
50
+ sum_qualified = float(row.get("sum_qualified_output") or 0)
51
+ sum_theoretical = float(row.get("sum_theoretical_output") or 0)
52
+ availability = sum_runtime / sum_available if sum_available > 0 else 0.0
53
+ quality = sum_qualified / sum_actual if sum_actual > 0 else 0.0
54
+ performance = sum_actual / sum_theoretical if sum_theoretical > 0 else 0.0
55
+ oee = availability * performance * quality
56
+ return {
57
+ "oee": round(oee, 4),
58
+ "availability": round(availability, 4),
59
+ "output": round(sum_actual, 2),
60
+ }
61
+
62
+
63
+ def _calc_mom(current, previous):
64
+ if previous == 0:
65
+ return 0.0 if current == 0 else None
66
+ return round((current - previous) / previous, 4)
67
+
68
+
69
+ def _ontology_fn_body(p):
70
+ params = dict(p.get_params() or {})
71
+ start_date = params.get("start_date") or "2025-01-01"
72
+ end_date = params.get("end_date") or "2026-06-30"
73
+ metric = params.get("metric", "oee")
74
+ unit_id = params.get("unit_id") or None
75
+
76
+ start_dt = datetime.strptime(start_date, "%Y-%m-%d")
77
+ end_dt = datetime.strptime(end_date, "%Y-%m-%d")
78
+ period_days = (end_dt - start_dt).days + 1
79
+ prev_end_dt = start_dt - timedelta(days=1)
80
+ prev_start_dt = prev_end_dt - timedelta(days=period_days - 1)
81
+ prev_start = prev_start_dt.strftime("%Y-%m-%d")
82
+ prev_end = prev_end_dt.strftime("%Y-%m-%d")
83
+
84
+ current_metrics = _query_period_metrics(p, start_date, end_date, unit_id)
85
+ previous_metrics = _query_period_metrics(p, prev_start, prev_end, unit_id)
86
+
87
+ current_value = current_metrics.get(metric, 0)
88
+ previous_value = previous_metrics.get(metric, 0)
89
+ mom = _calc_mom(current_value, previous_value)
90
+
91
+ result = {
92
+ "metric": metric,
93
+ "period": f"{start_date} ~ {end_date}",
94
+ "previous_period": f"{prev_start} ~ {prev_end}",
95
+ "current": current_value,
96
+ "previous": previous_value,
97
+ "mom": mom,
98
+ }
99
+ return p.function_result(
100
+ columns=["metric", "period", "previous_period", "current", "previous", "mom"],
101
+ data=[result],
102
+ row_count=1,
103
+ )
104
+
105
+
106
+ def main():
107
+ s = space.get(ctx.space_id or "")
108
+ _Ports = type(
109
+ "_Ports",
110
+ (),
111
+ {
112
+ "get_params": lambda self: dict(ctx.params or {}),
113
+ "function_result": lambda self, **kw: onto.function_result(**kw),
114
+ },
115
+ )
116
+ p = _Ports()
117
+ p.sql = s.sql
118
+ return _ontology_fn_body(p)