This commit is contained in:
rusefillc 2021-01-02 00:27:52 -05:00
parent 057897461c
commit 85819042bc
24 changed files with 1512 additions and 0 deletions

29
.gitattributes vendored Normal file
View File

@ -0,0 +1,29 @@
#
# see https://help.github.com/articles/dealing-with-line-endings/
#
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to Unix line endings on checkout.
*.xml text eol=lf
*.java text eol=lf
*.bat text eol=lf
*.sh text eol=lf
*.iml text eol=lf
*.txt text eol=lf
*.ini text eol=lf
*.rules text eol=lf
# KiCad files
*.dsn text eol=lf
*.kicad_pcb text eol=lf
*.net text eol=lf
*.pro text eol=lf
*.sch text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

View File

@ -0,0 +1,105 @@
package com.rusefi.pcb;
import com.rusefi.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static com.rusefi.pcb.PcbMergeTool.log;
/**
* 1/19/14
* (c) Andrey Belomutskiy
*/
public class ChangesModel {
private static final ChangesModel instance = new ChangesModel();
private static final String REMOVE = "remove";
private static final String ADD = "add";
private static final String MOVE = "move_module";
private static final String OPTIMIZE = "optimize";
private static final String COPY = "copy";
private static final String MERGE_NET = "merge_net";
public final Set<String> DEL_REQUESTS = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
public final List<NameAndOffset> ADD_REQUESTS = new ArrayList<NameAndOffset>();
public final List<NameAndOffset> MOVE_REQUESTS = new ArrayList<NameAndOffset>();
public final List<TwoFileRequest> OPTIMIZE_REQUESTS = new ArrayList<TwoFileRequest>();
public final List<TwoFileRequest> COPY_REQUESTS = new ArrayList<TwoFileRequest>();
/**
* Old net name > New net name
*/
public final Map<String, String> NET_MERGE_REQUESTS = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
public static ChangesModel getInstance() {
return instance;
}
public static void readConfiguration(String fileName) throws IOException {
if (!new File(fileName).isFile()) {
log("No such file: " + fileName);
return;
}
log("Reading commands from " + fileName);
List<String> a = FileUtils.readFileToList(fileName);
getInstance().read(a);
}
public void read(List<String> lines) {
int lineIndex = 0;
for (String line : lines) {
lineIndex++;
line = line.trim();
if (line.isEmpty())
continue;
if (line.startsWith("#"))
continue; // this line is a comment
if (line.toLowerCase().startsWith(REMOVE)) {
DEL_REQUESTS.add(line.substring(REMOVE.length()).trim());
continue;
} else if (line.toLowerCase().startsWith(ADD)) {
addAddRequest(line.substring(ADD.length()).trim());
continue;
} else if (line.toLowerCase().startsWith(MOVE)) {
addMoveRequest(line.substring(MOVE.length()).trim());
continue;
} else if (line.toLowerCase().startsWith(OPTIMIZE)) {
OPTIMIZE_REQUESTS.add(TwoFileRequest.parseTwoFile(line.substring(OPTIMIZE.length()).trim(), lineIndex));
continue;
} else if (line.toLowerCase().startsWith(COPY)) {
COPY_REQUESTS.add(TwoFileRequest.parseTwoFile(line.substring(COPY.length()).trim(), lineIndex));
continue;
} else if (line.toLowerCase().startsWith(MERGE_NET)) {
TwoFileRequest req = TwoFileRequest.parseTwoFile(line.substring(MERGE_NET.length()).trim(), lineIndex);
NET_MERGE_REQUESTS.put(req.input, req.output);
log("Net " + req.input + " to be merged into " + req.output);
continue;
}
System.err.println("ChangesModel: Ignoring invalid line: " + line);
}
log("Got " + DEL_REQUESTS.size() + " remove request(s)");
log("Got " + ADD_REQUESTS.size() + " add request(s)");
log("Got " + OPTIMIZE_REQUESTS.size() + " optimize request(s)");
log("Got " + NET_MERGE_REQUESTS.size() + " merge net request(s)");
}
private void addMoveRequest(String request) {
MOVE_REQUESTS.add(NameAndOffset.parseNameAndOffset(request));
}
private void addAddRequest(String request) {
ADD_REQUESTS.add(NameAndOffset.parseNameAndOffset(request));
}
public void clear() {
DEL_REQUESTS.clear();
ADD_REQUESTS.clear();
OPTIMIZE_REQUESTS.clear();
MOVE_REQUESTS.clear();
}
}

View File

