/** * Utility to export the current project state to a CSV file. * Synchronized with the StreamDataTable and ActionBar. */ import type { ProjectState } from '../types/project'; import { extractStreamInfo } from './streamInfo'; export function exportProjectToCsv(state: ProjectState) { const rows: string[][] = []; const header = [ 'Process', 'Subprocess', 'Stream Name', 'Type', 'Tin', 'Tout', 'ṁ', 'cp', 'CP', 'Q (kW)', 'Lat', 'Lon', 'Hours', 'Water In', 'Water Out', 'Density', 'Pressure', 'Notes', ]; rows.push(header); (state.proc_groups || []).forEach((subIdxs, gIdx) => { const gName = state.proc_group_names?.[gIdx] || `Process ${gIdx + 1}`; const gCoord = state.proc_group_coordinates?.[gIdx] || {}; subIdxs.forEach((si) => { const sub = state.processes[si]; if (!sub) return; const notes = sub.extra_info?.notes || ''; const lat = String(sub.lat || gCoord.lat || ''); const lon = String(sub.lon || gCoord.lon || ''); const hours = sub.hours || gCoord.hours || ''; const water_in = sub.extra_info?.water_content_in || ''; const water_out = sub.extra_info?.water_content_out || ''; const density = sub.extra_info?.density || ''; const pressure = sub.extra_info?.pressure || ''; // Add streams for this subprocess if (!sub.streams?.length && !sub.children?.length) { rows.push([gName, sub.name, '', '', '', '', '', '', '', '', lat, lon, hours, water_in, water_out, density, pressure, notes]); } else { (sub.streams || []).forEach((s) => { const si = extractStreamInfo(s as any); rows.push([ gName, sub.name, s.name, si.type || '', String(si.tin ?? ''), String(si.tout ?? ''), String(si.mdot ?? ''), String(si.cp ?? ''), String(si.CP ?? ''), si.Q ? si.Q.toFixed(1) : '', lat, lon, hours, String(si.water_in || water_in), String(si.water_out || water_out), String(si.density || density), String(si.pressure || pressure), notes, ]); }); // Also add streams for sub-subprocesses (children) (sub.children || []).forEach((child) => { const c_notes = child.extra_info?.notes || notes || ''; const c_lat = String(child.lat || sub.lat || gCoord.lat || ''); const c_lon = String(child.lon || sub.lon || gCoord.lon || ''); const c_hours = child.hours || hours || ''; const c_water_in = child.extra_info?.water_content_in || water_in || ''; const c_water_out = child.extra_info?.water_content_out || water_out || ''; const c_density = child.extra_info?.density || density || ''; const c_pressure = child.extra_info?.pressure || pressure || ''; const c_name = `${sub.name} › ${child.name}`; if (!(child.streams || []).length) { rows.push([gName, c_name, '', '', '', '', '', '', '', '', c_lat, c_lon, c_hours, c_water_in, c_water_out, c_density, c_pressure, c_notes]); } else { (child.streams || []).forEach((s) => { const si = extractStreamInfo(s as any); rows.push([ gName, c_name, s.name, si.type || '', String(si.tin ?? ''), String(si.tout ?? ''), String(si.mdot ?? ''), String(si.cp ?? ''), String(si.CP ?? ''), si.Q ? si.Q.toFixed(1) : '', c_lat, c_lon, c_hours, String(si.water_in || c_water_in), String(si.water_out || c_water_out), String(si.density || c_density), String(si.pressure || c_pressure), c_notes, ]); }); } }); } }); }); const csv = rows.map((r) => r.map((c) => `"${c}"`).join(',')).join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `heattransplan_export_${new Date().toISOString().slice(0, 10)}.csv`; a.click(); URL.revokeObjectURL(url); }