Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ path = planner.route(start, goal)
멈춥니다. 물리 계산과 센서 수신은 계속합니다. 선택적인 해안 경로 준비에 실패하거나
결과가 비어 있어도 검증된 해안 경로를 외해 목표로 바꾸지 않습니다.

기본 지형 자료 서버에 연결할 수 없으면 더 작은 구역으로 경로를 계산합니다.
검증된 자료를 사용하며 수심과 육지 확인 기준은 같습니다.

- 일반 구역: 20 km × 20 km
- 자료 서버 장애 시 재시도 구역: 6 km × 6 km
- 두 구역 모두 자료 부족: 추진을 멈추고 재시도

## 시험과 이용 조건

```sh
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ checked replacement is ready. Physics and sensor sampling continue during the
wait. Failed or empty optional renewal cannot replace a checked coastal route
with an offshore goal.

When the baseline terrain provider is unavailable, regional planning tries a
smaller region. It uses verified source data and the same depth and land checks.

- Normal region: 20 km × 20 km
- Source outage retry: 6 km × 6 km
- Missing data in both regions: hold thrust and retry

## Tests and terms

```sh
Expand Down
21 changes: 16 additions & 5 deletions src/posim_routing/regional.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,21 @@ async def prepare_port(self,state,target,key):
value.update(route_key=key,job=state['native']['job'],source='resident collision terrain',
policy=REGIONAL_POLICY,probe_position=current,mapped_land_checked=guard is not None)
return value
async def terrain_region(self, centre, extent):
"""Try a smaller verified region when the baseline provider is offline."""
for width in (extent, 6000) if extent > 6000 else (extent,):
job=await self.rpc('/terrain/jobs',dict(latitude=centre[1],longitude=centre[0],
width_m=width,height_m=width,samples=257,source_policy='auto'),20)
end=time.monotonic()+620
while job['status'] in ('queued','running') and time.monotonic()<end:
await asyncio.sleep(2);job=await self.rpc('/terrain/jobs/'+job['id'])
if job['status']=='ready':return job
if width==6000 or job['status']!='failed':break
try:
sources=await self.rpc('/terrain/jobs/'+job['id']+'/files/source-providers.json')
except Exception:break
if (sources.get('baseline_status') or {}).get('status')!='unavailable':break
raise RuntimeError('regional_terrain_unavailable')
async def prepare(self,state,target,key,nearest=None,needed=True):
from posim_terrain.canal_override import in_region
canal=in_region(state['position'])
Expand All @@ -276,11 +291,7 @@ async def prepare(self,state,target,key,nearest=None,needed=True):
centre=list(GEO.fwd(*pos,heading,min(6000.,nearest[0]-6000.))[:2])
extent=4000 if canal else 20000
if self.before_prepare is not None:await self.before_prepare()
job=await self.rpc('/terrain/jobs',dict(latitude=centre[1],longitude=centre[0],width_m=extent,height_m=extent,samples=257,source_policy='auto'),20)
end=time.monotonic()+620
while job['status'] in ('queued','running') and time.monotonic()<end:
await asyncio.sleep(2);job=await self.rpc('/terrain/jobs/'+job['id'])
if job['status']!='ready':raise RuntimeError('regional_terrain_unavailable')
job=await self.terrain_region(centre,extent)
grid=await self.rpc('/terrain/jobs/'+job['id']+'/files/terrain.json',timeout=20)
guard=None
if not canal:
Expand Down
39 changes: 39 additions & 0 deletions tests/test_regional_outage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""A source outage can reduce coverage, never invent missing depths."""
import unittest
from posim_routing.regional import RegionalNavigator


class RegionalOutage(unittest.IsolatedAsyncioTestCase):
async def test_offline_baseline_retries_a_smaller_verified_region(self):
widths=[]
async def rpc(path,body=None,timeout=10):
if body:
widths.append(body['width_m'])
return dict(id=str(body['width_m']),status='failed' if len(widths)==1 else 'ready')
return dict(baseline_status=dict(status='unavailable'))
job=await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
self.assertEqual(widths,[20000,6000])
self.assertEqual(job['status'],'ready')

async def test_other_generation_failures_do_not_trigger_a_retry(self):
widths=[]
async def rpc(path,body=None,timeout=10):
if body:
widths.append(body['width_m']);return dict(id='failed',status='failed')
return dict(baseline_status=dict(status='available'))
with self.assertRaisesRegex(RuntimeError,'regional_terrain_unavailable'):
await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
self.assertEqual(widths,[20000])

async def test_smaller_region_with_missing_data_still_holds(self):
widths=[]
async def rpc(path,body=None,timeout=10):
if body:
widths.append(body['width_m']);return dict(id='failed',status='failed')
return dict(baseline_status=dict(status='unavailable'))
with self.assertRaisesRegex(RuntimeError,'regional_terrain_unavailable'):
await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
self.assertEqual(widths,[20000,6000])


if __name__=='__main__':unittest.main()