@typecad/schematic
Version:
Generate KiCAD schematics from your typeCAD project
114 lines (95 loc) • 4.54 kB
text/typescript
import { Schematic, Component, Node } from './types';
import { KiCAD } from "./kicad";
import fs from "node:fs";
import SExpr from "s-expression.js";
import Handlebars from "handlebars";
import { randomUUID } from "node:crypto";
import { SymbolLibraryManager } from "./symbol-library-manager";
import { KiCADSchematic, schematic_template as main_schematic_template, GRID_UNIT_MM, PAGE_WIDTH_MM, PAGE_HEIGHT_MM, label_template as main_label_template } from "./kicad-schematic";
const S = new SExpr();
function generateSymLibTable(sheetname: string): void {
try {
// Check if build/lib directory exists and get all .kicad_sym files
const libDir = './build/lib';
if (!fs.existsSync(libDir)) {
console.log('📚 No lib directory found, skipping sym-lib-table generation');
return;
}
const files = fs.readdirSync(libDir);
const kicadSymFiles = files.filter(file => file.endsWith('.kicad_sym'));
if (kicadSymFiles.length === 0) {
console.log('📚 No .kicad_sym files found, skipping sym-lib-table generation');
return;
}
// Generate sym-lib-table content
let symLibTableContent = '(sym_lib_table\n\t(version 7)\n';
for (const symFile of kicadSymFiles) {
const symbolName = symFile.replace('.kicad_sym', '');
symLibTableContent += `\t(lib\n`;
symLibTableContent += `\t\t(name "${symbolName}")\n`;
symLibTableContent += `\t\t(type "KiCad")\n`;
symLibTableContent += `\t\t(uri "\${KIPRJMOD}/lib/${symFile}")\n`;
symLibTableContent += `\t\t(options "")\n`;
symLibTableContent += `\t\t(descr "")\n`;
symLibTableContent += `\t)\n`;
}
symLibTableContent += ')';
// Write sym-lib-table file in the same directory as the schematic
const symLibTablePath = `./build/sym-lib-table`;
fs.writeFileSync(symLibTablePath, symLibTableContent);
// console.log(`📚 Symbol library table written to ${symLibTablePath} (${kicadSymFiles.length} libraries)`);
} catch (err) {
console.error("Error generating sym-lib-table:", err);
}
}
export async function schematic(schematicData: Schematic) {
// Initialize the Symbol Library Manager ONCE
new KiCAD(); // Call constructor first to set kicad_path
const symbolManager = new SymbolLibraryManager(); // Now symbolManager can read the path
const kicad = new KiCADSchematic(symbolManager); // Pass the manager instance
kicad.sheetname = schematicData.Sheetname; // Set sheetname
// Deduplicate components by reference to avoid processing the same component twice
const processedReferences = new Set<string>();
const uniqueComponents = schematicData.Components.filter(component => {
if (!component.reference || processedReferences.has(component.reference)) {
return false;
}
processedReferences.add(component.reference);
return true;
});
// Update components and collect library symbols implicitly
uniqueComponents.forEach(component => {
if (component.symbol && !component.dnp) { // Skip DNP components early
const symbolInstanceData = kicad.update(component, S); // Pass SExpr instance S
if (symbolInstanceData) { // Only push if update was successful
kicad.symbols.push(symbolInstanceData);
}
}
});
// Generate labels for nets sequentially
for (const node of schematicData.Nodes) {
await kicad.net(node); // Wait for each net calculation to complete
}
// Process no_connect pins
kicad.processNoConnectPins(schematicData.Components);
let template = Handlebars.compile(main_schematic_template, { noEscape: true });
let output_schematic_data = {
lib_symbols: kicad.lib_symbols,
symbols: kicad.symbols,
labels: kicad.labels,
no_connects: kicad.no_connects,
uuid: kicad.uuid
};
let _schematic = template(output_schematic_data);
try {
fs.writeFileSync(`./build/${schematicData.Sheetname}.kicad_sch`, _schematic);
console.log(`🔣 Schematic written to ./build/${schematicData.Sheetname}.kicad_sch`);
// Generate sym-lib-table file
generateSymLibTable(schematicData.Sheetname);
} catch (err) {
console.error("Error writing schematic:", err);
process.exit(1);
return false;
}
return true;
}