feat: materialize distributed inputs in task pool
This commit is contained in:
+7
-4
@@ -59,15 +59,18 @@ ALLOWED_EXPORT_DIRS=
|
|||||||
# 源数据目录
|
# 源数据目录
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
UNPACK_SOURCE_DIRS=D:\Archives
|
UNPACK_SOURCE_DIRS=D:\Archives
|
||||||
SOURCE_PRODUCT_DIRS=D:\LuTan1_Image_Pool;D:\Sentinel1_Image_Pool_ZIP
|
TASK_POOL_ROOT=D:\Task_Pool
|
||||||
|
DINSAR_TASK_POOL_ROOT=D:\Task_Pool\DInSAR
|
||||||
|
SBAS_TASK_POOL_ROOT=D:\Task_Pool\SBAS
|
||||||
|
SOURCE_PRODUCT_DIRS=D:\LuTan1_Image_Pool;D:\Sentinel1_Image_Pool_ZIP;\\DESKTOP-N16HJ84\InSAR_Storage_2\LuTan-1\Archive;\\DESKTOP-N16HJ84\InSAR_Storage_2\Sentinel-1\Archive
|
||||||
SENTINEL1_STORAGE_DIRS=D:\Sentinel1_Image_Pool
|
SENTINEL1_STORAGE_DIRS=D:\Sentinel1_Image_Pool
|
||||||
INSAR_STORAGE_DIRS=D:\LuTan1_Image_Pool
|
INSAR_STORAGE_DIRS=D:\LuTan1_Image_Pool
|
||||||
MONITOR_RADAR_DIRS=D:\LuTan1_Image_Pool
|
MONITOR_RADAR_DIRS=D:\LuTan1_Image_Pool
|
||||||
MONITOR_DINSAR_DIRS=D:\DInSARResult
|
MONITOR_DINSAR_DIRS=D:\DInSARResult
|
||||||
ORBIT_SOURCE_DIRS=D:\LT1_data_lsarorbit;D:\Sentinel1_EOF_Pool
|
ORBIT_SOURCE_DIRS=D:\LT1_data_lsarorbit;D:\Sentinel1_EOF_Pool;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\LuTan-1;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\Sentinel-1
|
||||||
MONITOR_ORBIT_DIR=D:\LT1_data_lsarorbit
|
MONITOR_ORBIT_DIR=D:\LT1_data_lsarorbit
|
||||||
|
|
||||||
GF3_ARCHIVE_SOURCE_DIRS=D:\production_inputs\gf3\archives
|
GF3_ARCHIVE_SOURCE_DIRS=\\DESKTOP-N16HJ84\InSAR_Storage_1\GaoFen-3
|
||||||
GF3_ARCHIVE_EXTS=.zip,.tar,.tar.gz,.tgz
|
GF3_ARCHIVE_EXTS=.zip,.tar,.tar.gz,.tgz
|
||||||
GF3_UNPACK_DELETE_ARCHIVE=true
|
GF3_UNPACK_DELETE_ARCHIVE=true
|
||||||
GF3_LEGACY_GDAL_ENABLED=false
|
GF3_LEGACY_GDAL_ENABLED=false
|
||||||
@@ -275,7 +278,7 @@ GAMMA_SBAS_RUNTIME_ID=gamma_sbas_runtime_v1
|
|||||||
GAMMA_SBAS_WSL_DISTRO=Ubuntu-24.04
|
GAMMA_SBAS_WSL_DISTRO=Ubuntu-24.04
|
||||||
GAMMA_SBAS_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python
|
GAMMA_SBAS_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python
|
||||||
GAMMA_SBAS_ENV_SCRIPT=D:\Code\Insar_management_system_v2\deploy\wsl\profiles\gamma_env.sh
|
GAMMA_SBAS_ENV_SCRIPT=D:\Code\Insar_management_system_v2\deploy\wsl\profiles\gamma_env.sh
|
||||||
GAMMA_SBAS_WORK_ROOT=D:\production_runtime\sbas_insar_work
|
GAMMA_SBAS_WORK_ROOT=D:\Task_Pool\SBAS
|
||||||
GAMMA_SBAS_PRODUCT_ROOT=D:\production_results\timeseries\sbas
|
GAMMA_SBAS_PRODUCT_ROOT=D:\production_results\timeseries\sbas
|
||||||
GAMMA_SBAS_TRIAL_ROOT=D:\production_runtime\gamma_ipta_trials
|
GAMMA_SBAS_TRIAL_ROOT=D:\production_runtime\gamma_ipta_trials
|
||||||
GAMMA_SBAS_SCRIPT_TEMPLATE_ROOT=D:\Code\Insar_management_system_v2\backend\templates\gamma_sbas
|
GAMMA_SBAS_SCRIPT_TEMPLATE_ROOT=D:\Code\Insar_management_system_v2\backend\templates\gamma_sbas
|
||||||
|
|||||||
+21
-1
@@ -93,6 +93,14 @@ def _default_runtime_root(project_root: str) -> str:
|
|||||||
return os.path.join(normalized_root, "runtime")
|
return os.path.join(normalized_root, "runtime")
|
||||||
|
|
||||||
|
|
||||||
|
def _default_task_pool_root(project_root: str) -> str:
|
||||||
|
normalized_root = os.path.normpath(project_root)
|
||||||
|
drive, _tail = os.path.splitdrive(normalized_root)
|
||||||
|
if drive:
|
||||||
|
return os.path.join(drive + os.sep, "Task_Pool")
|
||||||
|
return os.path.join(normalized_root, "Task_Pool")
|
||||||
|
|
||||||
|
|
||||||
def _default_runtime_dir(project_root: str, *parts: str) -> str:
|
def _default_runtime_dir(project_root: str, *parts: str) -> str:
|
||||||
return os.path.join(_default_runtime_root(project_root), *parts)
|
return os.path.join(_default_runtime_root(project_root), *parts)
|
||||||
|
|
||||||
@@ -187,6 +195,9 @@ class Settings(BaseSettings):
|
|||||||
DB_SCHEMA_RESET_CONFIRM: bool = False
|
DB_SCHEMA_RESET_CONFIRM: bool = False
|
||||||
|
|
||||||
UNPACK_SOURCE_DIRS: str = ""
|
UNPACK_SOURCE_DIRS: str = ""
|
||||||
|
TASK_POOL_ROOT: str = ""
|
||||||
|
DINSAR_TASK_POOL_ROOT: str = ""
|
||||||
|
SBAS_TASK_POOL_ROOT: str = ""
|
||||||
SOURCE_PRODUCT_DIRS: str = ""
|
SOURCE_PRODUCT_DIRS: str = ""
|
||||||
SENTINEL1_STORAGE_DIRS: str = ""
|
SENTINEL1_STORAGE_DIRS: str = ""
|
||||||
ORBIT_SOURCE_DIRS: str = ""
|
ORBIT_SOURCE_DIRS: str = ""
|
||||||
@@ -441,6 +452,12 @@ class Settings(BaseSettings):
|
|||||||
"WATER_RESULTS_DIR",
|
"WATER_RESULTS_DIR",
|
||||||
os.path.join(backend_dir, "water_results"),
|
os.path.join(backend_dir, "water_results"),
|
||||||
)
|
)
|
||||||
|
if not self.TASK_POOL_ROOT:
|
||||||
|
object.__setattr__(self, "TASK_POOL_ROOT", _default_task_pool_root(project_root))
|
||||||
|
if not self.DINSAR_TASK_POOL_ROOT:
|
||||||
|
object.__setattr__(self, "DINSAR_TASK_POOL_ROOT", os.path.join(self.TASK_POOL_ROOT, "DInSAR"))
|
||||||
|
if not self.SBAS_TASK_POOL_ROOT:
|
||||||
|
object.__setattr__(self, "SBAS_TASK_POOL_ROOT", os.path.join(self.TASK_POOL_ROOT, "SBAS"))
|
||||||
if not self.SAR_ANALYSIS_READY_ROOT:
|
if not self.SAR_ANALYSIS_READY_ROOT:
|
||||||
object.__setattr__(
|
object.__setattr__(
|
||||||
self,
|
self,
|
||||||
@@ -780,7 +797,7 @@ class Settings(BaseSettings):
|
|||||||
object.__setattr__(
|
object.__setattr__(
|
||||||
self,
|
self,
|
||||||
"GAMMA_SBAS_WORK_ROOT",
|
"GAMMA_SBAS_WORK_ROOT",
|
||||||
os.path.join(_default_runtime_root(project_root), "sbas_insar_work"),
|
self.SBAS_TASK_POOL_ROOT,
|
||||||
)
|
)
|
||||||
if not self.GAMMA_SBAS_PRODUCT_ROOT:
|
if not self.GAMMA_SBAS_PRODUCT_ROOT:
|
||||||
object.__setattr__(
|
object.__setattr__(
|
||||||
@@ -990,6 +1007,9 @@ class Settings(BaseSettings):
|
|||||||
os.makedirs(settings.RESULT_QUARANTINE_ROOT, exist_ok=True)
|
os.makedirs(settings.RESULT_QUARANTINE_ROOT, exist_ok=True)
|
||||||
os.makedirs(settings.SAR_ANALYSIS_READY_ROOT, exist_ok=True)
|
os.makedirs(settings.SAR_ANALYSIS_READY_ROOT, exist_ok=True)
|
||||||
os.makedirs(settings.SAR_ANALYSIS_WORK_ROOT, exist_ok=True)
|
os.makedirs(settings.SAR_ANALYSIS_WORK_ROOT, exist_ok=True)
|
||||||
|
os.makedirs(settings.TASK_POOL_ROOT, exist_ok=True)
|
||||||
|
os.makedirs(settings.DINSAR_TASK_POOL_ROOT, exist_ok=True)
|
||||||
|
os.makedirs(settings.SBAS_TASK_POOL_ROOT, exist_ok=True)
|
||||||
for path in split_env_paths(settings.GF3_ARCHIVE_SOURCE_DIRS):
|
for path in split_env_paths(settings.GF3_ARCHIVE_SOURCE_DIRS):
|
||||||
os.makedirs(path, exist_ok=True)
|
os.makedirs(path, exist_ok=True)
|
||||||
for path in split_env_paths(settings.GF3_SARSCAPE_NATIVE_DIRS):
|
for path in split_env_paths(settings.GF3_SARSCAPE_NATIVE_DIRS):
|
||||||
|
|||||||
+118
-2
@@ -2,6 +2,7 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import asyncio
|
import asyncio
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import tarfile
|
||||||
import zipfile
|
import zipfile
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -43,6 +44,100 @@ def find_dinsar_source_to_copy(path: str) -> str:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
_DINSAR_ARCHIVE_SUFFIXES = (".tar.gz", ".tgz", ".zip", ".tar")
|
||||||
|
|
||||||
|
|
||||||
|
def _is_supported_archive(path: str) -> bool:
|
||||||
|
lower = str(path or "").lower()
|
||||||
|
return any(lower.endswith(suffix) for suffix in _DINSAR_ARCHIVE_SUFFIXES)
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_archive_member_name(member_name: str, archive_path: str) -> str:
|
||||||
|
name = str(member_name or "").replace("\\", "/").strip("/")
|
||||||
|
if not name or name.startswith("../") or "/../" in f"/{name}/":
|
||||||
|
raise ValueError(f"Unsafe archive member path in {archive_path}: {member_name}")
|
||||||
|
if os.path.isabs(name) or os.path.splitdrive(name)[0]:
|
||||||
|
raise ValueError(f"Unsafe archive member path in {archive_path}: {member_name}")
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_archive_to_dir(archive_path: str, dest_dir: str) -> int:
|
||||||
|
if zipfile.is_zipfile(archive_path):
|
||||||
|
extracted = 0
|
||||||
|
with zipfile.ZipFile(archive_path) as zip_obj:
|
||||||
|
for info in zip_obj.infolist():
|
||||||
|
rel_name = _safe_archive_member_name(info.filename, archive_path)
|
||||||
|
dest_path = os.path.abspath(os.path.join(dest_dir, rel_name))
|
||||||
|
if not dest_path.startswith(os.path.abspath(dest_dir) + os.sep):
|
||||||
|
raise ValueError(f"Unsafe ZIP member path: {info.filename}")
|
||||||
|
if info.is_dir():
|
||||||
|
os.makedirs(dest_path, exist_ok=True)
|
||||||
|
continue
|
||||||
|
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||||
|
with zip_obj.open(info, "r") as source, open(dest_path, "wb") as target:
|
||||||
|
shutil.copyfileobj(source, target, length=1024 * 1024)
|
||||||
|
extracted += 1
|
||||||
|
return extracted
|
||||||
|
|
||||||
|
if tarfile.is_tarfile(archive_path):
|
||||||
|
extracted = 0
|
||||||
|
with tarfile.open(archive_path, "r:*") as tar_obj:
|
||||||
|
for member in tar_obj:
|
||||||
|
rel_name = _safe_archive_member_name(member.name, archive_path)
|
||||||
|
dest_path = os.path.abspath(os.path.join(dest_dir, rel_name))
|
||||||
|
if not dest_path.startswith(os.path.abspath(dest_dir) + os.sep):
|
||||||
|
raise ValueError(f"Unsafe TAR member path: {member.name}")
|
||||||
|
if member.isdir():
|
||||||
|
os.makedirs(dest_path, exist_ok=True)
|
||||||
|
continue
|
||||||
|
if not member.isfile():
|
||||||
|
continue
|
||||||
|
source = tar_obj.extractfile(member)
|
||||||
|
if source is None:
|
||||||
|
continue
|
||||||
|
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||||
|
with source, open(dest_path, "wb") as target:
|
||||||
|
shutil.copyfileobj(source, target, length=1024 * 1024)
|
||||||
|
extracted += 1
|
||||||
|
return extracted
|
||||||
|
|
||||||
|
raise ValueError(f"Unsupported archive format: {archive_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def _materialize_dinsar_source(source_path: str, dest_dir: str) -> Dict[str, Any]:
|
||||||
|
normalized = os.path.normpath(os.path.abspath(str(source_path or "")))
|
||||||
|
if not os.path.exists(normalized):
|
||||||
|
raise FileNotFoundError(normalized)
|
||||||
|
|
||||||
|
if os.path.isdir(normalized):
|
||||||
|
shutil.copytree(normalized, dest_dir, dirs_exist_ok=True)
|
||||||
|
return {"mode": "copy_directory", "source_path": normalized}
|
||||||
|
|
||||||
|
if os.path.isfile(normalized) and _is_supported_archive(normalized):
|
||||||
|
os.makedirs(dest_dir, exist_ok=True)
|
||||||
|
extracted = _extract_archive_to_dir(normalized, dest_dir)
|
||||||
|
if extracted <= 0:
|
||||||
|
raise OSError(f"Archive extraction produced no files: {normalized}")
|
||||||
|
return {
|
||||||
|
"mode": "extract_archive",
|
||||||
|
"source_path": normalized,
|
||||||
|
"archive_path": normalized,
|
||||||
|
"extracted_files": extracted,
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.path.isfile(normalized):
|
||||||
|
os.makedirs(dest_dir, exist_ok=True)
|
||||||
|
target = os.path.join(dest_dir, os.path.basename(normalized))
|
||||||
|
shutil.copy2(normalized, target)
|
||||||
|
return {
|
||||||
|
"mode": "copy_file",
|
||||||
|
"source_path": normalized,
|
||||||
|
"relative_path": os.path.basename(target),
|
||||||
|
}
|
||||||
|
|
||||||
|
raise FileNotFoundError(normalized)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_orbit_dest_path(
|
def _resolve_orbit_dest_path(
|
||||||
orbit_dir: str,
|
orbit_dir: str,
|
||||||
role: str,
|
role: str,
|
||||||
@@ -191,6 +286,7 @@ def _build_dinsar_pair_metadata(
|
|||||||
package_format: str,
|
package_format: str,
|
||||||
include_orbit_files: bool,
|
include_orbit_files: bool,
|
||||||
orbit_entries: List[Dict[str, Any]],
|
orbit_entries: List[Dict[str, Any]],
|
||||||
|
source_materialization: Optional[Dict[str, Any]] = None,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"pair_key": item.get("pair_key"),
|
"pair_key": item.get("pair_key"),
|
||||||
@@ -214,6 +310,7 @@ def _build_dinsar_pair_metadata(
|
|||||||
"master_orbit_file_path": item.get("master_orbit_file_path"),
|
"master_orbit_file_path": item.get("master_orbit_file_path"),
|
||||||
"slave_orbit_file_path": item.get("slave_orbit_file_path"),
|
"slave_orbit_file_path": item.get("slave_orbit_file_path"),
|
||||||
"orbit_files": orbit_entries,
|
"orbit_files": orbit_entries,
|
||||||
|
"source_materialization": source_materialization or {},
|
||||||
"scene_pair_uid": item.get("scene_pair_uid") or item.get("pair_uid"),
|
"scene_pair_uid": item.get("scene_pair_uid") or item.get("pair_uid"),
|
||||||
"pair_uid": item.get("pair_uid") or item.get("scene_pair_uid"),
|
"pair_uid": item.get("pair_uid") or item.get("scene_pair_uid"),
|
||||||
"network_run_id": item.get("network_run_id"),
|
"network_run_id": item.get("network_run_id"),
|
||||||
@@ -1094,8 +1191,26 @@ async def run_dinsar_copy_items(
|
|||||||
failed_count += 1
|
failed_count += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
await asyncio.to_thread(shutil.copytree, master_src_path, master_dir, dirs_exist_ok=True)
|
master_materialization = await asyncio.to_thread(
|
||||||
await asyncio.to_thread(shutil.copytree, slave_src_path, slave_dir, dirs_exist_ok=True)
|
_materialize_dinsar_source,
|
||||||
|
master_src_path,
|
||||||
|
master_dir,
|
||||||
|
)
|
||||||
|
slave_materialization = await asyncio.to_thread(
|
||||||
|
_materialize_dinsar_source,
|
||||||
|
slave_src_path,
|
||||||
|
slave_dir,
|
||||||
|
)
|
||||||
|
source_materialization = {
|
||||||
|
"master": {
|
||||||
|
**master_materialization,
|
||||||
|
"target_relative_path": "master",
|
||||||
|
},
|
||||||
|
"slave": {
|
||||||
|
**slave_materialization,
|
||||||
|
"target_relative_path": "slave",
|
||||||
|
},
|
||||||
|
}
|
||||||
orbit_entries = await _copy_dinsar_orbit_files(
|
orbit_entries = await _copy_dinsar_orbit_files(
|
||||||
task_id,
|
task_id,
|
||||||
item,
|
item,
|
||||||
@@ -1112,6 +1227,7 @@ async def run_dinsar_copy_items(
|
|||||||
"zip" if export_zip else "folder",
|
"zip" if export_zip else "folder",
|
||||||
include_orbit_files,
|
include_orbit_files,
|
||||||
orbit_entries,
|
orbit_entries,
|
||||||
|
source_materialization,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if export_zip and zip_path:
|
if export_zip and zip_path:
|
||||||
|
|||||||
@@ -458,6 +458,35 @@ def _build_root_specs_from_settings() -> List[RootSpec]:
|
|||||||
owner_engine="pyint",
|
owner_engine="pyint",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
specs.extend(
|
||||||
|
_iter_single_root_specs(
|
||||||
|
env_var="TASK_POOL_ROOT",
|
||||||
|
path=settings.TASK_POOL_ROOT,
|
||||||
|
root_role="task_pool_root",
|
||||||
|
display_name="Task Pool Root",
|
||||||
|
scan_mode="workspace",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
specs.extend(
|
||||||
|
_iter_single_root_specs(
|
||||||
|
env_var="DINSAR_TASK_POOL_ROOT",
|
||||||
|
path=settings.DINSAR_TASK_POOL_ROOT,
|
||||||
|
root_role="task_pool_dinsar",
|
||||||
|
display_name="D-InSAR Task Pool",
|
||||||
|
scan_mode="workspace",
|
||||||
|
owner_engine="dinsar",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
specs.extend(
|
||||||
|
_iter_single_root_specs(
|
||||||
|
env_var="SBAS_TASK_POOL_ROOT",
|
||||||
|
path=settings.SBAS_TASK_POOL_ROOT,
|
||||||
|
root_role="task_pool_sbas",
|
||||||
|
display_name="SBAS Task Pool",
|
||||||
|
scan_mode="workspace",
|
||||||
|
owner_engine="sbas",
|
||||||
|
)
|
||||||
|
)
|
||||||
specs.extend(
|
specs.extend(
|
||||||
_iter_single_root_specs(
|
_iter_single_root_specs(
|
||||||
env_var="GAMMA_SBAS_WORK_ROOT",
|
env_var="GAMMA_SBAS_WORK_ROOT",
|
||||||
|
|||||||
@@ -210,3 +210,22 @@ Task_20250101_20250201
|
|||||||
- Task_Pool 根路径需要明确配置项,建议新增 `DINSAR_TASK_POOL_ROOT`,默认 `D:\Task_Pool\DInSAR`。
|
- Task_Pool 根路径需要明确配置项,建议新增 `DINSAR_TASK_POOL_ROOT`,默认 `D:\Task_Pool\DInSAR`。
|
||||||
- 中间文件清理需要先确认每个引擎的“可删目录”和“必须保留资产”清单,不能用一套规则覆盖全部。
|
- 中间文件清理需要先确认每个引擎的“可删目录”和“必须保留资产”清单,不能用一套规则覆盖全部。
|
||||||
- 前端 grouped 结果视图需要兼容旧 flat API 一段时间,避免已有页面一次性断裂。
|
- 前端 grouped 结果视图需要兼容旧 flat API 一段时间,避免已有页面一次性断裂。
|
||||||
|
|
||||||
|
## 2026-06-15 Task_Pool Materialize Update
|
||||||
|
|
||||||
|
Current direction:
|
||||||
|
|
||||||
|
- `TASK_POOL_ROOT` defaults to `D:\Task_Pool`.
|
||||||
|
- `DINSAR_TASK_POOL_ROOT` defaults to `D:\Task_Pool\DInSAR`.
|
||||||
|
- `SBAS_TASK_POOL_ROOT` defaults to `D:\Task_Pool\SBAS`.
|
||||||
|
- D-InSAR distribution materializes source inputs inside the task folder:
|
||||||
|
- directory sources are copied into `master/` and `slave/`;
|
||||||
|
- `S1_ZIP`, `LT1_ARCHIVE`, and other supported archives are extracted into `master/` and `slave/`;
|
||||||
|
- staged orbit files go into `orbit/`.
|
||||||
|
- Engines must consume local Task_Pool paths, not UNC source archive paths.
|
||||||
|
- `.dinsar_pair.json` records `source_materialization` so cleanup can distinguish copied directories, extracted archives, and staged files.
|
||||||
|
|
||||||
|
Cleanup implication:
|
||||||
|
|
||||||
|
- `master/`, `slave/`, `orbit/`, and engine `work/` folders are local materialized inputs/workspace and may be cleaned after all required results are registered.
|
||||||
|
- `publish/`, manifests, result assets, previews, and catalog metadata are preserved.
|
||||||
|
|||||||
@@ -252,3 +252,28 @@ GET /api/sbas-insar-products/{product_id}/assets/{asset_id}
|
|||||||
- 选中候选可生成 Stack Manifest;
|
- 选中候选可生成 Stack Manifest;
|
||||||
- 结果发布支持 GeoTIFF、预览图、监测点曲线和点矢量下载;
|
- 结果发布支持 GeoTIFF、预览图、监测点曲线和点矢量下载;
|
||||||
- 前端生产页和结果页构建通过。
|
- 前端生产页和结果页构建通过。
|
||||||
|
|
||||||
|
## 2026-06-15 SBAS Task_Pool Update
|
||||||
|
|
||||||
|
Recommended SBAS work root:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SBAS_TASK_POOL_ROOT=D:\Task_Pool\SBAS
|
||||||
|
GAMMA_SBAS_WORK_ROOT=D:\Task_Pool\SBAS
|
||||||
|
```
|
||||||
|
|
||||||
|
SBAS has its own Task_Pool sub-root and must not reuse `D:\Task_Pool\DInSAR`.
|
||||||
|
|
||||||
|
Recommended run layout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
D:\Task_Pool\SBAS\<stack_task>
|
||||||
|
├─ task_manifest.json
|
||||||
|
├─ sbas_stack_manifest.json
|
||||||
|
├─ sources
|
||||||
|
├─ orbits
|
||||||
|
├─ work
|
||||||
|
└─ publish
|
||||||
|
```
|
||||||
|
|
||||||
|
Source archives remain on UNC. Selected scenes and orbit files are materialized under the SBAS task directory before Gamma/LandSAR execution. Cleanup may remove `sources`, `orbits`, and `work` after result registration, but must preserve manifests, `publish`, previews, and catalog assets.
|
||||||
|
|||||||
@@ -23,13 +23,15 @@ This keeps the 20 TB storage useful for long-term source management while protec
|
|||||||
- `LT1_ARCHIVE` and `GF3_ARCHIVE` extract to a local materialized directory.
|
- `LT1_ARCHIVE` and `GF3_ARCHIVE` extract to a local materialized directory.
|
||||||
- Directory assets return `DIRECTORY_READY`.
|
- Directory assets return `DIRECTORY_READY`.
|
||||||
|
|
||||||
Default local materialize root is:
|
Default source materialization is task-scoped. D-InSAR and SBAS callers should pass a Task_Pool target directory:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
<PYINT_WORK_ROOT>\source_materialized\<source_format>
|
D:\Task_Pool\DInSAR\<task>\master
|
||||||
|
D:\Task_Pool\DInSAR\<task>\slave
|
||||||
|
D:\Task_Pool\SBAS\<stack>\sources\<YYYYMMDD>
|
||||||
```
|
```
|
||||||
|
|
||||||
Callers may pass `target_root` to force a D-InSAR Task_Pool or SBAS run-specific input directory.
|
The generic materialize endpoint still accepts `target_root` for ad hoc checks. Production callers must provide a Task_Pool destination.
|
||||||
|
|
||||||
## Production Boundary
|
## Production Boundary
|
||||||
|
|
||||||
@@ -104,6 +106,29 @@ Recommended source archive layout:
|
|||||||
└─ S1*.EOF
|
└─ S1*.EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Recommended local Task_Pool layout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
D:\Task_Pool
|
||||||
|
├─ DInSAR
|
||||||
|
│ └─ <pair_task>
|
||||||
|
│ ├─ task_manifest.json
|
||||||
|
│ ├─ .dinsar_pair.json
|
||||||
|
│ ├─ master
|
||||||
|
│ ├─ slave
|
||||||
|
│ ├─ orbit
|
||||||
|
│ ├─ work
|
||||||
|
│ └─ publish
|
||||||
|
└─ SBAS
|
||||||
|
└─ <stack_task>
|
||||||
|
├─ task_manifest.json
|
||||||
|
├─ sbas_stack_manifest.json
|
||||||
|
├─ sources
|
||||||
|
├─ orbits
|
||||||
|
├─ work
|
||||||
|
└─ publish
|
||||||
|
```
|
||||||
|
|
||||||
Date folders are optional for the scanner because source and orbit inventory recurse through configured roots. They are recommended for operator readability and migration checks.
|
Date folders are optional for the scanner because source and orbit inventory recurse through configured roots. They are recommended for operator readability and migration checks.
|
||||||
|
|
||||||
## Current Local Configuration Example
|
## Current Local Configuration Example
|
||||||
@@ -114,6 +139,10 @@ The local `.env` should keep legacy local roots and UNC roots side by side durin
|
|||||||
SOURCE_PRODUCT_DIRS=D:\LuTan1_Image_Pool;D:\Sentinel1_Image_Pool_ZIP;\\DESKTOP-N16HJ84\InSAR_Storage_2\LuTan-1\Archive;\\DESKTOP-N16HJ84\InSAR_Storage_2\Sentinel-1\Archive
|
SOURCE_PRODUCT_DIRS=D:\LuTan1_Image_Pool;D:\Sentinel1_Image_Pool_ZIP;\\DESKTOP-N16HJ84\InSAR_Storage_2\LuTan-1\Archive;\\DESKTOP-N16HJ84\InSAR_Storage_2\Sentinel-1\Archive
|
||||||
ORBIT_SOURCE_DIRS=D:\LT1_data_lsarorbit;D:\Sentinel1_EOF_Pool;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\LuTan-1;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\Sentinel-1
|
ORBIT_SOURCE_DIRS=D:\LT1_data_lsarorbit;D:\Sentinel1_EOF_Pool;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\LuTan-1;\\DESKTOP-N16HJ84\InSAR_Storage_2\Orbit\Sentinel-1
|
||||||
GF3_ARCHIVE_SOURCE_DIRS=\\DESKTOP-N16HJ84\InSAR_Storage_1\GaoFen-3
|
GF3_ARCHIVE_SOURCE_DIRS=\\DESKTOP-N16HJ84\InSAR_Storage_1\GaoFen-3
|
||||||
|
TASK_POOL_ROOT=D:\Task_Pool
|
||||||
|
DINSAR_TASK_POOL_ROOT=D:\Task_Pool\DInSAR
|
||||||
|
SBAS_TASK_POOL_ROOT=D:\Task_Pool\SBAS
|
||||||
|
GAMMA_SBAS_WORK_ROOT=D:\Task_Pool\SBAS
|
||||||
```
|
```
|
||||||
|
|
||||||
Do not store SMB credentials in `.env`. Credentials should be stored in Windows Credential Manager for the account that runs the backend/worker service.
|
Do not store SMB credentials in `.env`. Credentials should be stored in Windows Credential Manager for the account that runs the backend/worker service.
|
||||||
@@ -167,7 +196,7 @@ Keep `ORBIT_POOL_ENVI` and `PYINT_ORBIT_POOL_TXT` local. Add a later sync/materi
|
|||||||
After inventory scan verifies UNC assets:
|
After inventory scan verifies UNC assets:
|
||||||
|
|
||||||
1. D-InSAR Task_Pool stores source asset IDs and archive paths.
|
1. D-InSAR Task_Pool stores source asset IDs and archive paths.
|
||||||
2. Task preparation materializes master/slave scenes and orbit files locally.
|
2. Task preparation materializes master/slave scenes and orbit files under `D:\Task_Pool\DInSAR\<task>`.
|
||||||
3. Engines run only against local Task_Pool paths.
|
3. Engines run only against local Task_Pool paths.
|
||||||
4. Results register normally.
|
4. Results register normally.
|
||||||
5. Local materialized inputs and intermediate products are eligible for cleanup after result registration.
|
5. Local materialized inputs and intermediate products are eligible for cleanup after result registration.
|
||||||
|
|||||||
Reference in New Issue
Block a user