@ -0,0 +1,36 @@
package com.rusefi.pcb;
/**
* @author Andrey Belomutskiy
* 1/24/14
*/
public class NameAndOffset {
private final String name;
public final double x;
public final double y;
public NameAndOffset(String name, double x, double y) {
this.name = name;
this.x = x;
this.y = y;
}
public static NameAndOffset parseNameAndOffset(String request) {
String[] tokens = request.split(" ");
NameAndOffset result;
if (tokens.length == 1) {
result = new NameAndOffset(tokens[0], 0, 0);
} else if (tokens.length == 3) {
double x = Double.parseDouble(tokens[1]);
double y = Double.parseDouble(tokens[2]);
result = new NameAndOffset(tokens[0], x, y);
} else {
throw new IllegalArgumentException("Invalid: " + request);
}
return result;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,26 @@
package com.rusefi.pcb;
import com.rusefi.pcb.nodes.PcbNode;
import java.io.IOException;
/**
* @author Andrey Belomutskiy
* 1/24/14
*/
public class PcbCopyTool {
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.out.println("Two parameters expected: SOURCE DESTINATION");
return;
}
ChangesModel.readConfiguration("pcb_merge_changes.txt");
String input = args[0];
String output = args[1];
PcbNode.copy(input, output);
}
}

View File

