Merge pull request #5 from andrasfuchs/board-cloning

Command line arguments for auto-routing and auto-exporting
This commit is contained in:
Michael Hoffer 2020-02-10 09:23:16 +01:00 committed by GitHub
commit 8f7c324f4a
17 changed files with 403 additions and 116 deletions

View File

@ -47,6 +47,10 @@ Navigate to the [Gradle](http://www.gradle.org/) project (e.g., `path/to/freerou
#### Windows (CMD)
gradlew assemble
#### Generated Executables
All four .jar files will be generated in the _build\libs_ subfolder. You would typically run the _freerouting-executable.jar_ file.
## From the original author:
@ -54,7 +58,7 @@ Java Based Printed Circuit Board Routing Software from FreeRouting.net written b
http://www.freerouting.net/fen/viewtopic.php?f=4&t=255
by alfons <EFBFBD> Sat Mar 08, 2014 12:07 pm
by alfons © Sat Mar 08, 2014 12:07 pm
Because I am no more maintaining the Freerouting project since 4 years and future Java versions may block my Freerouting Java Web Start application completely, I finally decided to open the source of the Freerouting project under the GNU public license version 3.
@ -116,3 +120,25 @@ Additional steps for users of KiCad:
5) When you're finished, export the results into a Specctra session file (File / Export Specctra Session File). The router will generate a .ses file for you.
6) Go back to KiCad's Pcbnew and import the results (File / Import Specctra Session...).
Using the command line arguments:
=================================
Freerouter was designed as a GUI program, but it also can function as a command line tool. Typically you would have an input file (e.g. Specctra DSN) that you exported from you EDA (e.g. KiCad). If this file has unconnected routes, you would want to wire those with autorouter, and save the result in a format that you can then import back into your EDA.
The following command line arguments are supported by freerouter:
* -de [design input file]: loads up a Specctra .dsn file at startup
* -di [design input directory]: if the GUI is used, this sets the default folder for the open design dialogs
* -do [design output file]: saves a Specctra board (.dsn), a Specctra session file (.ses) or Eagle session script file (.scr) when the routing is finished
* -mp [number of passes]: sets the upper limit of the number of passes that will be performed
* -l [language]: "de" for German, otherwise it's English
A complete command line looks something like this if your are using PowerShell on Windows:
`
& "c:\Program Files\Java\jdk-11.0.6\bin\java.exe" -jar freerouting-executable.jar -de MyBoard.dsn -do MyBoard.ses -mp 100
`
This would read the _MyBoard.dsn_ file, do the auto-routing for the maximum of 100 passes, and then save the result into the _MyBoard.ses_ file.

View File

