Skip to content

Commit 33480d6

Browse files
authored
Merge pull request #4 from IOES-Lab/fix/regional-source-outage
Recover coastal planning from terrain provider outages
2 parents 91d57f0 + baa9b8f commit 33480d6

4 files changed

Lines changed: 69 additions & 5 deletions

File tree

‎README.ko.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ path = planner.route(start, goal)
119119
멈춥니다. 물리 계산과 센서 수신은 계속합니다. 선택적인 해안 경로 준비에 실패하거나
120120
결과가 비어 있어도 검증된 해안 경로를 외해 목표로 바꾸지 않습니다.
121121

122+
기본 지형 자료 서버에 연결할 수 없으면 더 작은 구역으로 경로를 계산합니다.
123+
검증된 자료를 사용하며 수심과 육지 확인 기준은 같습니다.
124+
125+
- 일반 구역: 20 km × 20 km
126+
- 자료 서버 장애 시 재시도 구역: 6 km × 6 km
127+
- 두 구역 모두 자료 부족: 추진을 멈추고 재시도
128+
122129
## 시험과 이용 조건
123130

124131
```sh

‎README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ checked replacement is ready. Physics and sensor sampling continue during the
117117
wait. Failed or empty optional renewal cannot replace a checked coastal route
118118
with an offshore goal.
119119

120+
When the baseline terrain provider is unavailable, regional planning tries a
121+
smaller region. It uses verified source data and the same depth and land checks.
122+
123+
- Normal region: 20 km × 20 km
124+
- Source outage retry: 6 km × 6 km
125+
- Missing data in both regions: hold thrust and retry
126+
120127
## Tests and terms
121128

122129
```sh

‎src/posim_routing/regional.py‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,21 @@ async def prepare_port(self,state,target,key):
254254
value.update(route_key=key,job=state['native']['job'],source='resident collision terrain',
255255
policy=REGIONAL_POLICY,probe_position=current,mapped_land_checked=guard is not None)
256256
return value
257+
async def terrain_region(self, centre, extent):
258+
"""Try a smaller verified region when the baseline provider is offline."""
259+
for width in (extent, 6000) if extent > 6000 else (extent,):
260+
job=await self.rpc('/terrain/jobs',dict(latitude=centre[1],longitude=centre[0],
261+
width_m=width,height_m=width,samples=257,source_policy='auto'),20)
262+
end=time.monotonic()+620
263+
while job['status'] in ('queued','running') and time.monotonic()<end:
264+
await asyncio.sleep(2);job=await self.rpc('/terrain/jobs/'+job['id'])
265+
if job['status']=='ready':return job
266+
if width==6000 or job['status']!='failed':break
267+
try:
268+
sources=await self.rpc('/terrain/jobs/'+job['id']+'/files/source-providers.json')
269+
except Exception:break
270+
if (sources.get('baseline_status') or {}).get('status')!='unavailable':break
271+
raise RuntimeError('regional_terrain_unavailable')
257272
async def prepare(self,state,target,key,nearest=None,needed=True):
258273
from posim_terrain.canal_override import in_region
259274
canal=in_region(state['position'])
@@ -276,11 +291,7 @@ async def prepare(self,state,target,key,nearest=None,needed=True):
276291
centre=list(GEO.fwd(*pos,heading,min(6000.,nearest[0]-6000.))[:2])
277292
extent=4000 if canal else 20000
278293
if self.before_prepare is not None:await self.before_prepare()
279-
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)
280-
end=time.monotonic()+620
281-
while job['status'] in ('queued','running') and time.monotonic()<end:
282-
await asyncio.sleep(2);job=await self.rpc('/terrain/jobs/'+job['id'])
283-
if job['status']!='ready':raise RuntimeError('regional_terrain_unavailable')
294+
job=await self.terrain_region(centre,extent)
284295
grid=await self.rpc('/terrain/jobs/'+job['id']+'/files/terrain.json',timeout=20)
285296
guard=None
286297
if not canal:

‎tests/test_regional_outage.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""A source outage can reduce coverage, never invent missing depths."""
2+
import unittest
3+
from posim_routing.regional import RegionalNavigator
4+
5+
6+
class RegionalOutage(unittest.IsolatedAsyncioTestCase):
7+
async def test_offline_baseline_retries_a_smaller_verified_region(self):
8+
widths=[]
9+
async def rpc(path,body=None,timeout=10):
10+
if body:
11+
widths.append(body['width_m'])
12+
return dict(id=str(body['width_m']),status='failed' if len(widths)==1 else 'ready')
13+
return dict(baseline_status=dict(status='unavailable'))
14+
job=await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
15+
self.assertEqual(widths,[20000,6000])
16+
self.assertEqual(job['status'],'ready')
17+
18+
async def test_other_generation_failures_do_not_trigger_a_retry(self):
19+
widths=[]
20+
async def rpc(path,body=None,timeout=10):
21+
if body:
22+
widths.append(body['width_m']);return dict(id='failed',status='failed')
23+
return dict(baseline_status=dict(status='available'))
24+
with self.assertRaisesRegex(RuntimeError,'regional_terrain_unavailable'):
25+
await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
26+
self.assertEqual(widths,[20000])
27+
28+
async def test_smaller_region_with_missing_data_still_holds(self):
29+
widths=[]
30+
async def rpc(path,body=None,timeout=10):
31+
if body:
32+
widths.append(body['width_m']);return dict(id='failed',status='failed')
33+
return dict(baseline_status=dict(status='unavailable'))
34+
with self.assertRaisesRegex(RuntimeError,'regional_terrain_unavailable'):
35+
await RegionalNavigator(rpc).terrain_region([118.,24.],20000)
36+
self.assertEqual(widths,[20000,6000])
37+
38+
39+
if __name__=='__main__':unittest.main()

0 commit comments

Comments
 (0)