@ -0,0 +1,256 @@
package com.rusefi.pcb;
import com.rusefi.pcb.nodes.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* (c) Andrey Belomutskiy
* 12/16/13.
*/
public class PcbMergeTool {
private static Networks networks = new Networks();
public static void main(String[] args) throws IOException {
if (args.length != 3) {
System.out.println("Three parameters expected: SOURCE_PCB_FILENAME DESTINATION_PCB_FILENAME CHANGES_LIST_FILENAME");
return;
}
String sourcePcb = args[0];
String destination = args[1];
String changes = args[2];
ChangesModel.readConfiguration(changes);
log("Running COPY commands");
for (TwoFileRequest or : ChangesModel.getInstance().COPY_REQUESTS)
PcbNode.copy(or.input, or.output);
log("Running OPTIMIZE commands");
for (TwoFileRequest or : ChangesModel.getInstance().OPTIMIZE_REQUESTS)
RemoveUnneededTraces.optimize(or.input, or.output);
PcbNode destNode = PcbNode.readFromFile(sourcePcb);
for (PcbNode net : destNode.iterate("net")) {
String netName = net.getChild(1); // todo: nicer method?
if (!Networks.isLocalNetwork(netName))
networks.registerNetworkIfPcbSpecific(netName);
}
log("Running ADD commands");
for (NameAndOffset addRequest : ChangesModel.getInstance().ADD_REQUESTS) {
PcbNode node = PcbMoveTool.readAndMove(addRequest.getName(), addRequest.x, addRequest.y);
mergePcb(destNode, node);
}
log("Running MOVE commands");
for (NameAndOffset moveRequest : ChangesModel.getInstance().MOVE_REQUESTS) {
String moduleName = moveRequest.getName();
ModuleNode module = findModuleByName(destNode, moduleName);
if (module == null) {
log("Module not found: " + moduleName);
continue;
}
PointNode at = module.at;
at.setLocation(at.x + moveRequest.x, at.y + moveRequest.y);
}
removeNodes(destNode);
destNode.write(destination);
RemoveUnneededTraces.optimize(destination, destination);
}
private static ModuleNode findModuleByName(PcbNode destNode, String moduleName) {
for (PcbNode node : destNode.iterate("module")) {
ModuleNode mn = (ModuleNode) node;
if (moduleName.toLowerCase().equals(mn.getReference().toLowerCase()))
return mn;
}
return null;
}
private static void mergePcb(PcbNode destNode, PcbNode source) throws IOException {
/**
* original local net name > new net name in combined PCB
*/
Map<String, String> netNameMapping = new HashMap<>();
/**
* original local net ID (as string) > new net ID
*/
Map<String, Integer> netIdMapping = new HashMap<>();
for (PcbNode net : source.iterate("net")) {
String netId = net.getChild(0);
String netName = net.getChild(1); // todo: nicer method?
String newName = networks.registerNetworkIfPcbSpecific(netName);
netNameMapping.put(netName, newName);
netIdMapping.put(netId, networks.getId(newName));
}
List<PcbNode> zones = source.iterate("zone");
log("Processing " + zones.size() + " zone(s)");
for (PcbNode z : zones) {
ZoneNode zone = (ZoneNode) z;
if (zone.getLayerNode().isSilkcreenLayer())
destNode.addChild(zone);
}
List<PcbNode> arcs = source.iterate("gr_arc");
log("Processing " + arcs.size() + " arc(s)");
for (PcbNode arc : arcs)
destNode.addChild(arc);
List<PcbNode> lines = source.iterate("gr_line");
log("Processing " + lines.size() + " line(s)");
for (PcbNode l : lines) {
GrLineNode line = (GrLineNode) l;
if (line.layerNode.isSilkcreenLayer())
destNode.addChild(line);
}
List<PcbNode> labels = source.iterate("gr_text");
log("Processing " + labels.size() + " label(s)");
for (PcbNode label : labels) {
destNode.addChild(label);
}
List<PcbNode> modules = source.iterate("module");
log("Processing " + modules.size() + " module(s)");
for (PcbNode module : modules) {
for (PcbNode pad : module.iterate("pad")) {
if (!pad.hasChild("net"))
continue;
fixNetId(netIdMapping, netNameMapping, pad);
// PcbNode net = pad.find("net");
// String localName = netNameMapping.get(net.getChild(1));
// net.setString(1, localName);
// net.setInt(0, networks.getId(localName));
}
destNode.addChild(module);
}
List<PcbNode> segments = source.iterate("segment");
log("Processing " + segments.size() + " segments");
for (PcbNode segment : segments) {
// if (!segment.hasChild("net"))
// continue;
fixNetId(netIdMapping, netNameMapping, segment);
destNode.addChild(segment);
}
List<PcbNode> vias = source.iterate("via");
log("Processing " + vias.size() + " vias");
for (PcbNode via : vias) {
fixNetId(netIdMapping, netNameMapping, via);
destNode.addChild(via);
}
// for (PcbNode zone : source.iterate("zone")) {
// fixNetId(netIdMapping, zone);
// destNode.addChild(zone);
// }
}
public static void removeNodes(PcbNode source) {
for (PcbNode module : source.iterate("module")) {
if (shouldRemove((ModuleNode) module))
source.removeChild(module);
}
}
private static boolean shouldRemove(ModuleNode module) {
for (PcbNode fp_text : module.iterate("fp_text")) {
if ("reference".equals(fp_text.getChild(0))) {
String name = fp_text.getChild(1);
if (ChangesModel.getInstance().DEL_REQUESTS.contains(name))
return true;
}
}
return false;
}
private static void fixNetId(Map<String, Integer> netIdMapping, Map<String, String> netNameMapping, PcbNode node) {
NetNode net = (NetNode) node.find("net");
String originalId = net.id;
Integer currentNetId = netIdMapping.get(originalId);
String globalName = networks.nameById.get(currentNetId);
// String newName = netNameMapping.get(originalName);
// if (newName == null)
// throw new NullPointerException("?");
if (ChangesModel.getInstance().NET_MERGE_REQUESTS.containsKey(globalName)) {
String newName = ChangesModel.getInstance().NET_MERGE_REQUESTS.get(globalName);
log("Will merge " + globalName + " into " + newName + ". ID was " + currentNetId);
currentNetId = networks.networks.get(newName);
if (currentNetId == null)
throw new NullPointerException("Cannot find net: " + newName);
log("New ID: " + currentNetId);
globalName = newName;
}
net.setInt(0, currentNetId);
if (net.getName() != null)
net.setString(1, globalName);
}
private static class Networks {
/**
* Net name > Net Id
*/
private Map<String, Integer> networks = new HashMap<>();
private Map<Integer, String> nameById = new HashMap<>();
public String registerNetworkIfPcbSpecific(String name) {
if (isLocalNetwork(name)) {
String newName = "F-0000" + networks.size();
log("Board-specific net: " + name + " would be " + newName);
registerNet(newName);
int newId = networks.get(newName);
log(newName + " is " + newId);
return newName;
} else {
if (networks.containsKey(name)) {
log("Existing global net: " + name);
return name;
}
log("New global net: " + name);
registerNet(name);
return name;
}
}
private static boolean isLocalNetwork(String name) {
return name.startsWith("N-00");
}
private void registerNet(String name) {
networks.put(name, networks.size());
nameById.put(networks.get(name), name);
}
public int getId(String localName) {
Integer value = networks.get(localName);
if (value == null)
throw new NullPointerException("No id for " + localName);
return value;
}
}
public static void log(String s) {
System.out.println(s);
}
}

View File

