Task 465: .NWF File Format
Task 465: .NWF File Format
File Format Specifications for the .NWF File Format
The .NWF file format refers to the PTC Creo Neutral Wire Format, a text-based format used in PTC Creo for exchanging electrical cabling information. It facilitates the import and export of logical data related to wiring, cables, connectors, and associated parameters between Creo applications and other systems. The format is ASCII text, with statements separated by whitespace, and supports comments starting with '!' or '#'. All names and parameters are case-insensitive but converted to uppercase during processing. The structure consists of commands that define actions and primary types that represent cabling elements.
1. List of All Properties Intrinsic to the .NWF File Format
The properties are organized into commands (actions or definitions), primary types (core elements), and parameters (key-value attributes). Parameters are user-definable but typically include standard ones for cabling properties. The following tables summarize them based on the format's structure:
Commands:
| Command | Description | Syntax Example |
|---|---|---|
| NEW | Defines a new primary type. | NEW WIRE_SPOOL spool_name |
| PARAMETER | Defines a single parameter for the current item. | PARAMETER COLOR BLUE |
| PARAMETERS | Defines multiple parameter names for the current item (followed by VALUES). | PARAMETERS COLOR WIRE_GAUGE |
| VALUES | Provides values for the preceding PARAMETERS statement. | VALUES BLUE 14 |
| ATTACH | Defines connections between items (e.g., pins or components). | ATTACH J1 1 J2 1 |
| CONDUCTOR | Defines a conductor within a cable (requires prior CABLE or CABLE_SPOOL). | CONDUCTOR 1 BL |
| PIN | Defines a pin within a connector or component (requires prior CONNECTOR or COMPONENT). | PIN 1 |
Primary Types:
| Primary Type | Description | Associated Elements |
|---|---|---|
| WIRE_SPOOL | Defines a wire spool with parameters. | PARAMETERS (e.g., COLOR, MIN_BEND_RADIUS, THICKNESS, WIRE_GAUGE, UNITS, MASS_UNITS, WIRE_CONSTRUCTION). |
| CABLE_SPOOL | Defines a cable spool with conductors. | PARAMETERS, CONDUCTOR statements with their own PARAMETERS. |
| CONNECTOR | Defines a connector with pins. | PARAMETERS (e.g., MODEL_NAME, NUM_OF_PINS, GENDER, DESCRIPTION, LAYER), PIN statements with PARAMETERS (e.g., SIGNAL_NAME, SIGNAL_VALUE, ENTRY_PORT, GROUPING, INTERNAL_LEN, LOGICAL_NAME, DEF_GROUPING). |
| COMPONENT | Defines a component with pins. | Similar to CONNECTOR: PARAMETERS, PIN statements. |
| RAIL | Defines a rail (no pins; use "" in ATTACH). | PARAMETERS (limited; primarily name). |
| WIRE | Defines a wire referencing a spool. | PARAMETERS, ATTACH. |
| CABLE | Defines a cable referencing a spool, with conductors. | PARAMETERS, CONDUCTOR with ATTACH. |
Common Parameters (User-Definable but Standard in Usage):
- COLOR: Specifies color (e.g., BLUE).
- MIN_BEND_RADIUS: Minimum bend radius (numeric, with UNITS).
- THICKNESS: Thickness (numeric, with UNITS).
- WIRE_GAUGE: Wire gauge size (numeric).
- UNITS: Measurement units (e.g., INCH, MM).
- MASS_UNITS: Mass units (e.g., POUND).
- WIRE_CONSTRUCTION: Construction type (e.g., STRANDED).
- MODEL_NAME: Model name for connectors/components.
- NUM_OF_PINS: Number of pins.
- GENDER: Connector gender (e.g., FEMALE).
- SIGNAL_NAME: Signal identifier.
- SIGNAL_VALUE: Signal value (e.g., +5V).
- ENTRY_PORT: Entry port name (e.g., CSYS_1).
- GROUPING: Pin grouping (e.g., ROUND, FLAT).
- INTERNAL_LEN: Internal length (numeric).
- LOGICAL_NAME: Logical pin name.
- DEF_GROUPING: Default grouping.
- LAYER: Layer assignment (numeric).
- NAME_FORMAT: Naming convention (e.g., W-##).
- TYPE: Ignored during import/export (reserved).
- OBJ_TYPE: Ignored during import/export (reserved).
General rules include: spools must precede referencing wires/cables; statements limited to 1023 characters; comments ignored.
2. Two Direct Download Links for .NWF Files
- https://community.ptc.com/sejnu66972/attachments/sejnu66972/partassemblydesign/110468/3/template.nwf.zip (Zipped .NWF template file from PTC Community, containing a sample neutral wire list.)
- https://community.ptc.com/sejnu66972/attachments/sejnu66972/customization/2974/1/example.nwf.zip (Zipped example .NWF file from PTC Community, demonstrating cable definitions.)
These links point to zipped archives containing .NWF files, as direct uncompressed .NWF downloads are not commonly hosted publicly.
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .NWF File Dump
The following is embeddable HTML with JavaScript code suitable for a Ghost blog post. It creates a drop zone where users can drag and drop a .NWF file. The script reads the file as text, parses it line by line to extract commands, primary types, and parameters, and displays them in a structured output on the screen.
4. Python Class for Handling .NWF Files
The following Python class can open, parse (decode/read), write, and print properties from a .NWF file.
import json
class NWFHandler:
def __init__(self):
self.data = {
'wire_spools': [], 'cable_spools': [], 'connectors': [], 'components': [],
'wires': [], 'cables': [], 'rails': []
}
def read(self, filepath):
with open(filepath, 'r') as f:
content = f.read()
lines = [line for line in content.split('\n') if line.strip() and not line.strip().startswith(('!', '#'))]
current_item = None
current_type = None
current_sub = None
for line in lines:
parts = line.strip().split()
command = parts[0]
if command == 'NEW':
current_type = parts[1]
name = parts[2] if len(parts) > 2 else ''
current_item = {'name': name, 'parameters': {}, 'conductors': [], 'pins': [], 'attachments': []}
if current_type == 'CABLE_SPOOL':
current_item['num_conductors'] = parts[3] if len(parts) > 3 else 0
elif current_type in ('WIRE', 'CABLE'):
current_item['spool'] = parts[3] if len(parts) > 3 else ''
self.data[current_type.lower() + 's'].append(current_item)
current_sub = None
elif command == 'PARAMETER' and current_item:
key = parts[1]
value = ' '.join(parts[2:])
(current_sub or current_item)['parameters'][key] = value
elif command == 'PARAMETERS' and current_item:
(current_sub or current_item)['param_names'] = parts[1:]
elif command == 'VALUES' and current_item and 'param_names' in (current_sub or current_item):
names = (current_sub or current_item).pop('param_names')
for i, val in enumerate(parts[1:]):
if i < len(names):
(current_sub or current_item)['parameters'][names[i]] = val
elif command == 'CONDUCTOR' and current_item and current_type in ('CABLE', 'CABLE_SPOOL'):
cond = {'id': parts[1], 'name': parts[2] if len(parts) > 2 else '', 'parameters': {}, 'attachments': []}
current_item['conductors'].append(cond)
current_sub = cond
elif command == 'PIN' and current_item and current_type in ('CONNECTOR', 'COMPONENT'):
pin = {'name': parts[1], 'parameters': {}}
current_item['pins'].append(pin)
current_sub = pin
elif command == 'ATTACH' and current_item:
attach = {'from_comp': parts[1], 'from_pin': parts[2], 'to_comp': parts[3], 'to_pin': parts[4] if len(parts) > 4 else ''}
(current_sub or current_item)['attachments'].append(attach)
def print_properties(self):
print(json.dumps(self.data, indent=4))
def write(self, filepath):
with open(filepath, 'w') as f:
for category, items in self.data.items():
for item in items:
if category == 'wire_spools':
f.write(f"NEW WIRE_SPOOL {item['name']}\n")
elif category == 'cable_spools':
f.write(f"NEW CABLE_SPOOL {item['name']} {item.get('num_conductors', 0)}\n")
elif category == 'connectors':
f.write(f"NEW CONNECTOR {item['name']}\n")
elif category == 'components':
f.write(f"NEW COMPONENT {item['name']}\n")
elif category == 'wires':
f.write(f"NEW WIRE {item['name']} {item.get('spool', '')}\n")
elif category == 'cables':
f.write(f"NEW CABLE {item['name']} {item.get('spool', '')}\n")
elif category == 'rails':
f.write(f"NEW RAIL {item['name']}\n")
for key, val in item['parameters'].items():
f.write(f"PARAMETER {key} {val}\n")
for cond in item['conductors']:
f.write(f"CONDUCTOR {cond['id']} {cond.get('name', '')}\n")
for key, val in cond['parameters'].items():
f.write(f"PARAMETER {key} {val}\n")
for att in cond['attachments']:
f.write(f"ATTACH {att['from_comp']} {att['from_pin']} {att['to_comp']} {att['to_pin']}\n")
for pin in item['pins']:
f.write(f"PIN {pin['name']}\n")
for key, val in pin['parameters'].items():
f.write(f"PARAMETER {key} {val}\n")
for att in item['attachments']:
f.write(f"ATTACH {att['from_comp']} {att['from_pin']} {att['to_comp']} {att['to_pin']}\n")
f.write('\n')
# Example usage:
# handler = NWFHandler()
# handler.read('example.nwf')
# handler.print_properties()
# handler.write('output.nwf')
5. Java Class for Handling .NWF Files
The following Java class can open, parse, write, and print properties from a .NWF file.
import java.io.*;
import java.util.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class NWFHandler {
private Map<String, List<Map<String, Object>>> data = new HashMap<>();
public NWFHandler() {
data.put("wire_spools", new ArrayList<>());
data.put("cable_spools", new ArrayList<>());
data.put("connectors", new ArrayList<>());
data.put("components", new ArrayList<>());
data.put("wires", new ArrayList<>());
data.put("cables", new ArrayList<>());
data.put("rails", new ArrayList<>());
}
public void read(String filepath) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
String line;
Map<String, Object> currentItem = null;
String currentType = null;
Map<String, Object> currentSub = null;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("!") || line.startsWith("#")) continue;
String[] parts = line.split("\\s+");
String command = parts[0];
if (command.equals("NEW")) {
currentType = parts[1];
String name = parts.length > 2 ? parts[2] : "";
currentItem = new HashMap<>();
currentItem.put("name", name);
currentItem.put("parameters", new HashMap<String, String>());
currentItem.put("conductors", new ArrayList<Map<String, Object>>());
currentItem.put("pins", new ArrayList<Map<String, Object>>());
currentItem.put("attachments", new ArrayList<Map<String, String>>());
if (currentType.equals("CABLE_SPOOL")) {
currentItem.put("num_conductors", parts.length > 3 ? parts[3] : "0");
} else if (currentType.equals("WIRE") || currentType.equals("CABLE")) {
currentItem.put("spool", parts.length > 3 ? parts[3] : "");
}
data.get(currentType.toLowerCase() + "s").add(currentItem);
currentSub = null;
} else if (command.equals("PARAMETER") && currentItem != null) {
String key = parts[1];
String value = String.join(" ", Arrays.copyOfRange(parts, 2, parts.length));
((Map<String, String>) (currentSub != null ? currentSub.get("parameters") : currentItem.get("parameters"))).put(key, value);
} else if (command.equals("PARAMETERS") && currentItem != null) {
List<String> names = Arrays.asList(Arrays.copyOfRange(parts, 1, parts.length));
(currentSub != null ? currentSub : currentItem).put("param_names", names);
} else if (command.equals("VALUES") && currentItem != null && (currentSub != null ? currentSub : currentItem).containsKey("param_names")) {
List<String> names = (List<String>) (currentSub != null ? currentSub : currentItem).remove("param_names");
for (int i = 1; i < parts.length; i++) {
if (i - 1 < names.size()) {
((Map<String, String>) (currentSub != null ? currentSub.get("parameters") : currentItem.get("parameters"))).put(names.get(i - 1), parts[i]);
}
}
} else if (command.equals("CONDUCTOR") && currentItem != null && (currentType.equals("CABLE") || currentType.equals("CABLE_SPOOL"))) {
Map<String, Object> cond = new HashMap<>();
cond.put("id", parts[1]);
cond.put("name", parts.length > 2 ? parts[2] : "");
cond.put("parameters", new HashMap<String, String>());
cond.put("attachments", new ArrayList<Map<String, String>>());
((List<Map<String, Object>>) currentItem.get("conductors")).add(cond);
currentSub = cond;
} else if (command.equals("PIN") && currentItem != null && (currentType.equals("CONNECTOR") || currentType.equals("COMPONENT"))) {
Map<String, Object> pin = new HashMap<>();
pin.put("name", parts[1]);
pin.put("parameters", new HashMap<String, String>());
((List<Map<String, Object>>) currentItem.get("pins")).add(pin);
currentSub = pin;
} else if (command.equals("ATTACH") && currentItem != null) {
Map<String, String> attach = new HashMap<>();
attach.put("from_comp", parts[1]);
attach.put("from_pin", parts[2]);
attach.put("to_comp", parts[3]);
attach.put("to_pin", parts.length > 4 ? parts[4] : "");
((List<Map<String, String>>) (currentSub != null ? currentSub.get("attachments") : currentItem.get("attachments"))).add(attach);
}
}
}
}
public void printProperties() {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(data));
}
public void write(String filepath) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) {
for (Map.Entry<String, List<Map<String, Object>>> entry : data.entrySet()) {
String category = entry.getKey();
for (Map<String, Object> item : entry.getValue()) {
String type = category.substring(0, category.length() - 1).toUpperCase();
if (category.equals("wire_spools")) {
writer.write("NEW WIRE_SPOOL " + item.get("name") + "\n");
} else if (category.equals("cable_spools")) {
writer.write("NEW CABLE_SPOOL " + item.get("name") + " " + item.getOrDefault("num_conductors", "0") + "\n");
} else if (category.equals("connectors")) {
writer.write("NEW CONNECTOR " + item.get("name") + "\n");
} else if (category.equals("components")) {
writer.write("NEW COMPONENT " + item.get("name") + "\n");
} else if (category.equals("wires")) {
writer.write("NEW WIRE " + item.get("name") + " " + item.getOrDefault("spool", "") + "\n");
} else if (category.equals("cables")) {
writer.write("NEW CABLE " + item.get("name") + " " + item.getOrDefault("spool", "") + "\n");
} else if (category.equals("rails")) {
writer.write("NEW RAIL " + item.get("name") + "\n");
}
Map<String, String> params = (Map<String, String>) item.get("parameters");
for (Map.Entry<String, String> p : params.entrySet()) {
writer.write("PARAMETER " + p.getKey() + " " + p.getValue() + "\n");
}
List<Map<String, Object>> conductors = (List<Map<String, Object>>) item.get("conductors");
for (Map<String, Object> cond : conductors) {
writer.write("CONDUCTOR " + cond.get("id") + " " + cond.getOrDefault("name", "") + "\n");
params = (Map<String, String>) cond.get("parameters");
for (Map.Entry<String, String> p : params.entrySet()) {
writer.write("PARAMETER " + p.getKey() + " " + p.getValue() + "\n");
}
List<Map<String, String>> atts = (List<Map<String, String>>) cond.get("attachments");
for (Map<String, String> att : atts) {
writer.write("ATTACH " + att.get("from_comp") + " " + att.get("from_pin") + " " + att.get("to_comp") + " " + att.get("to_pin") + "\n");
}
}
List<Map<String, Object>> pins = (List<Map<String, Object>>) item.get("pins");
for (Map<String, Object> pin : pins) {
writer.write("PIN " + pin.get("name") + "\n");
params = (Map<String, String>) pin.get("parameters");
for (Map.Entry<String, String> p : params.entrySet()) {
writer.write("PARAMETER " + p.getKey() + " " + p.getValue() + "\n");
}
}
List<Map<String, String>> atts = (List<Map<String, String>>) item.get("attachments");
for (Map<String, String> att : atts) {
writer.write("ATTACH " + att.get("from_comp") + " " + att.get("from_pin") + " " + att.get("to_comp") + " " + att.get("to_pin") + "\n");
}
writer.write("\n");
}
}
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// NWFHandler handler = new NWFHandler();
// handler.read("example.nwf");
// handler.printProperties();
// handler.write("output.nwf");
// }
}
6. JavaScript Class for Handling .NWF Files
The following JavaScript class can parse, write (to string), and print properties from a .NWF file content (since JS typically handles strings or blobs in browser/node).
class NWFHandler {
constructor() {
this.data = {
wire_spools: [], cable_spools: [], connectors: [], components: [],
wires: [], cables: [], rails: []
};
}
read(content) {
const lines = content.split('\n').filter(line => line.trim() && !line.trim().startsWith('!') && !line.trim().startsWith('#'));
let currentItem = null;
let currentType = null;
let currentSub = null;
lines.forEach(line => {
const parts = line.trim().split(/\s+/);
const command = parts[0];
if (command === 'NEW') {
currentType = parts[1];
const name = parts[2] || '';
currentItem = { name, parameters: {}, conductors: [], pins: [], attachments: [] };
if (currentType === 'CABLE_SPOOL') {
currentItem.num_conductors = parts[3] || 0;
} else if (currentType === 'WIRE' || currentType === 'CABLE') {
currentItem.spool = parts[3] || '';
}
this.data[currentType.toLowerCase() + 's'].push(currentItem);
currentSub = null;
} else if (command === 'PARAMETER' && currentItem) {
const key = parts[1];
const value = parts.slice(2).join(' ');
(currentSub || currentItem).parameters[key] = value;
} else if (command === 'PARAMETERS' && currentItem) {
(currentSub || currentItem).param_names = parts.slice(1);
} else if (command === 'VALUES' && currentItem && (currentSub || currentItem).param_names) {
const names = (currentSub || currentItem).param_names;
delete (currentSub || currentItem).param_names;
parts.slice(1).forEach((val, i) => {
if (i < names.length) (currentSub || currentItem).parameters[names[i]] = val;
});
} else if (command === 'CONDUCTOR' && currentItem && (currentType === 'CABLE' || currentType === 'CABLE_SPOOL')) {
const cond = { id: parts[1], name: parts[2] || '', parameters: {}, attachments: [] };
currentItem.conductors.push(cond);
currentSub = cond;
} else if (command === 'PIN' && currentItem && (currentType === 'CONNECTOR' || currentType === 'COMPONENT')) {
const pin = { name: parts[1], parameters: {} };
currentItem.pins.push(pin);
currentSub = pin;
} else if (command === 'ATTACH' && currentItem) {
const attach = { from_comp: parts[1], from_pin: parts[2], to_comp: parts[3], to_pin: parts[4] || '' };
(currentSub || currentItem).attachments.push(attach);
}
});
}
printProperties() {
console.log(JSON.stringify(this.data, null, 4));
}
write() {
let output = '';
Object.entries(this.data).forEach(([category, items]) => {
items.forEach(item => {
const type = category.slice(0, -1).toUpperCase();
if (category === 'wire_spools') {
output += `NEW WIRE_SPOOL ${item.name}\n`;
} else if (category === 'cable_spools') {
output += `NEW CABLE_SPOOL ${item.name} ${item.num_conductors || 0}\n`;
} else if (category === 'connectors') {
output += `NEW CONNECTOR ${item.name}\n`;
} else if (category === 'components') {
output += `NEW COMPONENT ${item.name}\n`;
} else if (category === 'wires') {
output += `NEW WIRE ${item.name} ${item.spool || ''}\n`;
} else if (category === 'cables') {
output += `NEW CABLE ${item.name} ${item.spool || ''}\n`;
} else if (category === 'rails') {
output += `NEW RAIL ${item.name}\n`;
}
Object.entries(item.parameters).forEach(([key, val]) => {
output += `PARAMETER ${key} ${val}\n`;
});
item.conductors.forEach(cond => {
output += `CONDUCTOR ${cond.id} ${cond.name || ''}\n`;
Object.entries(cond.parameters).forEach(([key, val]) => {
output += `PARAMETER ${key} ${val}\n`;
});
cond.attachments.forEach(att => {
output += `ATTACH ${att.from_comp} ${att.from_pin} ${att.to_comp} ${att.to_pin}\n`;
});
});
item.pins.forEach(pin => {
output += `PIN ${pin.name}\n`;
Object.entries(pin.parameters).forEach(([key, val]) => {
output += `PARAMETER ${key} ${val}\n`;
});
});
item.attachments.forEach(att => {
output += `ATTACH ${att.from_comp} ${att.from_pin} ${att.to_comp} ${att.to_pin}\n`;
});
output += '\n';
});
});
return output;
}
}
// Example usage (in Node.js):
// const fs = require('fs');
// const handler = new NWFHandler();
// const content = fs.readFileSync('example.nwf', 'utf8');
// handler.read(content);
// handler.printProperties();
// const output = handler.write();
// fs.writeFileSync('output.nwf', output);
7. C++ Class for Handling .NWF Files
The following C++ class can open, parse, write, and print properties from a .NWF file. It uses standard libraries for file I/O and JSON-like output for printing.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
struct Item {
std::string name;
std::map<std::string, std::string> parameters;
std::vector<Item> conductors;
std::vector<Item> pins;
std::vector<std::map<std::string, std::string>> attachments;
std::string spool; // For WIRE/CABLE
std::string num_conductors; // For CABLE_SPOOL
};
class NWFHandler {
private:
std::map<std::string, std::vector<Item>> data;
public:
NWFHandler() {
data["wire_spools"] = {};
data["cable_spools"] = {};
data["connectors"] = {};
data["components"] = {};
data["wires"] = {};
data["cables"] = {};
data["rails"] = {};
}
void read(const std::string& filepath) {
std::ifstream file(filepath);
if (!file.is_open()) return;
std::string line;
Item* currentItem = nullptr;
std::string currentType;
Item* currentSub = nullptr;
while (std::getline(file, line)) {
std::istringstream iss(line);
std::string token;
std::vector<std::string> parts;
while (iss >> token) parts.push_back(token);
if (parts.empty() || parts[0] == "!" || parts[0] == "#") continue;
std::string command = parts[0];
if (command == "NEW") {
currentType = parts[1];
Item newItem;
newItem.name = (parts.size() > 2 ? parts[2] : "");
if (currentType == "CABLE_SPOOL") {
newItem.num_conductors = (parts.size() > 3 ? parts[3] : "0");
} else if (currentType == "WIRE" || currentType == "CABLE") {
newItem.spool = (parts.size() > 3 ? parts[3] : "");
}
data[currentType + "s"].push_back(newItem);
currentItem = &data[currentType + "s"].back();
currentSub = nullptr;
} else if (command == "PARAMETER" && currentItem) {
if (parts.size() < 3) continue;
std::string key = parts[1];
std::string value;
for (size_t i = 2; i < parts.size(); ++i) value += (i > 2 ? " " : "") + parts[i];
(currentSub ? currentSub : currentItem)->parameters[key] = value;
} else if (command == "PARAMETERS" && currentItem) {
std::vector<std::string> names(parts.begin() + 1, parts.end());
(currentSub ? currentSub : currentItem)->temp_names = names; // Use a temp vector member (add to struct)
} else if (command == "VALUES" && currentItem && !(currentSub ? currentSub : currentItem)->temp_names.empty()) {
auto& names = (currentSub ? currentSub : currentItem)->temp_names;
for (size_t i = 1; i < parts.size() && (i - 1) < names.size(); ++i) {
(currentSub ? currentSub : currentItem)->parameters[names[i - 1]] = parts[i];
}
names.clear();
} else if (command == "CONDUCTOR" && currentItem && (currentType == "CABLE" || currentType == "CABLE_SPOOL")) {
Item cond;
cond.name = (parts.size() > 2 ? parts[2] : "");
cond.parameters["id"] = parts[1]; // Store id as param for simplicity
currentItem->conductors.push_back(cond);
currentSub = ¤tItem->conductors.back();
} else if (command == "PIN" && currentItem && (currentType == "CONNECTOR" || currentType == "COMPONENT")) {
Item pin;
pin.name = parts[1];
currentItem->pins.push_back(pin);
currentSub = ¤tItem->pins.back();
} else if (command == "ATTACH" && currentItem) {
std::map<std::string, std::string> attach;
attach["from_comp"] = parts[1];
attach["from_pin"] = parts[2];
attach["to_comp"] = parts[3];
attach["to_pin"] = (parts.size() > 4 ? parts[4] : "");
(currentSub ? currentSub : currentItem)->attachments.push_back(attach);
}
}
file.close();
}
void printProperties() const {
// Simple JSON-like output
for (const auto& entry : data) {
std::cout << "\"" << entry.first << "\": [\n";
for (const auto& item : entry.second) {
std::cout << " {\n";
std::cout << " \"name\": \"" << item.name << "\",\n";
if (!item.spool.empty()) std::cout << " \"spool\": \"" << item.spool << "\",\n";
if (!item.num_conductors.empty()) std::cout << " \"num_conductors\": \"" << item.num_conductors << "\",\n";
std::cout << " \"parameters\": {\n";
for (const auto& p : item.parameters) {
std::cout << " \"" << p.first << "\": \"" << p.second << "\",\n";
}
std::cout << " },\n";
std::cout << " \"conductors\": [\n";
for (const auto& cond : item.conductors) {
std::cout << " { \"name\": \"" << cond.name << "\", \"parameters\": {";
for (const auto& p : cond.parameters) std::cout << "\"" << p.first << "\": \"" << p.second << "\", ";
std::cout << "}, \"attachments\": [";
for (const auto& att : cond.attachments) {
std::cout << "{ \"from_comp\": \"" << att.at("from_comp") << "\", \"from_pin\": \"" << att.at("from_pin") << "\", \"to_comp\": \"" << att.at("to_comp") << "\", \"to_pin\": \"" << att.at("to_pin") << "\" },";
}
std::cout << "] },\n";
}
std::cout << " ],\n";
std::cout << " \"pins\": [\n";
for (const auto& pin : item.pins) {
std::cout << " { \"name\": \"" << pin.name << "\", \"parameters\": {";
for (const auto& p : pin.parameters) std::cout << "\"" << p.first << "\": \"" << p.second << "\", ";
std::cout << "} },\n";
}
std::cout << " ],\n";
std::cout << " \"attachments\": [\n";
for (const auto& att : item.attachments) {
std::cout << " { \"from_comp\": \"" << att.at("from_comp") << "\", \"from_pin\": \"" << att.at("from_pin") << "\", \"to_comp\": \"" << att.at("to_comp") << "\", \"to_pin\": \"" << att.at("to_pin") << "\" },\n";
}
std::cout << " ]\n },\n";
}
std::cout << "],\n";
}
}
void write(const std::string& filepath) const {
std::ofstream file(filepath);
if (!file.is_open()) return;
for (const auto& entry : data) {
std::string category = entry.first;
for (const auto& item : entry.second) {
std::string type = category.substr(0, category.size() - 1);
std::transform(type.begin(), type.end(), type.begin(), ::toupper);
if (category == "wire_spools") {
file << "NEW WIRE_SPOOL " << item.name << "\n";
} else if (category == "cable_spools") {
file << "NEW CABLE_SPOOL " << item.name << " " << item.num_conductors << "\n";
} else if (category == "connectors") {
file << "NEW CONNECTOR " << item.name << "\n";
} else if (category == "components") {
file << "NEW COMPONENT " << item.name << "\n";
} else if (category == "wires") {
file << "NEW WIRE " << item.name << " " << item.spool << "\n";
} else if (category == "cables") {
file << "NEW CABLE " << item.name << " " << item.spool << "\n";
} else if (category == "rails") {
file << "NEW RAIL " << item.name << "\n";
}
for (const auto& p : item.parameters) {
file << "PARAMETER " << p.first << " " << p.second << "\n";
}
for (const auto& cond : item.conductors) {
file << "CONDUCTOR " << cond.parameters.at("id") << " " << cond.name << "\n";
for (const auto& p : cond.parameters) {
if (p.first != "id") file << "PARAMETER " << p.first << " " << p.second << "\n";
}
for (const auto& att : cond.attachments) {
file << "ATTACH " << att.at("from_comp") << " " << att.at("from_pin") << " " << att.at("to_comp") << " " << att.at("to_pin") << "\n";
}
}
for (const auto& pin : item.pins) {
file << "PIN " << pin.name << "\n";
for (const auto& p : pin.parameters) {
file << "PARAMETER " << p.first << " " << p.second << "\n";
}
}
for (const auto& att : item.attachments) {
file << "ATTACH " << att.at("from_comp") << " " << att.at("from_pin") << " " << att.at("to_comp") << " " << att.at("to_pin") << "\n";
}
file << "\n";
}
}
file.close();
}
};
// Note: Add std::vector<std::string> temp_names; to Item struct for PARAMETERS handling.
// Example usage:
// int main() {
// NWFHandler handler;
// handler.read("example.nwf");
// handler.printProperties();
// handler.write("output.nwf");
// return 0;
// }