| """ |
| Assistente CAD/BIM - Geracao de Scripts AutoCAD e Dynamo/Revit |
| Para posicionamento de sistemas de prevencao de incendio - NT-01/2025 |
| """ |
| import math |
| import json |
| from datetime import datetime |
| from typing import Dict, List, Tuple |
|
|
|
|
| class CADAssistant: |
| """ |
| Gera scripts .scr (AutoCAD) e Python (Dynamo/Revit API) |
| para posicionamento automatico de sistemas de incendio. |
| """ |
|
|
| BLOCOS = { |
| "extintor": "EXTINTOR_PSCIP", |
| "hidrante": "HIDRANTE_PSCIP", |
| "saida_emerg": "SAIDA_EMERG_PSCIP", |
| "detector": "DETECTOR_FUMACA_PSCIP", |
| "acionador": "ACIONADOR_MANUAL_PSCIP", |
| "ilum_emerg": "BLOCO_AUTONOMO_PSCIP", |
| } |
| LAYERS = { |
| "extintores": "INCENDIO-EXTINTORES", |
| "hidrantes": "INCENDIO-HIDRANTES", |
| "sinalizacao": "INCENDIO-SINALIZACAO", |
| "deteccao": "INCENDIO-DETECCAO", |
| "iluminacao": "INCENDIO-ILUMINACAO", |
| } |
| CORES = {"extintores":"1","hidrantes":"5","sinalizacao":"3","deteccao":"2","iluminacao":"6"} |
|
|
| def calcular_posicoes_extintores(self, larg, comp, qtd): |
| posicoes, dist_max = [], 14.0 |
| if qtd <= 0: return posicoes |
| cols = max(1, math.ceil(larg / dist_max)) |
| rows = max(1, math.ceil(comp / dist_max)) |
| while cols * rows < qtd: |
| if larg / (cols+1) >= comp / (rows+1): cols += 1 |
| else: rows += 1 |
| px, py = larg / cols, comp / rows |
| for row in range(rows): |
| for col in range(cols): |
| posicoes.append((round(px*(col+0.5),2), round(py*(row+0.5),2))) |
| if len(posicoes) >= qtd: break |
| if len(posicoes) >= qtd: break |
| return posicoes[:qtd] |
|
|
| def calcular_posicoes_saidas(self, larg, comp, num_saidas): |
| pos = [] |
| if num_saidas >= 1: pos.append((larg/2, 0.0, "SUL")) |
| if num_saidas >= 2: pos.append((larg/2, comp, "NORTE")) |
| if num_saidas >= 3: pos.append((0.0, comp/2, "OESTE")) |
| if num_saidas >= 4: pos.append((larg, comp/2, "LESTE")) |
| return pos[:num_saidas] |
|
|
| def calcular_posicoes_detectores(self, larg, comp): |
| posicoes, passo = [], 8.0 |
| cols, rows = max(1, math.ceil(larg/passo)), max(1, math.ceil(comp/passo)) |
| px, py = larg/cols, comp/rows |
| return [(round(px*(c+0.5),2), round(py*(r+0.5),2)) |
| for r in range(rows) for c in range(cols)] |
|
|
| def gerar_script_autocad(self, dados): |
| larg = dados.get("largura_m", 30.0) |
| comp = dados.get("comprimento_m", 30.0) |
| qtd_ext = dados.get("qtd_extintores", 4) |
| num_saidas = dados.get("num_saidas_emergencia", 2) |
| tem_det = dados.get("tem_deteccao", False) |
| escala = dados.get("escala", 100) |
| f = escala |
|
|
| linhas = [ |
| "; ===================================================", |
| "; Script AutoCAD - Agente CBMGO - NT-01/2025", |
| f"; Gerado em: {datetime.now().strftime('%d/%m/%Y %H:%M')}", |
| "; ===================================================", "", |
| "; Criando layers PSCIP...", |
| ] |
| for nome, layer in self.LAYERS.items(): |
| linhas.append(f"-LAYER N {layer} C {self.CORES[nome]} {layer} L CONTINUOUS {layer} ") |
|
|
| linhas += ["", f"; --- EXTINTORES ({qtd_ext} un.) - Distancia max. 15m ---", |
| f"-LAYER SET {self.LAYERS['extintores']} "] |
| for i, (x, y) in enumerate(self.calcular_posicoes_extintores(larg, comp, qtd_ext), 1): |
| xc, yc = round(x*f), round(y*f) |
| linhas.append(f"INSERT {self.BLOCOS['extintor']} {xc},{yc} 1 1 0 ") |
|
|
| linhas += ["", f"; --- SAIDAS DE EMERGENCIA ({num_saidas} un.) ---", |
| f"-LAYER SET {self.LAYERS['sinalizacao']} "] |
| for i, (x, y, d) in enumerate(self.calcular_posicoes_saidas(larg, comp, num_saidas), 1): |
| xc, yc = round(x*f), round(y*f) |
| ang = {"SUL":90,"NORTE":270,"LESTE":180,"OESTE":0}.get(d, 0) |
| linhas.append(f"INSERT {self.BLOCOS['saida_emerg']} {xc},{yc} 1 1 {ang} ") |
|
|
| if tem_det: |
| linhas += ["", "; --- DETECTORES DE FUMACA ---", |
| f"-LAYER SET {self.LAYERS['deteccao']} "] |
| for x, y in self.calcular_posicoes_detectores(larg, comp): |
| linhas.append(f"INSERT {self.BLOCOS['detector']} {round(x*f)},{round(y*f)} 1 1 0 ") |
|
|
| linhas += ["", "QSAVE ", "", "; FIM DO SCRIPT"] |
| return "\n".join(linhas) |
|
|
| def gerar_relatorio_posicionamento(self, dados): |
| larg = dados.get("largura_m", 30.0) |
| comp = dados.get("comprimento_m", 30.0) |
| qtd_ext = dados.get("qtd_extintores", 4) |
| num_saidas = dados.get("num_saidas_emergencia", 2) |
|
|
| pos_ext = self.calcular_posicoes_extintores(larg, comp, qtd_ext) |
| pos_saidas = self.calcular_posicoes_saidas(larg, comp, num_saidas) |
|
|
| linhas = [ |
| "=" * 50, "RELATORIO DE POSICIONAMENTO - SISTEMAS PSCIP", |
| f"Data: {datetime.now().strftime('%d/%m/%Y %H:%M')}", |
| f"Planta: {larg:.1f}m x {comp:.1f}m = {larg*comp:.0f}m2", |
| "=" * 50, f"\nEXTINTORES ({qtd_ext} unidades):", |
| ] |
| for i, (x, y) in enumerate(pos_ext, 1): |
| linhas.append(f" EXT-{i:02d}: X={x:.2f}m, Y={y:.2f}m") |
|
|
| linhas.append(f"\nSAIDAS DE EMERGENCIA ({num_saidas} unidades):") |
| for i, (x, y, d) in enumerate(pos_saidas, 1): |
| linhas.append(f" SAIDA-{i:02d}: X={x:.2f}m, Y={y:.2f}m (parede {d})") |
|
|
| linhas += ["", "Notas:", "- Posicoes calculadas pelo Agente CBMGO", |
| "- Distancia maxima ao extintor: 14m (NT-01/2025 Anexo B)", |
| "- Validar com projeto arquitetonico", "=" * 50] |
| return "\n".join(linhas) |
|
|
|
|
| _cad_instance = None |
|
|
| def get_cad_assistant(): |
| global _cad_instance |
| if _cad_instance is None: |
| _cad_instance = CADAssistant() |
| return _cad_instance |
|
|
|
|
| if __name__ == "__main__": |
| cad = CADAssistant() |
| dados = {"largura_m": 25.0, "comprimento_m": 40.0, |
| "qtd_extintores": 5, "num_saidas_emergencia": 3, |
| "tem_deteccao": True, "escala": 100} |
| print(cad.gerar_relatorio_posicionamento(dados)) |
|
|