@ -0,0 +1,139 @@
package com.rusefi.pcb;
import com.rusefi.pcb.nodes.PcbNode;
import java.io.IOException;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 12/8/13
*/
public class PcbMoveTool {
public static void main(String[] args) throws IOException {
if (args.length != 4) {
System.out.println("Four parameters expected: SRC_FILENAME DST_FILENAME X Y");
return;
}
String srcFileName = args[0];
String dstFileName = args[1];
double x = Double.parseDouble(args[2]);
double y = Double.parseDouble(args[3]);
PcbNode node = readAndMove(srcFileName, x, y);
node.write(dstFileName);
}
public static PcbNode readAndMove(String fileName, double x, double y) throws IOException {
PcbNode node = PcbNode.readFromFile(fileName);
move(node, x, y);
return node;
}
public static void move(PcbNode pcbNode, double dx, double dy) {
System.out.println("Moving " + pcbNode + ": dx=" +dx + " dy=" + dy);
List<PcbNode> dimensions = pcbNode.iterate("dimension");
System.out.println("Moving " + dimensions.size() + " dimension");
for (PcbNode dimension : dimensions) {
moveAt(dx, dy, dimension.find("gr_text"));
movePts(dx, dy, dimension.find("feature1"));
movePts(dx, dy, dimension.find("feature2"));
movePts(dx, dy, dimension.find("crossbar"));
movePts(dx, dy, dimension.find("arrow1a"));
movePts(dx, dy, dimension.find("arrow1b"));
movePts(dx, dy, dimension.find("arrow2a"));
movePts(dx, dy, dimension.find("arrow2b"));
}
List<PcbNode> gr_lines = pcbNode.iterate("gr_line");
System.out.println("Moving " + gr_lines.size() + " gr_lines");
for (PcbNode gr_line : gr_lines)
moveStartEnd(dx, dy, gr_line);
List<PcbNode> gr_arcs = pcbNode.iterate("gr_arc");
System.out.println("Moving " + gr_arcs.size() + " gr_arcs");
for (PcbNode gr_arc : gr_arcs) {
PcbNode start = gr_arc.find("start");
moveCoordinatesInFirstChildren(dx, dy, start);
PcbNode end = gr_arc.find("end");
moveCoordinatesInFirstChildren(dx, dy, end);
}
List<PcbNode> gr_circles = pcbNode.iterate("gr_circle");
System.out.println("Moving " + gr_circles.size() + " gr_circles");
for (PcbNode gr_circle : gr_circles) {
PcbNode start = gr_circle.find("center");
moveCoordinatesInFirstChildren(dx, dy, start);
PcbNode end = gr_circle.find("end");
moveCoordinatesInFirstChildren(dx, dy, end);
}
List<PcbNode> gr_texts = pcbNode.iterate("gr_text");
System.out.println("Moving " + gr_texts.size() + " gr_texts");
for (PcbNode gr_text : gr_texts)
moveAt(dx, dy, gr_text);
List<PcbNode> zones = pcbNode.iterate("zone");
System.out.println("Moving " + zones.size() + " zones");
for (PcbNode zone : zones) {
List<PcbNode> filledPolygons = zone.iterate("filled_polygon");
for (PcbNode filledPolygon : filledPolygons)
movePts(dx, dy, filledPolygon);
List<PcbNode> polygons = zone.iterate("polygon");
for (PcbNode polygon : polygons)
movePts(dx, dy, polygon);
}
List<PcbNode> segments = pcbNode.iterate("segment");
System.out.println("Moving " + segments.size() + " segments");
for (PcbNode segment : segments)
moveStartEnd(dx, dy, segment);
List<PcbNode> vias = pcbNode.iterate("via");
System.out.println("Moving " + vias.size() + " vias");
for (PcbNode via : vias)
moveAt(dx, dy, via);
List<PcbNode> modules = pcbNode.iterate("module");
System.out.println("Moving " + modules.size() + " modules");
for (PcbNode module : modules)
moveAt(dx, dy, module);
}
public static void movePts(double dx, double dy, PcbNode polygon) {
PcbNode pts = polygon.find("pts");
for (PcbNode point : pts.nodes())
moveCoordinatesInFirstChildren(dx, dy, point);
}
public static void moveStartEnd(double dx, double dy, PcbNode segment) {
PcbNode start = segment.find("start");
moveCoordinatesInFirstChildren(dx, dy, start);
PcbNode end = segment.find("end");
moveCoordinatesInFirstChildren(dx, dy, end);
}
public static void moveAt(double dx, double dy, PcbNode module) {
PcbNode at = module.find("at");
moveCoordinatesInFirstChildren(dx, dy, at);
}
public static void moveCoordinatesInFirstChildren(double dx, double dy, PcbNode at) {
moveCoordinates(dx, dy, at, 0);
}
private static void moveCoordinates(double dx, double dy, PcbNode at, int index) {
double x = at.asDouble(index);
double y = at.asDouble(index + 1);
at.setDouble(index, x + dx);
at.setDouble(index + 1, y + dy);
}
}

View File