@ -18,11 +18,7 @@
*/
package eu.mihosoft.freerouting.autoroute;
import java.util.Iterator;
import java.util.Collection;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.*;
import eu.mihosoft.freerouting.datastructures.TimeLimit;
import eu.mihosoft.freerouting.datastructures.UndoableObjects;
@ -46,6 +42,7 @@ import eu.mihosoft.freerouting.logger.FRLogger;
*/
public class BatchAutorouter
{
private HashSet<String> already_checked_board_hashes = new HashSet<String>();
/**
* Autoroutes ripup passes until the eu.mihosoft.freerouting.board is completed or the autorouter is stopped by the user,
@ -109,6 +106,8 @@ public class BatchAutorouter
this.retain_autoroute_database = false;
}
private LinkedList<Integer> diffBetweenBoards = new LinkedList<Integer>();
/**
* Autoroutes ripup passes until the eu.mihosoft.freerouting.board is completed or the autorouter is stopped by the user.
* Returns true if the eu.mihosoft.freerouting.board is completed.
@ -124,12 +123,51 @@ public class BatchAutorouter
{
this.is_interrupted = true;
}
Integer curr_pass_no = hdlg.get_settings().autoroute_settings.get_pass_no();
var current_board_hash = this.routing_board.get_hash();
if (already_checked_board_hashes.contains(current_board_hash))
{
FRLogger.logger.warn("This board was already evaluated, so we stop autorouter to avoid the endless loop.");
thread.request_stop();
break;
}
Integer curr_pass_no = hdlg.get_settings().autoroute_settings.get_start_pass_no();
if (curr_pass_no > hdlg.get_settings().autoroute_settings.get_stop_pass_no())
{
thread.request_stop();
break;
}
String start_message = resources.getString("batch_autorouter") + " " + resources.getString("stop_message") + " " + resources.getString("pass") + " " + curr_pass_no.toString() + ": ";
hdlg.screen_messages.set_status_message(start_message);
FRLogger.traceEntry("BatchAutorouter.autoroute_pass("+curr_pass_no+")");
var boardBefore = this.routing_board.clone();
FRLogger.traceEntry("BatchAutorouter.autoroute_pass #"+curr_pass_no+" on board '"+current_board_hash+"' making {} changes");
already_checked_board_hashes.add(this.routing_board.get_hash());
still_unrouted_items = autoroute_pass(curr_pass_no, true);
FRLogger.traceExit("BatchAutorouter.autoroute_pass("+curr_pass_no+")");
// let's check if there was enough change in the last pass, because if it were little, so should probably stop
var newTraceDifferences = this.routing_board.diff_traces(boardBefore);
diffBetweenBoards.add(newTraceDifferences);
if (diffBetweenBoards.size() > 20) {
diffBetweenBoards.removeFirst();
OptionalDouble average = diffBetweenBoards
.stream()
.mapToDouble(a -> a)
.average();
if (average.getAsDouble() < 20.0)
{
FRLogger.logger.warn("There were only " + average.getAsDouble() + " changes in the last 20 passes, so it's very likely that autorouter can't improve the result much further. It is recommended to stop it and finish the board manually.");
}
}
FRLogger.traceExit("BatchAutorouter.autoroute_pass #"+curr_pass_no+" on board '"+current_board_hash+"' making {} changes", newTraceDifferences);
// check if there are still unrouted items
if (still_unrouted_items && !is_interrupted)
{
hdlg.get_settings().autoroute_settings.increment_pass_no();
@ -137,9 +175,12 @@ public class BatchAutorouter
}
if (!(this.remove_unconnected_vias || still_unrouted_items || this.is_interrupted))
{
// clean up the route if the eu.mihosoft.freerouting.board is completed and if fanout is used.
// clean up the route if the board is completed and if fanout is used.
remove_tails(Item.StopConnectionOption.NONE);
}
already_checked_board_hashes.clear();
return !this.is_interrupted;
}

View File

@ -30,12 +30,12 @@ import eu.mihosoft.freerouting.geometry.planar.TileShape;
import java.awt.Graphics;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.MessageDigest;
import java.util.*;
import eu.mihosoft.freerouting.datastructures.ShapeTree.TreeEntry;
@ -85,6 +85,101 @@ public class BasicBoard implements java.io.Serializable
insert_outline(p_outline_shapes, p_outline_cl_class_no);
}
private byte[] serialize(boolean basicProfile)
{
try
{
var output_stream = new ByteArrayOutputStream();
var object_stream = new ObjectOutputStream(output_stream);
if (basicProfile) {
object_stream.writeObject(this.get_traces());
object_stream.writeObject(this.get_vias());
object_stream.writeObject(this.item_list);
} else {
object_stream.writeObject(this);
}
object_stream.close();
return output_stream.toByteArray();
}
catch (Exception e) {
FRLogger.logger.error(e);
}
return null;
}
private static BasicBoard deserialize(byte[] object_byte_array)
{
try
{
var input_stream = new ByteArrayInputStream(object_byte_array);
var object_stream = new ObjectInputStream(input_stream);
return (BasicBoard)object_stream.readObject();
}
catch (Exception e) {
FRLogger.logger.error(e);
}
return null;
}
public BasicBoard clone()
{
return deserialize(this.serialize(false));
}
public String get_hash()
{
try
{
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(this.serialize(true));
byte[] hashedBytes = digest.digest();
return convert_byte_array_to_hex_string(hashedBytes);
}
catch (Exception e) {
FRLogger.logger.error(e);
}
return null;
}
public int diff_traces(BasicBoard compare_to)
{
int result = 0;
HashSet<Integer> traceIds = new HashSet<Integer>();
for (Trace trace : this.get_traces()) {
traceIds.add(trace.get_id_no());
}
for (Trace trace : compare_to.get_traces()) {
if (!traceIds.contains(trace.get_id_no()))
{
result++;
} else {
traceIds.remove(trace.get_id_no());
}
}
result += traceIds.size();
return result;
}
private static String convert_byte_array_to_hex_string(byte[] arrayBytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < arrayBytes.length; i++) {
stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
return stringBuffer.toString();
}
/**
* Inserts a trace into the eu.mihosoft.freerouting.board, whose geometry is described by
* a Polyline. p_clearance_class is the index in the clearance_matix,

View File

@ -1453,7 +1453,7 @@ public abstract class Item implements Drawable, SearchTreeObject, ObjectInfoPane
* to other items
*/
private int clearance_class;
/** The eu.mihosoft.freerouting.board this Itewm is on */
/** The eu.mihosoft.freerouting.board this Item is on */
transient public BasicBoard board;
/** The nets, to which this item belongs */
int[] net_no_arr;

View File

@ -93,7 +93,7 @@ public class AutorouteSettings
}
else if (next_token == Keyword.START_PASS_NO)
{
result.set_pass_no(DsnFile.read_integer_scope(p_scanner));
result.set_start_pass_no(DsnFile.read_integer_scope(p_scanner));
}
else if (next_token == Keyword.LAYER_RULE)
{
@ -280,7 +280,7 @@ public class AutorouteSettings
p_file.new_line();
p_file.write("(start_pass_no ");
{
Integer pass_no = p_settings.get_pass_no();
Integer pass_no = p_settings.get_start_pass_no();
p_file.write(pass_no.toString());
}
p_file.write(")");

View File

@ -173,78 +173,69 @@ public class BoardFrame extends javax.swing.JFrame
* If p_is_import, the design is read from a scpecctra dsn file.
* Returns false, if the file is invalid.
*/
boolean read(java.io.InputStream p_input_stream, boolean p_is_import, javax.swing.JTextField p_message_field)
{
boolean read(java.io.InputStream p_input_stream, boolean p_is_import, javax.swing.JTextField p_message_field) {
java.awt.Point viewport_position = null;
if (p_is_import)
{
DsnFile.ReadResult read_result = board_panel.board_handling.import_design(p_input_stream, this.board_observers,
DsnFile.ReadResult read_result = null;
if (p_is_import) {
read_result = board_panel.board_handling.import_design(p_input_stream, this.board_observers,
this.item_id_no_generator, this.test_level);
if (read_result != DsnFile.ReadResult.OK)
{
if (p_message_field != null)
{
if (read_result == DsnFile.ReadResult.OUTLINE_MISSING)
{
p_message_field.setText(resources.getString("error_7"));
}
else
{
p_message_field.setText(resources.getString("error_6"));
}
}
return false;
if (read_result == DsnFile.ReadResult.OK) {
viewport_position = new java.awt.Point(0, 0);
initialize_windows();
}
viewport_position = new java.awt.Point(0,0);
initialize_windows();
}
else
{
} else {
java.io.ObjectInputStream object_stream = null;
try
{
try {
object_stream = new java.io.ObjectInputStream(p_input_stream);
}
catch (java.io.IOException e)
{
} catch (java.io.IOException e) {
return false;
}
boolean read_ok = board_panel.board_handling.read_design(object_stream, this.test_level);
if (!read_ok)
{
if (!read_ok) {
return false;
}
java.awt.Point frame_location;
java.awt.Rectangle frame_bounds;
try
{
try {
viewport_position = (java.awt.Point) object_stream.readObject();
frame_location = (java.awt.Point) object_stream.readObject();
frame_bounds = (java.awt.Rectangle) object_stream.readObject();
}
catch (Exception e)
{
} catch (Exception e) {
return false;
}
this.setLocation(frame_location);
this.setBounds(frame_bounds);
allocate_permanent_subwindows();
for (int i = 0; i < this.permanent_subwindows.length; ++i)
{
for (int i = 0; i < this.permanent_subwindows.length; ++i) {
this.permanent_subwindows[i].read(object_stream);
}
}
try
{
try {
p_input_stream.close();
}
catch (java.io.IOException e)
{
} catch (java.io.IOException e) {
return false;
}
return update_gui(p_is_import, read_result, viewport_position, p_message_field);
}
private boolean update_gui(boolean p_is_import, DsnFile.ReadResult read_result, java.awt.Point viewport_position, javax.swing.JTextField p_message_field)
{
if (p_is_import) {
if (read_result != DsnFile.ReadResult.OK) {
if (p_message_field != null) {
if (read_result == DsnFile.ReadResult.OUTLINE_MISSING) {
p_message_field.setText(resources.getString("error_7"));
} else {
p_message_field.setText(resources.getString("error_6"));
}
}
return false;
}
}
java.awt.Dimension panel_size = board_panel.board_handling.graphics_context.get_panel_size();
board_panel.setSize(panel_size);
board_panel.setPreferredSize(panel_size);

View File

@ -24,10 +24,14 @@
package eu.mihosoft.freerouting.gui;
import eu.mihosoft.freerouting.board.TestLevel;
import eu.mihosoft.freerouting.interactive.ThreadActionListener;
import eu.mihosoft.freerouting.logger.FRLogger;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
/**
*
@ -76,17 +80,15 @@ public class MainApplication extends javax.swing.JFrame
board_option = BoardFrame.Option.SINGLE_FRAME;
}
FRLogger.logger.info("Opening '"+startupOptions.design_file_name+"'...");
DesignFile design_file = DesignFile.get_instance(startupOptions.design_file_name, false);
FRLogger.logger.info("Opening '"+startupOptions.design_input_filename+"'...");
DesignFile design_file = DesignFile.get_instance(startupOptions.design_input_filename, false);
if (design_file == null)
{
System.out.print(resources.getString("message_6") + " ");
System.out.print(startupOptions.design_file_name);
System.out.println(" " + resources.getString("message_7"));
FRLogger.logger.error(resources.getString("message_6") + " " + startupOptions.design_input_filename + " " + resources.getString("message_7"));
return;
}
String message = resources.getString("loading_design") + " "
+ startupOptions.design_file_name;
+ startupOptions.design_input_filename;
WindowMessage welcome_window = WindowMessage.show(message);
final BoardFrame new_frame =
create_board_frame(design_file, null, board_option,
@ -95,12 +97,68 @@ public class MainApplication extends javax.swing.JFrame
welcome_window.dispose();
if (new_frame == null)
{
FRLogger.logger.error("Couldn't create window frame");
System.exit(1);
return;
}
new_frame.board_panel.board_handling.settings.autoroute_settings.set_stop_pass_no(new_frame.board_panel.board_handling.settings.autoroute_settings.get_start_pass_no() + startupOptions.max_passes - 1);
if (startupOptions.max_passes < 99999)
{
var thread = new_frame.board_panel.board_handling.start_batch_autorouter();
thread.addListener(new ThreadActionListener() {
@Override
public void autorouterStarted() {
}
@Override
public void autorouterAborted() {
ExportBoardToFile(startupOptions.design_output_filename);
}
@Override
public void autorouterFinished() {
ExportBoardToFile(startupOptions.design_output_filename);
}
private void ExportBoardToFile(String filename) {
if ((filename != null)
&& ((filename.toLowerCase().endsWith(".dsn"))
|| (filename.toLowerCase().endsWith(".ses"))
|| (filename.toLowerCase().endsWith(".scr")))) {
FRLogger.logger.info("Saving '" + filename + "'...");
try {
String filename_only = new File(filename).getName();
String design_name = filename_only.substring(0, filename_only.length() - 4);
java.io.OutputStream output_stream = new java.io.FileOutputStream(filename);
if (filename.toLowerCase().endsWith(".dsn")) {
new_frame.board_panel.board_handling.export_to_dsn_file(output_stream, design_name, false);
} else if (filename.toLowerCase().endsWith(".ses")) {
new_frame.board_panel.board_handling.export_specctra_session_file(design_name, output_stream);
} else if (filename.toLowerCase().endsWith(".scr")) {
java.io.ByteArrayOutputStream session_output_stream = new ByteArrayOutputStream();
new_frame.board_panel.board_handling.export_specctra_session_file(filename, session_output_stream);
java.io.InputStream input_stream = new ByteArrayInputStream(session_output_stream.toByteArray());
new_frame.board_panel.board_handling.export_eagle_session_file(input_stream, output_stream);
}
Runtime.getRuntime().exit(0);
} catch (Exception e) {
FRLogger.logger.error(e);
}
} else {
FRLogger.logger.error("Couldn't export board to '" + filename + "'.");
}
}
});
}
new_frame.addWindowListener(new java.awt.event.WindowAdapter()
{
@Override
public void windowClosed(java.awt.event.WindowEvent evt)
{

View File

@ -1,5 +1,7 @@
package eu.mihosoft.freerouting.gui;
import eu.mihosoft.freerouting.logger.FRLogger;
import java.util.Locale;
/**
@ -11,8 +13,10 @@ public class StartupOptions {
boolean test_version_option = false;
boolean session_file_option = false;
boolean webstart_option = false;
String design_file_name = null;
String design_dir_name = null;
String design_input_filename = null;
String design_output_filename = null;
String design_input_directory_name = null;
int max_passes = 99999;
java.util.Locale current_locale = java.util.Locale.ENGLISH;
private StartupOptions() {
@ -30,31 +34,42 @@ public class StartupOptions {
private void process(String[] p_args) {
for (int i = 0; i < p_args.length; ++i) {
if (p_args[i].startsWith("-de"))
// the design file is provided
{
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
single_design_option = true;
design_file_name = p_args[i + 1];
try {
if (p_args[i].startsWith("-de")) {
// the design file is provided
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
single_design_option = true;
design_input_filename = p_args[i + 1];
}
} else if (p_args[i].startsWith("-di")) {
// the design directory is provided
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
design_input_directory_name = p_args[i + 1];
}
} else if (p_args[i].startsWith("-do")) {
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
design_output_filename = p_args[i + 1];
}
} else if (p_args[i].startsWith("-mp")) {
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
max_passes = Integer.decode(p_args[i + 1]);
}
} else if (p_args[i].startsWith("-l")) {
// the locale is provided
if (p_args.length > i + 1 && p_args[i + 1].startsWith("d")) {
current_locale = java.util.Locale.GERMAN;
}
} else if (p_args[i].startsWith("-s")) {
session_file_option = true;
} else if (p_args[i].startsWith("-w")) {
webstart_option = true;
} else if (p_args[i].startsWith("-test")) {
test_version_option = true;
}
} else if (p_args[i].startsWith("-di"))
// the design directory is provided
}
catch (Exception e)
{
if (p_args.length > i + 1 && !p_args[i + 1].startsWith("-")) {
design_dir_name = p_args[i + 1];
}
} else if (p_args[i].startsWith("-l"))
// the locale is provided
{
if (p_args.length > i + 1 && p_args[i + 1].startsWith("d")) {
current_locale = java.util.Locale.GERMAN;
}
} else if (p_args[i].startsWith("-s")) {
session_file_option = true;
} else if (p_args[i].startsWith("-w")) {
webstart_option = true;
} else if (p_args[i].startsWith("-test")) {
test_version_option = true;
FRLogger.logger.error("There was a problem parsing the '"+p_args[i]+"' parameter", e);
}
}
}
@ -68,6 +83,6 @@ public class StartupOptions {
}
public String getDesignDir() {
return design_dir_name;
return design_input_directory_name;
}
}

View File

@ -23,7 +23,6 @@
*/
package eu.mihosoft.freerouting.gui;
import java.awt.*;
import java.text.DecimalFormat;
/**
@ -88,7 +87,7 @@ public class WindowAutorouteDetailParameter extends BoardSavableSubWindow
main_panel.add(start_pass_label);
start_pass_no = new javax.swing.JFormattedTextField(number_format);
start_pass_no.setColumns(4);
start_pass_no.setColumns(5);
this.start_pass_no.addKeyListener(new StartPassFieldKeyListener());
this.start_pass_no.addFocusListener(new StartPassFieldFocusListener());
gridbag_constraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
@ -214,7 +213,7 @@ public class WindowAutorouteDetailParameter extends BoardSavableSubWindow
this.via_cost_field.setValue(settings.get_via_costs());
this.plane_via_cost_field.setValue(settings.get_plane_via_costs());
this.start_ripup_costs.setValue(settings.get_start_ripup_costs());
this.start_pass_no.setValue(settings.get_pass_no());
this.start_pass_no.setValue(settings.get_start_pass_no());
for (int i = 0; i < preferred_direction_trace_cost_arr.length; ++i)
{
this.preferred_direction_trace_cost_arr[i].setValue(settings.get_preferred_direction_trace_costs(layer_structure.get_layer_no(i)));
@ -398,7 +397,7 @@ public class WindowAutorouteDetailParameter extends BoardSavableSubWindow
{
if (p_evt.getKeyChar() == '\n')
{
int old_value = board_handling.settings.autoroute_settings.get_pass_no();
int old_value = board_handling.settings.autoroute_settings.get_start_pass_no();
Object input = start_pass_no.getValue();
int input_value;
if (input instanceof Number)
@ -416,13 +415,23 @@ public class WindowAutorouteDetailParameter extends BoardSavableSubWindow
{
input_value = old_value;
}
board_handling.settings.autoroute_settings.set_pass_no(input_value);
start_pass_no.setValue(input_value);
set_start_pass_no(input_value);
}
}
}
public void set_start_pass_no(int input_value)
{
board_handling.settings.autoroute_settings.set_start_pass_no(input_value);
start_pass_no.setValue(input_value);
}
public void set_stop_pass_no(int input_value)
{
board_handling.settings.autoroute_settings.set_stop_pass_no(input_value);
}
private class StartPassFieldFocusListener implements java.awt.event.FocusListener
{

View File

@ -290,7 +290,7 @@ public class WindowAutorouteParameter extends BoardSavableSubWindow
{
eu.mihosoft.freerouting.interactive.AutorouteSettings autoroute_settings = board_handling.settings.autoroute_settings;
autoroute_settings.set_with_fanout(fanout_pass_button.isSelected());
autoroute_settings.set_pass_no(1);
autoroute_settings.set_start_pass_no(1);
}
}
@ -301,7 +301,7 @@ public class WindowAutorouteParameter extends BoardSavableSubWindow
{
eu.mihosoft.freerouting.interactive.AutorouteSettings autoroute_settings = board_handling.settings.autoroute_settings;
autoroute_settings.set_with_autoroute(autoroute_pass_button.isSelected());
autoroute_settings.set_pass_no(1);
autoroute_settings.set_start_pass_no(1);
}
}
@ -312,7 +312,7 @@ public class WindowAutorouteParameter extends BoardSavableSubWindow
{
eu.mihosoft.freerouting.interactive.AutorouteSettings autoroute_settings = board_handling.settings.autoroute_settings;
autoroute_settings.set_with_postroute(postroute_pass_button.isSelected());
autoroute_settings.set_pass_no(1);
autoroute_settings.set_start_pass_no(1);
}
}
}

View File

@ -135,17 +135,28 @@ public class AutorouteSettings implements java.io.Serializable
return start_ripup_costs;
}
public void set_pass_no(int p_value)
public void set_start_pass_no(int p_value)
{
start_pass_no = Math.max(p_value, 1);
start_pass_no = Math.min(start_pass_no, 99);
start_pass_no = Math.min(start_pass_no, 99999);
}
public int get_pass_no()
public int get_start_pass_no()
{
return start_pass_no;
}
public void set_stop_pass_no(int p_value)
{
stop_pass_no = Math.max(p_value, start_pass_no);
stop_pass_no = Math.min(stop_pass_no, 99999);
}
public int get_stop_pass_no()
{
return stop_pass_no;
}
public void increment_pass_no()
{
++start_pass_no;
@ -347,6 +358,7 @@ public class AutorouteSettings implements java.io.Serializable
private int plane_via_costs;
private int start_ripup_costs;
private int start_pass_no;
private int stop_pass_no;
private final boolean[] layer_active_arr;
private final boolean[] preferred_direction_is_horizontal_arr;
private final double[] preferred_direction_trace_cost_arr;

View File

@ -53,6 +53,9 @@ public class BatchAutorouterThread extends InteractiveActionThread
protected void thread_action()
{
for (ThreadActionListener hl : this.listeners)
hl.autorouterStarted();
FRLogger.traceEntry("BatchAutorouterThread.thread_action()");
try
@ -70,7 +73,7 @@ public class BatchAutorouterThread extends InteractiveActionThread
hdlg.screen_messages.set_status_message(start_message);
boolean fanout_first =
hdlg.get_settings().autoroute_settings.get_with_fanout() &&
hdlg.get_settings().autoroute_settings.get_pass_no() <= 1;
hdlg.get_settings().autoroute_settings.get_start_pass_no() <= 1;
if (fanout_first)
{
BatchFanout.fanout_board(this);
@ -133,6 +136,16 @@ public class BatchAutorouterThread extends InteractiveActionThread
}
FRLogger.traceExit("BatchAutorouterThread.thread_action()");
for (ThreadActionListener hl : this.listeners)
{
if (this.is_stop_requested()) {
hl.autorouterAborted();
}
else {
hl.autorouterFinished();
}
}
}
public void draw(java.awt.Graphics p_graphics)

View File

@ -43,6 +43,7 @@ import eu.mihosoft.freerouting.geometry.planar.PolylineShape;
import eu.mihosoft.freerouting.gui.BoardPanel;
import eu.mihosoft.freerouting.gui.ComboBoxLayer;
import eu.mihosoft.freerouting.logger.FRLogger;
import eu.mihosoft.freerouting.rules.BoardRules;
import eu.mihosoft.freerouting.board.LayerStructure;
import eu.mihosoft.freerouting.board.RoutingBoard;
@ -739,7 +740,7 @@ public class BoardHandling extends BoardHandlingImpl
{
// reset the start pass number in the autorouter in case
// a batch autorouter is undone.
this.settings.autoroute_settings.set_pass_no(1);
this.settings.autoroute_settings.set_start_pass_no(1);
}
screen_messages.set_status_message(resources.getString("undo"));
}
@ -1023,6 +1024,7 @@ public class BoardHandling extends BoardHandlingImpl
}
catch (Exception e)
{
FRLogger.logger.error(e);
return false;
}
board.set_test_level(p_test_level);
@ -1466,15 +1468,18 @@ public class BoardHandling extends BoardHandlingImpl
/**
* Start the batch autorouter on the whole Board
*/
public void start_batch_autorouter()
public InteractiveActionThread start_batch_autorouter()
{
if (board_is_read_only)
{
return;
return null;
}
board.generate_snapshot();
this.interactive_action_thread = InteractiveActionThread.get_batch_autorouter_instance(this);
this.interactive_action_thread.start();
return this.interactive_action_thread;
}
/**

View File

@ -205,7 +205,7 @@ public class ExpandTestState extends InteractiveState
this.control_settings = new AutorouteControl(hdlg.get_routing_board(), route_net_no, hdlg.settings);
// this.control_settings.ripup_allowed = true;
// this.control_settings.is_fanout = true;
this.control_settings.ripup_pass_no = hdlg.settings.autoroute_settings.get_pass_no();
this.control_settings.ripup_pass_no = hdlg.settings.autoroute_settings.get_start_pass_no();
this.control_settings.ripup_costs = this.control_settings.ripup_pass_no * hdlg.settings.autoroute_settings.get_start_ripup_costs();
this.control_settings.vias_allowed = false;
this.autoroute_engine = new AutorouteEngine(board, this.control_settings.trace_clearance_class_no, false);

View File

@ -23,6 +23,10 @@
*/
package eu.mihosoft.freerouting.interactive;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
/**
* Used for running an eu.mihosoft.freerouting.interactive action in a seperate Thread,
* that can be stopped by the user.
@ -31,6 +35,11 @@ package eu.mihosoft.freerouting.interactive;
*/
public abstract class InteractiveActionThread extends Thread implements eu.mihosoft.freerouting.datastructures.Stoppable
{
protected List<ThreadActionListener> listeners = new ArrayList<ThreadActionListener>();
public void addListener(ThreadActionListener toAdd) {
listeners.add(toAdd);
}
public static InteractiveActionThread get_autoroute_instance(BoardHandling p_board_handling)
{
@ -234,3 +243,4 @@ public abstract class InteractiveActionThread extends Thread implements eu.mihos
private final java.io.InputStream input_stream;
}
}

View File

@ -0,0 +1,7 @@
package eu.mihosoft.freerouting.interactive;
public interface ThreadActionListener {
void autorouterStarted();
void autorouterAborted();
void autorouterFinished();
}

View File

@ -29,6 +29,11 @@ public class FRLogger {
}
public static void traceExit(String perfId)
{
traceExit(perfId, null);
}
public static void traceExit(String perfId, Object result)
{
var timeElapsed = Duration.between(perfData.get(perfId.hashCode()), java.time.Instant.now()).toMillis();
@ -36,6 +41,6 @@ public class FRLogger {
if (timeElapsed < 0) {
timeElapsed = 0;
}
logger.trace("Method '" + perfId + "' was performed in " + performanceFormat.format(timeElapsed/1000.0) + " seconds.");
logger.trace("Method '" + perfId.replace("{}", result != null ? result.toString() : "(null)") + "' was performed in " + performanceFormat.format(timeElapsed/1000.0) + " seconds.");
}
}