feat(GUI): Implement route plotting and fuzzy search

Implement route plotting in GUI
Use batch fuzzy search to find systems
search all systems at once after adding
added some error checking
This commit is contained in:
Daniel S. 2019-08-05 00:05:44 +02:00
parent ec3972b06c
commit c290d5eb12
18 changed files with 1075 additions and 391 deletions

40
.chglog/CHANGELOG.tpl.md Normal file
View File

@ -0,0 +1,40 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
{{ range .Commits -}}
- {{ .Header }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
{{ range .Commits -}}
- {{ .Header }}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

13
.chglog/config.yml Normal file
View File

@ -0,0 +1,13 @@
style: gitlab
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://gitlab.com/Earthnuker/ed_lrr
options:
header:
pattern: "^(.*)$"
pattern_maps:
- Subject
notes:
keywords:
- BREAKING CHANGE

15
.gitignore vendored
View File

@ -1,15 +1,14 @@
/target
/dist
/build
rust/target
**/*.rs.bk
*.tmp
*.idx
.vscode/**
*.csv
*.router
plot.py
*.tmp
*.idx
*.pyd
build.bat
test.bat
__pycache__
DL
*.egg-info
pip-wheel-metadata
rust/target/*
build

View File

@ -1,2 +1,3 @@
include rust/Cargo.toml
include rust/Cargo.toml
include rust/.cargo/config
recursive-include rust/src *

11
build.bat Normal file
View File

@ -0,0 +1,11 @@
rm -rf build dist *.egg-info exe
python build_gui.py
pip uninstall -y ed_lrr_gui
pip install -I . setuptools_rust
python setup.py build
python setup.py bdist_wheel
python setup.py sdist
mkdir exe
cd exe
pyinstaller --noupx --noconsole --key="ED_LRR_GUI" --name ED_LRR_GUI ..\ed_lrr_gui\__main__.py
cd ..

View File

@ -1,2 +1,4 @@
from _ed_lrr import *
from . import gui
from .router import Router
from .preprocess import Preprocessor

View File

@ -2,21 +2,28 @@ import sys
import os
import requests as RQ
from datetime import datetime, timedelta
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt, QObject
from PyQt5.QtWidgets import (
QMainWindow,
QApplication,
QFileDialog,
QProgressDialog,
QTreeWidgetItem,
QAction,
QMessageBox,
)
from urllib.request import Request, urlopen
import gzip
import pathlib
from PyQt5.QtGui import QPalette, QColor
import ed_lrr_gui
import ed_lrr_gui.config as cfg
from ed_lrr_gui.gui.ed_lrr import Ui_ED_LRR
from ed_lrr_gui import Router, Preprocessor
import multiprocessing as MP
import queue
import csv
import _ed_lrr
def sizeof_fmt(num, suffix="B"):
@ -37,6 +44,85 @@ class ProgressDialog(QProgressDialog):
self.setWindowModality(Qt.WindowModal)
class Job(QObject):
progress = pyqtSignal("PyQt_PyObject")
def __init__(self, app, cls, *args, build_progress=None, **kwargs):
super().__init__()
self.job = cls(*args, **kwargs)
self.timer = QTimer(app)
self.app = app
self.timer.timeout.connect(self.interval)
self.timer.start(100)
self.last_val = None
self.build_progress = build_progress
self.progress_dialog = None
self.state = {}
def start(self):
"""
if self.diag_prog is None:
self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window)
if self.dl_thread:
self.diag_prog.canceled.connect(self.dl_canceled)
self.diag_prog.show()
t_elapsed = datetime.today() - self.dl_started
rate = args["done"] / t_elapsed.total_seconds()
remaining = (args["size"] - args["done"]) / rate
rate = round(rate, 2)
# print(rate, remaining)
try:
t_rem = timedelta(seconds=remaining)
except OverflowError:
t_rem = "-"
msg = "Downloading {} [{}/{}] ({}/s)\n[{}/{}]".format(
filename,
sizeof_fmt(args["done"]),
sizeof_fmt(args["size"]),
sizeof_fmt(rate),
t_round(t_elapsed),
t_round(t_rem),
)
self.diag_prog.setLabelText(msg)
self.diag_prog.setWindowTitle("Downloading EDSM Dumps")
self.diag_prog.setValue((args["done"] * 1000) // args["size"])
"""
self.started = datetime.today()
if self.build_progress:
self.progress_dialog = ProgressDialog("", "Cancel", 0, 1000, self.app)
self.progress_dialog.canceled.connect(self.cancel)
self.progress_dialog.show()
self.progress.connect(self.__build_progress)
else:
self.progress.connect(
lambda *args, **kwargs: print("PROGRESS:", *args, **kwargs)
)
return self.job.start()
def __build_progress(self, *args, **kwargs):
kwargs["self"] = self
return self.build_progress(*args, **kwargs)
def cancel(self):
self.job.terminate()
self.job = None
def done(self):
return (self.job.is_alive() == False) and (self.job.queue.empty())
def interval(self):
while True:
try:
res = self.job.queue.get(True, 0.1)
except queue.Empty:
return
if res == self.last_val:
continue
self.state.update(res)
self.progress.emit(self.state)
self.last_val = res
class DownloadThread(QThread):
progress = pyqtSignal("PyQt_PyObject")
@ -79,45 +165,65 @@ class App(QApplication):
def __init__(self):
super().__init__(sys.argv)
self.setStyle("Fusion")
self.set_pallete()
self.setup_styles()
def set_pallete(self):
colors = {
"Window": QColor(53, 53, 53),
"WindowText": Qt.white,
"Base": QColor(15, 15, 15),
"AlternateBase": QColor(53, 53, 53),
"ToolTipBase": Qt.white,
"ToolTipText": Qt.white,
"Text": Qt.white,
"Button": QColor(53, 53, 53),
"ButtonText": Qt.white,
"BrightText": Qt.red,
"Highlight": QColor(255, 128, 0),
"HighlightedText": Qt.black,
def set_style(self, style):
print("LOAD:", style)
self.setPalette(self.styles[style])
def setup_styles(self):
self.styles = {}
styles = {
"Dark": {
"Window": QColor(53, 53, 53),
"WindowText": Qt.white,
"Base": QColor(15, 15, 15),
"AlternateBase": QColor(53, 53, 53),
"ToolTipBase": Qt.white,
"ToolTipText": Qt.white,
"Text": Qt.white,
"Button": QColor(53, 53, 53),
"ButtonText": Qt.white,
"BrightText": Qt.red,
"Highlight": QColor(255, 128, 0),
"HighlightedText": Qt.black,
}
}
palette = QPalette()
for entry, color in colors.items():
palette.setColor(getattr(QPalette, entry), color)
if color == Qt.darkGray:
palette.setColor(
QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53)
)
else:
palette.setColor(
QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray
)
self.setPalette(palette)
for style, colors in styles.items():
palette = QPalette()
for entry, color in colors.items():
palette.setColor(getattr(QPalette, entry), color)
if color == Qt.darkGray:
palette.setColor(
QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53)
)
else:
palette.setColor(
QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray
)
self.styles[style] = palette
self.styles["Light"] = self.style().standardPalette()
class ED_LRR(Ui_ED_LRR):
dl_thread = None
diag_prog = None
dl_started = None
system_found = pyqtSignal("PyQt_PyObject")
def __init__(self):
super().__init__()
self.config = cfg.load()
self.jobs = {}
def start_job(self, cls, *args, **kwargs):
print("START JOB:", cls, args, kwargs)
name = cls.__name__
if name in self.jobs and self.jobs[name].done():
del self.jobs[name]
if not name in self.jobs:
self.jobs[name] = Job(self.app, cls, *args, **kwargs)
self.jobs[name].start()
def get_open_file(self, filetypes, callback=None):
fileName, _ = QFileDialog.getOpenFileName(
@ -148,6 +254,8 @@ class ED_LRR(Ui_ED_LRR):
self.config.history_out_path.append(path)
self.inp_sys_lst.addItem(path)
self.inp_out_pp.addItem(path)
self.inp_sys_lst.setCurrentText(path)
self.inp_out_pp.setCurrentText(path)
def set_bodies_file(self, path):
if path not in self.config.history_bodies_path:
@ -170,7 +278,7 @@ class ED_LRR(Ui_ED_LRR):
def set_comp_mode(self, _):
if self.rd_comp.isChecked():
comp_mode = "Compute Route"
self.btn_add.setText("Search+Add")
self.btn_add.setText("Add")
if self.rd_precomp.isChecked():
comp_mode = "Precompute Graph"
self.btn_add.setText("Select")
@ -178,7 +286,7 @@ class ED_LRR(Ui_ED_LRR):
self.lst_sys.setEnabled(self.rd_comp.isChecked())
self.btn_rm.setEnabled(self.rd_comp.isChecked())
self.cmb_mode.setEnabled(self.rd_comp.isChecked())
self.btn_permute.setEnabled(self.rd_comp.isChecked())
self.chk_permute.setEnabled(self.rd_comp.isChecked())
self.lbl_keep.setEnabled(self.rd_comp.isChecked())
self.lbl_mode.setEnabled(self.rd_comp.isChecked())
self.chk_permute_keep_first.setEnabled(self.rd_comp.isChecked())
@ -190,11 +298,17 @@ class ED_LRR(Ui_ED_LRR):
mode = self.cmb_mode.currentText()
self.lbl_greedyness.setEnabled(mode == "A*-Search")
self.sld_greedyness.setEnabled(mode == "A*-Search")
self.log("ROUTE_MODE", mode)
def set_greedyness(self, value):
self.lbl_greedyness.setText("Greedyness Factor ({:.0%})".format(value / 100))
@property
def systems(self):
ret = []
for n in range(self.lst_sys.topLevelItemCount()):
ret.append(self.sys_to_dict(n))
return ret
def sys_to_dict(self, n):
header = [
self.lst_sys.headerItem().data(c, 0)
@ -204,34 +318,108 @@ class ED_LRR(Ui_ED_LRR):
self.lst_sys.topLevelItem(n).data(c, 0)
for c in range(self.lst_sys.topLevelItem(n).columnCount())
]
return dict(zip(header, system))
ret = dict(zip(header, system))
ret["id"] = getattr(self.lst_sys.topLevelItem(n), "__id__", None)
ret.pop(None, None)
return ret
def error(self, msg):
QMessageBox.critical(self.main_window, "ERROR!", msg)
def get_sys_list(self):
if not self.inp_sys_lst.currentText():
self.error("System list is required!")
return
path = pathlib.Path(self.inp_sys_lst.currentText())
if not path.exists():
self.error("System list does not exist, run download and preprocess first!")
return
return path
def run(self):
settings = {}
settings["permute"] = (
self.chk_permute_keep_first.checkState(),
self.chk_permute_keep_last.checkState(),
if not all(s["Type"] for s in self.systems):
self.error('Not all systens have been resolved, please click "Search All"')
return
print(self.systems)
systems = [str(s["id"]) for s in self.systems]
jump_range = self.sb_range.value()
mode = self.cmb_mode.currentText()
primary = self.chk_primary.isChecked()
keep_first = self.chk_permute_keep_first.isChecked()
keep_last = self.chk_permute_keep_last.isChecked()
permute = self.chk_permute.isChecked()
greedyness = (
self.sld_greedyness.value() / 100
if self.sld_greedyness.isEnabled()
else None
)
settings["range"] = self.sb_range.value()
settings["systems"] = [
self.sys_to_dict(i) for i in range(self.lst_sys.topLevelItemCount())
]
print(settings)
progress = ProgressDialog(
"(Not actually computing)", "Cancel", 0, 0, self.main_window
path = self.get_sys_list()
if path is None:
return
precomp = path.with_suffix(".idx")
path = str(path)
if not precomp.exists():
precomp = None
else:
precomp = str(precomp)
mode = {
"Breadth-First Search": "bfs",
"A*-Search": "astar",
"Greedy-Search": "greedy",
}[mode]
print(
systems,
jump_range,
mode,
primary,
permute,
(keep_first, keep_last),
greedyness,
path,
precomp,
)
progress.setWindowTitle("Computing Route")
progress.setFixedSize(400, 100)
progress.setRange(0, 0)
progress.show()
self.start_job(
Router,
systems,
jump_range,
0.1,
mode,
primary,
permute,
keep_first,
keep_last,
greedyness,
precomp,
path,
)
def find_sys_by_names(self, names):
t_s = datetime.today()
if not self.get_sys_list():
return None
ret = _ed_lrr.find_sys(names, self.inp_sys_lst.currentText())
print("Took:", datetime.today() - t_s)
return ret
def resolve_systems(self):
names = []
for n in range(self.lst_sys.topLevelItemCount()):
names.append(self.sys_to_dict(n)["Name"])
systems = self.find_sys_by_names(names)
if systems is None:
return
for i, name in enumerate(names):
_, system = systems[name]
self.lst_sys.topLevelItem(i).setData(0, 0, system["system"])
self.lst_sys.topLevelItem(i).setData(1, 0, system["star_type"])
self.lst_sys.topLevelItem(i).__id__ = system["id"]
# diff, item = self.find_sys_by_name(name)
# print("Found", (diff, item))
def add_system(self):
n = self.lst_sys.topLevelItemCount() + 1
name = self.inp_sys.text()
item = QTreeWidgetItem(
self.lst_sys, [str(n) + ". Name: " + name, "Type: " + name[::-1]]
)
item.sys_id = "SYS_ID_HERE"
item = QTreeWidgetItem(self.lst_sys, [name, None])
item.resolved = False
item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)
def remove_system(self):
@ -253,7 +441,7 @@ class ED_LRR(Ui_ED_LRR):
self.diag_prog = None
self.dl_started = None
def handle_progress(self, args):
def handle_dl_progress(self, args):
filename = os.path.split(args["outfile"])[-1]
if self.diag_prog is None:
self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window)
@ -291,10 +479,15 @@ class ED_LRR(Ui_ED_LRR):
self.inp_bodies_dl.currentText(),
self.inp_bodies_dest_dl.currentText(),
)
self.dl_thread.progress.connect(self.handle_progress)
self.dl_thread.progress.connect(self.handle_dl_progress)
self.dl_thread.start()
print(".")
def update_permute_chk(self, state):
self.chk_permute_keep_first.setEnabled(state)
self.chk_permute_keep_last.setEnabled(state)
self.lbl_keep.setEnabled(state)
def setup_signals(self):
self.btn_download.clicked.connect(self.run_download)
self.inp_systems_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\s.json")
@ -307,6 +500,8 @@ class ED_LRR(Ui_ED_LRR):
self.btn_go.clicked.connect(self.run)
self.btn_add.clicked.connect(self.add_system)
self.btn_rm.clicked.connect(self.remove_system)
self.chk_permute.stateChanged.connect(self.update_permute_chk)
self.btn_search.clicked.connect(self.resolve_systems)
self.btn_out_browse_pp.clicked.connect(
lambda: self.get_save_file("CSV File (*.csv)", self.set_sys_lst)
)
@ -331,6 +526,14 @@ class ED_LRR(Ui_ED_LRR):
cfg.write(self.config)
print("BYEEEEEE!")
def setup_styles(self, win, app):
for name in app.styles:
action = QAction(app)
action.setObjectName("action_load_style_" + name)
action.setText(name)
action.triggered.connect(lambda _, name=name: app.set_style(name))
self.menuStyle.addAction(action)
def setupUi(self, MainWindow, app):
super().setupUi(MainWindow)
self.update_dropdowns()
@ -339,6 +542,8 @@ class ED_LRR(Ui_ED_LRR):
self.setup_signals()
self.lst_sys.setHeaderLabels(["Name", "Type"])
self.set_route_mode()
self.update_permute_chk(self.chk_permute.isChecked())
self.setup_styles(MainWindow, app)
def main():

View File

@ -26,6 +26,14 @@ class Ui_ED_LRR(object):
ED_LRR.setDocumentMode(False)
ED_LRR.setTabShape(QtWidgets.QTabWidget.Rounded)
self.centralwidget = QtWidgets.QWidget(ED_LRR)
<<<<<<< Updated upstream
=======
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
>>>>>>> Stashed changes
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
@ -214,9 +222,12 @@ class Ui_ED_LRR(object):
self.rd_precomp.setObjectName("rd_precomp")
self.gr_mode.addWidget(self.rd_precomp, 0, 2, 1, 1)
self.formLayout_2.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.gr_mode)
<<<<<<< Updated upstream
self.btn_permute = QtWidgets.QPushButton(self.tab_route)
self.btn_permute.setObjectName("btn_permute")
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.btn_permute)
=======
>>>>>>> Stashed changes
self.lst_sys = QtWidgets.QTreeWidget(self.tab_route)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
@ -231,6 +242,7 @@ class Ui_ED_LRR(object):
self.lst_sys.setAlternatingRowColors(True)
self.lst_sys.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.lst_sys.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
<<<<<<< Updated upstream
self.lst_sys.setHeaderHidden(True)
self.lst_sys.setObjectName("lst_sys")
self.lst_sys.headerItem().setText(0, "1")
@ -241,6 +253,19 @@ class Ui_ED_LRR(object):
self.lbl_range = QtWidgets.QLabel(self.tab_route)
self.lbl_range.setObjectName("lbl_range")
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.lbl_range)
=======
self.lst_sys.setHeaderHidden(False)
self.lst_sys.setObjectName("lst_sys")
self.lst_sys.headerItem().setText(0, "Name")
self.lst_sys.header().setVisible(False)
self.formLayout_2.setWidget(7, QtWidgets.QFormLayout.SpanningRole, self.lst_sys)
self.sb_range = QtWidgets.QDoubleSpinBox(self.tab_route)
self.sb_range.setObjectName("sb_range")
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.sb_range)
self.lbl_range = QtWidgets.QLabel(self.tab_route)
self.lbl_range.setObjectName("lbl_range")
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.lbl_range)
>>>>>>> Stashed changes
self.gr_opts = QtWidgets.QGridLayout()
self.gr_opts.setObjectName("gr_opts")
self.cmb_mode = QtWidgets.QComboBox(self.tab_route)
@ -268,16 +293,28 @@ class Ui_ED_LRR(object):
self.lbl_mode = QtWidgets.QLabel(self.tab_route)
self.lbl_mode.setObjectName("lbl_mode")
self.gr_opts.addWidget(self.lbl_mode, 0, 1, 1, 1)
<<<<<<< Updated upstream
self.formLayout_2.setLayout(9, QtWidgets.QFormLayout.SpanningRole, self.gr_opts)
self.btn_go = QtWidgets.QPushButton(self.tab_route)
self.btn_go.setFlat(False)
self.btn_go.setObjectName("btn_go")
self.formLayout_2.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.btn_go)
=======
self.formLayout_2.setLayout(10, QtWidgets.QFormLayout.SpanningRole, self.gr_opts)
self.btn_go = QtWidgets.QPushButton(self.tab_route)
self.btn_go.setFlat(False)
self.btn_go.setObjectName("btn_go")
self.formLayout_2.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.btn_go)
>>>>>>> Stashed changes
self.gridLayout_4 = QtWidgets.QGridLayout()
self.gridLayout_4.setObjectName("gridLayout_4")
self.chk_permute_keep_last = QtWidgets.QCheckBox(self.tab_route)
self.chk_permute_keep_last.setObjectName("chk_permute_keep_last")
<<<<<<< Updated upstream
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 2, 1, 1)
=======
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 3, 1, 1)
>>>>>>> Stashed changes
self.chk_permute_keep_first = QtWidgets.QCheckBox(self.tab_route)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@ -286,7 +323,11 @@ class Ui_ED_LRR(object):
self.chk_permute_keep_first.setSizePolicy(sizePolicy)
self.chk_permute_keep_first.setTristate(False)
self.chk_permute_keep_first.setObjectName("chk_permute_keep_first")
<<<<<<< Updated upstream
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 1, 1, 1)
=======
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 2, 1, 1)
>>>>>>> Stashed changes
self.lbl_keep = QtWidgets.QLabel(self.tab_route)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
@ -294,8 +335,19 @@ class Ui_ED_LRR(object):
sizePolicy.setHeightForWidth(self.lbl_keep.sizePolicy().hasHeightForWidth())
self.lbl_keep.setSizePolicy(sizePolicy)
self.lbl_keep.setObjectName("lbl_keep")
<<<<<<< Updated upstream
self.gridLayout_4.addWidget(self.lbl_keep, 0, 0, 1, 1)
self.formLayout_2.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_4)
=======
self.gridLayout_4.addWidget(self.lbl_keep, 0, 1, 1, 1)
self.formLayout_2.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_4)
self.chk_permute = QtWidgets.QCheckBox(self.tab_route)
self.chk_permute.setObjectName("chk_permute")
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.chk_permute)
self.btn_search = QtWidgets.QPushButton(self.tab_route)
self.btn_search.setObjectName("btn_search")
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.btn_search)
>>>>>>> Stashed changes
self.tabs.addTab(self.tab_route, "")
self.tab_log = QtWidgets.QWidget()
self.tab_log.setObjectName("tab_log")
@ -317,14 +369,32 @@ class Ui_ED_LRR(object):
self.menu.setObjectName("menu")
self.menu_file = QtWidgets.QMenu(self.menu)
self.menu_file.setObjectName("menu_file")
<<<<<<< Updated upstream
=======
self.menuWindow = QtWidgets.QMenu(self.menu)
self.menuWindow.setObjectName("menuWindow")
self.menuStyle = QtWidgets.QMenu(self.menuWindow)
self.menuStyle.setObjectName("menuStyle")
>>>>>>> Stashed changes
ED_LRR.setMenuBar(self.menu)
self.bar_status = QtWidgets.QStatusBar(ED_LRR)
self.bar_status.setObjectName("bar_status")
ED_LRR.setStatusBar(self.bar_status)
self.menu_act_quit = QtWidgets.QAction(ED_LRR)
self.menu_act_quit.setObjectName("menu_act_quit")
<<<<<<< Updated upstream
self.menu_file.addAction(self.menu_act_quit)
self.menu.addAction(self.menu_file.menuAction())
=======
self.actionA = QtWidgets.QAction(ED_LRR)
self.actionA.setObjectName("actionA")
self.actionB = QtWidgets.QAction(ED_LRR)
self.actionB.setObjectName("actionB")
self.menu_file.addAction(self.menu_act_quit)
self.menuWindow.addAction(self.menuStyle.menuAction())
self.menu.addAction(self.menu_file.menuAction())
self.menu.addAction(self.menuWindow.menuAction())
>>>>>>> Stashed changes
self.retranslateUi(ED_LRR)
self.tabs.setCurrentIndex(2)
@ -358,12 +428,20 @@ class Ui_ED_LRR(object):
self.tabs.setTabText(self.tabs.indexOf(self.tab_preprocess), _translate("ED_LRR", "Preprocess"))
self.lbl_sys_lst.setText(_translate("ED_LRR", "System List"))
self.btn_sys_lst_browse.setText(_translate("ED_LRR", "..."))
<<<<<<< Updated upstream
self.btn_add.setText(_translate("ED_LRR", "Search+Add"))
=======
self.btn_add.setText(_translate("ED_LRR", "Add"))
>>>>>>> Stashed changes
self.inp_sys.setPlaceholderText(_translate("ED_LRR", "System Name"))
self.btn_rm.setText(_translate("ED_LRR", "Remove"))
self.rd_comp.setText(_translate("ED_LRR", "Compute Route"))
self.rd_precomp.setText(_translate("ED_LRR", "Precompute Graph"))
<<<<<<< Updated upstream
self.btn_permute.setText(_translate("ED_LRR", "Permute"))
=======
self.lst_sys.headerItem().setText(1, _translate("ED_LRR", "Type"))
>>>>>>> Stashed changes
self.lbl_range.setText(_translate("ED_LRR", "Jump Range (Ly)"))
self.cmb_mode.setCurrentText(_translate("ED_LRR", "Breadth-First Search"))
self.cmb_mode.setItemText(0, _translate("ED_LRR", "Breadth-First Search"))
@ -376,8 +454,22 @@ class Ui_ED_LRR(object):
self.chk_permute_keep_last.setText(_translate("ED_LRR", "Last"))
self.chk_permute_keep_first.setText(_translate("ED_LRR", "First"))
self.lbl_keep.setText(_translate("ED_LRR", "Keep Endpoints:"))
<<<<<<< Updated upstream
self.tabs.setTabText(self.tabs.indexOf(self.tab_route), _translate("ED_LRR", "Route"))
self.tabs.setTabText(self.tabs.indexOf(self.tab_log), _translate("ED_LRR", "Log"))
self.menu_file.setTitle(_translate("ED_LRR", "File"))
self.menu_act_quit.setText(_translate("ED_LRR", "Quit"))
self.menu_act_quit.setShortcut(_translate("ED_LRR", "Ctrl+Q"))
=======
self.chk_permute.setText(_translate("ED_LRR", "Permute"))
self.btn_search.setText(_translate("ED_LRR", "Search All"))
self.tabs.setTabText(self.tabs.indexOf(self.tab_route), _translate("ED_LRR", "Route"))
self.tabs.setTabText(self.tabs.indexOf(self.tab_log), _translate("ED_LRR", "Log"))
self.menu_file.setTitle(_translate("ED_LRR", "File"))
self.menuWindow.setTitle(_translate("ED_LRR", "Window"))
self.menuStyle.setTitle(_translate("ED_LRR", "Style"))
self.menu_act_quit.setText(_translate("ED_LRR", "Quit"))
self.menu_act_quit.setShortcut(_translate("ED_LRR", "Ctrl+Q"))
self.actionA.setText(_translate("ED_LRR", "A"))
self.actionB.setText(_translate("ED_LRR", "B"))
>>>>>>> Stashed changes

View File

@ -44,6 +44,15 @@
<enum>QTabWidget::Rounded</enum>
</property>
<widget class="QWidget" name="centralwidget">
<<<<<<< Updated upstream
=======
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
>>>>>>> Stashed changes
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabs">
@ -363,7 +372,11 @@
<item row="1" column="0">
<widget class="QPushButton" name="btn_add">
<property name="text">
<<<<<<< Updated upstream
<string>Search+Add</string>
=======
<string>Add</string>
>>>>>>> Stashed changes
</property>
</widget>
</item>
@ -402,6 +415,7 @@
</item>
</layout>
</item>
<<<<<<< Updated upstream
<item row="4" column="0">
<widget class="QPushButton" name="btn_permute">
<property name="text">
@ -409,6 +423,8 @@
</property>
</widget>
</item>
=======
>>>>>>> Stashed changes
<item row="7" column="0" colspan="2">
<widget class="QTreeWidget" name="lst_sys">
<property name="sizePolicy">
@ -445,26 +461,53 @@
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="headerHidden">
<<<<<<< Updated upstream
<bool>true</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
=======
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">Name</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
>>>>>>> Stashed changes
</property>
</column>
</widget>
</item>
<<<<<<< Updated upstream
<item row="8" column="1">
<widget class="QDoubleSpinBox" name="sb_range"/>
</item>
<item row="8" column="0">
=======
<item row="9" column="1">
<widget class="QDoubleSpinBox" name="sb_range"/>
</item>
<item row="9" column="0">
>>>>>>> Stashed changes
<widget class="QLabel" name="lbl_range">
<property name="text">
<string>Jump Range (Ly)</string>
</property>
</widget>
</item>
<<<<<<< Updated upstream
<item row="9" column="0" colspan="2">
=======
<item row="10" column="0" colspan="2">
>>>>>>> Stashed changes
<layout class="QGridLayout" name="gr_opts">
<item row="0" column="2">
<widget class="QComboBox" name="cmb_mode">
@ -536,7 +579,11 @@
</item>
</layout>
</item>
<<<<<<< Updated upstream
<item row="10" column="0">
=======
<item row="11" column="0">
>>>>>>> Stashed changes
<widget class="QPushButton" name="btn_go">
<property name="text">
<string>GO!</string>
@ -548,14 +595,22 @@
</item>
<item row="4" column="1">
<layout class="QGridLayout" name="gridLayout_4">
<<<<<<< Updated upstream
<item row="0" column="2">
=======
<item row="0" column="3">
>>>>>>> Stashed changes
<widget class="QCheckBox" name="chk_permute_keep_last">
<property name="text">
<string>Last</string>
</property>
</widget>
</item>
<<<<<<< Updated upstream
<item row="0" column="1">
=======
<item row="0" column="2">
>>>>>>> Stashed changes
<widget class="QCheckBox" name="chk_permute_keep_first">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@ -571,7 +626,11 @@
</property>
</widget>
</item>
<<<<<<< Updated upstream
<item row="0" column="0">
=======
<item row="0" column="1">
>>>>>>> Stashed changes
<widget class="QLabel" name="lbl_keep">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -586,6 +645,23 @@
</item>
</layout>
</item>
<<<<<<< Updated upstream
=======
<item row="4" column="0">
<widget class="QCheckBox" name="chk_permute">
<property name="text">
<string>Permute</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QPushButton" name="btn_search">
<property name="text">
<string>Search All</string>
</property>
</widget>
</item>
>>>>>>> Stashed changes
</layout>
</widget>
<widget class="QWidget" name="tab_log">
@ -633,7 +709,23 @@
</property>
<addaction name="menu_act_quit"/>
</widget>
<<<<<<< Updated upstream
<addaction name="menu_file"/>
=======
<widget class="QMenu" name="menuWindow">
<property name="title">
<string>Window</string>
</property>
<widget class="QMenu" name="menuStyle">
<property name="title">
<string>Style</string>
</property>
</widget>
<addaction name="menuStyle"/>
</widget>
<addaction name="menu_file"/>
<addaction name="menuWindow"/>
>>>>>>> Stashed changes
</widget>
<widget class="QStatusBar" name="bar_status"/>
<action name="menu_act_quit">
@ -644,6 +736,19 @@
<string>Ctrl+Q</string>
</property>
</action>
<<<<<<< Updated upstream
=======
<action name="actionA">
<property name="text">
<string>A</string>
</property>
</action>
<action name="actionB">
<property name="text">
<string>B</string>
</property>
</action>
>>>>>>> Stashed changes
</widget>
<tabstops>
<tabstop>rd_comp</tabstop>

View File

@ -14,24 +14,13 @@ class Preprocessor(Process):
self.args = args
self.kwargs = kwargs
self.kwargs["callback"] = self.callback
self.start()
def __iter__(self):
while self.is_alive():
try:
self.state.update(self.queue.get(True, 0.5))
yield self.state
except queue.Empty:
pass
while not self.queue.empty():
self.state.update(self.queue.get(True, 0.5))
yield self.state
def callback(self, state):
self.queue.put({"status": state})
def run(self):
_ed_lrr.preprocess(*self.args, **self.kwargs)
res = _ed_lrr.preprocess(*self.args, **self.kwargs)
self.queue.put({"result": res})
if __name__ == "__main__":

View File

@ -14,18 +14,6 @@ class Router(Process):
self.args = args
self.kwargs = kwargs
self.kwargs["callback"] = self.callback
self.start()
def __iter__(self):
while self.is_alive():
try:
self.state.update(self.queue.get(True, 0.5))
yield self.state
except queue.Empty:
pass
while not self.queue.empty():
self.state.update(self.queue.get(True, 0.5))
yield self.state
def callback(self, state):
self.queue.put({"status": state})

88
rust/Cargo.lock generated
View File

@ -168,10 +168,19 @@ dependencies = [
"indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
<<<<<<< Updated upstream:rust/Cargo.lock
"rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
@ -205,6 +214,19 @@ dependencies = [
[[package]]
name = "ghost"
version = "0.1.0"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "heck"
version = "0.3.1"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
@ -294,7 +316,11 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
<<<<<<< Updated upstream:rust/Cargo.lock
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
@ -302,7 +328,11 @@ name = "mashup-impl"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
<<<<<<< Updated upstream:rust/Cargo.lock
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -377,15 +407,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-hack"
<<<<<<< Updated upstream:rust/Cargo.lock
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
name = "proc-macro-hack-impl"
<<<<<<< Updated upstream:rust/Cargo.lock
version = "0.4.1"
=======
version = "0.4.2"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -592,7 +633,7 @@ dependencies = [
[[package]]
name = "rstar"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -679,6 +720,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "spin"
version = "0.5.0"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "structopt"
version = "0.2.18"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -748,6 +807,11 @@ name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.7"
@ -792,6 +856,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5297b71943dc9fea26a3241b178c140ee215798b7f79f7773fd61683e25bca74"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
>>>>>>> Stashed changes:Cargo.lock
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c"
"checksum inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21df85981fe094480bc2267723d3dc0fd1ae0d1f136affc659b7398be615d922"
@ -813,8 +881,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27"
"checksum permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c"
<<<<<<< Updated upstream:rust/Cargo.lock
"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8"
"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a"
=======
"checksum proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b"
"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842"
>>>>>>> Stashed changes:Cargo.lock
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d09e6e2d3fa5ae1a8af694f865e03e763e730768b16e3097851ff0b7f2276086"
"checksum pyo3-derive-backend 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d7ae8ab3017515cd7c82d88ce49b55e12a56c602dc69993e123da45c91b186"
@ -837,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd"
"checksum regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed09217220c272b29ef237a974ad58515bde75f194e3ffa7e6d0bf0f3b01f86"
"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48"
"checksum rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd08ae4f9661517777346592956ea6cdbba2895a28037af7daa600382f4b4001"
"checksum rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90bb34cd8efef7ed3ebfb29e713e51301c3e60fba37c3e9185a1afaf9ce643a"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
@ -849,6 +922,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6"
"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7"
"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107"
>>>>>>> Stashed changes:Cargo.lock
"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c"
"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330"
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
@ -858,6 +938,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
>>>>>>> Stashed changes:Cargo.lock
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@ -11,12 +11,12 @@ license = "WTFPL"
crate-type = ["cdylib"]
[profile.release]
debug=true
#debug=true
[dependencies]
csv = "1.1.1"
serde = { version = "1.0", features = ["derive"] }
rstar = "0.4.0"
rstar = "0.5.0"
humantime = "1.2.0"
permutohedron = "0.2.4"
serde_json = "1.0.40"
@ -25,6 +25,7 @@ fnv = "1.0.6"
bincode = "1.1.4"
sha3 = "0.8.2"
byteorder = "1.3.2"
strsim = "0.9.2"
[dependencies.pyo3]
version = "0.7.0"

View File

@ -27,6 +27,7 @@ impl SystemSerde {
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct System {
pub id: u32,
@ -37,3 +38,16 @@ pub struct System {
pub distance: u32,
pub pos: [f32; 3],
}
impl Ord for System {
fn cmp(&self, other: &Self) -> Ordering {
return self.id.cmp(&other.id);
}
}
impl PartialOrd for System {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other));
}
}

View File

@ -1,14 +1,43 @@
extern crate strsim;
mod common;
mod preprocess;
mod route;
use pyo3::prelude::*;
use pyo3::types::{PyDict,PyList};
use std::collections::HashMap;
use pyo3::exceptions::*;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
use std::path::PathBuf;
use common::{System,SystemSerde};
fn find_matches(path:&PathBuf,names:Vec<String>) -> Result<HashMap<String,(f64,Option<System>)>,String> {
let mut best: HashMap<String,(f64,Option<System>)> = HashMap::new();
for name in &names {
best.insert(name.to_string(),(0.0,None));
};
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(rdr) => rdr,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
}
};
let systems=reader
.deserialize::<SystemSerde>();
for sys in systems {
let sys=sys.unwrap();
for name in &names {
best.entry(name.clone()).and_modify(|ent| {
let d1=strsim::normalized_levenshtein(&sys.system,&name);
if d1>ent.0 {
*ent=(d1,Some(sys.build()))
}
});
}
}
return Ok(best);
}
#[pymodule]
pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
/// preprocess(infile_systems, infile_bodies, outfile, callback)
/// --
///
@ -24,108 +53,139 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
use preprocess::*;
let state = PyDict::new(py);
let state_dict = PyDict::new(py);
callback.call(py,(state_dict,),None).unwrap();
callback.call(py, (state_dict,), None).unwrap();
let callback_wrapped = move |state: &PreprocessState| {
// println!("SEND: {:?}",state);
state_dict.set_item("file",state.file.clone())?;
state_dict.set_item("total",state.total)?;
state_dict.set_item("done",state.done)?;
callback.call(py,(state_dict,),None)
state_dict.set_item("file", state.file.clone())?;
state_dict.set_item("total", state.total)?;
state_dict.set_item("count", state.count)?;
state_dict.set_item("done", state.done)?;
state_dict.set_item("message", state.message.clone())?;
callback.call(py, (state_dict,), None)
};
preprocess_files(&PathBuf::from(infile_bodies), &PathBuf::from(infile_systems), &PathBuf::from(outfile), Box::new(callback_wrapped)).unwrap();
return Ok(state.to_object(py));
preprocess_files(
&PathBuf::from(infile_bodies),
&PathBuf::from(infile_systems),
&PathBuf::from(outfile),
Box::new(callback_wrapped),
)
.unwrap();
Ok(state.to_object(py))
}
/// route(infile, hops, range, mode,primary, greedyness, precomp, callback)
///find_sys(sys_names,sys_list)
/// --
///
/// Find system by name
#[pyfn(m, "find_sys")]
fn find_sys(py:Python,sys_names:Vec<String>,sys_list:String) -> PyResult<PyObject> {
let path=PathBuf::from(sys_list);
match find_matches(&path,sys_names) {
Ok(vals) => {
let ret=PyDict::new(py);
for (key,(diff,sys)) in vals {
let ret_dict=PyDict::new(py);
if let Some(val)= sys {
let pos = PyList::new(py, val.pos.iter());
ret_dict.set_item("star_type", val.star_type.clone())?;
ret_dict.set_item("system", val.system.clone())?;
ret_dict.set_item("body", val.body.clone())?;
ret_dict.set_item("distance", val.distance)?;
ret_dict.set_item("pos", pos)?;
ret_dict.set_item("id", val.id)?;
ret.set_item(key,(diff,ret_dict).to_object(py))?;
}
}
return Ok(ret.to_object(py));
},
Err(e) => {
return Err(PyErr::new::<ValueError, _>(e));
}
}
}
/// route(infile, hops, range, radius_mult, mode,primary, greedyness, precomp, callback)
/// --
///
/// Compute a Route using the suplied parameters
#[pyfn(m, "route")]
#[allow(clippy::too_many_arguments)]
fn route(
py: Python<'static>,
hops: Vec<String>,
range: f32,
radius_mult: f32,
mode: String,
primary: bool,
permute: bool,
keep_first: bool,
keep_last: bool,
greedyness: Option<f32>,
precomp: Option<String>,
path: String,
callback: PyObject,
) -> PyResult<PyObject> {
use route::*;
// TODO: Verify Parameters
let mode = match Mode::parse(&mode) {
Ok(val) => val,
Err(e) => {
return Err(PyErr::new::<ValueError,_>(e));
return Err(PyErr::new::<ValueError, _>(e));
}
};
let state_dict = PyDict::new(py);
callback.call(py,(state_dict,),None).unwrap();
callback.call(py, (state_dict,), None).unwrap();
let callback_wrapped = move |state: &SearchState| {
println!("SEND: {:?}",state);
state_dict.set_item("mode",state.mode.clone())?;
state_dict.set_item("system",state.system.clone())?;
state_dict.set_item("body",state.body.clone())?;
state_dict.set_item("depth",state.depth)?;
state_dict.set_item("queue_size",state.queue_size)?;
state_dict.set_item("d_rem",state.d_rem)?;
state_dict.set_item("d_total",state.d_total)?;
state_dict.set_item("prc_done",state.prc_done)?;
state_dict.set_item("n_seen",state.n_seen)?;
state_dict.set_item("prc_seen",state.prc_seen)?;
callback.call(py,(state_dict,),None)
state_dict.set_item("mode", state.mode.clone())?;
state_dict.set_item("system", state.system.clone())?;
state_dict.set_item("body", state.body.clone())?;
state_dict.set_item("depth", state.depth)?;
state_dict.set_item("queue_size", state.queue_size)?;
state_dict.set_item("d_rem", state.d_rem)?;
state_dict.set_item("d_total", state.d_total)?;
state_dict.set_item("prc_done", state.prc_done)?;
state_dict.set_item("n_seen", state.n_seen)?;
state_dict.set_item("prc_seen", state.prc_seen)?;
callback.call(py, (state_dict,), None)
};
let opts=RouteOpts{
systems:hops,
range:Some(range),
let mut systems=Vec::new();
for sys in hops {
systems.push(route::SysEntry::parse(&sys))
}
println!("SYSTEMS: {:?}",systems);
let opts = RouteOpts {
systems,
range: Some(range),
file_path: PathBuf::from(path),
precomp_file: precomp.map(PathBuf::from),
callback: Box::new(callback_wrapped),
mode,
factor: greedyness,
precompute: false,
permute: permute,
keep_first: true,
keep_last: true,
primary
permute,
keep_first,
keep_last,
primary,
radius_mult
};
let none=().to_object(py);
let none = ().to_object(py);
match route(opts) {
Ok(Some(route)) => {
let hops=route.iter().map(|hop| {
let pos=PyList::new(py,hop.pos.iter());
let elem=PyDict::new(py);
elem.set_item("star_type",hop.star_type.clone()).unwrap();
elem.set_item("system",hop.system.clone()).unwrap();
elem.set_item("body",hop.body.clone()).unwrap();
elem.set_item("distance",hop.distance).unwrap();
elem.set_item("pos",pos).unwrap();
return elem;
let hops = route.iter().map(|hop| {
let pos = PyList::new(py, hop.pos.iter());
let elem = PyDict::new(py);
elem.set_item("star_type", hop.star_type.clone()).unwrap();
elem.set_item("system", hop.system.clone()).unwrap();
elem.set_item("body", hop.body.clone()).unwrap();
elem.set_item("distance", hop.distance).unwrap();
elem.set_item("pos", pos).unwrap();
elem
});
let lst=PyList::new(py,hops);
return Ok(lst.to_object(py));
let lst = PyList::new(py, hops);
Ok(lst.to_object(py))
}
Ok(None) => {
return Ok(none);
},
Err(e) => {
return Err(PyErr::new::<ValueError,_>(e));
}
};
/*
let state = PyDict::new(py);
state.set_item("infile", infile)?;
state.set_item("source", source)?;
state.set_item("dest", dest)?;
state.set_item("range", range)?;
state.set_item("mode", mode)?;
state.set_item("greedyness", greedyness)?;
state.set_item("precomp", precomp)?;
state.set_item("callback", callback)?;
return callback.call(py,(state.to_object(py),),None);
*/
Ok(None) => Ok(none),
Err(e) => Err(PyErr::new::<ValueError, _>(e)),
}
}
Ok(())
}

