# 공정 런타임·접촉 계산·외부 연결 — 업데이트

기존 UI, 공장 위험학습/선택학습, Gangnam 코스는 보존했습니다. 공정이 학습된 정책으로 바뀌었다는 주장은 하지 않습니다. 이번 변경은 실행 안정화, 실측 연결 경로, 재료 접촉과 입출고입니다.

## 실행 수정

- 공정 실행 한 번으로 빈 작업셀의 작업지시 생성 → 릴리스 → 재생을 수행합니다. 계획 작업은 릴리스하고, 진행 중 작업은 계속합니다. 완료 배치는 보관 후 새 배치를 만듭니다.
- 서버 인증 실패 시 기기 모드로 준비하며, 서버 미연결을 표시합니다. 사이트 자체의 로그인/접근권한을 우회하지 않습니다. 기기 모드의 작업은 외부 MES에 자동 전송되지 않습니다.
- 실행 요청 하나만 진행합니다. 일시정지하면 다음 예약 실행을 취소합니다. 서버 모드는 0.5초 단위 요청, 기기 모드는 0.1초 단위 계산입니다. 재생 속도에 따라 한 요청의 계산 스텝 수가 달라집니다. 기기 저장은 계산과 분리하여 주기적으로 수행합니다.
- 일부 모바일/WebView·HTTP 환경에서 randomUUID가 없는 경우 getRandomValues 기반 UUID로 처리합니다.
- UR 형상은 저용량 공식 충돌 메시를 동시에 불러온 뒤 표시합니다. WebGL에서는 공식 시각 메시를 뒤이어 적용합니다. 화면 밖의 공장 렌더링은 중지합니다. CPU 3D는 픽셀별 z-buffer를 사용합니다.

## 작업지시와 버퍼

수량 1~1,000개를 입력할 수 있습니다. 4개 이하 기존 사례는 유지하며, 초과 수량은 한 부품씩 버퍼에 투입합니다. 각 버퍼는 2×2 슬롯을 순환합니다. 완제품 배송 → 출고 컨베이어 배출이 끝난 부품은 화면에서 제외되고 이력에 남습니다. 전체 수량을 한 번에 치구 위에 겹쳐 생성하지 않습니다.

서버 새 배치는 R2에 이전 전체 상태를 저장합니다. `GET /v1/factory/cell/history` 목록과 `/v1/factory/cell/history/{key}`에서 읽을 수 있습니다. 기기 배치는 별도 IndexedDB의 batch 키로 보관합니다. 저장 실패는 알림을 표시하고 JSON 내보내기를 유지합니다. 한 작업셀은 동시에 한 배치를 처리하며, 생산 능력·납기 보장 수치가 아닙니다.

## 접촉 물리

UI의 **부품 낙하·접촉 시험**에서 2kg 박스를 2.2m 높이에 놓으면 실제 적분으로 낙하·바닥 접촉·정지하는 과정을 확인할 수 있습니다.

`cell-contact-physics.mjs`는 중력 9.81m/s², 반암시적 적분, 지지면, 축 정렬 강체 박스 접촉, 수직 반발, 마찰 감쇠, 박스 적층 및 장애물 측면 충돌을 계산합니다. 부품 질량에 따른 지지력과 충돌 횟수가 상태에 기록됩니다. 그리퍼·컨베이어 위 부품은 명시적인 운동학 구속입니다. 자유 상태 부품은 지지면 밖으로 벗어나면 낙하합니다.

이는 실제 강체 접촉 계산의 제한된 구현입니다. 회전 관성·다물체 토크·UR 링크의 정밀 충돌·연성 접촉·마찰 기반 파지 성공 예측·센서 보정까지 검증한 고충실도 물리 엔진은 아닙니다. 실제 로봇 배포 전에는 해당 기체/툴/치구의 보정과 별도 시뮬레이터·현장 시험이 필요합니다.

## 자동차와 사람

도로·공장 공통 모델을 사용합니다. 자동차는 4.6m급 일반 세단: 곡면 차체, 지붕, 앞/뒤/측면 유리, 미러, 문/손잡이, LED 램프, 그릴, 타이어와 휠입니다. 특정 제조사의 인증 CAD가 아닙니다. 휠은 실제 주행 변위에 따라 회전합니다. 사람은 머리·얼굴·몸통·어깨·팔·손·고관절·무릎·신발을 가지며, 도로 보행자의 실제 이동 변위에 따라 팔다리가 움직입니다. 공장 작업자는 헬멧·작업복·반사띠를 표시합니다. 공장 환경 개체는 수동 배치이며 사람/자동차의 자율 이동 계획은 추가하지 않았습니다.

## 연결 패널과 인증

`GET /v1/integrations/status`, `POST /v1/integrations/{mes|robot}/config`, `/test` 제공. 기존 Sites 소유자 인증과 조직 토큰을 그대로 사용하고 설정/외부 호출은 block·daton 역할로 제한합니다. HTTPS 공개 게이트웨이 주소와 Bearer 토큰을 입력합니다. Cloudflare Worker가 사설 LAN의 로봇 IP에 직접 접속하지 않습니다. 현장 게이트웨이를 승인된 HTTPS reverse proxy/VPN 경로에 두어야 합니다. URL의 localhost/IP literal/사설용 도메인·리다이렉트는 허용하지 않습니다.