@ -0,0 +1,138 @@
package com.rusefi.pcb;
import com.rusefi.pcb.nodes.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class RemoveUnneededTraces {
private final static Set<String> alreadyRemoved = new HashSet<String>();
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.out.println("two parameters expected: INPUT_FILE OUTPUT_FILE");
return;
}
String input = args[0];
String output = args[1];
optimize(input, output);
}
public static void optimize(String input, String output) throws IOException {
PcbNode destNode = PcbNode.readFromFile(input);
while (removeUnusedSegments(destNode) || removeUnusedVias(destNode)) {
System.out.println("Still removing...");
}
destNode.write(output);
}
private static boolean removeUnusedVias(PcbNode destNode) {
List<ViaNode> unused = findUnusedVias(destNode);
for (ViaNode via : unused) {
System.out.println("Removing via: " + via);
boolean removed = destNode.removeChild(via);
if (!removed)
throw new IllegalStateException("not removed: " + removed);
}
return !unused.isEmpty();
}
private static List<ViaNode> findUnusedVias(PcbNode destNode) {
List<ViaNode> result = new ArrayList<ViaNode>();
List<PcbNode> stuff = destNode.iterate("segment");
// stuff.addAll(destNode.iterate("segment"));
for (PcbNode n : destNode.iterate("via")) {
ViaNode via = (ViaNode) n;
int count = 0;
for (PcbNode segment : stuff)
if (segment.isConnected(via.location))
count++;
if (via.netId == NetNode.GND_NET_ID) {
if (count == 0)
result.add(via);
} else {
if (count < 2)
result.add(via);
}
}
return result;
}
private static boolean removeUnusedSegments(PcbNode destNode) {
List<PcbNode> stuff = new ArrayList<PcbNode>(destNode.iterate("module"));
stuff.addAll(destNode.iterate("via"));
Object o = destNode.iterate("segment");
List<SegmentNode> segments = (List<SegmentNode>) o;
System.out.println(segments.size() + " segment(s)");
List<SegmentNode> unused = findUnusedSegments(segments, stuff);
for (SegmentNode segment : unused) {
boolean removed = destNode.removeChild(segment);
if (!removed)
throw new IllegalStateException();
String netName = segment.net.id;
if (!alreadyRemoved.contains(netName)) {
alreadyRemoved.add(netName);
System.out.println("Unused segment in network " + netName + ": " + segment);
}
}
return !unused.isEmpty();
}
private static List<SegmentNode> findUnusedSegments(List<SegmentNode> segments, List<PcbNode> modules) {
List<SegmentNode> unused = new ArrayList<SegmentNode>();
for (SegmentNode segment : segments) {
if (isUnused(segments, segment, modules)) {
// System.out.println("Unused on " + segment.net.id + ": " + segment);
unused.add(segment);
}
}
return unused;
}
public static boolean isUnused(List<SegmentNode> segments, SegmentNode segment, List<PcbNode> modules) {
PointNode start = segment.start;
PointNode end = segment.end;
if (isConnected(start, segments, segment) == null && isConnected(start, modules, null) == null) {
System.out.println("Not connected start: " + segment);
return true;
}
PcbNode endModule = isConnected(end, modules, null);
if (isConnected(end, segments, segment) == null && endModule == null) {
System.out.println("Not connected end: " + segment);
return true;
}
return false;
}
private static PcbNode isConnected(PointNode point, List<? extends PcbNode> elements, SegmentNode parent) {
for (PcbNode segmentNode : elements) {
if (segmentNode == parent)
continue;
if (segmentNode.isConnected(point))
return segmentNode;
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package com.rusefi.pcb;
/**
* @author Andrey Belomutskiy
* 1/24/14
*/
public class TwoFileRequest {
public final String input;
public final String output;
public TwoFileRequest(String input, String output) {
this.input = input;
this.output = output;
}
static TwoFileRequest parseTwoFile(String request, int lineIndex) {
String[] tokens = request.split(" ");
if (tokens.length != 2)
throw new IllegalArgumentException("Unexpected token count in [" + request + "] @" + lineIndex);
return new TwoFileRequest(tokens[0], tokens[1]);
}
}

View File

@ -0,0 +1,15 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PcbNode;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 5/30/2014
*/
public class AddNetNode extends PcbNode {
public AddNetNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
}
}

View File

@ -0,0 +1,24 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PadNode;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 1/21/14.
*/
public class CirclePadNode extends PadNode {
public CirclePadNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
}
@Override
public String toString() {
return "CirclePadNode{" +
"at=" + at +
", size=" + size +
'}';
}
}

View File

@ -0,0 +1,15 @@
package com.rusefi.pcb.nodes;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 2/7/14.
*/
public class GrLineNode extends PcbNode {
public final LayerNode layerNode;
public GrLineNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
layerNode = (LayerNode) find("layer");
}
}

View File

@ -0,0 +1,22 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PcbNode;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 2/7/14.
*/
public class LayerNode extends PcbNode {
public final String name;
public LayerNode(String nodeName, int closingIndex, List<Object> children) {
super(nodeName, closingIndex, children);
name = (String) children.get(0);
}
public boolean isSilkcreenLayer() {
return name.equals("B.SilkS") || name.equals("F.SilkS");
}
}

View File

