@dazitech/cli 3.1.0 → 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 (65) 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 +318 -40
  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 +1 -1
  9. package/dist/docs/index.json +1 -13
  10. package/dist/docs/onto/dazi_script_sdk_reference.md +1 -1
  11. package/dist/docs/onto/dazi_script_seed_data_guide.md +1 -1
  12. package/dist/docs/onto/function-guide.md +6 -6
  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 -169
  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 +3 -2
  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 -242
  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 +12 -12
  17. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +14 -13
  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 +70 -15
  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 +4 -4
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_category_mount.py +1 -1
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +1 -1
  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 +2 -2
  60. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_category_mount.py +1 -1
  61. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +1 -1
  62. package/dist/prompts/index.json +1 -1
  63. package/dist/prompts/onto/planning-design.md +226 -104
  64. package/dist/prompts/onto/script-publish-run.md +8 -6
  65. package/package.json +1 -1
@@ -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)
@@ -0,0 +1,126 @@
1
+ """OEE 分解分析 equip_ops.fn.oee_analysis
2
+
3
+ 参数:start_date, end_date, equipment_id(可选), group_by=plant|unit|equipment|equip_type
4
+ 返回:group_id, group_name, availability, performance, quality, oee, runtime_hours, output_qty
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_oee_analysis.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.oee_analysis
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
+ "equip_type": ("equip_type_id", "any(category)"),
26
+ }
27
+
28
+
29
+ def _build_ops_where(start_date, end_date, plant_id=None, equipment_id=None):
30
+ clauses = []
31
+ if start_date and end_date:
32
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
33
+ if plant_id:
34
+ clauses.append(f"plant_id = '{plant_id}'")
35
+ if equipment_id:
36
+ clauses.append(f"equipment_id = '{equipment_id}'")
37
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
38
+
39
+
40
+ def _calc_oee_metrics(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 {
51
+ "availability": round(availability, 4),
52
+ "performance": round(performance, 4),
53
+ "quality": round(quality, 4),
54
+ "oee": round(oee, 4),
55
+ "runtime_hours": round(sum_runtime / 60.0, 2),
56
+ "output_qty": round(sum_actual, 2),
57
+ }
58
+
59
+
60
+ def _ontology_fn_body(p):
61
+ params = dict(p.get_params() or {})
62
+ start_date = params.get("start_date", "")
63
+ end_date = params.get("end_date", "")
64
+ equipment_id = params.get("equipment_id") or None
65
+ group_by = params.get("group_by", "plant")
66
+ group_id_col, group_name_expr = _GROUP_BY_MAP.get(group_by, _GROUP_BY_MAP["plant"])
67
+ where_clause = _build_ops_where(start_date, end_date, equipment_id=equipment_id)
68
+
69
+ sql = f"""
70
+ SELECT
71
+ {group_id_col} AS group_id,
72
+ {group_name_expr} AS group_name,
73
+ sum(runtime_min) AS sum_runtime_min,
74
+ sum(calendar_minutes - planned_downtime_min) AS sum_available_min,
75
+ sum(actual_output_qty) AS sum_actual_output,
76
+ sum(qualified_output_qty) AS sum_qualified_output,
77
+ sumIf(runtime_min * ideal_cycle_rate, runtime_min > 0) AS sum_theoretical_output
78
+ FROM fact_equipment_daily_ops
79
+ {where_clause}
80
+ GROUP BY {group_id_col}
81
+ ORDER BY group_id
82
+ """
83
+
84
+ result = p.sql.query(sql)
85
+ if not result:
86
+ return p.function_result(
87
+ columns=[
88
+ "group_id", "group_name", "availability", "performance",
89
+ "quality", "oee", "runtime_hours", "output_qty",
90
+ ],
91
+ data=[],
92
+ row_count=0,
93
+ )
94
+
95
+ data = []
96
+ for row in result:
97
+ metrics = _calc_oee_metrics(row)
98
+ data.append({
99
+ "group_id": str(row.get("group_id") or ""),
100
+ "group_name": str(row.get("group_name") or ""),
101
+ **metrics,
102
+ })
103
+
104
+ return p.function_result(
105
+ columns=[
106
+ "group_id", "group_name", "availability", "performance",
107
+ "quality", "oee", "runtime_hours", "output_qty",
108
+ ],
109
+ data=data,
110
+ row_count=len(data),
111
+ )
112
+
113
+
114
+ def main():
115
+ s = space.get(ctx.space_id or "")
116
+ _Ports = type(
117
+ "_Ports",
118
+ (),
119
+ {
120
+ "get_params": lambda self: dict(ctx.params or {}),
121
+ "function_result": lambda self, **kw: onto.function_result(**kw),
122
+ },
123
+ )
124
+ p = _Ports()
125
+ p.sql = s.sql
126
+ return _ontology_fn_body(p)
@@ -0,0 +1,105 @@
1
+ """运行计划对比 equip_ops.fn.plan_vs_actual
2
+
3
+ 参数:fiscal_year, fiscal_month, plan_version, plant_id(可选)
4
+ JOIN fact_equipment_plan 与 fact_equipment_daily_ops
5
+ 返回:planned vs actual runtime/output/energy, execution_rate
6
+
7
+ 发布:
8
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_plan_vs_actual.py \
9
+ --space space_cate_test01 --register-function-id equip_ops.fn.plan_vs_actual
10
+ """
11
+
12
+ TEST_ARGUMENTS = {
13
+ "v": 1,
14
+ "arguments": {
15
+ "fiscal_year": 2026,
16
+ "fiscal_month": 6,
17
+ "plan_version": "2026月度计划",
18
+ },
19
+ "object_type_code": "PlanAnalysis",
20
+ }
21
+
22
+
23
+ def _ontology_fn_body(p):
24
+ params = dict(p.get_params() or {})
25
+ fiscal_year = int(params.get("fiscal_year", 2026))
26
+ fiscal_month = int(params.get("fiscal_month", 1))
27
+ plan_version = params.get("plan_version", "2026月度计划")
28
+ plant_id = params.get("plant_id") or None
29
+
30
+ plant_cond = "" if not plant_id else f"AND p.plant_id = '{plant_id}'"
31
+
32
+ sql = f"""
33
+ SELECT
34
+ sum(p.planned_runtime_min) AS planned_runtime_min,
35
+ sum(p.planned_output_qty) AS planned_output_qty,
36
+ sum(p.planned_energy) AS planned_energy,
37
+ sum(o.runtime_min) AS actual_runtime_min,
38
+ sum(o.actual_output_qty) AS actual_output_qty,
39
+ sum(o.energy_consumption) AS actual_energy
40
+ FROM fact_equipment_plan AS p
41
+ LEFT JOIN fact_equipment_daily_ops AS o
42
+ ON p.equipment_id = o.equipment_id AND p.date_key = o.date_key
43
+ WHERE p.fiscal_year = {fiscal_year}
44
+ AND p.fiscal_month = {fiscal_month}
45
+ AND p.plan_version = '{plan_version}'
46
+ {plant_cond}
47
+ """
48
+
49
+ rows = p.sql.query(sql)
50
+ row = rows[0] if rows else {}
51
+
52
+ planned_runtime = float(row.get("planned_runtime_min") or 0)
53
+ planned_output = float(row.get("planned_output_qty") or 0)
54
+ planned_energy = float(row.get("planned_energy") or 0)
55
+ actual_runtime = float(row.get("actual_runtime_min") or 0)
56
+ actual_output = float(row.get("actual_output_qty") or 0)
57
+ actual_energy = float(row.get("actual_energy") or 0)
58
+
59
+ runtime_rate = actual_runtime / planned_runtime if planned_runtime > 0 else 0.0
60
+ output_rate = actual_output / planned_output if planned_output > 0 else 0.0
61
+ energy_rate = actual_energy / planned_energy if planned_energy > 0 else 0.0
62
+ execution_rate = (runtime_rate + output_rate + energy_rate) / 3.0
63
+
64
+ data = [{
65
+ "fiscal_year": fiscal_year,
66
+ "fiscal_month": fiscal_month,
67
+ "plan_version": plan_version,
68
+ "planned_runtime_hours": round(planned_runtime / 60.0, 2),
69
+ "actual_runtime_hours": round(actual_runtime / 60.0, 2),
70
+ "runtime_execution_rate": round(runtime_rate, 4),
71
+ "planned_output_qty": round(planned_output, 2),
72
+ "actual_output_qty": round(actual_output, 2),
73
+ "output_execution_rate": round(output_rate, 4),
74
+ "planned_energy": round(planned_energy, 2),
75
+ "actual_energy": round(actual_energy, 2),
76
+ "energy_execution_rate": round(energy_rate, 4),
77
+ "execution_rate": round(execution_rate, 4),
78
+ }]
79
+
80
+ return p.function_result(
81
+ columns=[
82
+ "fiscal_year", "fiscal_month", "plan_version",
83
+ "planned_runtime_hours", "actual_runtime_hours", "runtime_execution_rate",
84
+ "planned_output_qty", "actual_output_qty", "output_execution_rate",
85
+ "planned_energy", "actual_energy", "energy_execution_rate",
86
+ "execution_rate",
87
+ ],
88
+ data=data,
89
+ row_count=1,
90
+ )
91
+
92
+
93
+ def main():
94
+ s = space.get(ctx.space_id or "")
95
+ _Ports = type(
96
+ "_Ports",
97
+ (),
98
+ {
99
+ "get_params": lambda self: dict(ctx.params or {}),
100
+ "function_result": lambda self, **kw: onto.function_result(**kw),
101
+ },
102
+ )
103
+ p = _Ports()
104
+ p.sql = s.sql
105
+ return _ontology_fn_body(p)
@@ -0,0 +1,104 @@
1
+ """故障 TOP 设备 equip_ops.fn.top_fault_equipment
2
+
3
+ 参数:start_date, end_date, limit=10, plant_id(可选)
4
+ 按非计划停机时长 TOP 设备
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_top_fault_equipment.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.top_fault_equipment
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {
14
+ "start_date": "2025-01-01",
15
+ "end_date": "2026-06-30",
16
+ "limit": 10,
17
+ },
18
+ "object_type_code": "EquipmentAnalysis",
19
+ }
20
+
21
+
22
+ def _build_where(start_date, end_date, plant_id=None):
23
+ clauses = ["e.is_planned = 0"]
24
+ if start_date and end_date:
25
+ clauses.append(
26
+ f"e.start_time >= '{start_date} 00:00:00' AND e.start_time <= '{end_date} 23:59:59'"
27
+ )
28
+ if plant_id:
29
+ clauses.append(f"e.plant_id = '{plant_id}'")
30
+ return "WHERE " + " AND ".join(clauses)
31
+
32
+
33
+ def _ontology_fn_body(p):
34
+ params = dict(p.get_params() or {})
35
+ start_date = params.get("start_date", "")
36
+ end_date = params.get("end_date", "")
37
+ limit = int(params.get("limit", 10) or 10)
38
+ plant_id = params.get("plant_id") or None
39
+ where_clause = _build_where(start_date, end_date, plant_id)
40
+
41
+ sql = f"""
42
+ SELECT
43
+ e.equipment_id,
44
+ any(e.equipment_code) AS equipment_code,
45
+ any(e.equipment_name) AS equipment_name,
46
+ any(eq.plant_name) AS plant_name,
47
+ any(eq.unit_name) AS unit_name,
48
+ sum(e.duration_min) AS downtime_min,
49
+ count() AS event_count
50
+ FROM fact_downtime_event AS e
51
+ LEFT JOIN dim_equipment AS eq ON e.equipment_id = eq.equipment_id
52
+ {where_clause}
53
+ GROUP BY e.equipment_id
54
+ ORDER BY downtime_min DESC
55
+ LIMIT {limit}
56
+ """
57
+
58
+ result = p.sql.query(sql)
59
+ if not result:
60
+ return p.function_result(
61
+ columns=[
62
+ "rank", "equipment_id", "equipment_code", "equipment_name",
63
+ "plant_name", "unit_name", "downtime_hours", "event_count",
64
+ ],
65
+ data=[],
66
+ row_count=0,
67
+ )
68
+
69
+ data = []
70
+ for rank, row in enumerate(result, start=1):
71
+ data.append({
72
+ "rank": rank,
73
+ "equipment_id": str(row.get("equipment_id") or ""),
74
+ "equipment_code": str(row.get("equipment_code") or ""),
75
+ "equipment_name": str(row.get("equipment_name") or ""),
76
+ "plant_name": str(row.get("plant_name") or ""),
77
+ "unit_name": str(row.get("unit_name") or ""),
78
+ "downtime_hours": round(float(row.get("downtime_min") or 0) / 60.0, 2),
79
+ "event_count": int(row.get("event_count") or 0),
80
+ })
81
+
82
+ return p.function_result(
83
+ columns=[
84
+ "rank", "equipment_id", "equipment_code", "equipment_name",
85
+ "plant_name", "unit_name", "downtime_hours", "event_count",
86
+ ],
87
+ data=data,
88
+ row_count=len(data),
89
+ )
90
+
91
+
92
+ def main():
93
+ s = space.get(ctx.space_id or "")
94
+ _Ports = type(
95
+ "_Ports",
96
+ (),
97
+ {
98
+ "get_params": lambda self: dict(ctx.params or {}),
99
+ "function_result": lambda self, **kw: onto.function_result(**kw),
100
+ },
101
+ )
102
+ p = _Ports()
103
+ p.sql = s.sql
104
+ return _ontology_fn_body(p)
@@ -0,0 +1,120 @@
1
+ """工艺单元对标 equip_ops.fn.unit_comparison
2
+
3
+ 参数:start_date, end_date, metric=oee|availability|output, plant_id(可选)
4
+ 按工艺单元对标排名
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/DAZI_TEST/本体/ontos/设备运营/functions/equip_ops_fn_unit_comparison.py \
8
+ --space space_cate_test01 --register-function-id equip_ops.fn.unit_comparison
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {
14
+ "start_date": "2025-01-01",
15
+ "end_date": "2026-06-30",
16
+ "metric": "oee",
17
+ },
18
+ "object_type_code": "EquipmentAnalysis",
19
+ }
20
+
21
+
22
+ def _build_ops_where(start_date, end_date, plant_id=None):
23
+ clauses = []
24
+ if start_date and end_date:
25
+ clauses.append(f"calendar_date >= '{start_date}' AND calendar_date <= '{end_date}'")
26
+ if plant_id:
27
+ clauses.append(f"plant_id = '{plant_id}'")
28
+ return ("WHERE " + " AND ".join(clauses)) if clauses else ""
29
+
30
+
31
+ def _calc_metric(row, metric):
32
+ sum_runtime = float(row.get("sum_runtime_min") or 0)
33
+ sum_available = float(row.get("sum_available_min") or 0)
34
+ sum_actual = float(row.get("sum_actual_output") or 0)
35
+ sum_qualified = float(row.get("sum_qualified_output") or 0)
36
+ sum_theoretical = float(row.get("sum_theoretical_output") or 0)
37
+ availability = sum_runtime / sum_available if sum_available > 0 else 0.0
38
+ quality = sum_qualified / sum_actual if sum_actual > 0 else 0.0
39
+ performance = sum_actual / sum_theoretical if sum_theoretical > 0 else 0.0
40
+ oee = availability * performance * quality
41
+ if metric == "availability":
42
+ return round(availability, 4)
43
+ if metric == "output":
44
+ return round(sum_actual, 2)
45
+ return round(oee, 4)
46
+
47
+
48
+ def _ontology_fn_body(p):
49
+ params = dict(p.get_params() or {})
50
+ start_date = params.get("start_date", "")
51
+ end_date = params.get("end_date", "")
52
+ metric = params.get("metric", "oee")
53
+ plant_id = params.get("plant_id") or None
54
+ where_clause = _build_ops_where(start_date, end_date, plant_id)
55
+
56
+ sql = f"""
57
+ SELECT
58
+ unit_id,
59
+ any(unit_name) AS unit_name,
60
+ any(plant_name) AS plant_name,
61
+ sum(runtime_min) AS sum_runtime_min,
62
+ sum(calendar_minutes - planned_downtime_min) AS sum_available_min,
63
+ sum(actual_output_qty) AS sum_actual_output,
64
+ sum(qualified_output_qty) AS sum_qualified_output,
65
+ sumIf(runtime_min * ideal_cycle_rate, runtime_min > 0) AS sum_theoretical_output
66
+ FROM fact_equipment_daily_ops
67
+ {where_clause}
68
+ GROUP BY unit_id
69
+ """
70
+
71
+ result = p.sql.query(sql)
72
+ if not result:
73
+ return p.function_result(
74
+ columns=["rank", "unit_id", "unit_name", "plant_name", "metric", "metric_value"],
75
+ data=[],
76
+ row_count=0,
77
+ )
78
+
79
+ scored = []
80
+ for row in result:
81
+ scored.append({
82
+ "unit_id": str(row.get("unit_id") or ""),
83
+ "unit_name": str(row.get("unit_name") or ""),
84
+ "plant_name": str(row.get("plant_name") or ""),
85
+ "metric_value": _calc_metric(row, metric),
86
+ })
87
+
88
+ scored.sort(key=lambda x: x["metric_value"], reverse=True)
89
+
90
+ data = []
91
+ for rank, item in enumerate(scored, start=1):
92
+ data.append({
93
+ "rank": rank,
94
+ "unit_id": item["unit_id"],
95
+ "unit_name": item["unit_name"],
96
+ "plant_name": item["plant_name"],
97
+ "metric": metric,
98
+ "metric_value": item["metric_value"],
99
+ })
100
+
101
+ return p.function_result(
102
+ columns=["rank", "unit_id", "unit_name", "plant_name", "metric", "metric_value"],
103
+ data=data,
104
+ row_count=len(data),
105
+ )
106
+
107
+
108
+ def main():
109
+ s = space.get(ctx.space_id or "")
110
+ _Ports = type(
111
+ "_Ports",
112
+ (),
113
+ {
114
+ "get_params": lambda self: dict(ctx.params or {}),
115
+ "function_result": lambda self, **kw: onto.function_result(**kw),
116
+ },
117
+ )
118
+ p = _Ports()
119
+ p.sql = s.sql
120
+ return _ontology_fn_body(p)