BIM Yazılım Mimarisi: AEC Endüstrisi için 3D Modelleme ve İşbirliği
Il Yapı Bilgi Modellemesi (BIM) dünyayı kökten değiştirdi mimarlık, mühendislik ve inşaatın (AEC) tasarladığı, inşa ettiği ve yönettiği yer binalar. 2024 yılında ABD'deki mimarlık firmalarının %66'sından fazlası BIM araçlarını kullanıyor. küresel BIM yazılımı pazarının değeri 7,96 milyar dolar iken 2024 yılında ise 2033 yılına kadar 33,64 milyar olacağı tahmin ediliyor (CAGR %17,37).
Peki modern BIM yazılımının perde arkasında neler oluyor? Nasıl bir 3 boyutlu model hafızada mı? Çok kullanıcılı işbirliği paylaşılan bir modelde nasıl çalışır? Peki bulut bu sistemlerin dağıtımını nasıl değiştirdi? Bu yazıda inşa edeceğiz Bir BIM sisteminin eksiksiz mimarisi, IFC standardını analiz etme, BCF formatını işbirliği ve 3D web oluşturmanın teknik zorlukları.
Ne Öğreneceksiniz
- IFC 4.3 (ISO 16739-1:2024) standardı ve BIM veri şeması
- Three.js/WebGL ile web BIM görüntüleyicisinin mimarisi
- İşbirliği katmanı: BIM İşbirliği Formatı (BCF) ve çatışma yönetimi
- IFC ayrıştırma hattı: ikili dosyadan oluşturulabilir 3B sahneye
- 3D kesişim algoritmalarıyla otomatik çakışma tespiti
- Bulut BIM: çok kiracılı mimari ve geometri akışı
- Binaların Dijital İkizi için IoT ile entegrasyon
IFC Standardı: BIM'in DNA'sı
L'Endüstri Temel Sınıfları (IFC) açık veri şeması Farklı BIM yazılımları arasında birlikte çalışabilirliğe olanak tanır. IFC 4.3 ADD2 sürümü 2024 yılında onaylanan ISO 16739-1:2024 standardı haline geldi. Geliştirilmekte ve sürdürülmektedir. BuildingSMART International'dan.
IFC, binanın her bir öğesinin (duvar, kapı, pencere, kiriş, fikstür) standart özelliklere ve ilişkilere sahip bir sınıftır diğer öğelerle önceden tanımlanmıştır. 3B geometri meta verilerden ayrıdır STEP gösterimi (ISO 10303) aracılığıyla.
| IFC versiyonu | Yıl | Ana Özellikler | Durum |
|---|---|---|---|
| IFC 2x3 | 2006 | İlk kararlı sürüm, temel mimari | Eski (hala popüler) |
| IFC 4 | 2013 | Genişletilmiş MEP, gelişmiş yapılar, 4D/5D | Yaygın olarak desteklenmektedir |
| IFC 4.3 | 2022/2024 | Sivil altyapılar, demiryolu, jeoteknik | ISO 16739-1:2024 (Önerilen) |
# Esempio di entità IFC in formato STEP (file .ifc)
# Un semplice muro con le sue proprietà e geometria
#100 = IFCPROJECT('2O2Fr$t4X7Dn_hFJ7mkFuC',
$,'Progetto Residenziale Milano',$,$,$,$,$,
(#101,)); # IfcUnitAssignment
#200 = IFCWALL('0LV8Xs0bzEIeVBuDgFJqEY',
$,'Muro Perimetrale Est','Muro in laterizio',$,
#500, # IfcLocalPlacement
#600, # IfcProductDefinitionShape
$,
.SOLIDWALL.);
#300 = IFCWALLTYPE('1Kl0KQPzj5E9XhKaSyTd5Q',
$,'MuroEsterno300mm',$,$,$,$,$,$,
.SOLIDWALL.);
# Proprietà del muro
#400 = IFCPROPERTYSET('3ZYW59saX3kxpqB_7Pnv3D',
$,'Pset_WallCommon',$,
(#401,#402,#403,#404));
#401 = IFCPROPERTYSINGLEVALUE('Reference',$,
IFCIDENTIFIER('MW-300'),$);
#402 = IFCPROPERTYSINGLEVALUE('IsExternal',$,
IFCBOOLEAN(.T.),$);
#403 = IFCPROPERTYSINGLEVALUE('LoadBearing',$,
IFCBOOLEAN(.T.),$);
#404 = IFCPROPERTYSINGLEVALUE('FireRating',$,
IFCLABEL('REI120'),$);
Python'da ifcopenshell ile IFC ayrıştırıcısı
Açık kaynak kütüphanesi ifcopenshell standart referanstır IFC dosyalarını okumak, düzenlemek ve oluşturmak için. IFC 2x3, IFC 4 ve IFC 4.3'ü destekler.
import ifcopenshell
import ifcopenshell.geom
import numpy as np
from dataclasses import dataclass, field
from typing import List, Dict, Optional
@dataclass
class BIMElement:
"""Rappresentazione unificata di un elemento BIM."""
global_id: str
ifc_type: str
name: str
properties: Dict[str, any] = field(default_factory=dict)
geometry: Optional[dict] = None
level: Optional[str] = None
classification: Optional[str] = None
class IFCParser:
"""
Parser IFC per estrarre elementi, proprietà e geometrie.
Supporta IFC 2x3, 4 e 4.3.
"""
# Tipologie di elementi architettonici da estrarre
SPATIAL_ELEMENTS = [
'IfcBuilding', 'IfcBuildingStorey', 'IfcSpace'
]
ARCHITECTURAL_ELEMENTS = [
'IfcWall', 'IfcSlab', 'IfcColumn', 'IfcBeam',
'IfcDoor', 'IfcWindow', 'IfcRoof', 'IfcStair',
'IfcRailing', 'IfcCurtainWall'
]
MEP_ELEMENTS = [
'IfcDuctSegment', 'IfcPipeSegment',
'IfcCableCarrierSegment', 'IfcFlowTerminal'
]
def __init__(self, ifc_path: str):
self.model = ifcopenshell.open(ifc_path)
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
self.settings.set(self.settings.WELD_VERTICES, True)
def extract_all_elements(self) -> List[BIMElement]:
"""Estrae tutti gli elementi con proprietà e geometria."""
elements = []
all_types = (
self.ARCHITECTURAL_ELEMENTS +
self.MEP_ELEMENTS +
self.SPATIAL_ELEMENTS
)
for ifc_type in all_types:
for entity in self.model.by_type(ifc_type):
element = self._parse_entity(entity)
if element:
elements.append(element)
return elements
def _parse_entity(self, entity) -> Optional[BIMElement]:
"""Converte un'entità IFC in BIMElement."""
try:
# Proprietà di base
props = self._get_properties(entity)
# Geometria 3D
geometry = None
try:
shape = ifcopenshell.geom.create_shape(self.settings, entity)
geometry = self._extract_geometry(shape)
except Exception:
pass # Alcuni elementi non hanno geometria
# Piano di appartenenza
level = self._get_storey(entity)
return BIMElement(
global_id=entity.GlobalId,
ifc_type=entity.is_a(),
name=entity.Name or '',
properties=props,
geometry=geometry,
level=level,
classification=self._get_classification(entity),
)
except Exception as e:
return None
def _get_properties(self, entity) -> dict:
"""Estrae tutti i PropertySet dell'entità."""
props = {}
for definition in entity.IsDefinedBy:
if definition.is_a('IfcRelDefinesByProperties'):
prop_set = definition.RelatingPropertyDefinition
if prop_set.is_a('IfcPropertySet'):
for prop in prop_set.HasProperties:
if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
props[prop.Name] = prop.NominalValue.wrappedValue
return props
def _extract_geometry(self, shape) -> dict:
"""Converte la geometria IFC in formato Three.js-compatibile."""
geometry = shape.geometry
# Vertici
verts = np.array(geometry.verts).reshape(-1, 3)
# Facce triangolari
faces = np.array(geometry.faces).reshape(-1, 3)
# Normali
normals = np.array(geometry.normals).reshape(-1, 3)
return {
'vertices': verts.tolist(),
'faces': faces.tolist(),
'normals': normals.tolist(),
'vertex_count': len(verts),
'face_count': len(faces),
}
def _get_storey(self, entity) -> Optional[str]:
"""Determina il piano di appartenenza dell'elemento."""
for rel in entity.ContainedInStructure:
container = rel.RelatingStructure
if container.is_a('IfcBuildingStorey'):
return container.Name
return None
def _get_classification(self, entity) -> Optional[str]:
"""Estrae la classificazione (OmniClass, Uniclass, ecc.)."""
for rel in entity.HasAssociations:
if rel.is_a('IfcRelAssociatesClassification'):
ref = rel.RelatingClassification
return f"{ref.ReferencedSource.Name}:{ref.Identification}"
return None
def get_spatial_hierarchy(self) -> dict:
"""
Costruisce la gerarchia spaziale: Building > Storey > Space > Elements
"""
hierarchy = {}
buildings = self.model.by_type('IfcBuilding')
for building in buildings:
b_data = {
'id': building.GlobalId,
'name': building.Name,
'storeys': {},
}
for rel in building.IsDecomposedBy:
for storey in rel.RelatedObjects:
if storey.is_a('IfcBuildingStorey'):
elevation = storey.Elevation or 0
b_data['storeys'][storey.GlobalId] = {
'name': storey.Name,
'elevation': elevation,
'elements': self._get_storey_elements(storey),
}
hierarchy[building.GlobalId] = b_data
return hierarchy
Three.js ile BIM Web Görüntüleyici
Bir web BIM görüntüleyicinin milyonlarca poligon içeren modelleri 60 fps'yi koruyarak işlemesi gerekir tarayıcıda. Temel teknikler arasında kesik kesik ayıklama, LOD (Ayrıntı Düzeyi), Tekrarlanan öğeler ve aşamalı yükleme için örnekli oluşturma.
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
// Abilita raycast accelerato per selezione elementi
THREE.Mesh.prototype.raycast = acceleratedRaycast;
class BIMViewer {
private scene: THREE.Scene;
private camera: THREE.PerspectiveCamera;
private renderer: THREE.WebGLRenderer;
private controls: OrbitControls;
private elementMap: Map<string, THREE.Mesh> = new Map();
private selectedElement: THREE.Mesh | null = null;
// Materiali
private defaultMaterial = new THREE.MeshLambertMaterial({
color: 0xcccccc,
transparent: true,
opacity: 0.85,
});
private selectedMaterial = new THREE.MeshLambertMaterial({
color: 0x007bff,
transparent: true,
opacity: 0.9,
});
constructor(container: HTMLElement) {
// Scene setup
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xf0f0f0);
// Camera
this.camera = new THREE.PerspectiveCamera(
45,
container.clientWidth / container.clientHeight,
0.1,
10000
);
this.camera.position.set(50, 30, 50);
// Renderer con antialiasing e shadow map
this.renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance',
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.appendChild(this.renderer.domElement);
// Luci
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(100, 100, 50);
dirLight.castShadow = true;
this.scene.add(ambientLight, dirLight);
// Controls
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.05;
// Event listeners
this.renderer.domElement.addEventListener('click', this.onElementClick.bind(this));
window.addEventListener('resize', this.onWindowResize.bind(this));
}
loadModel(elements: BIMElementGeometry[]) {
/**
* Carica il modello BIM ottimizzando per performance.
* Usa InstanedMesh per elementi ripetuti (porte, finestre).
*/
const elementsByType = this.groupByType(elements);
for (const [type, typeElements] of Object.entries(elementsByType)) {
if (typeElements.length > 10) {
// Instanced rendering per elementi ripetuti
this.addInstancedElements(type, typeElements);
} else {
// Mesh singole per elementi unici
typeElements.forEach(elem => this.addSingleElement(elem));
}
}
// Centra la camera sul modello
this.fitCameraToModel();
this.animate();
}
private addSingleElement(element: BIMElementGeometry) {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(element.vertices.flat(), 3)
);
geometry.setAttribute(
'normal',
new THREE.Float32BufferAttribute(element.normals.flat(), 3)
);
geometry.setIndex(element.faces.flat());
// Ottimizzazione: BVH per raycast veloce
geometry.computeBoundsTree = MeshBVH.bind(null, geometry);
geometry.computeBoundsTree();
const material = this.getMaterialForType(element.ifcType);
const mesh = new THREE.Mesh(geometry, material.clone());
mesh.userData = {
globalId: element.globalId,
ifcType: element.ifcType,
name: element.name,
properties: element.properties,
};
mesh.castShadow = true;
mesh.receiveShadow = true;
this.elementMap.set(element.globalId, mesh);
this.scene.add(mesh);
}
private onElementClick(event: MouseEvent) {
const rect = this.renderer.domElement.getBoundingClientRect();
const mouse = new THREE.Vector2(
((event.clientX - rect.left) / rect.width) * 2 - 1,
-((event.clientY - rect.top) / rect.height) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const meshes = Array.from(this.elementMap.values());
const intersects = raycaster.intersectObjects(meshes);
if (intersects.length > 0) {
const clicked = intersects[0].object as THREE.Mesh;
this.selectElement(clicked.userData['globalId']);
}
}
selectElement(globalId: string) {
// Ripristina il materiale dell'elemento precedente
if (this.selectedElement) {
const type = this.selectedElement.userData['ifcType'];
(this.selectedElement.material as THREE.MeshLambertMaterial)
.copy(this.getMaterialForType(type));
}
const mesh = this.elementMap.get(globalId);
if (mesh) {
(mesh.material as THREE.MeshLambertMaterial).copy(this.selectedMaterial);
this.selectedElement = mesh;
// Emit evento con proprietà dell'elemento
this.onElementSelected(mesh.userData);
}
}
private animate() {
requestAnimationFrame(this.animate.bind(this));
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
}
Çakışma Algılama: 3D Çakışma Algılama
Çakışma tespiti, BIM öğeleri arasındaki engellemeleri otomatik olarak tanımlar farklı disiplinler (örneğin yapısal bir kirişle kesişen bir tüp). Bu bir tasarım aşamasındaki en kritik BIM kullanım durumlarından biridir.
import ifcopenshell
import ifcopenshell.geom
import numpy as np
from scipy.spatial import KDTree
from typing import List, Tuple
from dataclasses import dataclass
@dataclass
class Clash:
element1_id: str
element1_type: str
element2_id: str
element2_type: str
clash_type: str # 'hard' | 'soft' | 'clearance'
distance: float
location: Tuple[float, float, float]
severity: str # 'critical' | 'major' | 'minor'
class ClashDetector:
"""
Rilevamento automatico di conflitti geometrici tra elementi BIM.
Supporta Hard Clash, Soft Clash e Clearance Clash.
"""
DISCIPLINE_PAIRS = [
('IfcBeam', 'IfcDuctSegment'),
('IfcBeam', 'IfcPipeSegment'),
('IfcColumn', 'IfcDuctSegment'),
('IfcSlab', 'IfcPipeSegment'),
('IfcWall', 'IfcDuctSegment'),
]
# Tolleranza minima per soft clash (mm convertiti in m)
CLEARANCE_TOLERANCE = {
'IfcDuctSegment': 0.10, # 100mm di spazio attorno ai condotti
'IfcPipeSegment': 0.05, # 50mm attorno ai tubi
'default': 0.025, # 25mm default
}
def __init__(self, ifc_model):
self.model = ifc_model
self.settings = ifcopenshell.geom.settings()
self.settings.set(self.settings.USE_WORLD_COORDS, True)
def detect_clashes(self, disciplines: List[str] = None) -> List[Clash]:
"""
Rileva tutti i clash tra le discipline specificate.
"""
clashes = []
pairs = disciplines or self.DISCIPLINE_PAIRS
for type_a, type_b in pairs:
elements_a = self._get_element_aabbs(type_a)
elements_b = self._get_element_aabbs(type_b)
if not elements_a or not elements_b:
continue
# Accelera la ricerca con KDTree
centers_b = np.array([e['center'] for e in elements_b])
tree = KDTree(centers_b)
for elem_a in elements_a:
# Cerca elementi vicini (raggio approssimativo)
radius = np.linalg.norm(elem_a['extents']) + max(
np.linalg.norm(e['extents']) for e in elements_b
)
nearby_indices = tree.query_ball_point(elem_a['center'], radius)
for idx in nearby_indices:
elem_b = elements_b[idx]
clash = self._check_aabb_clash(elem_a, elem_b, type_b)
if clash:
clashes.append(clash)
return clashes
def _get_element_aabbs(self, ifc_type: str) -> List[dict]:
"""Calcola AABB (Axis-Aligned Bounding Box) per ogni elemento."""
aabbs = []
for entity in self.model.by_type(ifc_type):
try:
shape = ifcopenshell.geom.create_shape(self.settings, entity)
verts = np.array(shape.geometry.verts).reshape(-1, 3)
min_pt = verts.min(axis=0)
max_pt = verts.max(axis=0)
center = (min_pt + max_pt) / 2
extents = (max_pt - min_pt) / 2
aabbs.append({
'id': entity.GlobalId,
'type': ifc_type,
'name': entity.Name or '',
'min': min_pt,
'max': max_pt,
'center': center,
'extents': extents,
})
except Exception:
pass
return aabbs
def _check_aabb_clash(self, a: dict, b: dict, type_b: str) -> Optional[Clash]:
"""Controlla se due AABB si intersecano (hard clash) o sono troppo vicine."""
clearance = self.CLEARANCE_TOLERANCE.get(type_b, self.CLEARANCE_TOLERANCE['default'])
# Hard clash: le bounding box si sovrappongono
overlap = all(
a['min'][i] <= b['max'][i] and a['max'][i] >= b['min'][i]
for i in range(3)
)
if overlap:
return Clash(
element1_id=a['id'],
element1_type=a['type'],
element2_id=b['id'],
element2_type=b['type'],
clash_type='hard',
distance=0.0,
location=tuple((a['center'] + b['center']) / 2),
severity='critical',
)
# Clearance clash: troppo vicini
distance = max(
max(a['min'][i] - b['max'][i], b['min'][i] - a['max'][i], 0)
for i in range(3)
)
if distance < clearance:
return Clash(
element1_id=a['id'],
element1_type=a['type'],
element2_id=b['id'],
element2_type=b['type'],
clash_type='clearance',
distance=round(distance * 1000, 1), # mm
location=tuple((a['center'] + b['center']) / 2),
severity='major' if distance < clearance / 2 else 'minor',
)
return None
Bulut BIM Mimarisi: Çok Kullanıcılı İşbirliği
Bulut BIM sisteminin temel zorluğu gerçek zamanlı işbirliği yüzlerce MB ağırlığa sahip modellerde. Autodesk BIM 360 gibi kurumsal çözümler, Trimble Connect ve Bentley iTwin farklı yaklaşımlar benimsiyor ancak hepsi ortak kalıpları paylaşıyor: model federasyonu, artımlı değişiklik setleri e çatışma çözümü.
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime
import hashlib
import json
@dataclass
class BIMChangeSet:
"""Rappresenta un set di modifiche atomiche al modello BIM."""
id: str
model_id: str
author_id: str
timestamp: datetime
operations: List[dict] # Lista di operazioni CRUD sugli elementi
parent_revision: str
description: str
discipline: str # 'architectural' | 'structural' | 'mep'
class BIMCollaborationService:
"""
Gestione della collaborazione multi-disciplina su modelli BIM.
Implementa Operational Transformation per merge dei changeset.
"""
def __init__(self, storage, event_bus):
self.storage = storage
self.event_bus = event_bus
async def apply_changeset(self, changeset: BIMChangeSet) -> dict:
"""
Applica un changeset al modello con conflict detection.
"""
# Carica revisione parent
parent = await self.storage.get_revision(changeset.parent_revision)
# Verifica conflitti con changeset paralleli
concurrent_changes = await self.storage.get_concurrent_changesets(
model_id=changeset.model_id,
since=parent.timestamp,
exclude_id=changeset.id,
)
conflicts = self._detect_conflicts(changeset, concurrent_changes)
if conflicts and not self._auto_resolvable(conflicts):
return {
'status': 'conflict',
'conflicts': conflicts,
'message': 'Risoluzione manuale richiesta',
}
# Applica le operazioni
new_revision_id = await self.storage.apply_operations(
model_id=changeset.model_id,
operations=changeset.operations,
parent_revision=changeset.parent_revision,
)
# Notifica tutti i collaboratori
await self.event_bus.publish('model.updated', {
'model_id': changeset.model_id,
'revision_id': new_revision_id,
'author': changeset.author_id,
'discipline': changeset.discipline,
'change_count': len(changeset.operations),
})
return {
'status': 'success',
'revision_id': new_revision_id,
'applied_operations': len(changeset.operations),
}
def _detect_conflicts(self, changeset: BIMChangeSet,
concurrent: List[BIMChangeSet]) -> List[dict]:
"""Identifica elementi modificati concorrentemente."""
changed_ids = {op['element_id'] for op in changeset.operations}
conflicts = []
for concurrent_cs in concurrent:
concurrent_ids = {op['element_id'] for op in concurrent_cs.operations}
overlap = changed_ids.intersection(concurrent_ids)
for element_id in overlap:
conflicts.append({
'element_id': element_id,
'conflict_with': concurrent_cs.id,
'author': concurrent_cs.author_id,
'discipline': concurrent_cs.discipline,
})
return conflicts
def _auto_resolvable(self, conflicts: List[dict]) -> bool:
"""
Determina se i conflitti possono essere risolti automaticamente.
Regola: modifiche a discipline diverse sullo stesso elemento
possono essere mergate se riguardano attributi diversi.
"""
# Implementazione semplificata: conflitti tra discipline diverse
# sono auto-resolvibili se gli attributi modificati non si sovrappongono
disciplines_involved = {c['discipline'] for c in conflicts}
return len(disciplines_involved) > 1 # Discipline diverse = auto-merge
Dijital İkiz için BIM-IoT entegrasyonu
Statik BIM'e geçişin bir sonraki adımı Dijital İkiz dinamik: Binaya kurulu sensörlerden gelen gerçek zamanlı IoT verileriyle zenginleştirilmiş bir BIM modeli (sıcaklık, doluluk, hava kalitesi, enerji tüketimi).
BIM Dijital İkizinin Bileşenleri
- BIM Modeli Katmanı: Statik özelliklere sahip 3D IFC geometrisi
- IoT Sensör Katmanı: GlobalId aracılığıyla IFC öğelerine eşlenen fiziksel sensörler
- Zaman Serisi Katmanı: Geçmiş sensör verileri için InfluxDB veya TimescaleDB
- Analitik Katmanı: Tüketim tahmini, anormallik tespiti, doluluk tahmini için makine öğrenimi
- Görselleştirme Katmanı: Gerçek zamanlı IoT veri paylaşımına sahip 3D BIM görüntüleyici
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
from datetime import datetime
import asyncio
class BIMDigitalTwin:
"""
Integrazione BIM + IoT per Digital Twin degli edifici.
Mappa sensori fisici agli elementi IFC e persiste i dati in InfluxDB.
"""
def __init__(self, ifc_model, influx_client: InfluxDBClient,
sensor_mapping: dict):
"""
sensor_mapping: { 'sensor_id': 'ifc_global_id' }
Mappa ogni sensore fisico all'elemento IFC corrispondente.
"""
self.model = ifc_model
self.influx = influx_client
self.write_api = influx_client.write_api(write_options=SYNCHRONOUS)
self.query_api = influx_client.query_api()
self.sensor_mapping = sensor_mapping
async def ingest_sensor_reading(self, sensor_id: str,
measurement_type: str,
value: float,
timestamp: datetime):
"""
Persiste una lettura del sensore associandola all'elemento IFC.
"""
ifc_element_id = self.sensor_mapping.get(sensor_id)
if not ifc_element_id:
return
# Recupera metadati elemento IFC
element = self.model.by_guid(ifc_element_id)
storey = self._get_storey(element)
point = (
Point(measurement_type)
.tag("sensor_id", sensor_id)
.tag("ifc_element_id", ifc_element_id)
.tag("ifc_type", element.is_a())
.tag("storey", storey or "unknown")
.field("value", value)
.time(timestamp)
)
self.write_api.write(bucket="bim_sensors", record=point)
async def get_element_history(self, ifc_element_id: str,
measurement: str,
hours: int = 24) -> list:
"""Recupera la storia delle letture per un elemento IFC."""
query = f'''
from(bucket: "bim_sensors")
|> range(start: -{hours}h)
|> filter(fn: (r) => r["ifc_element_id"] == "{ifc_element_id}")
|> filter(fn: (r) => r["_measurement"] == "{measurement}")
|> aggregateWindow(every: 5m, fn: mean)
'''
tables = self.query_api.query(query)
readings = []
for table in tables:
for record in table.records:
readings.append({
'timestamp': record.get_time().isoformat(),
'value': record.get_value(),
})
return readings
async def detect_anomalies(self, storey: str,
measurement: str) -> list:
"""
Rileva anomalie nelle letture dei sensori usando z-score.
"""
query = f'''
from(bucket: "bim_sensors")
|> range(start: -7d)
|> filter(fn: (r) => r["storey"] == "{storey}")
|> filter(fn: (r) => r["_measurement"] == "{measurement}")
'''
# Implementa anomaly detection basica su z-score...
return []
BIM Bulutunun Teknik Zorlukları
- Büyük dosyalar: Karmaşık BIM modelleri 500 MB'ı aşabilir. HTTP Aralığı istekleri ve öğelerin aşamalı olarak yüklenmesiyle akışı kullanın.
- Disiplin çatışmaları: Mimari, yapı ve MEP sıklıkla aynı mekanları değiştirir. Mağaza düzeyinde kilitleme ve anlık bildirimleri uygulayın.
- Sürüm oluşturma: BIM modelleri kaynak kodu gibi metinsel farklılıklara uygun değildir. Değişiklik kümesi ve nesne düzeyinde sürüm oluşturma yaklaşımını benimser.
- Birlikte çalışabilirlik: Tüm yazılımlar IFC'yi doğru şekilde dışa aktarmaz. İçe aktarmadan önce doğrulama ve normalleştirme için ifcopenshell'i kullanın.
Sonuçlar ve Sonraki Adımlar
Modern BIM mimarisi, 3D ayrıştırmayı entegre eden karmaşık bir dağıtılmış sistemdir. gerçek zamanlı işbirliği, bulut oluşturma ve IoT. IFC 4.3 standardı (ISO 16739-1:2024) ifcopenshell ve Three.js gibi kütüphaneler, araçlar arasında birlikte çalışabilirliği garanti eder özel görüntüleyiciler ve işlem hatları oluşturmanıza olanak tanır. Dijital İkiz’e geçiş, IoT ve ML entegrasyonuyla BIM'in gerçek binalara doğru doğal evrimidir akıllı.
PropTech serisindeki diğer makaleleri keşfedin
- Madde 03 - Akıllı Bina IoT: Sensör Entegrasyonu ve Uç Bilgi İşlem
- Madde 08 - Sanal Emlak Turları: WebGL ve 3D Web Tech
- Madde 07 - PostGIS ile Jeo-uzaysal Arama ve Konum Hizmetleri