@ -0,0 +1,45 @@
package com.rusefi.pcb.nodes;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class ModuleNode extends PcbNode {
final List<PadNode> pads;
public final PointNode at;
private final String reference;
public ModuleNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
Object o = iterate("pad");
pads = (List<PadNode>) o;
at = (PointNode) find("at");
reference = iterate("fp_text").get(0).getChild(1);
}
public String getReference() {
return reference;
}
@Override
public String toString() {
return "ModuleNode{" +
"reference=" + reference +
", pads.size=" + pads.size() +
'}';
}
@Override
public boolean isConnected(PointNode point) {
PointNode offsetPoint = at.translate(point);
for (PadNode pad : pads) {
if (pad.isConnected(offsetPoint))
return true;
}
return false;
}
}

View File

@ -0,0 +1,37 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PcbNode;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class NetNode extends PcbNode {
public final String id;
final String name;
public static int GND_NET_ID;
public NetNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
id = getChild(0);
name = children.size() > 1 ? getChild(1) : null;
if (name != null)
System.out.println("NetNode(" + name + " network: " + id + ")");
if ("GND".equalsIgnoreCase(name))
GND_NET_ID = Integer.parseInt(id);
}
public String getName() {
return name;
}
@Override
public String toString() {
return "NetNode{" +
"id='" + id + '\'' +
'}';
}
}

View File

@ -0,0 +1,44 @@
package com.rusefi.pcb.nodes;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 1/21/14.
*/
public abstract class PadNode extends PcbNode {
protected final PointNode at;
protected final SizeNode size;
protected final String name;
public PadNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
name = (String) children.get(0);
at = (PointNode) find("at");
size = (SizeNode) find("size");
}
@Override
public boolean isConnected(PointNode point) {
return point.isConnected(at, size);
}
public static PcbNode parse(String nodeName, int i, List<Object> children) {
Object shape = children.get(2);
if ("rect".equals(shape))
return new RectPadNode(nodeName, i, children);
if ("circle".equals(shape))
return new CirclePadNode(nodeName, i, children);
if ("oval".equals(shape))
return new CirclePadNode(nodeName, i, children); // yes, let's treat oval as circle. good enough
throw new IllegalStateException("Unknown pad shape: " + shape.toString());
}
@Override
public String toString() {
return "PadNode{" +
"at=" + at +
", size=" + size +
'}';
}
}

View File

