feat: align DInSAR workflow with three-engine task pool
This commit is contained in:
@@ -428,6 +428,71 @@ button:focus-visible {
|
||||
font-size: 11px;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.batch-engine-results,
|
||||
.dinsar-engine-result-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.batch-engine-chip,
|
||||
.dinsar-engine-result-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(100, 116, 139, 0.24);
|
||||
background: rgba(148, 163, 184, 0.1);
|
||||
color: #475569;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.batch-engine-chip.tone-ready,
|
||||
.dinsar-engine-result-chip.tone-ready {
|
||||
border-color: rgba(22, 163, 74, 0.26);
|
||||
background: rgba(22, 163, 74, 0.1);
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.batch-engine-chip.tone-error,
|
||||
.dinsar-engine-result-chip.tone-error {
|
||||
border-color: rgba(220, 38, 38, 0.26);
|
||||
background: rgba(220, 38, 38, 0.1);
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.batch-engine-chip.tone-warn,
|
||||
.dinsar-engine-result-chip.tone-warn {
|
||||
border-color: rgba(217, 119, 6, 0.28);
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.batch-engine-chip.tone-info,
|
||||
.dinsar-engine-result-chip.tone-info {
|
||||
border-color: rgba(37, 99, 235, 0.26);
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.dinsar-catalog-cleanup-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dinsar-catalog-cleanup-plan {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.batch-item select,
|
||||
.batch-item input {
|
||||
font-size: 12px;
|
||||
|
||||
@@ -38,18 +38,16 @@ const ENGINE_STATUS_LABEL = {
|
||||
|
||||
const ENGINE_LABEL = {
|
||||
sarscape: 'SARscape',
|
||||
isce2: 'ISCE2',
|
||||
pyint: 'PyINT / Gamma',
|
||||
landsar: 'LANDSAR',
|
||||
};
|
||||
|
||||
const TASK_TYPE_LABEL = {
|
||||
ISCE2_RUN: 'ISCE2生产',
|
||||
PYINT_RUN: 'PyINT/Gamma生产',
|
||||
LANDSAR_RUN: 'LandSAR生产',
|
||||
IDL_RUN_DINSAR: 'ENVI生产',
|
||||
};
|
||||
const DINSAR_PRODUCTION_TASK_TYPES = ['ISCE2_RUN', 'PYINT_RUN', 'LANDSAR_RUN', 'IDL_RUN_DINSAR'];
|
||||
const DINSAR_PRODUCTION_TASK_TYPES = ['PYINT_RUN', 'LANDSAR_RUN', 'IDL_RUN_DINSAR'];
|
||||
|
||||
const STATUS_LABEL = {
|
||||
PENDING: '等待中',
|
||||
@@ -112,7 +110,6 @@ function formatStatus(status) {
|
||||
}
|
||||
|
||||
function taskTypeToEngine(taskType) {
|
||||
if (taskType === 'ISCE2_RUN') return 'isce2';
|
||||
if (taskType === 'PYINT_RUN') return 'pyint';
|
||||
if (taskType === 'LANDSAR_RUN') return 'landsar';
|
||||
if (taskType === 'IDL_RUN_DINSAR') return 'sarscape';
|
||||
@@ -602,8 +599,6 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
|
||||
? 'PyINT/Gamma 会按目标网格尺寸自动换算多视;新增 Gamma 残余重去平在解缠后执行 rascc_mask/quad_fit/quad_sub,再导出 native 和标准 GeoTIFF。'
|
||||
: selectedEngine === 'landsar'
|
||||
? 'LandSAR 当前使用已跑通的稳定参数。GACOS 大气相位改正需要外部大气延迟文件,未配置文件前不可启用;垂直向形变为可选输出,默认关闭。'
|
||||
: selectedEngine === 'isce2'
|
||||
? '这些参数现在按执行、交付、增强分组展示。结果异常时,优先尝试关闭增强项,再回看基础几何和配对质量。'
|
||||
: '这些参数影响当前引擎的生产模板。建议先使用默认值,只有在结果边界、噪声或几何表现异常时再逐项调整。';
|
||||
const pyintPreviewBlocksSubmit = selectedEngine === 'pyint' && pyintPreview && pyintPreview.allow_submit === false;
|
||||
const taskMonitor = useTaskMonitor({
|
||||
@@ -617,9 +612,7 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
|
||||
? {
|
||||
task_id: latestRunWithTask.task_id,
|
||||
task_type:
|
||||
latestRunWithTask.engine === 'isce2'
|
||||
? 'ISCE2_RUN'
|
||||
: latestRunWithTask.engine === 'pyint'
|
||||
latestRunWithTask.engine === 'pyint'
|
||||
? 'PYINT_RUN'
|
||||
: latestRunWithTask.engine === 'landsar'
|
||||
? 'LANDSAR_RUN'
|
||||
@@ -1305,11 +1298,6 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
|
||||
disabled={readOnly}
|
||||
style={{ width: 140, padding: '5px 8px', borderRadius: 4, border: '1px solid #e2e8f0', fontSize: 13 }}
|
||||
/>
|
||||
{selectedEngine === 'isce2' && currentDefaultTimeoutSec > 0 && (
|
||||
<div style={{ fontSize: 11, color: '#94a3b8', marginTop: 4 }}>
|
||||
ISCE2 默认按单对任务使用 {currentDefaultTimeoutSec} 秒;批量目录会串行逐对套用该超时。
|
||||
</div>
|
||||
)}
|
||||
{selectedEngine === 'pyint' && currentDefaultTimeoutSec > 0 && (
|
||||
<div style={{ fontSize: 11, color: '#94a3b8', marginTop: 4 }}>
|
||||
PyINT 默认按单对任务使用 {currentDefaultTimeoutSec} 秒;当前会逐对串行创建工作区并运行外部 PyINT / Gamma 流程,native 会在主流程和重去平后统一写入。
|
||||
|
||||
@@ -12,5 +12,14 @@ export const queueDinsarCatalogRebuild = payload =>
|
||||
export const listDinsarProducts = (params = {}) =>
|
||||
apiClient.get('/dinsar-products', { params }).then(r => r.data);
|
||||
|
||||
export const listDinsarProductPairs = (params = {}) =>
|
||||
apiClient.get('/dinsar-products/pairs', { params }).then(r => r.data);
|
||||
|
||||
export const getDinsarProductDetail = productId =>
|
||||
apiClient.get(`/dinsar-products/${encodeURIComponent(productId)}`).then(r => r.data);
|
||||
|
||||
export const getDinsarProductCleanupPlan = productId =>
|
||||
apiClient.get(`/dinsar-products/${encodeURIComponent(productId)}/cleanup-intermediates/plan`).then(r => r.data);
|
||||
|
||||
export const getDinsarPairCleanupPlan = pairKey =>
|
||||
apiClient.get(`/dinsar-products/pairs/${encodeURIComponent(pairKey)}/cleanup-intermediates/plan`).then(r => r.data);
|
||||
|
||||
@@ -4,7 +4,8 @@ import apiClient from '../api/client';
|
||||
import {
|
||||
getDinsarCatalogStatus,
|
||||
getDinsarProductDetail,
|
||||
listDinsarProducts,
|
||||
getDinsarProductCleanupPlan,
|
||||
listDinsarProductPairs,
|
||||
queueDinsarCatalogRebuild,
|
||||
queueDinsarProductPublish,
|
||||
} from '../api/dinsarProducts';
|
||||
@@ -22,6 +23,11 @@ const STATUS_TONE_MAP = {
|
||||
WARN: 'warn',
|
||||
ERROR: 'error',
|
||||
REBUILDING: 'info',
|
||||
ready: 'ready',
|
||||
missing: 'neutral',
|
||||
failed: 'error',
|
||||
blocked: 'warn',
|
||||
legacy: 'neutral',
|
||||
};
|
||||
|
||||
function formatDateTime(value) {
|
||||
@@ -33,6 +39,19 @@ function formatDateTime(value) {
|
||||
}
|
||||
}
|
||||
|
||||
function formatBytes(value) {
|
||||
const size = Number(value || 0);
|
||||
if (!Number.isFinite(size) || size <= 0) return '0 B';
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let next = size;
|
||||
let index = 0;
|
||||
while (next >= 1024 && index < units.length - 1) {
|
||||
next /= 1024;
|
||||
index += 1;
|
||||
}
|
||||
return `${next.toFixed(index === 0 ? 0 : 1)} ${units[index]}`;
|
||||
}
|
||||
|
||||
function parseDirectoryList(value) {
|
||||
return [...new Set(
|
||||
String(value || '')
|
||||
@@ -50,6 +69,15 @@ function StatusPill({ label, tone = 'neutral' }) {
|
||||
return <span className={`dinsar-status-pill tone-${tone}`}>{label}</span>;
|
||||
}
|
||||
|
||||
function engineStatusTone(status) {
|
||||
const normalized = String(status || '').toLowerCase();
|
||||
if (normalized === 'ready') return 'ready';
|
||||
if (normalized === 'failed') return 'error';
|
||||
if (normalized === 'blocked') return 'warn';
|
||||
if (normalized === 'running') return 'info';
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
function MetaField({ label, value, multiline = false }) {
|
||||
const displayValue = value === null || value === undefined || value === '' ? '-' : value;
|
||||
return (
|
||||
@@ -68,8 +96,12 @@ export default function DinsarCatalogPanel({
|
||||
}) {
|
||||
const [catalogStatus, setCatalogStatus] = useState(null);
|
||||
const [products, setProducts] = useState([]);
|
||||
const [productPairs, setProductPairs] = useState([]);
|
||||
const [selectedPairKey, setSelectedPairKey] = useState('');
|
||||
const [selectedProductId, setSelectedProductId] = useState(null);
|
||||
const [selectedProduct, setSelectedProduct] = useState(null);
|
||||
const [cleanupPlan, setCleanupPlan] = useState(null);
|
||||
const [cleanupPlanLoading, setCleanupPlanLoading] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [actionLoading, setActionLoading] = useState(false);
|
||||
@@ -94,8 +126,8 @@ export default function DinsarCatalogPanel({
|
||||
);
|
||||
|
||||
const engineOptions = useMemo(
|
||||
() => buildDinsarEngineOptions(products, { includeKnown: true }),
|
||||
[products]
|
||||
() => buildDinsarEngineOptions([], { includeKnown: true }),
|
||||
[]
|
||||
);
|
||||
const selectedEngineMeta = useMemo(
|
||||
() => (engineFilter === DINSAR_ENGINE_ALL ? null : getDinsarEngineMeta(engineFilter)),
|
||||
@@ -114,7 +146,7 @@ export default function DinsarCatalogPanel({
|
||||
try {
|
||||
const [statusData, productData] = await Promise.all([
|
||||
getDinsarCatalogStatus(),
|
||||
listDinsarProducts({
|
||||
listDinsarProductPairs({
|
||||
limit: listLimit,
|
||||
offset: 0,
|
||||
engine_code: engineFilter === DINSAR_ENGINE_ALL ? undefined : engineFilter,
|
||||
@@ -122,18 +154,35 @@ export default function DinsarCatalogPanel({
|
||||
}),
|
||||
]);
|
||||
setCatalogStatus(statusData);
|
||||
const nextItems = Array.isArray(productData?.items) ? productData.items : [];
|
||||
const nextPairs = Array.isArray(productData?.items) ? productData.items : [];
|
||||
setProductPairs(nextPairs);
|
||||
const nextItems = nextPairs
|
||||
.map((item) => ({
|
||||
id: item.primary_product_id,
|
||||
engine_code: item.primary_engine_code,
|
||||
pair_key: item.pair_key,
|
||||
}))
|
||||
.filter((item) => item.id);
|
||||
setProducts(nextItems);
|
||||
setSelectedPairKey((current) => {
|
||||
if (current && nextPairs.some((item) => (item.pair_key || `pair:${item.primary_product_id}`) === current)) {
|
||||
return current;
|
||||
}
|
||||
const first = nextPairs[0];
|
||||
return first ? (first.pair_key || `pair:${first.primary_product_id}`) : '';
|
||||
});
|
||||
setSelectedProductId((current) => {
|
||||
if (current && nextItems.some((item) => item.id === current)) {
|
||||
return current;
|
||||
}
|
||||
return nextItems[0]?.id ?? null;
|
||||
return nextPairs[0]?.primary_product_id ?? null;
|
||||
});
|
||||
} catch (error) {
|
||||
setActionMessage(`结果目录状态加载失败:${error?.response?.data?.detail || error.message}`);
|
||||
setCatalogStatus(null);
|
||||
setProducts([]);
|
||||
setProductPairs([]);
|
||||
setSelectedPairKey('');
|
||||
setSelectedProductId(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -143,9 +192,11 @@ export default function DinsarCatalogPanel({
|
||||
const loadProductDetail = useCallback(async (productId) => {
|
||||
if (!productId) {
|
||||
setSelectedProduct(null);
|
||||
setCleanupPlan(null);
|
||||
return;
|
||||
}
|
||||
setSelectedProduct(null);
|
||||
setCleanupPlan(null);
|
||||
setDetailLoading(true);
|
||||
try {
|
||||
const detail = await getDinsarProductDetail(productId);
|
||||
@@ -159,6 +210,21 @@ export default function DinsarCatalogPanel({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadCleanupPlan = useCallback(async () => {
|
||||
if (!selectedProductId) return;
|
||||
setCleanupPlanLoading(true);
|
||||
try {
|
||||
const plan = await getDinsarProductCleanupPlan(selectedProductId);
|
||||
setCleanupPlan(plan);
|
||||
} catch (error) {
|
||||
setCleanupPlan({
|
||||
error: error?.response?.data?.detail || error.message || '中间文件清理计划加载失败',
|
||||
});
|
||||
} finally {
|
||||
setCleanupPlanLoading(false);
|
||||
}
|
||||
}, [selectedProductId]);
|
||||
|
||||
useEffect(() => {
|
||||
loadCatalog();
|
||||
}, [loadCatalog]);
|
||||
@@ -292,7 +358,7 @@ export default function DinsarCatalogPanel({
|
||||
<strong>手动发布与目录重建</strong>
|
||||
<p>
|
||||
这里用于把既有结果目录重新发布为标准结果包,并按最新规则重建目录索引。
|
||||
如果同一对影像存在 ENVI 与 ISCE2 两套结果,它们会依赖 `engine_code` 与 `run_key` 分别登记,不会互相覆盖。
|
||||
同一对影像的 ENVI/SARscape、LandSAR、Gamma/PyINT 结果会按任务聚合展示,底层仍依赖 `engine_code` 与 `run_key` 分别登记。
|
||||
</p>
|
||||
</div>
|
||||
<div className="dinsar-catalog-manage-form">
|
||||
@@ -335,7 +401,7 @@ export default function DinsarCatalogPanel({
|
||||
<div>
|
||||
<strong>结果包列表</strong>
|
||||
<span>
|
||||
{loading ? '加载中...' : `当前展示 ${products.length} 条`}
|
||||
{loading ? '加载中...' : `当前展示 ${productPairs.length} 个任务`}
|
||||
</span>
|
||||
</div>
|
||||
{queryApplied && <StatusPill label={`检索: ${queryApplied}`} tone="info" />}
|
||||
@@ -375,36 +441,52 @@ export default function DinsarCatalogPanel({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{products.length === 0 ? (
|
||||
{productPairs.length === 0 ? (
|
||||
<div className="dinsar-catalog-empty">
|
||||
{loading ? '正在加载结果包...' : '当前筛选条件下没有结果包。'}
|
||||
</div>
|
||||
) : (
|
||||
<div className="dinsar-catalog-list">
|
||||
{products.map((item) => {
|
||||
{productPairs.map((item) => {
|
||||
const tone = STATUS_TONE_MAP[item.status] || 'neutral';
|
||||
const engineMeta = getDinsarEngineMeta(item.engine_code);
|
||||
const satelliteFamily = inferSatelliteFamilyFromResultLike(item);
|
||||
const rowKey = item.pair_key || `pair:${item.primary_product_id}`;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
key={rowKey}
|
||||
type="button"
|
||||
className={`dinsar-catalog-list-item ${selectedProductId === item.id ? 'active' : ''}`}
|
||||
onClick={() => setSelectedProductId(item.id)}
|
||||
className={`dinsar-catalog-list-item ${selectedPairKey === rowKey ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
setSelectedPairKey(rowKey);
|
||||
setSelectedProductId(item.primary_product_id || null);
|
||||
}}
|
||||
>
|
||||
<div className="dinsar-catalog-list-item-top">
|
||||
<strong>{item.display_name || item.product_id}</strong>
|
||||
<strong>{item.task_alias || item.task_name || item.pair_key || '未命名任务'}</strong>
|
||||
<StatusPill label={item.status || 'UNKNOWN'} tone={tone} />
|
||||
</div>
|
||||
<div className="dinsar-catalog-list-item-badges">
|
||||
<span className={`dinsar-engine-badge tone-${engineMeta.tone}`}>{engineMeta.shortLabel}</span>
|
||||
{satelliteFamily && (
|
||||
<span className="dinsar-engine-badge tone-unknown">{formatSatelliteFamilyLabel(satelliteFamily)}</span>
|
||||
)}
|
||||
<span>{formatDateTime(item.published_at)}</span>
|
||||
<span>{formatDateTime(item.latest_published_at)}</span>
|
||||
</div>
|
||||
<div className="dinsar-engine-result-row">
|
||||
{['sarscape', 'landsar', 'pyint'].map((engineCode) => {
|
||||
const result = item.engine_results?.[engineCode] || {};
|
||||
const engineMeta = getDinsarEngineMeta(engineCode);
|
||||
return (
|
||||
<span
|
||||
key={engineCode}
|
||||
className={`dinsar-engine-result-chip tone-${engineStatusTone(result.status)}`}
|
||||
>
|
||||
{engineMeta.shortLabel}: {result.status || 'missing'}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="dinsar-catalog-list-item-meta">
|
||||
{(item.task_alias || item.task_name || '-')}{item.run_key ? ` / ${item.run_key}` : ''}
|
||||
已有结果 {item.available_engine_count || 0} / 3,ready {item.ready_engine_count || 0}
|
||||
</div>
|
||||
<div className="dinsar-catalog-list-item-meta">
|
||||
{item.pair_key || '-'}
|
||||
@@ -432,7 +514,7 @@ export default function DinsarCatalogPanel({
|
||||
</div>
|
||||
|
||||
{!selectedProductId ? (
|
||||
<div className="dinsar-catalog-empty">请选择一个结果包查看详情。</div>
|
||||
<div className="dinsar-catalog-empty">请选择一个已有结果的任务查看详情。</div>
|
||||
) : detailLoading || !selectedProduct ? (
|
||||
<div className="dinsar-catalog-empty">正在加载详情...</div>
|
||||
) : selectedProduct?.error ? (
|
||||
@@ -587,6 +669,58 @@ export default function DinsarCatalogPanel({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="dinsar-catalog-section-card">
|
||||
<div className="dinsar-catalog-section-title">中间文件清理计划</div>
|
||||
<div className="dinsar-catalog-cleanup-head">
|
||||
<div>
|
||||
<MetaField label="当前能力" value="dry-run,只生成计划,不执行删除" />
|
||||
</div>
|
||||
<button type="button" onClick={loadCleanupPlan} disabled={cleanupPlanLoading}>
|
||||
{cleanupPlanLoading ? '计算中...' : '生成计划'}
|
||||
</button>
|
||||
</div>
|
||||
{!cleanupPlan ? (
|
||||
<div className="dinsar-catalog-empty inline">尚未生成清理计划。</div>
|
||||
) : cleanupPlan.error ? (
|
||||
<div className="dinsar-catalog-empty inline error">{cleanupPlan.error}</div>
|
||||
) : (
|
||||
<div className="dinsar-catalog-cleanup-plan">
|
||||
<div className="dinsar-catalog-detail-grid">
|
||||
<div className="dinsar-catalog-section-card nested">
|
||||
<MetaField label="可清理" value={cleanupPlan.deletable ? '是' : '否'} />
|
||||
<MetaField label="候选项" value={cleanupPlan.candidate_count} />
|
||||
<MetaField label="候选大小" value={formatBytes(cleanupPlan.total_size_bytes)} />
|
||||
</div>
|
||||
<div className="dinsar-catalog-section-card nested">
|
||||
<MetaField label="manifest" value={cleanupPlan.checks?.manifest_exists ? '存在' : '缺失'} />
|
||||
<MetaField label="必要资产" value={cleanupPlan.checks?.required_assets_ok ? '完整' : '缺失'} />
|
||||
<MetaField label="当前引擎" value={cleanupPlan.checks?.current_engine ? '是' : '否'} />
|
||||
</div>
|
||||
</div>
|
||||
{Array.isArray(cleanupPlan.blockers) && cleanupPlan.blockers.length > 0 && (
|
||||
<div className="dinsar-catalog-issue-list">
|
||||
{cleanupPlan.blockers.map((blocker) => (
|
||||
<div key={blocker} className="dinsar-catalog-issue-item warn">
|
||||
{blocker}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="dinsar-catalog-asset-list">
|
||||
{(cleanupPlan.candidates || []).map((candidate) => (
|
||||
<div key={candidate.path} className={`dinsar-catalog-asset-item ${candidate.exists ? 'ok' : 'missing'}`}>
|
||||
<div className="dinsar-catalog-asset-top">
|
||||
<strong>{candidate.reason}</strong>
|
||||
<span>{candidate.exists ? formatBytes(candidate.size_bytes) : '不存在'}</span>
|
||||
</div>
|
||||
<div className="break-all">{candidate.path}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -2,6 +2,16 @@ import { useEffect } from 'react';
|
||||
import { useBatchStore, useUiStore, useAuthStore } from '../store';
|
||||
import { useI18n } from '../i18n/I18nContext';
|
||||
import useBatchOperations from '../hooks/useBatchOperations';
|
||||
import { getDinsarEngineMeta } from '../utils/dinsarEngines';
|
||||
|
||||
function engineResultTone(status) {
|
||||
const normalized = String(status || '').toLowerCase();
|
||||
if (normalized === 'ready') return 'ready';
|
||||
if (normalized === 'failed') return 'error';
|
||||
if (normalized === 'blocked') return 'warn';
|
||||
if (normalized === 'running') return 'info';
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
export default function BatchPanel() {
|
||||
const { language } = useI18n();
|
||||
@@ -124,6 +134,24 @@ export default function BatchPanel() {
|
||||
M: {item.master_imaging_date || '-'} / S: {item.slave_imaging_date || '-'}
|
||||
</div>
|
||||
)}
|
||||
{batchTab === 'dinsar' && (
|
||||
<div className="batch-engine-results">
|
||||
{['sarscape', 'landsar', 'pyint'].map((engineCode) => {
|
||||
const engineMeta = getDinsarEngineMeta(engineCode);
|
||||
const result = item.engine_results?.[engineCode] || {};
|
||||
const status = result.status || 'missing';
|
||||
return (
|
||||
<span
|
||||
key={engineCode}
|
||||
className={`batch-engine-chip tone-${engineResultTone(status)}`}
|
||||
title={result.skip_reason || result.run_key || ''}
|
||||
>
|
||||
{engineMeta.shortLabel}: {status}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<select
|
||||
value={item.status || 'PENDING'}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const DINSAR_ENGINE_ALL = '__ALL__';
|
||||
|
||||
export const KNOWN_DINSAR_ENGINE_CODES = ['sarscape', 'envi', 'isce2', 'pyint', 'landsar'];
|
||||
export const KNOWN_DINSAR_ENGINE_CODES = ['sarscape', 'landsar', 'pyint'];
|
||||
|
||||
const DINSAR_ENGINE_META = {
|
||||
sarscape: {
|
||||
@@ -8,16 +8,6 @@ const DINSAR_ENGINE_META = {
|
||||
shortLabel: 'ENVI',
|
||||
tone: 'envi',
|
||||
},
|
||||
envi: {
|
||||
label: 'Legacy ENVI',
|
||||
shortLabel: 'ENVI-L',
|
||||
tone: 'envi',
|
||||
},
|
||||
isce2: {
|
||||
label: 'ISCE2',
|
||||
shortLabel: 'ISCE2',
|
||||
tone: 'isce2',
|
||||
},
|
||||
pyint: {
|
||||
label: 'PyINT / Gamma',
|
||||
shortLabel: 'PyINT',
|
||||
|
||||
Reference in New Issue
Block a user