토큰은 서버의 소유자 키로 AES-GCM 암호화하여 저장하며 클라이언트 응답/감사에 넣지 않습니다. 이는 같은 저장소에 키가 있는 애플리케이션 암호화이며 별도 HSM/KMS를 의미하지 않습니다. 실제 운영 보안 설계 시 Workers Secret/KMS 및 현장 네트워크 정책과 결합해야 합니다. 연결 실패를 성공으로 표시하지 않습니다.

## MES 계약

제공 Python 파일 `/bridges/mes_contract_server.py`는 SQLite를 사용하는 **계약 시험 서버**입니다. 실제 고객 MES가 아닙니다. 실제 MES의 지원 API에 맞춰 동일 REST 계약을 매핑하는 현장 어댑터가 필요합니다. 기본 경로는 아래와 같고 서버 API config의 paths로 변경할 수 있습니다.

|메서드|기본 경로|계약|
|---|---|---|
|GET|/health|`{ok:true,service:"asla-mes-adapter",sandbox:true/false}`|
|GET|/api/work-orders|`{items:[{external_id,scenario,quantity}]}`|
|POST|/api/events|`{delivery_id,stream_id,simulation:true,events:[...]}` → `{accepted_seq}`|

사이트의 `POST /v1/integrations/mes/pull`은 `expected_version`을 받아 작업지시를 가져옵니다. 기존 배치가 있으면 다음 외부 작업은 deferred로 보고합니다. `POST /v1/integrations/mes/push`는 미확인 이벤트를 최대 200개 보내고 **실제 accepted_seq 응답 후에만 ACK**합니다. 실패 응답·응답 형식 오류 시 ACK하지 않습니다. stream_id는 배치별 고유 ID이므로 다음 배치 seq가 다시 1부터 시작해도 충돌하지 않습니다. 시험 서버는 stream_id+seq를 SQLite 고유키로 중복 제거합니다.

## UR 실기체 계약

현장 파일 `/bridges/ur_rtde_bridge.py`: SDU Robotics `ur-rtde`의 RTDEReceiveInterface/RTDEControlInterface를 사용합니다. 설치와 실행 예:

```
python -m pip install ur-rtde
# UR_HOST와 ASLA_BRIDGE_TOKEN은 현장 환경에서 설정합니다.
python ur_rtde_bridge.py
```

공식 API: https://sdurobotics.gitlab.io/ur_rtde/pages/getting_started/quick_start.html
UR RTDE: https://docs.universal-robots.com/tutorials/communication-protocol-tutorials/rtde-guide.html

처음에는 수신만 허용합니다. 현장 `ASLA_ENABLE_MOTION=1`이 있어야 동작 요청을 받습니다. 실제 하드웨어에서 이 파일을 실행하기 전에 기체/툴/작업공간의 시운전이 필요합니다. 제어 인터페이스가 로봇 프로그램을 업로드할 수 있으므로 기존 현장 제어와 충돌하지 않도록 구성해야 합니다. 제공 파일은 e-Series 환경을 기준으로 하며 PolyScope X 등의 별도 플래그/URCap 설정은 현장 매뉴얼을 따릅니다.

|게이트웨이 경로|응답/동작|
|---|---|
|GET /health|실제 수신 연결 상태, service=asla-ur-bridge, motion_enabled|
|GET /state|실측 q[6], tcp[6], timestamp(초), safety_status, motion_enabled|
|POST /command|command_id, kind=movej/stop, q, speed≤0.15rad/s, acceleration≤0.2rad/s²|

사이트 `/v1/integrations/robot/state`는 3초 이내 텔레메트리만 받아들입니다. 조그는 실측 관절값에서 ±0.05rad 목표를 만들고, 사용자 동작 체크·로봇 슬롯·최신 상태를 검사합니다. 현장 게이트웨이에서도 보호/비상정지, 하중/관절 범위와 별도로 현재 자세와 목표의 0.35rad 시운전 범위를 검사합니다. 명령의 accepted는 완료가 아닙니다. 실제 목표 도달은 다음 텔레메트리로 확인해야 합니다. 페이지의 정지 명령은 네트워크를 거치는 일반 정지이며 안전 인증 비상정지 회로를 대체하지 않습니다.

가상 공정을 실제 로봇에서 자동 실행시키지는 않습니다. 3D 장면의 좌표와 현장 로봇/작업물/툴 좌표를 보정한 적이 없기 때문입니다. 현재 작업에서 제공된 실제 장비 주소·토큰·실측 데이터는 없으므로 실제 기체 및 고객 MES의 접속·성능 검수는 아직 하지 않았습니다. 이를 기존 모의 공정 완료와 혼동하지 않도록 UI에서 구분합니다.

## 검증

현재 자동 검증: Node 69개 + Python 2개, 총 71개 통과. 기존 공장 격자와 도로 모듈도 서버 인증 실패 시 명시적인 기기 모의 실행으로 준비됩니다. 기기 실행에는 서버 서명/감사 증명을 붙이지 않습니다.

`node --test tests/*.test.mjs`, `python tests/bridges_test.py`.

기존 도로/ASLA 시험 외에: 4사례 × 8개 완제품, 접촉·낙하·적층·마찰·측면 충돌, 단일 실행 요청/일시정지/재시작, 구형 체크포인트 마이그레이션, UUID 호환, 차량·사람 메시와 관절, CPU 깊이 검사, 역할·토큰 암호화·MES 수신확인 실패 시 ACK 금지·실기체 최신 상태 게이트를 검증합니다. Python 시험은 로컬 HTTP MES 시험 서버→SQLite 저장과 모의 RTDE 인터페이스를 사용합니다. 실제 현장 성능 검수 결과가 아닙니다.