@ -0,0 +1,257 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.*;
import com.rusefi.util.FileUtils;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 12/8/13
*/
public class PcbNode {
public final String nodeName;
public final int closingIndex;
public final List<Object> children;
public PcbNode(String nodeName, int closingIndex, List<Object> children) {
this.nodeName = nodeName;
this.closingIndex = closingIndex;
this.children = children;
}
/**
* @see #write
*/
public static PcbNode readFromFile(String fileName) throws IOException {
String content = FileUtils.readFile(fileName);
PcbNode node = parse(content);
System.out.println("GND network: " + NetNode.GND_NET_ID);
return node;
}
public static void copy(String inputFileName, String outputFileName) throws IOException {
System.out.println("From " + inputFileName + " to " + outputFileName);
PcbNode node = readFromFile(inputFileName);
PcbMergeTool.removeNodes(node);
node.write(outputFileName);
}
@Override
public String toString() {
return "PcbNode{" +
nodeName +
", children=" + children.size() +
'}';
}
private static PcbNode parse(String s, int index, int depth) {
log("Reading node from " + index, depth);
if (s.charAt(index) != '(')
throw new IllegalStateException("opening bracket expected");
index++;
String nodeName = readToken(s, index, depth);
index += nodeName.length();
List<Object> children = new ArrayList<Object>();
while (true) {
while (isWhitespace(s.charAt(index)))
index++;
char c = s.charAt(index);
if (c == ')')
break;
if (s.charAt(index) == '(') {
PcbNode child = parse(s, index, depth + 1);
children.add(child);
index = child.closingIndex;
continue;
}
String child = readToken(s, index, depth);
children.add(child);
index += child.length();
}
if ("segment".equals(nodeName)) {
return new SegmentNode(nodeName, index + 1, children);
} else if ("pad".equals(nodeName)) {
return PadNode.parse(nodeName, index + 1, children);
} else if ("net".equals(nodeName)) {
return new NetNode(nodeName, index + 1, children);
} else if ("add_net".equals(nodeName)) {
return new AddNetNode(nodeName, index + 1, children);
} else if ("gr_line".equals(nodeName)) {
return new GrLineNode(nodeName, index + 1, children);
} else if ("layer".equals(nodeName)) {
return new LayerNode(nodeName, index + 1, children);
} else if ("module".equals(nodeName)) {
return new ModuleNode(nodeName, index + 1, children);
} else if ("size".equals(nodeName) || "width".equals(nodeName)) {
return new SizeNode(nodeName, index + 1, children);
} else if ("zone".equals(nodeName)) {
return new ZoneNode(nodeName, index + 1, children);
} else if ("via".equals(nodeName)) {
return new ViaNode(nodeName, index + 1, children);
} else if ("start".equals(nodeName) || "end".equals(nodeName) || "at".equals(nodeName)) {
return new PointNode(nodeName, index + 1, children);
}
return new PcbNode(nodeName, index + 1, children);
}
private static String readToken(String s, int index, int depth) {
log("Reading token from " + index, depth);
if (s.charAt(index) == '"') {
String result = s.substring(index, s.indexOf('"', index + 1) + 1);
log("Got quoted token: " + result, depth);
return result;
}
String result = "";
while (index < s.length()) {
char c = s.charAt(index);
if (c == ')' || isWhitespace(c))
break;
result += c;
index++;
}
if (result.length() == 0)
throw new IllegalStateException("Empty token");
log("Got token: " + result, depth);
return result;
}
private static void log(String s, int depth) {
// for (int i = 0; i < depth; i++)
// System.out.print(' ');
// System.out.println(s);
}
private static void log(String s) {
log(s, 0);
}
private static boolean isWhitespace(char c) {
return c == ' ' || c == '\r' || c == '\n';
}
public static PcbNode parse(String content) {
return parse(content, 0, 0);
}
public String pack() {
StringBuilder sb = new StringBuilder();
pack(sb, "");
return sb.toString();
}
private void pack(StringBuilder sb, String prefix) {
sb.append(prefix).append("(").append(nodeName);
for (Object child : children) {
if (child instanceof String) {
sb.append(" ").append(child);
continue;
}
PcbNode p = (PcbNode) child;
if (p == null)
throw new NullPointerException("Null child node");
sb.append("\r\n");
p.pack(sb, prefix + " ");
}
sb.append(")\r\n");
}
public void write(String fileName) throws IOException {
System.out.println("Writing to " + fileName);
String content = pack();
BufferedWriter bw = new BufferedWriter(new FileWriter(fileName));
bw.write(content);
bw.close();
}
public void setDouble(int i, double value) {
children.set(i, "" + value);
}
public double asDouble(int index) {
return Double.parseDouble((String) children.get(index));
}
public boolean hasChild(String key) {
return !iterate(key).isEmpty();
}
// @Nullable
public PcbNode findIfExists(String key) {
List<PcbNode> r = iterate(key);
if (r.isEmpty())
return null;
return find(key);
}
// @NotNull
public PcbNode find(String key) {
List<PcbNode> r = iterate(key);
if (r.size() != 1)
throw new IllegalStateException("More that one " + key + " in " + nodeName);
return r.get(0);
}
public List<PcbNode> nodes() {
List<PcbNode> result = new ArrayList<PcbNode>();
for (Object child : children) {
if (child instanceof String)
continue;
result.add((PcbNode) child);
}
return result;
}
public List<PcbNode> iterate(String key) {
List<PcbNode> result = new ArrayList<PcbNode>();
for (PcbNode p : nodes()) {
if (p.nodeName.equals(key))
result.add(p);
}
return result;
}
public void addChild(PcbNode node) {
children.add(node);
}
public List<Object> getChildren() {
return Collections.unmodifiableList(children);
}
public String getChild(int index) {
return (String) children.get(index);
}
public void setString(int index, String value) {
children.set(index, value);
}
public void setInt(int index, int value) {
children.set(index, "" + value);
}
public boolean removeChild(Object child) {
return children.remove(child);
}
public boolean isConnected(PointNode point) {
return false;
}
}

View File

@ -0,0 +1,77 @@
package com.rusefi.pcb.nodes;
import java.util.Collections;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class PointNode extends PcbNode {
public final double x;
public final double y;
public final double angle;
public PointNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
if (children.size() == 1) {
// xyz use-case
x = 0;
y = 0;
angle = 0;
return;
}
if (children.size() != 2 && children.size() != 3)
throw new IllegalStateException("Unexpected children count");
x = Double.parseDouble((String) children.get(0));
y = Double.parseDouble((String) children.get(1));
angle = children.size() == 2 ? 0 : Double.parseDouble((String) children.get(2));
}
public PointNode(double x, double y) {
this(x, y, 0);
}
public PointNode(double x, double y, double angle) {
super("", 0, Collections.emptyList());
this.x = x;
this.y = y;
this.angle = angle;
}
public boolean isConnected(PointNode at, SizeNode size) {
boolean isConnectedX = (x >= at.x - size.w / 2) && (x <= at.x + size.w / 2);
boolean isConnectedY = (y >= at.y - size.h / 2) && (y <= at.y + size.h / 2);
return isConnectedX && isConnectedY;
}
@Override
public String toString() {
return "PointNode{" +
"x=" + x +
", y=" + y +
", angle=" + angle +
'}';
}
// public boolean isSameLocation(PointNode point) {
// return x == point.x && y == point.y;
// }
public PointNode translate(PointNode at) {
double nx = at.x - x;
double ny = at.y - y;
double radian = angle / 180 * Math.PI;
double rx = Math.cos(radian) * nx - Math.sin(radian) * ny;
double ry = Math.sin(radian) * nx + Math.cos(radian) * ny;
return new PointNode(rx, ry);
}
public void setLocation(double x, double y) {
children.set(0, Double.toString(x));
children.set(1, Double.toString(y));
}
}

