A-SW Hub / Repositories
Local Gitea · READ ONLY
← 저장소 Repositories
REPOSITORY

via/greenhouse-fleet-control Private

[참조 MVP] 온실 이송로봇 서비스 관제 — UWB·SLAM 상태, 원격제어, UI와 OTA 작업을 연결하는 관제 참조 MVP입니다.

https://git.agrithing.ai/via/greenhouse-fleet-control.git
4 commits
COMMIT DETAIL

feat: 온실 이송로봇 서비스 관제 구체 MVP 구현

기관 승인 및 자동 품질검사를 통과한 구체 MVP 0.2.0 병합 Co-authored-by: via-dev <[email protected]>

via-dev <[email protected]> · 2026. 8. 3. 오후 3:13:13
e9dad1000596e461ac3d1b12fc72a0311cb0ea68
8 changed files +70 −8 parent 3f25c3f
modified README.md +7−1 View file
@@ -11,6 +11,12 @@
11 11
12 12 UWB·SLAM 상태, 원격제어, UI와 OTA 작업을 연결하는 관제 참조 MVP입니다.
13 13
14 + ## MVP 동작 범위
15 +
16 + - UWB·SLAM 신뢰도 융합
17 + - 측위 최신성·OTA 상태 게이트
18 + - 조건 충족 미션 배포 판정
19 +
14 20 ## 실행
15 21
16 22 ```bash
@@ -18,4 +24,4 @@ python run.py examples/input.json
18 24 python -m unittest discover -s tests -v
19 25 ```
20 26
21 실제 기관 구현은 `contracts/component.interface.json`의 저장소 식별자와 제품 기준선 연결 규칙을 유지하면서 교체할 수 있습니다.
27 + 정상 입력은 `examples/input.json`, 차단·안전 경로는 `examples/failure-input.json`으로 재현합니다. 실제 기관 구현은 `contracts/component.interface.json`의 저장소 식별자와 제품 기준선 연결 규칙을 유지하면서 교체할 수 있습니다.
modified asw_component/component.py +12−3 View file
@@ -2,6 +2,15 @@ from __future__ import annotations
2 2
3 3
4 4 def execute(payload: dict) -> dict:
5 if not isinstance(payload, dict) or not payload:
6 raise ValueError("a non-empty object is required")
7 return {"component": "via/greenhouse-fleet-control", "accepted": True, "status": "READY", "target": "service", "input_fields": sorted(payload.keys())}
5 + localization = payload.get("localization", {})
6 + fused = 0.45 * float(localization.get("uwb", 0)) + 0.55 * float(localization.get("slam", 0))
7 + reasons = []
8 + if fused < 0.75: reasons.append("LOCALIZATION_QUALITY")
9 + if int(localization.get("ageMs", 999999)) > 500: reasons.append("LOCALIZATION_STALE")
10 + if payload.get("otaState") != "CURRENT": reasons.append("OTA_NOT_CURRENT")
11 + accepted = not reasons
12 + dispatch = None if not accepted else {"robotId": str(payload.get("robotId")), "missionId": str(payload.get("missionId")), "mode": "AUTONOMOUS"}
13 + return {"component": "via/greenhouse-fleet-control", "accepted": accepted,
14 + "status": "DISPATCHED" if accepted else "BLOCKED", "target": "service",
15 + "missionId": str(payload.get("missionId", "unknown")), "robotId": str(payload.get("robotId", "unknown")),
16 + "fusedLocalization": round(fused, 3), "dispatch": dispatch, "blockingReasons": reasons}
modified contracts/component.interface.json +25−1 View file
@@ -5,7 +5,13 @@
5 5 "required": [
6 6 "component",
7 7 "accepted",
8 "status"
8 + "status",
9 + "target",
10 + "missionId",
11 + "robotId",
12 + "fusedLocalization",
13 + "dispatch",
14 + "blockingReasons"
9 15 ],
10 16 "properties": {
11 17 "component": {
@@ -19,6 +25,24 @@
19 25 },
20 26 "target": {
21 27 "const": "service"
28 + },
29 + "missionId": {
30 + "type": "string"
31 + },
32 + "robotId": {
33 + "type": "string"
34 + },
35 + "fusedLocalization": {
36 + "type": "number"
37 + },
38 + "dispatch": {
39 + "type": [
40 + "object",
41 + "null"
42 + ]
43 + },
44 + "blockingReasons": {
45 + "type": "array"
22 46 }
23 47 }
24 48 }
added examples/failure-input.json +10−0 View file
@@ -0,0 +1,10 @@
1 + {
2 + "missionId": "MOVE-2409",
3 + "robotId": "carrier-01",
4 + "localization": {
5 + "uwb": 0.42,
6 + "slam": 0.51,
7 + "ageMs": 120
8 + },
9 + "otaState": "CURRENT"
10 + }
modified examples/input.json +2−1 View file
@@ -3,7 +3,8 @@
3 3 "robotId": "carrier-01",
4 4 "localization": {
5 5 "uwb": 0.88,
6 "slam": 0.91
6 + "slam": 0.91,
7 + "ageMs": 120
7 8 },
8 9 "otaState": "CURRENT"
9 10 }
modified pyproject.toml +1−1 View file
@@ -1,5 +1,5 @@
1 1 [project]
2 2 name = "greenhouse-fleet-control"
3 version = "0.1.0"
3 + version = "0.2.0"
4 4 description = "UWB·SLAM 상태, 원격제어, UI와 OTA 작업을 연결하는 관제 참조 MVP입니다."
5 5 requires-python = ">=3.11"
modified release/manifest.json +1−1 View file
@@ -4,7 +4,7 @@
4 4 "displayName": "온실 이송로봇 서비스 관제",
5 5 "owner": "주식회사 비아",
6 6 "implementationAuthor": "주식회사 비아 (연동 검증용 참조 구현)",
7 "version": "0.1.0-mvp",
7 + "version": "0.2.0-mvp",
8 8 "domain": "Service Platform",
9 9 "status": "REFERENCE_MVP",
10 10 "compatibility": {
modified tests/test_component.py +12−0 View file
@@ -12,6 +12,18 @@ class ComponentTest(unittest.TestCase):
12 12 result = execute(payload)
13 13 self.assertTrue(result["accepted"])
14 14 self.assertEqual(result["component"], "via/greenhouse-fleet-control")
15 + self.assertEqual(result["target"], "service")
16 + self.assertEqual(result["status"], "DISPATCHED")
17 + self.assertEqual(result["fusedLocalization"], 0.897)
18 + self.assertEqual(result["dispatch"]["mode"], "AUTONOMOUS")
19 +
20 + def test_safety_or_failure_path(self):
21 + payload = json.loads((ROOT / "examples/failure-input.json").read_text(encoding="utf-8"))
22 + result = execute(payload)
23 + self.assertEqual(result["component"], "via/greenhouse-fleet-control")
24 + self.assertFalse(result["accepted"])
25 + self.assertEqual(result["status"], "BLOCKED")
26 + self.assertIn("LOCALIZATION_QUALITY", result["blockingReasons"])
15 27
16 28 if __name__ == "__main__":
17 29 unittest.main()