View File

@ -1,8 +1,10 @@
use crate::common::{System, SystemSerde};
use core::cmp::Ordering;
use csv::StringRecord;
use fnv::{FnvHashMap, FnvHashSet};
use humantime::format_duration;
use permutohedron::LexicalPermutation;
use pyo3::prelude::*;
use rstar::{PointDistance, RTree, RTreeObject, AABB};
use sha3::{Digest, Sha3_256};
use std::collections::VecDeque;
@ -12,8 +14,6 @@ use std::io::Seek;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;
use std::time::Instant;
use pyo3::prelude::*;
use crate::common::{System, SystemSerde};
#[derive(Debug)]
pub struct SearchState {
@ -26,9 +26,23 @@ pub struct SearchState {
pub d_total: f32,
pub prc_done: f32,
pub n_seen: usize,
pub prc_seen: f32
pub prc_seen: f32,
}
#[derive(Debug, Clone)]
pub enum SysEntry {
ID(u32),
Name(String),
}
impl SysEntry {
pub fn parse(s: &str) -> SysEntry {
match s.parse() {
Ok(n) => SysEntry::ID(n),
_ => SysEntry::Name(s.to_owned()),
}
}
}
pub struct RouteOpts {
pub range: Option<f32>,
@ -41,8 +55,9 @@ pub struct RouteOpts {
pub keep_last: bool,
pub factor: Option<f32>,
pub mode: Mode,
pub systems: Vec<String>,
pub callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>
pub radius_mult: f32,
pub systems: Vec<SysEntry>,
pub callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
}
#[derive(Debug)]
@ -52,9 +67,9 @@ pub enum Mode {
AStar,
}
impl Mode {
impl Mode {
pub fn parse(s: &str) -> Result<Mode, String> {
let s=s.to_lowercase();
let s = s.to_lowercase();
match s.as_ref() {
"bfs" => Ok(Mode::BFS),
"greedy" => Ok(Mode::Greedy),
@ -160,8 +175,8 @@ impl LineCache {
}
pub fn get(&mut self, id: u32) -> Option<System> {
let pos = self.cache[id as usize];
self.file.seek(std::io::SeekFrom::Start(pos)).unwrap();
let rec = Self::read_record(&mut self.file).unwrap();
self.file.seek(std::io::SeekFrom::Start(pos)).ok()?;
let rec = Self::read_record(&mut self.file)?;
let sys: SystemSerde = rec.deserialize(self.header.as_ref()).unwrap();
Some(sys.build())
}
@ -175,18 +190,26 @@ pub struct Router {
range: f32,
primary_only: bool,
path: PathBuf,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>
radius_mult: f32,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
}
impl Router {
pub fn new(path: &PathBuf, range: f32, primary_only: bool,callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>) -> Result<Self,String> {
pub fn new(
path: &PathBuf,
range: f32,
radius_mult: f32,
primary_only: bool,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
) -> Result<Self, String> {
let mut scoopable = FnvHashSet::default();
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(rdr) => rdr,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
let t_load = Instant::now();
println!("Loading {}...", path.to_str().unwrap());
let systems: Vec<System> = reader
.deserialize::<SystemSerde>()
@ -220,25 +243,54 @@ impl Router {
primary_only,
cache: LineCache::new(path),
path: path.clone(),
callback: callback,
callback,
radius_mult,
};
println!(
"{} Systems loaded in {}",
ret.tree.size(),
format_duration(t_load.elapsed())
);
Ok(ret)
}
pub fn from_file(filename: &PathBuf,callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>) -> Result<(PathBuf, Self),String> {
let mut reader = BufReader::new(File::open(&filename).unwrap());
pub fn from_file(
filename: &PathBuf,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
) -> Result<(PathBuf, Self), String> {
let t_load = Instant::now();
let mut reader = BufReader::new(match File::open(&filename) {
Ok(fh) => fh,
Err(e) => {
return Err(format!(
"Error opening file {}: {}",
filename.to_str().unwrap(),
e
))
}
});
println!("Loading {}", filename.to_str().unwrap());
let (primary, range, file_hash, path, route_tree): (
bool,
f32,
Vec<u8>,
String,
FnvHashMap<u32, u32>,
) = bincode::deserialize_from(&mut reader).unwrap();
) = match bincode::deserialize_from(&mut reader) {
Ok(res) => res,
Err(e) => {
return Err(format!(
"Error loading file {}: {}",
filename.to_str().unwrap(),
e
))
}
};
let path = PathBuf::from(path);
if hash_file(&path) != file_hash {
return Err("File hash mismatch!".to_string());
}
let cache = LineCache::new(&path);
let cache = LineCache::new(&path).ok();
Ok((
path.clone(),
Self {
@ -249,14 +301,12 @@ impl Router {
cache,
primary_only: primary,
path,
callback
callback,
radius_mult: 0f32,
},
))
}
fn closest(&self, point: &[f32; 3]) -> &System {
self.tree.nearest_neighbor(point).unwrap()
}
fn points_in_sphere(&self, center: &[f32; 3], radius: f32) -> impl Iterator<Item = &System> {
self.tree.locate_within_distance(*center, radius * radius)
}
@ -265,18 +315,23 @@ impl Router {
self.points_in_sphere(&sys.pos, sys.mult * r)
}
fn valid(&self, sys: &System) -> bool {
self.scoopable.contains(&sys.id)
fn valid(&self, sys: &System, src: &System, dst: &System) -> bool {
let scoopable = self.scoopable.contains(&sys.id);
if self.radius_mult == 0.0 {
return scoopable;
}
let df = src.distp(dst);
return (sys.distp(src) + sys.distp(dst)) < (df * (1.0 + self.radius_mult));
}
pub fn best_name_multiroute(
pub fn best_multiroute(
&self,
waypoints: &[String],
waypoints: &Vec<System>,
range: f32,
keep: (bool,bool),
keep: (bool, bool),
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
let mut best_score: f32 = std::f32::MAX;
let mut waypoints = waypoints.to_owned();
let mut best_permutation_waypoints = waypoints.to_owned();
@ -287,17 +342,12 @@ impl Router {
loop {
let c_first = waypoints.first().cloned();
let c_last = waypoints.last().cloned();
let valid = (keep.0 && (c_first == first)) && (keep.1 && (c_last==last));
let valid = (keep.0 && (c_first == first)) && (keep.1 && (c_last == last));
if valid {
let mut total_d = 0.0;
for pair in waypoints.windows(2) {
match pair {
[src, dst] => {
let (mut src, dst) =
(self.name_to_systems(&src)?, self.name_to_systems(&dst)?);
src.sort_by_key(|&p| (p.mult * 10.0) as u8);
let src = src.last().unwrap();
let dst = dst.last().unwrap();
total_d += src.distp2(dst);
}
_ => return Err("Invalid routing parameters!".to_string()),
@ -313,35 +363,19 @@ impl Router {
}
}
println!("Best permutation: {:?}", best_permutation_waypoints);
self.name_multiroute(&best_permutation_waypoints, range, mode, factor)
self.multiroute(&best_permutation_waypoints, range, mode, factor)
}
pub fn name_multiroute(
&self,
waypoints: &[String],
range: f32,
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
let mut coords = Vec::new();
for p_name in waypoints {
let mut p_l = self.name_to_systems(p_name)?;
p_l.sort_by_key(|&p| (p.mult * 10.0) as u8);
let p = p_l.last().unwrap();
coords.push((p_name, p.pos));
}
self.multiroute(coords.as_slice(), range, mode, factor)
}
pub fn multiroute(
&self,
waypoints: &[(&String, [f32; 3])],
waypoints: &Vec<System>,
range: f32,
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
let mut route: Vec<System> = Vec::new();
for pair in waypoints.windows(2) {
match *pair {
match pair.clone() {
[src, dst] => {
let block = match mode {
Mode::BFS => self.route_bfs(&src, &dst, range)?,
@ -358,56 +392,88 @@ impl Router {
}
}
}
_ => return Err("Invalid routing parameters!".to_string()),
_ => {
return Err("Invalid routing parameters!".to_owned());
}
}
}
Ok(route)
}
fn name_to_systems(&self, name: &str) -> Result<Vec<&System>,String> {
fn resolve_systems(&self, systems: &[SysEntry]) -> Result<Vec<System>, String> {
let mut ret = Vec::new();
let mut sys_by_id: FnvHashMap<u32, &System> = FnvHashMap::default();
let mut sys_by_name: FnvHashMap<String, &System> = FnvHashMap::default();
for sys in &self.tree {
if sys.system == name {
return Ok(self.neighbours(&sys, 0.0).collect());
for ent in systems {
match ent {
SysEntry::ID(i) => {
let i = *i;
if sys.id == i {
sys_by_id.insert(i, sys);
}
}
SysEntry::Name(n) => {
if &sys.body == n || &sys.system == n {
sys_by_name.insert(n.to_string(), sys);
}
}
}
}
}
Err(format!("System not found: \"{}\"", name))
for ent in systems {
match ent {
SysEntry::ID(i) => match sys_by_id.get(i) {
Some(sys) => ret.push(sys.clone().clone()),
None => {
return Err(format!("System: {:?} not found", ent));
}
},
SysEntry::Name(n) => match sys_by_name.get(n) {
Some(sys) => ret.push(sys.clone().clone()),
None => {
return Err(format!("System: {:?} not found", ent));
}
},
}
}
Ok(ret)
}
pub fn route_astar(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
src: &System,
dst: &System,
range: f32,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
if factor == 0.0 {
return self.route_bfs(src, dst, range);
}
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
let d_total = dist(&start_sys.pos,&goal_sys.pos);
let mut d_rem=d_total;
let mut state=SearchState {
mode:"A-Star".into(),
depth:0,
queue_size:0,
let src_name = src.system.clone();
let dst_name = dst.system.clone();
let start_sys = src;
let goal_sys = dst;
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut d_rem = d_total;
let mut state = SearchState {
mode: "A-Star".into(),
depth: 0,
queue_size: 0,
d_rem: d_total,
d_total: d_total,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
body: start_sys.body.clone(),
system: start_sys.system.clone()
system: start_sys.system.clone(),
};
let total = self.tree.size() as f32;
let t_start = Instant::now();
let mut t_last = Instant::now();
let mut prev = FnvHashMap::default();
let mut seen = FnvHashSet::default();
let mut found = false;
let mut maxd = 0;
let mut queue: Vec<(usize, usize, &System)> = Vec::new();
queue.push((
0, // depth
@ -417,31 +483,22 @@ impl Router {
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
while let Some((depth, _, sys)) = queue.pop() {
if (depth+1) > maxd {
maxd = depth+1;
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
state.body=sys.body.clone();
state.system=sys.system.clone();
if t_last.elapsed().as_millis() > 100 {
t_last = Instant::now();
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
state.body = sys.body.clone();
state.system = sys.system.clone();
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
return Err(format!("{:?}", e).to_string());
}
};
print!(
"[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%) \r",
format_duration(t_start.elapsed()),
depth,
queue.len(),
seen.len(),
((seen.len() * 100) as f32) / total
);
std::io::stdout().flush().unwrap();
}
if sys.id == goal_sys.id {
found = true;
@ -449,16 +506,15 @@ impl Router {
}
queue.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| (self.valid(nb, &src, &dst) || (nb.id == goal_sys.id)))
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let d_g = (nb.distp(goal_sys) / range) as usize;
let dist = dist(&nb.pos, &goal_sys.pos);
if dist < d_rem {
d_rem = dist;
let d_g=nb.distp(goal_sys);
if d_g<d_rem {
d_rem=d_g;
}
(depth + 1, d_g, nb)
(depth + 1, (d_g / range) as usize, nb)
}),
);
queue.sort_by(|b, a| {
@ -475,7 +531,7 @@ impl Router {
println!();
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -494,54 +550,54 @@ impl Router {
pub fn route_greedy(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
src: &System,
dst: &System,
range: f32,
) -> Result<Vec<System>,String> {
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
) -> Result<Vec<System>, String> {
let src_name = src.system.clone();
let dst_name = dst.system.clone();
let start_sys = src;
let goal_sys = dst;
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut d_rem=d_total;
let mut state=SearchState {
mode:"Greedy".into(),
depth:0,
queue_size:0,
let mut d_rem = d_total;
let mut state = SearchState {
mode: "Greedy".into(),
depth: 0,
queue_size: 0,
d_rem: d_total,
d_total: d_total,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
body:start_sys.body.clone(),
system:start_sys.system.clone(),
body: start_sys.body.clone(),
system: start_sys.system.clone(),
};
let total = self.tree.size() as f32;
let mut t_last = Instant::now();
let mut prev = FnvHashMap::default();
let mut seen = FnvHashSet::default();
let mut found = false;
let mut maxd = 0;
let mut queue: Vec<(f32, f32, usize, &System)> = Vec::new();
queue.push((-goal_sys.mult, start_sys.distp2(goal_sys), 0, &start_sys));
queue.push((-goal_sys.mult, start_sys.distp(goal_sys), 0, &start_sys));
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
while let Some((_, _, depth, sys)) = queue.pop() {
if (depth+1) > maxd {
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
state.body=sys.body.clone();
state.system=sys.system.clone();
if t_last.elapsed().as_millis() > 100 {
t_last = Instant::now();
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
state.body = sys.body.clone();
state.system = sys.system.clone();
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
return Err(format!("{:?}", e).to_string());
}
};
maxd = depth+1;
}
if sys.id == goal_sys.id {
found = true;
@ -549,15 +605,15 @@ impl Router {
}
queue.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| (self.valid(nb, &src, &dst) || (nb.id == goal_sys.id)))
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let dist = dist(&nb.pos, &goal_sys.pos);
if dist < d_rem {
d_rem = dist;
let d_g=nb.distp(goal_sys);
if d_g<d_rem {
d_rem=d_g;
}
(-nb.mult, nb.distp2(goal_sys), depth + 1, nb)
(-nb.mult, d_g, depth + 1, nb)
}),
);
queue.sort_by(|a, b| fcmp(a.0, b.0).then(fcmp(a.1, b.1)));
@ -565,7 +621,7 @@ impl Router {
}
}
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -582,10 +638,7 @@ impl Router {
Ok(v)
}
pub fn precompute(&mut self, src: &str) -> Result<(),String> {
let mut sys_l = self.name_to_systems(src)?;
sys_l.sort_by_key(|&sys| (sys.mult * 10.0) as u8);
let sys = sys_l.last().unwrap();
pub fn precompute(&mut self, src: &System) -> Result<(), String> {
let total = self.tree.size() as f32;
let t_start = Instant::now();
let mut prev = FnvHashMap::default();
@ -593,8 +646,8 @@ impl Router {
let mut depth = 0;
let mut queue: VecDeque<(usize, &System)> = VecDeque::new();
let mut queue_next: VecDeque<(usize, &System)> = VecDeque::new();
queue.push_front((0, &sys));
seen.insert(sys.id);
queue.push_front((0, &src));
seen.insert(src.id);
while !queue.is_empty() {
print!(
"[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%) \r",
@ -622,7 +675,7 @@ impl Router {
self.route_tree = Some(prev);
let ofn = format!(
"{}_{}{}.router",
src.replace("*", "").replace(" ", "_"),
src.system.replace("*", "").replace(" ", "_"),
self.range,
if self.primary_only { "_primary" } else { "" }
);
@ -634,13 +687,17 @@ impl Router {
String::from(self.path.to_str().unwrap()),
self.route_tree.as_ref().unwrap(),
);
return match bincode::serialize_into(&mut out_fh, &data) {
match bincode::serialize_into(&mut out_fh, &data) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Error: {}",e).to_string())
};
Err(e) => Err(format!("Error: {}", e).to_string()),
}
}
fn get_systems_by_ids(&mut self, path: &PathBuf, ids: &[u32]) -> Result<FnvHashMap<u32, System>,String> {
fn get_systems_by_ids(
&mut self,
path: &PathBuf,
ids: &[u32],
) -> Result<FnvHashMap<u32, System>, String> {
let mut ret = FnvHashMap::default();
if let Some(c) = &mut self.cache.as_mut() {
let mut missing = false;
@ -659,13 +716,12 @@ impl Router {
return Ok(ret);
}
}
let mut reader = match csv::ReaderBuilder::new()
.from_path(path) {
Ok(reader) => reader,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(reader) => reader,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
reader
.deserialize::<SystemSerde>()
.map(|res| res.unwrap())
@ -674,30 +730,20 @@ impl Router {
ret.insert(sys.id, sys.build());
})
.count();
for id in ids {
if !ret.contains_key(&id) {
return Err(format!("ID {} not found", id));
}
}
Ok(ret)
}
fn get_system_by_name(path: &PathBuf, name: &str) -> Result<System,String> {
// TODO: Fuzzy search (https://github.com/andylokandy/simsearch-rs)
let mut reader = csv::ReaderBuilder::new()
.from_path(path)
.unwrap_or_else(|e| {
eprintln!("Error opening {}: {}", path.to_str().unwrap(), e);
std::process::exit(1);
});
let sys = reader
.deserialize::<SystemSerde>()
.map(|res| res.unwrap())
.find(|sys| sys.system == name);
match sys {
Some(system) => Ok(system.build()),
None => Err(format!("System {} not found!",name).to_string())
}
}
pub fn route_to(&mut self, dst: &str, systems_path: &PathBuf) -> Result<Vec<System>,String> {
pub fn route_to(
&mut self,
dst: &System,
systems_path: &PathBuf,
) -> Result<Vec<System>, String> {
let prev = self.route_tree.as_ref().unwrap();
let dst = Self::get_system_by_name(&systems_path, dst)?;
if !prev.contains_key(&dst.id) {
return Err(format!("System-ID {} not found", dst.id).to_string());
};
@ -719,7 +765,7 @@ impl Router {
let sys = match id_map.get(&sys_id) {
Some(sys) => sys,
None => {
return Err(format!("System-ID {} not found!",sys_id));
return Err(format!("System-ID {} not found!", sys_id));
}
};
v.push(sys.clone())
@ -729,29 +775,29 @@ impl Router {
pub fn route_bfs(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
start_sys: &System,
goal_sys: &System,
range: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
println!("Running BFS");
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
let src_name = start_sys.system.clone();
let dst_name = goal_sys.system.clone();
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut state=SearchState {
mode:"BFS".into(),
depth:0,
queue_size:0,
d_rem: d_total,
d_total: d_total,
let mut d_rem = d_total;
let mut state = SearchState {
mode: "BFS".into(),
depth: 0,
queue_size: 0,
d_rem,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
system: start_sys.system.clone(),
body: start_sys.body.clone()
body: start_sys.body.clone(),
};
{
let d = dist(&start_sys.pos, &goal_sys.pos);
println!("Plotting route from {} to {}...", src_name, dst_name);
println!(
"Jump Range: {} Ly, Distance: {} Ly, Estimated Jumps: {}",
@ -765,41 +811,47 @@ impl Router {
let mut seen = FnvHashSet::default();
let mut depth = 0;
let mut found = false;
let mut t_last = Instant::now();
let mut queue: VecDeque<(usize, &System)> = VecDeque::new();
let mut queue_next: VecDeque<(usize, &System)> = VecDeque::new();
let mut d_rem = dist(&start_sys.pos, &goal_sys.pos);
queue.push_front((0, &start_sys));
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
{
let s=queue.get(0).unwrap().1;
state.system=s.system.clone();
state.body=s.body.clone();
}
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
}
};
while let Some((d, sys)) = queue.pop_front() {
if sys.id == goal_sys.id {
found = true;
break;
}
if t_last.elapsed().as_millis() > 100 {
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
{
let s = queue.get(0).unwrap().1;
state.system = s.system.clone();
state.body = s.body.clone();
}
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}", e).to_string());
}
};
t_last = Instant::now();
}
queue_next.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| {
(self.valid(nb, &start_sys, &goal_sys) || (nb.id == goal_sys.id))
})
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let dist = dist(&nb.pos, &goal_sys.pos);
let dist = nb.distp(goal_sys);
if dist < d_rem {
d_rem = dist;
}
@ -813,7 +865,7 @@ impl Router {
println!();
println!();
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -830,54 +882,71 @@ impl Router {
Ok(v)
}
}
pub fn route(opts: RouteOpts) -> Result<Option<Vec<System>>,String> {
pub fn route(opts: RouteOpts) -> Result<Option<Vec<System>>, String> {
if opts.systems.is_empty() {
return if opts.precomp_file.is_some() {
Err("Error: Please specify exatly one system".to_string())
if opts.precomp_file.is_some() {
return Err("Error: Please specify exatly one system".to_owned());
} else if opts.precompute {
Err("Error: Please specify at least one system".to_string())
return Err("Error: Please specify at least one system".to_owned());
} else {
Err("Error: Please specify at least two systems".to_string())
}
return Err("Error: Please specify at least two systems".to_owned());
};
}
let mut path = opts.file_path;
let mut router: Router = if opts.precomp_file.is_some() {
let (path_,ret) = Router::from_file(&opts.precomp_file.clone().unwrap(),Box::new(opts.callback))?;
path=path_;
let (path_, ret) =
Router::from_file(&opts.precomp_file.clone().unwrap(), Box::new(opts.callback))?;
path = path_;
ret
} else if opts.range.is_some() {
Router::new(&path, opts.range.unwrap(), opts.primary, Box::new(opts.callback))?
Router::new(
&path,
opts.range.unwrap(),
opts.radius_mult,
opts.primary,
Box::new(opts.callback),
)?
} else {
return Err("Please specify a jump range!".to_string());
Router::new(
&path,
opts.range.unwrap(),
opts.radius_mult,
opts.primary,
opts.callback,
)?
};
let systems: Vec<System> = router
.resolve_systems(&opts.systems)?
.iter()
.map(|sys| sys.clone())
.collect();
if opts.precompute {
for sys in opts.systems {
for sys in systems {
router.route_tree = None;
router.precompute(&sys)?;
}
return Ok(None)
return Ok(None);
}
let route = if router.route_tree.is_some() {
router.route_to(opts.systems.first().unwrap(), &path)?
router.route_to(systems.first().unwrap(), &path)?
} else if opts.permute {
router.best_name_multiroute(
&opts.systems,
router.best_multiroute(
&systems,
opts.range.unwrap(),
(opts.keep_first,opts.keep_last),
(opts.keep_first, opts.keep_last),
opts.mode,
opts.factor.unwrap_or(1.0),
)?
} else {
router.name_multiroute(
&opts.systems,
router.multiroute(
&systems,
opts.range.unwrap(),
opts.mode,
opts.factor.unwrap_or(1.0),
)?
};
if route.is_empty() {
return Err("No route found!".to_string())
return Err("No route found!".to_string());
}
return Ok(Some(route));
Ok(Some(route))
}

View File

@ -18,7 +18,14 @@ setup(
],
packages=["ed_lrr_gui"],
entry_points={"console_scripts": ["ed_lrr_gui=ed_lrr_gui.__main__:main"]},
install_requires=["PyQt5", "appdirs", "PyYAML", "requests", "python-dateutil"],
install_requires=[
"PyQt5",
"appdirs",
"PyYAML",
"requests",
"python-dateutil",
"pyperclip",
],
include_package_data=True,
zip_safe=False,
)

4
test.bat Normal file
View File

@ -0,0 +1,4 @@
python build_gui.py
pip uninstall -y ed_lrr_gui
pip install -e .
ed_lrr_gui