View File

@ -0,0 +1,24 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PadNode;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 1/21/14.
*/
public class RectPadNode extends PadNode {
public RectPadNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
}
@Override
public String toString() {
return "RectPadNode{" +
"name=" + name +
", at=" + at +
", size=" + size +
'}';
}
}

View File

@ -0,0 +1,35 @@
package com.rusefi.pcb.nodes;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class SegmentNode extends PcbNode {
public final NetNode net;
public final PointNode start;
public final PointNode end;
private final SizeNode size;
public SegmentNode(String nodeName, int closingIndex, List<Object> children) {
super(nodeName, closingIndex, children);
net = (NetNode) find("net");
start = (PointNode) find("start");
end = (PointNode) find("end");
size = (SizeNode) find("width");
}
public boolean isConnected(PointNode point) {
return point.isConnected(start, size) || point.isConnected(end, size);
}
@Override
public String toString() {
return "SegmentNode{" +
"net=" + net +
", start=" + start +
", end=" + end +
'}';
}
}

View File

@ -0,0 +1,33 @@
package com.rusefi.pcb.nodes;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class SizeNode extends PcbNode {
public final double w;
public final double h;
public SizeNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
if (children.size() == 1) {
w = h = Double.parseDouble((String) children.get(0));
return;
}
if (children.size() != 2)
throw new IllegalStateException("Size: " + children.size());
w = Double.parseDouble((String) children.get(0));
h = Double.parseDouble((String) children.get(1));
}
@Override
public String toString() {
return "SizeNode{" +
"w=" + w +
", h=" + h +
'}';
}
}

View File

@ -0,0 +1,36 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.PcbNode;
import com.rusefi.pcb.nodes.PointNode;
import com.rusefi.pcb.nodes.SizeNode;
import java.util.List;
/**
* @author Andrey Belomutskiy
* 1/21/14
*/
public class ViaNode extends PcbNode {
public final PointNode location;
final SizeNode size;
public final int netId;
public ViaNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
location = (PointNode) find("at");
size = (SizeNode) find("size");
netId = Integer.parseInt(find("net").getChild(0));
}
@Override
public String toString() {
return "ViaNode{" +
"location=" + location +
'}';
}
@Override
public boolean isConnected(PointNode point) {
return point.isConnected(location, size);
}
}

View File

@ -0,0 +1,23 @@
package com.rusefi.pcb.nodes;
import com.rusefi.pcb.nodes.LayerNode;
import com.rusefi.pcb.nodes.PcbNode;
import java.util.List;
/**
* (c) Andrey Belomutskiy
* 2/11/14.
*/
public class ZoneNode extends PcbNode {
private final LayerNode layerNode;
public ZoneNode(String nodeName, int i, List<Object> children) {
super(nodeName, i, children);
layerNode = (LayerNode) find("layer");
}
public LayerNode getLayerNode() {
return layerNode;
}
}

View File

@ -0,0 +1,59 @@
package com.rusefi.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Generic file utilities
* <p/>
* 12/9/13
* (c) Andrey Belomutskiy
*/
public class FileUtils {
private FileUtils() {
}
/**
* @param fileName
* @return Full content of the file as one String
* @throws IOException
*/
public static String readFile(String fileName) throws IOException {
checkExistence(fileName);
System.out.println("Reading " + fileName);
StringBuilder sb = new StringBuilder();
String line;
BufferedReader br = new BufferedReader(new FileReader(fileName));
while (((line = br.readLine()) != null))
sb.append(line).append("\r\n");
br.close();
return sb.toString();
}
private static void checkExistence(String fileName) {
if (!new File(fileName).isFile()) {
System.err.println("File not found: " + fileName);
System.exit(-1);
}
}
public static List<String> readFileToList(String fileName) throws IOException {
checkExistence(fileName);
List<String> result = new ArrayList<String>();
System.out.println("Reading " + fileName);
String line;
BufferedReader br = new BufferedReader(new FileReader(fileName));
while (((line = br.readLine()) != null))
result.add(line);
return result;
}
}

View File

@ -0,0 +1,14 @@
package com.rusefi.netlist;
import com.rusefi.pcb.NameAndOffset;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class NameAndOffsetTest {
@Test
public void test() {
NameAndOffset n = NameAndOffset.parseNameAndOffset("1 2 3");
assertEquals("1", n.getName());
}
}