/* global React */
/* ===========================================================
VOLTLAB · QUADRO DE MOTORES
Lista de motores de uma instalação: corrente nominal,
fator de partida, dimensionamento de cabo e corrente total.
Tabelas extraídas da planilha CÁLCULOS.xlsx:
· D - MOTORES → fator de partida (tipo × método)
· D - CABOS → ampacidade (seção × isolação × B1/B2)
Integra-se ao barramento de projeto (Puxar / Enviar).
=========================================================== */
const { useState: useStMot, useMemo: useMemoMot } = React;
const useMotPersist = (key, initial) =>
window.useLocalStorage ? window.useLocalStorage(key, initial) : useStMot(initial);
/* ── D - MOTORES · fator de partida (tipo de carga × método) ── */
const MOT_FATOR_PARTIDA = {
Normal: { DIN: 1.00, SOFT: 1.00, INVERSOR: 1.00, AUTOTRAFO: 10.0 },
"Bomba C/Carga": { DIN: 1.50, SOFT: 1.25, INVERSOR: 1.00, AUTOTRAFO: 3.0 },
Esteira: { DIN: 1.20, SOFT: 1.00, INVERSOR: 1.00, AUTOTRAFO: 4.0 },
};
const MOT_TIPOS = ["Normal", "Bomba C/Carga", "Esteira"];
const MOT_METODOS = [
{ v: "DIN", label: "Direta (DIN)" },
{ v: "SOFT", label: "Soft-Starter" },
{ v: "INVERSOR", label: "Inversor" },
{ v: "AUTOTRAFO", label: "Autotrafo" },
];
/* ── D - CABOS · ampacidade (mm² × isolação × instalação B1/B2) ── */
const MOT_CABOS = [
{ sec: 1.0, iso: "PVC", B1: 12, B2: 12 },
{ sec: 1.5, iso: "PVC", B1: 17, B2: 15 },
{ sec: 2.5, iso: "PVC", B1: 21, B2: 20 },
{ sec: 4.0, iso: "PVC", B1: 28, B2: 27 },
{ sec: 6.0, iso: "PVC", B1: 36, B2: 34 },
{ sec: 10, iso: "PVC", B1: 50, B2: 46 },
{ sec: 10, iso: "XLPE", B1: 66, B2: 61 },
{ sec: 16, iso: "PVC", B1: 68, B2: 62 },
{ sec: 16, iso: "XLPE", B1: 88, B2: 82 },
{ sec: 25, iso: "XLPE", B1: 117, B2: 105 },
{ sec: 35, iso: "XLPE", B1: 144, B2: 128 },
{ sec: 50, iso: "XLPE", B1: 175, B2: 154 },
{ sec: 70, iso: "XLPE", B1: 222, B2: 194 },
{ sec: 95, iso: "XLPE", B1: 269, B2: 233 },
{ sec: 120, iso: "XLPE", B1: 312, B2: 268 },
{ sec: 150, iso: "XLPE", B1: 358, B2: 307 },
{ sec: 185, iso: "XLPE", B1: 409, B2: 350 },
{ sec: 240, iso: "XLPE", B1: 483, B2: 413 },
{ sec: 300, iso: "XLPE", B1: 556, B2: 474 },
];
/* corrente nominal — fator A/CV por faixa (calibrado da planilha) */
function correnteNominal(cv, V, fase) {
const P = (parseFloat(cv) || 0) * 735.49875; // CV → W
if (P <= 0 || !V) return 0;
// rendimento × fator de potência típico por faixa (W22 IR3)
let cosfi_rend;
if (cv <= 3) cosfi_rend = 0.473;
else if (cv <= 7.5) cosfi_rend = 0.52;
else if (cv <= 15) cosfi_rend = 0.55;
else if (cv <= 50) cosfi_rend = 0.567;
else cosfi_rend = 0.567;
// divisor conforme o nº de fases
const div = fase === "monofasico" ? V
: fase === "bifasico" ? (2 * V)
: (Math.sqrt(3) * V); // trifásico (padrão)
return P / (div * cosfi_rend);
}
const MOT_FASES = [
{ v: "trifasico", label: "Trifásico", abbr: "3F" },
{ v: "bifasico", label: "Bifásico", abbr: "2F" },
{ v: "monofasico", label: "Monofásico", abbr: "1F" },
];
const faseAbbr = (f) => MOT_FASES.find(x => x.v === f)?.abbr || "3F";
/* dimensiona cabo a partir da corrente de projeto */
function dimensionarCabo(corrente, metodoInst, isolacaoMin) {
// filtra por isolação aceita: se XLPE permitido, usa qualquer; se só PVC, restringe
const candidatos = MOT_CABOS.filter(c => isolacaoMin === "PVC" ? true : true);
for (const c of candidatos) {
const amp = metodoInst === "B1" ? c.B1 : c.B2;
if (amp >= corrente) return c;
}
return MOT_CABOS[MOT_CABOS.length - 1];
}
/* ── ícones ── */
const MtIc = ({ n, s = 16 }) => {
const p = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" };
const d = {
motor: <>>,
bolt: ,
cable: <>>,
plus: <>>,
trash: <>>,
dl: <>>,
reset: <>>,
check: ,
info: <>>,
sigma: ,
gauge: <>>,
};
return ;
};
/* ── dropdown padrão (.vl-dd) ── */
const MtDropdown = ({ value, onChange, options }) => {
const [open, setOpen] = useStMot(false);
const ref = React.useRef(null);
React.useEffect(() => {
if (!open) return;
const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener("mousedown", h);
return () => document.removeEventListener("mousedown", h);
}, [open]);
const cur = options.find(o => o.value === value) || options[0];
return (
{open && (
{options.map(o => (
{ onChange(o.value); setOpen(false); }}>{o.label}
))}
)}
);
};
const motFmt = (n, d = 1) => isFinite(n) ? n.toLocaleString("pt-BR", { minimumFractionDigits: d, maximumFractionDigits: d }) : "—";
/* motores de exemplo (da planilha) */
const MOT_SAMPLE = [
{ id: "m1", nome: "Bomba 01", cv: 100, V: 380, fase: "trifasico", tipo: "Bomba C/Carga", metodo: "SOFT", inst: "B2", iso: "XLPE" },
{ id: "m2", nome: "Bomba 02", cv: 100, V: 380, fase: "trifasico", tipo: "Bomba C/Carga", metodo: "SOFT", inst: "B2", iso: "XLPE" },
{ id: "m3", nome: "Compressor", cv: 30, V: 380, fase: "trifasico", tipo: "Bomba C/Carga", metodo: "SOFT", inst: "B2", iso: "XLPE" },
{ id: "m4", nome: "Ventilador", cv: 3, V: 380, fase: "trifasico", tipo: "Bomba C/Carga", metodo: "DIN", inst: "B2", iso: "PVC" },
{ id: "m5", nome: "Dosadora", cv: 1, V: 220, fase: "monofasico", tipo: "Bomba C/Carga", metodo: "DIN", inst: "B2", iso: "PVC" },
];
const QuadroMotores = () => {
const [motores, setMotores] = useMotPersist("vl_quadro_motores_v1", MOT_SAMPLE);
const [sel, setSel] = useStMot(MOT_SAMPLE[0].id);
const rows = useMemoMot(() => motores.map(m => {
const In = correnteNominal(m.cv, m.V, m.fase || "trifasico");
const fp = MOT_FATOR_PARTIDA[m.tipo]?.[m.metodo] ?? 1;
const Ipart = In * fp;
const cabo = dimensionarCabo(In, m.inst, m.iso);
const amp = m.inst === "B1" ? cabo.B1 : cabo.B2;
return { ...m, In, fp, Ipart, cabo, amp };
}), [motores]);
const totals = useMemoMot(() => {
const In = rows.reduce((s, r) => s + r.In, 0);
const pot = rows.reduce((s, r) => s + (parseFloat(r.cv) || 0), 0);
const potKW = pot * 0.73549875;
const maiorPartida = rows.reduce((mx, r) => r.Ipart > mx.Ipart ? r : mx, { Ipart: 0 });
// corrente de pico (maior partida + nominal dos demais) — dimensionamento do barramento
const pico = maiorPartida.Ipart > 0 ? (In - maiorPartida.In) + maiorPartida.Ipart : In;
return { In, pot, potKW, maiorPartida, pico };
}, [rows]);
const selM = rows.find(r => r.id === sel) || rows[0];
const add = () => {
const m = { id: "m" + Date.now(), nome: "Motor " + (motores.length + 1), cv: 5, V: 380, fase: "trifasico", tipo: "Normal", metodo: "DIN", inst: "B2", iso: "PVC" };
setMotores([...motores, m]); setSel(m.id);
};
const upd = (id, patch) => setMotores(motores.map(m => m.id === id ? { ...m, ...patch } : m));
const del = (id) => { setMotores(motores.filter(m => m.id !== id)); if (sel === id) setSel(null); };
const reset = () => { setMotores(MOT_SAMPLE); setSel(MOT_SAMPLE[0].id); if (window.vlToast) window.vlToast("✓ Restaurado"); };
const exportCSV = () => {
const head = ["NOME","POT(CV)","TENSAO(V)","FASE","TIPO","METODO","In(A)","FATOR PARTIDA","Ipart(A)","INSTAL","CABO(mm2)","ISOLACAO","AMPACIDADE(A)"];
const lines = [head.join(";")];
rows.forEach(r => lines.push([r.nome, r.cv, r.V, faseAbbr(r.fase), r.tipo, r.metodo, r.In.toFixed(1), r.fp, r.Ipart.toFixed(1), r.inst, r.cabo.sec, r.cabo.iso, r.amp].join(";")));
lines.push("");
lines.push(["TOTAL In (A)", totals.In.toFixed(1)].join(";"));
lines.push(["Corrente de pico (A)", totals.pico.toFixed(1)].join(";"));
lines.push(["Potencia total (kW)", totals.potKW.toFixed(1)].join(";"));
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob(["\uFEFF" + lines.join("\n")], { type: "text/csv;charset=utf-8" }));
a.download = "quadro-motores.csv"; a.click();
if (window.vlToast) window.vlToast("✓ CSV exportado");
};
const Sync = window.VL_ProjetoSync;
return (
{/* ───────── LISTA DE MOTORES ───────── */}
ENTRADA · LISTA DE MOTORES
Quadro de cargas motrizes
AUTO
{Sync && { const t = s.get("tensao"); const sis = s.get("sistema"); setMotores(motores.map(m => ({ ...m, ...(t!=null?{V:parseFloat(t)}:{}), ...(sis?{fase:sis}:{}) }))); }}
push={() => ({ tensao: parseFloat(selM?.V) || undefined, sistema: selM?.fase, corrente: +totals.In.toFixed(2), potencia: +totals.potKW.toFixed(3) })}
/>}
MOTORCVPARTIDAIn (A)CABO
{rows.map(r => (
setSel(r.id)}>
{r.nome}
{faseAbbr(r.fase)} · {r.tipo} · {r.V} V
{r.cv}
{r.metodo}
{motFmt(r.In)}
{r.cabo.sec}
{r.cabo.iso} · {r.inst}
))}
{/* totalizadores */}
Motores{String(rows.length).padStart(2, "0")}
Corrente total In{motFmt(totals.In)} A
Potência instalada{motFmt(totals.potKW)} kW
{/* ───────── DETALHE DO MOTOR SELECIONADO ───────── */}
{/* ───────── DIMENSIONAMENTO DO BARRAMENTO / TOTAL ───────── */}
DIMENSIONAMENTO GLOBAL · QUADRO
Corrente de projeto do alimentador geral
pico = Σ In + (fator partida − 1)·maior motor
CORRENTE NOMINAL Σ
{motFmt(totals.In)}A
CORRENTE DE PICO
{motFmt(totals.pico)}A
MAIOR PARTIDA
{totals.maiorPartida?.nome ? motFmt(totals.maiorPartida.Ipart) : "—"}A
POTÊNCIA TOTAL
{motFmt(totals.potKW)}kW
Disjuntor geral sugerido ≥ {Math.ceil(totals.pico / 5) * 5} A
{totals.maiorPartida?.nome ? ` · maior partida: ${totals.maiorPartida.nome} (${totals.maiorPartida.metodo})` : ""}
{/* ───────── TABELAS DE REFERÊNCIA ───────── */}
REFERÊNCIA · FATOR DE PARTIDA
Tipo de carga × método
D-MOTORES
TIPODINSOFTINVERSORAUTOTRAFO
{MOT_TIPOS.map(t => (
{t}
{["DIN","SOFT","INVERSOR","AUTOTRAFO"].map(mt => (
{MOT_FATOR_PARTIDA[t][mt].toFixed(2)}
))}
))}
REFERÊNCIA · AMPACIDADE
Seção × isolação × instalação
D-CABOS · A
SEÇÃO (mm²)ISOLAÇÃOB1 (A)B2 (A)
{MOT_CABOS.map((c, i) => {
const isSel = selM && selM.cabo.sec === c.sec && selM.cabo.iso === c.iso;
return (
{c.sec}
{c.iso}
{c.B1}
{c.B2}
);
})}
);
};
window.VL_QuadroMotores = QuadroMotores;