diff --git a/frontend/app/(protected)/navigation/page.tsx b/frontend/app/(protected)/navigation/page.tsx
index 69b5c03..36a7edb 100644
--- a/frontend/app/(protected)/navigation/page.tsx
+++ b/frontend/app/(protected)/navigation/page.tsx
@@ -95,10 +95,17 @@ const NavigationPage: React.FC = () => {
}, [])
const handleModelError = useCallback((error: string) => {
- console.error('Model loading error:', error)
+ console.error('[NavigationPage] Model loading error:', error)
setModelError(error)
setIsModelReady(false)
}, [])
+
+ useEffect(() => {
+ if (selectedModelPath) {
+ setIsModelReady(false);
+ setModelError(null);
+ }
+ }, [selectedModelPath]);
useEffect(() => {
if (urlObjectId && urlObjectTitle && (!currentObject.id || currentObject.id !== urlObjectId)) {
@@ -206,6 +213,7 @@ const NavigationPage: React.FC = () => {
objectId={objectId || undefined}
onClose={closeMonitoring}
onSelectModel={(path) => {
+ console.log('[NavigationPage] Model selected:', path);
setSelectedModelPath(path)
setModelError(null)
setIsModelReady(false)
@@ -291,20 +299,37 @@ const NavigationPage: React.FC = () => {
{modelError ? (
-
-
-
- Ошибка загрузки 3D модели
-
-
- {modelError}
-
-
- Используйте навигацию по этажам для просмотра детекторов
-
+ <>
+ {console.log('[NavigationPage] Rendering error message, modelError:', modelError)}
+
+
+
+ Ошибка загрузки 3D модели
+
+
+ {modelError}
+
+
+ Используйте навигацию по этажам для просмотра детекторов
- ) : (
+
+ >
+ ) : !selectedModelPath ? (
+
+
+
+ 3D модель не загружена
+
+
+ Модель не готова к отображению
+
+
+ Выберите модель из навигации по этажам
+
+
+
+ ) : (
= ({
if (!isInitializedRef.current || isDisposedRef.current) {
return
}
-
- // Check if modelPath is provided
- if (!modelPath) {
+
+ if (!modelPath || modelPath.trim() === '') {
console.warn('[ModelViewer] No model path provided')
- onError?.('Путь к 3D модели не задан')
+ // Не вызываем onError для пустого пути - это нормальное состояние при инициализации
+ setIsLoading(false)
return
}
@@ -158,13 +158,24 @@ const ModelViewer: React.FC = ({
return
}
- const oldMeshes = sceneRef.current.meshes.slice();
- oldMeshes.forEach(m => m.dispose());
-
+ const currentModelPath = modelPath;
+ console.log('[ModelViewer] Starting model load:', currentModelPath);
+
setIsLoading(true)
setLoadingProgress(0)
setShowModel(false)
- console.log('Loading GLTF model:', modelPath)
+ setModelReady(false)
+
+ const oldMeshes = sceneRef.current.meshes.slice();
+ const activeCameraId = sceneRef.current.activeCamera?.uniqueId;
+ console.log('[ModelViewer] Cleaning up old meshes. Total:', oldMeshes.length);
+ oldMeshes.forEach(m => {
+ if (m.uniqueId !== activeCameraId) {
+ m.dispose();
+ }
+ });
+
+ console.log('[ModelViewer] Loading GLTF model:', currentModelPath)
// UI элемент загрузчика (есть эффект замедленности)
const progressInterval = setInterval(() => {
@@ -178,15 +189,44 @@ const ModelViewer: React.FC = ({
}, 100)
try {
- const result = await ImportMeshAsync(modelPath, sceneRef.current)
-
+ console.log('[ModelViewer] Calling ImportMeshAsync with path:', currentModelPath);
+
+ // Проверим доступность файла через fetch
+ try {
+ const testResponse = await fetch(currentModelPath, { method: 'HEAD' });
+ console.log('[ModelViewer] File availability check:', {
+ url: currentModelPath,
+ status: testResponse.status,
+ statusText: testResponse.statusText,
+ ok: testResponse.ok
+ });
+ } catch (fetchError) {
+ console.error('[ModelViewer] File fetch error:', fetchError);
+ }
+
+ const result = await ImportMeshAsync(currentModelPath, sceneRef.current)
+ console.log('[ModelViewer] ImportMeshAsync completed successfully');
+ console.log('[ModelViewer] Import result:', {
+ meshesCount: result.meshes.length,
+ particleSystemsCount: result.particleSystems.length,
+ skeletonsCount: result.skeletons.length,
+ animationGroupsCount: result.animationGroups.length
+ });
+
+ if (isDisposedRef.current || modelPath !== currentModelPath) {
+ console.log('[ModelViewer] Model loading aborted - model changed during load')
+ clearInterval(progressInterval)
+ setIsLoading(false)
+ return;
+ }
+
importedMeshesRef.current = result.meshes
clearInterval(progressInterval)
setLoadingProgress(100)
- console.log('GLTF Model loaded successfully!')
- console.log('ImportMeshAsync result:', result)
+ console.log('[ModelViewer] GLTF Model loaded successfully!', result)
+
if (result.meshes.length > 0) {
const boundingBox = result.meshes[0].getHierarchyBoundingVectors()
@@ -210,9 +250,11 @@ const ModelViewer: React.FC = ({
// Плавное появление модели
setTimeout(() => {
- if (!isDisposedRef.current) {
+ if (!isDisposedRef.current && modelPath === currentModelPath) {
setShowModel(true)
setIsLoading(false)
+ } else {
+ console.log('Model display aborted - model changed during animation')
}
}, 500)
} else {
@@ -222,9 +264,14 @@ const ModelViewer: React.FC = ({
}
} catch (error) {
clearInterval(progressInterval)
- console.error('Error loading GLTF model:', error)
- const errorMessage = error instanceof Error ? error.message : String(error)
- onError?.(`Ошибка загрузки модели: ${errorMessage}`)
+ // Only report error if this loading is still relevant
+ if (!isDisposedRef.current && modelPath === currentModelPath) {
+ console.error('Error loading GLTF model:', error)
+ const errorMessage = error instanceof Error ? error.message : String(error)
+ onError?.(`Ошибка загрузки модели: ${errorMessage}`)
+ } else {
+ console.log('Error occurred but loading was aborted - model changed')
+ }
setIsLoading(false)
}
}
@@ -271,9 +318,26 @@ const ModelViewer: React.FC = ({
try {
const meta: any = (m as any)?.metadata
const extras: any = meta?.gltf?.extras ?? meta?.extras ?? (m as any)?.extras
+
const sid = extras?.Sensor_ID ?? extras?.sensor_id ?? extras?.SERIAL_NUMBER ?? extras?.serial_number
- if (sid == null) return false
- return String(sid).trim() === sensorId
+ if (sid != null) {
+ return String(sid).trim() === sensorId
+ }
+
+ const monitoringSensorInstance = extras?.bonsaiPset_ARBM_PSet_MonitoringSensor_Instance
+ if (monitoringSensorInstance && typeof monitoringSensorInstance === 'string') {
+ try {
+ const parsedInstance = JSON.parse(monitoringSensorInstance)
+ const instanceSensorId = parsedInstance?.Sensor_ID
+ if (instanceSensorId != null) {
+ return String(instanceSensorId).trim() === sensorId
+ }
+ } catch (parseError) {
+ console.warn('[ModelViewer] Error parsing MonitoringSensor_Instance JSON:', parseError)
+ }
+ }
+
+ return false
} catch (error) {
console.warn('[ModelViewer] Error matching sensor mesh:', error)
return false
diff --git a/frontend/components/navigation/DetectorMenu.tsx b/frontend/components/navigation/DetectorMenu.tsx
index 2c6da2a..69a5b2d 100644
--- a/frontend/components/navigation/DetectorMenu.tsx
+++ b/frontend/components/navigation/DetectorMenu.tsx
@@ -166,7 +166,7 @@ const DetectorMenu: React.FC = ({ detector, isOpen, onClose,
@@ -187,13 +187,13 @@ const DetectorMenu: React.FC = ({ detector, isOpen, onClose,
diff --git a/frontend/components/navigation/Monitoring.tsx b/frontend/components/navigation/Monitoring.tsx
index 5ebebf4..fa8679b 100644
--- a/frontend/components/navigation/Monitoring.tsx
+++ b/frontend/components/navigation/Monitoring.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import Image from 'next/image';
interface MonitoringProps {
@@ -12,6 +12,13 @@ const Monitoring: React.FC
= ({ onClose, onSelectModel }) => {
const [models, setModels] = useState<{ title: string; path: string }[]>([]);
const [loadError, setLoadError] = useState(null);
+ const handleSelectModel = useCallback((modelPath: string) => {
+ console.log(`[NavigationPage] Model selected: ${modelPath}`);
+ onSelectModel?.(modelPath);
+ }, [onSelectModel]);
+
+ console.log('[Monitoring] Models:', models, 'Error:', loadError);
+
// Загружаем список доступных моделей из assets/big-models через API
useEffect(() => {
const fetchModels = async () => {
@@ -64,53 +71,29 @@ const Monitoring: React.FC = ({ onClose, onSelectModel }) => {
)}
-
-
- {objectImageError ? (
-
-
- Предпросмотр 3D недоступен
-
-
- Изображение модели не найдено
-
-
- ) : (
-
setObjectImageError(true)}
- />
- )}
-
-
-
{loadError && (
Ошибка загрузки списка моделей: {loadError}
)}
-
- {models.length > 0 ? (
- models.map((model, idx) => (
+ {models.length > 0 && (
+ <>
+ {/* Большая панорамная карточка для приоритетной модели */}
+ {models[0] && (