diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index dd190d0..ec68e80 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Cache the Maven packages to speed up build - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/release-new-version.yml b/.github/workflows/release-new-version.yml index 863416d..8a1f4ef 100644 --- a/.github/workflows/release-new-version.yml +++ b/.github/workflows/release-new-version.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Cache the Maven packages to speed up build - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.gitignore b/.gitignore index 7b89667..9b1d9de 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ local.properties workspace.xml settings.json target/ +GEMINI.MD diff --git a/.settings/eclipse-java-google-style.xml b/.settings/eclipse-java-google-style.xml new file mode 100644 index 0000000..7bb6804 --- /dev/null +++ b/.settings/eclipse-java-google-style.xml @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 0000000..d4313d4 --- /dev/null +++ b/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=false diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 0f0bc12..1b6e1ef 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,22 +1,9 @@ -# Java Version +eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=1.8 - -# Indentation -org.eclipse.jdt.core.formatter.tabulation.char=space -org.eclipse.jdt.core.formatter.tabulation.size=4 -org.eclipse.jdt.core.formatter.indentation.size=4 - -# Braces -org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line - -# Line Wrapping -org.eclipse.jdt.core.formatter.lineSplit=120 -org.eclipse.jdt.core.formatter.join_wrapped_lines=false - -# Other -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=true diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml deleted file mode 100644 index f4ef8aa..0000000 --- a/.settings/org.eclipse.wst.common.project.facet.core.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/pom.xml b/pom.xml index 3936d8f..cc95adc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.pesterenan MechPeste - 0.8.0 + 0.9.0 @@ -61,10 +61,9 @@ 2.37.0 - - 4.19 - ${project.basedir}/.settings/org.eclipse.jdt.core.prefs - + + 1.22.0 + @@ -95,7 +94,7 @@ com.google.protobuf protobuf-java - 3.22.1 + 4.32.0 diff --git a/src/main/java/com/pesterenan/Main.java b/src/main/java/com/pesterenan/Main.java deleted file mode 100644 index ac0b287..0000000 --- a/src/main/java/com/pesterenan/Main.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.pesterenan; - -import java.io.IOException; - -import com.pesterenan.utils.Utilities; -import com.pesterenan.utils.Vector; - -import krpc.client.Connection; -import krpc.client.RPCException; -import krpc.client.services.Drawing; -import krpc.client.services.Drawing.Line; -import krpc.client.services.SpaceCenter; -import krpc.client.services.SpaceCenter.Control; -import krpc.client.services.SpaceCenter.DockingPort; -import krpc.client.services.SpaceCenter.ReferenceFrame; -import krpc.client.services.SpaceCenter.SASMode; -import krpc.client.services.SpaceCenter.Vessel; - -public class Main { - private static Connection connection; - private static SpaceCenter spaceCenter; - private static Drawing drawing; - private static Control control; - - private static Vessel activeVessel; - private static Vessel targetVessel; - - private static ReferenceFrame orbitalRefVessel; - private static ReferenceFrame vesselRefFrame; - private static ReferenceFrame orbitalRefBody; - private static final double SPEED_LIMIT = 3.0; - private static final double DISTANCE_LIMIT = 25.0; - private static Line distanceLine; - private static Line distLineXAxis; - private static Line distLineYAxis; - private static Line distLineZAxis; - private static DockingPort myDockingPort; - private static DockingPort targetDockingPort; - private static Vector positionMyDockingPort; - private static Vector positionTargetDockingPort; - - private static void initializeParameters() { - try { - connection = Connection.newInstance(); - spaceCenter = SpaceCenter.newInstance(connection); - drawing = Drawing.newInstance(connection); - activeVessel = spaceCenter.getActiveVessel(); - targetVessel = spaceCenter.getTargetVessel(); - vesselRefFrame = activeVessel.getReferenceFrame(); - orbitalRefVessel = activeVessel.getOrbitalReferenceFrame(); - orbitalRefBody = activeVessel.getOrbit().getBody().getReferenceFrame(); - - myDockingPort = activeVessel.getParts().getDockingPorts().get(0); - targetDockingPort = targetVessel.getParts().getDockingPorts().get(0); - - positionMyDockingPort = new Vector(myDockingPort.position(orbitalRefVessel)); - positionTargetDockingPort = new Vector(targetDockingPort.position(orbitalRefVessel)); - - createLines(positionMyDockingPort, positionTargetDockingPort); - - } catch (IOException | RPCException e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - try { - // Initialize parameters for the script, connection and setup: - initializeParameters(); - - // Setting up the control - control = activeVessel.getControl(); - control.setSAS(true); - control.setRCS(false); - control.setSASMode(SASMode.STABILITY_ASSIST); - - Vector targetDirection = new Vector(activeVessel.position(orbitalRefVessel)) - .subtract(new Vector(targetVessel.position(orbitalRefVessel))).multiply(-1); - activeVessel.getAutoPilot().setReferenceFrame(orbitalRefVessel); - activeVessel.getAutoPilot().setTargetDirection(targetDirection.toTriplet()); - activeVessel.getAutoPilot().setTargetRoll(90); - activeVessel.getAutoPilot().engage(); - // Fazer a nave apontar usando o piloto automático, na marra - while (Math.abs(activeVessel.getAutoPilot().getError()) > 1) { - activeVessel.getAutoPilot().wait_(); - } - control.setRCS(true); - activeVessel.getAutoPilot().disengage(); - control.setSAS(true); - control.setSASMode(SASMode.STABILITY_ASSIST); - - // PRIMEIRA PARTE DO DOCKING: APROXIMAÇÃO - - Vector targetPosition = new Vector(targetVessel.position(vesselRefFrame)); - double lastXTargetPos = targetPosition.x; - double lastYTargetPos = targetPosition.y; - double lastZTargetPos = targetPosition.z; - long sleepTime = 25; - double currentXAxisSpeed = 0; - double currentYAxisSpeed = 0; - double currentZAxisSpeed = 0; - while (Math.abs(lastYTargetPos) >= DISTANCE_LIMIT) { - targetPosition = new Vector(targetVessel.position(vesselRefFrame)); - - // Atualizar posições para linhas - positionMyDockingPort = new Vector(myDockingPort.position(vesselRefFrame)); - positionTargetDockingPort = new Vector(targetDockingPort.position(vesselRefFrame)); - updateLines(positionMyDockingPort, positionTargetDockingPort); - - // Calcular velocidade de cada eixo: - currentXAxisSpeed = (targetPosition.x - lastXTargetPos) * sleepTime; - currentYAxisSpeed = (targetPosition.y - lastYTargetPos) * sleepTime; - currentZAxisSpeed = (targetPosition.z - lastZTargetPos) * sleepTime; - - // Calcular a aceleração para cada eixo no RCS: - float forwardsError = calculateThrottle(DISTANCE_LIMIT, DISTANCE_LIMIT * 2, currentYAxisSpeed, - targetPosition.y, SPEED_LIMIT); - float sidewaysError = calculateThrottle(0, 10, currentXAxisSpeed, targetPosition.x, SPEED_LIMIT); - float upwardsError = calculateThrottle(0, 10, currentZAxisSpeed, targetPosition.z, SPEED_LIMIT); - control.setForward((float) forwardsError); - control.setRight((float) sidewaysError); - control.setUp((float) -upwardsError); - - // Guardar últimas posições: - lastXTargetPos = targetPosition.x; - lastYTargetPos = targetPosition.y; - lastZTargetPos = targetPosition.z; - Thread.sleep(sleepTime); - } - - // SEGUNDA PARTE APONTAR PRO LADO CONTRARIO: - Vector direcaoContrariaDockingPortAlvo = new Vector(targetDockingPort.direction(orbitalRefVessel)) - .multiply(-1); - control.setSAS(false); - control.setRCS(false); - activeVessel.getAutoPilot().engage(); - activeVessel.getAutoPilot().setReferenceFrame(orbitalRefVessel); - activeVessel.getAutoPilot().setTargetDirection(direcaoContrariaDockingPortAlvo.toTriplet()); - activeVessel.getAutoPilot().setTargetRoll(90); - while (Math.abs(activeVessel.getAutoPilot().getError()) > 1) { - activeVessel.getAutoPilot().wait_(); - } - activeVessel.getAutoPilot().disengage(); - control.setSAS(true); - control.setSASMode(SASMode.STABILITY_ASSIST); - Thread.sleep(1000); - control.setRCS(true); - - while (targetVessel != null) { - targetPosition = new Vector(targetDockingPort.position(vesselRefFrame)); - - // Atualizar posições para linhas - positionMyDockingPort = new Vector(myDockingPort.position(vesselRefFrame)); - updateLines(positionMyDockingPort, targetPosition); - - // Calcular velocidade de cada eixo: - currentXAxisSpeed = (targetPosition.x - lastXTargetPos) * sleepTime; - currentYAxisSpeed = (targetPosition.y - lastYTargetPos) * sleepTime; - currentZAxisSpeed = (targetPosition.z - lastZTargetPos) * sleepTime; - - // Calcular a aceleração para cada eixo no RCS: - float forwardsError = calculateThrottle(5, 10, currentYAxisSpeed, targetPosition.y, SPEED_LIMIT); - float sidewaysError = calculateThrottle(-1, 10, currentXAxisSpeed, targetPosition.x, SPEED_LIMIT); - float upwardsError = calculateThrottle(-1, 10, currentZAxisSpeed, targetPosition.z, SPEED_LIMIT); - control.setForward((float) forwardsError); - control.setRight((float) sidewaysError); - control.setUp((float) -upwardsError); - - // Guardar últimas posições: - lastXTargetPos = targetPosition.x; - lastYTargetPos = targetPosition.y; - lastZTargetPos = targetPosition.z; - Thread.sleep(sleepTime); - } - // // Close the connection when finished - // connection.close(); - } catch (RPCException | InterruptedException e) { - e.printStackTrace(); - } - } - - private static float calculateThrottle(double minDistance, double maxDistance, double currentSpeed, - double currentPosition, double speedLimit) { - double limiter = Utilities.remap(minDistance, maxDistance, 0, 1, Math.abs(currentPosition), true); - double change = (Utilities.remap(-speedLimit, speedLimit, -1.0, 1.0, - currentSpeed + (Math.signum(currentPosition) * (limiter * speedLimit)), true)); - return (float) change; - } - - private static void createLines(Vector start, Vector end) { - try { - distanceLine = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); - distLineXAxis = drawing.addLine(start.toTriplet(), new Vector(end.x, 0.0, 0.0).toTriplet(), vesselRefFrame, - true); - distLineYAxis = drawing.addLine(start.toTriplet(), new Vector(end.x, end.y, 0.0).toTriplet(), - vesselRefFrame, true); - distLineZAxis = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); - distanceLine.setThickness(0.5f); - distLineXAxis.setThickness(0.25f); - distLineYAxis.setThickness(0.25f); - distLineZAxis.setThickness(0.25f); - distLineXAxis.setColor(new Vector(1.0, 0.0, 0.0).toTriplet()); - distLineYAxis.setColor(new Vector(0.0, 1.0, 0.0).toTriplet()); - distLineZAxis.setColor(new Vector(0.0, 0.0, 1.0).toTriplet()); - } catch (RPCException e) { - } - } - - private static void updateLines(Vector start, Vector end) { - // Updating drawing lines: - try { - distanceLine.setStart(start.toTriplet()); - distanceLine.setEnd(end.toTriplet()); - distLineXAxis.setStart(start.toTriplet()); - distLineXAxis.setEnd(new Vector(end.x, 0.0, 0.0).toTriplet()); - distLineYAxis.setStart(distLineXAxis.getEnd()); - distLineYAxis.setEnd(new Vector(end.x, end.y, 0.0).toTriplet()); - distLineZAxis.setStart(distLineYAxis.getEnd()); - distLineZAxis.setEnd(end.toTriplet()); - } catch (RPCException e) { - } - } -} diff --git a/src/main/java/com/pesterenan/MechPeste.java b/src/main/java/com/pesterenan/MechPeste.java index da9689e..89f1685 100644 --- a/src/main/java/com/pesterenan/MechPeste.java +++ b/src/main/java/com/pesterenan/MechPeste.java @@ -1,212 +1,47 @@ package com.pesterenan; -import static com.pesterenan.views.StatusJPanel.isBtnConnectVisible; -import static com.pesterenan.views.StatusJPanel.setStatusMessage; - -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.swing.DefaultListModel; -import javax.swing.ListModel; - -import com.pesterenan.model.ActiveVessel; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Vector; -import com.pesterenan.views.CreateManeuverJPanel; +import com.pesterenan.model.ConnectionManager; +import com.pesterenan.model.VesselManager; import com.pesterenan.views.FunctionsAndTelemetryJPanel; import com.pesterenan.views.MainGui; - -import krpc.client.Connection; -import krpc.client.RPCException; -import krpc.client.services.KRPC; -import krpc.client.services.SpaceCenter; -import krpc.client.services.SpaceCenter.Node; -import krpc.client.services.SpaceCenter.Vessel; +import com.pesterenan.views.StatusDisplay; +import java.lang.reflect.InvocationTargetException; +import javax.swing.SwingUtilities; public class MechPeste { - private static KRPC krpc; - private static MechPeste mechPeste; - private static SpaceCenter spaceCenter; - private static Connection connection; - private static int currentVesselId = -1; - private static ActiveVessel currentVessel = null; - - public static void main(String[] args) { - MechPeste.newInstance().connectToKSP(); - MechPeste.newInstance().checkActiveVessel(); - } - - public static MechPeste newInstance() { - if (mechPeste == null) { - mechPeste = new MechPeste(); - } - return mechPeste; - } - - public static Connection getConnection() { - return connection; - } - - public static SpaceCenter getSpaceCenter() { - return spaceCenter; - } - - public static ListModel getActiveVessels(String search) { - DefaultListModel list = new DefaultListModel<>(); - try { - List vessels = spaceCenter.getVessels(); - vessels = vessels.stream().filter(v -> filterVessels(v, search)).collect(Collectors.toList()); - vessels.forEach(v -> { - try { - String vesselName = v.hashCode() + " - \t" + v.getName(); - list.addElement(vesselName); - } catch (RPCException ignored) { - } - }); - } catch (RPCException | NullPointerException ignored) { - } - return list; - } + private ConnectionManager connectionManager = null; + private VesselManager vesselManager; + private static MechPeste instance; - public static ListModel getCurrentManeuvers() { - DefaultListModel list = new DefaultListModel<>(); - try { - List maneuvers = getSpaceCenter().getActiveVessel().getControl().getNodes(); - maneuvers.forEach(m -> { - try { - String maneuverStr = String.format("%d - Dv: %.1f {P: %.1f, N: %.1f, R: %.1f} AP: %.1f, PE: %.1f", - maneuvers.indexOf(m) + 1, m.getDeltaV(), m.getPrograde(), m.getNormal(), m.getRadial(), - m.getOrbit().getApoapsisAltitude(), m.getOrbit().getPeriapsisAltitude()); - list.addElement(maneuverStr); - } catch (RPCException ignored) { - } - }); - } catch (RPCException | NullPointerException ignored) { - } - return list; - } - - public static String getVesselInfo(int selectedIndex) { - try { - Vessel activeVessel = spaceCenter.getVessels().stream().filter(v -> v.hashCode() == selectedIndex) - .findFirst().get(); - String name = activeVessel.getName().length() > 40 - ? activeVessel.getName().substring(0, 40) + "..." - : activeVessel.getName(); - String vesselInfo = String.format("Nome: %s\t\t\t | Corpo: %s", name, - activeVessel.getOrbit().getBody().getName()); - return vesselInfo; - } catch (RPCException | NullPointerException ignored) { - } - return ""; - } - - public static void changeToVessel(int selectedIndex) { - try { - Vessel activeVessel = spaceCenter.getVessels().stream().filter(v -> v.hashCode() == selectedIndex) - .findFirst().get(); - spaceCenter.setActiveVessel(activeVessel); - } catch (RPCException | NullPointerException e) { - System.out.println(Bundle.getString("status_couldnt_switch_vessel")); - } - } - - public static void cancelControl(ActionEvent e) { - currentVessel.cancelControl(); - } + public ConnectionManager getConnectionManager() { + return connectionManager; + } - private static boolean filterVessels(Vessel vessel, String search) { - if (search == "all") { - return true; - } - double TEN_KILOMETERS = 10000.0; - try { - Vessel active = MechPeste.getSpaceCenter().getActiveVessel(); - if (vessel.getOrbit().getBody().getName().equals(active.getOrbit().getBody().getName())) { - final Vector activePos = new Vector(active.position(active.getSurfaceReferenceFrame())); - final Vector vesselPos = new Vector(vessel.position(active.getSurfaceReferenceFrame())); - final double distance = Vector.distance(activePos, vesselPos); - switch (search) { - case "closest" : - if (distance < TEN_KILOMETERS) { - return true; - } - break; - case "samebody" : - return true; - } - } - } catch (RPCException ignored) { - } - return false; - } - - private MechPeste() { - MainGui.newInstance(); - } + public VesselManager getVesselManager() { + return vesselManager; + } - public KRPC.GameScene getCurrentGameScene() throws RPCException { - return krpc.getCurrentGameScene(); - } + private MechPeste() {} - public void startModule(Map commands) { - currentVessel.startModule(commands); + public static MechPeste newInstance() { + if (instance == null) { + instance = new MechPeste(); } + return instance; + } - public void connectToKSP() { - setStatusMessage(Bundle.getString("status_connecting")); - try { - connection = Connection.newInstance("MechPeste - Pesterenan"); - krpc = KRPC.newInstance(connection); - spaceCenter = SpaceCenter.newInstance(getConnection()); - setStatusMessage(Bundle.getString("status_connected")); - isBtnConnectVisible(false); - } catch (IOException e) { - setStatusMessage(Bundle.getString("status_error_connection")); - isBtnConnectVisible(true); - } + public static void main(String[] args) { + MechPeste app = MechPeste.newInstance(); + try { + SwingUtilities.invokeAndWait(() -> MainGui.newInstance()); + } catch (InvocationTargetException | InterruptedException e) { + System.err.println("Error while invoking GUI: " + e.getMessage()); } - public void checkConnection() { - try { - if (!MechPeste.newInstance().getCurrentGameScene().equals(KRPC.GameScene.FLIGHT)) { - setStatusMessage(Bundle.getString("status_ready")); - return; - } - getConnection().close(); - } catch (RPCException | NullPointerException | IOException e) { - setStatusMessage(Bundle.getString("status_error_connection")); - isBtnConnectVisible(true); - } - } - - private void checkActiveVessel() { - while (getConnection() != null) { - try { - if (!MechPeste.newInstance().getCurrentGameScene().equals(KRPC.GameScene.FLIGHT)) { - Thread.sleep(100); - return; - } - int activeVesselId = spaceCenter.getActiveVessel().hashCode(); - // If the current active vessel changes, create a new connection - if (currentVesselId != activeVesselId) { - currentVessel = new ActiveVessel(); - currentVesselId = currentVessel.getCurrentVesselId(); - } - if (currentVesselId != -1) { - currentVessel.recordTelemetryData(); - if (currentVessel.hasModuleRunning()) { - setStatusMessage(currentVessel.getCurrentStatus()); - } - FunctionsAndTelemetryJPanel.updateTelemetry(currentVessel.getTelemetryData()); - CreateManeuverJPanel.updatePanel(getCurrentManeuvers()); - } - Thread.sleep(100); - } catch (RPCException | InterruptedException ignored) { - } - } - } + StatusDisplay statusDisplay = MainGui.newInstance().getStatusPanel(); + FunctionsAndTelemetryJPanel telemetryPanel = MainGui.newInstance().getTelemetryPanel(); + app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); + app.vesselManager = new VesselManager(app.connectionManager, statusDisplay, telemetryPanel); + app.vesselManager.startTelemetryLoop(); + } } diff --git a/src/main/java/com/pesterenan/controllers/Controller.java b/src/main/java/com/pesterenan/controllers/Controller.java index c0901f7..910f770 100644 --- a/src/main/java/com/pesterenan/controllers/Controller.java +++ b/src/main/java/com/pesterenan/controllers/Controller.java @@ -2,22 +2,32 @@ import com.pesterenan.model.ActiveVessel; -public class Controller extends ActiveVessel implements Runnable { - - public Controller() { - super(); - } - - public void run() { - try { - while (!Thread.interrupted()) { - long currentTime = System.currentTimeMillis(); - if (currentTime > timer + 100) { - recordTelemetryData(); - timer = currentTime; - } - } - } catch (Exception ignored) { - } - } +public class Controller implements Runnable { + protected final ActiveVessel vessel; + private volatile boolean isRunning = true; + private String currentStatus = ""; + + public Controller(ActiveVessel vessel) { + this.vessel = vessel; + } + + public void run() { + // This method should be overridden by subclasses. + } + + public void stop() { + this.isRunning = false; + } + + public boolean isRunning() { + return this.isRunning; + } + + public String getCurrentStatus() { + return currentStatus; + } + + public void setCurrentStatus(String status) { + this.currentStatus = status; + } } diff --git a/src/main/java/com/pesterenan/controllers/DockingController.java b/src/main/java/com/pesterenan/controllers/DockingController.java index 978fb31..e6f1704 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -1,300 +1,396 @@ package com.pesterenan.controllers; -import static com.pesterenan.MechPeste.getConnection; -import static com.pesterenan.MechPeste.getSpaceCenter; - -import java.util.Map; - +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; import com.pesterenan.utils.Utilities; import com.pesterenan.utils.Vector; import com.pesterenan.views.DockingJPanel; -import com.pesterenan.views.StatusJPanel; - +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import krpc.client.RPCException; +import krpc.client.Stream; +import krpc.client.StreamException; import krpc.client.services.Drawing; import krpc.client.services.Drawing.Line; +import krpc.client.services.KRPC; import krpc.client.services.SpaceCenter.Control; import krpc.client.services.SpaceCenter.DockingPort; +import krpc.client.services.SpaceCenter.DockingPortState; import krpc.client.services.SpaceCenter.ReferenceFrame; import krpc.client.services.SpaceCenter.SASMode; import krpc.client.services.SpaceCenter.Vessel; public class DockingController extends Controller { - private Drawing drawing; - private Vessel targetVessel; - private Control control; - - private ReferenceFrame orbitalRefVessel; - private ReferenceFrame vesselRefFrame; - private Line distanceLine; - private Line distLineXAxis; - private Line distLineYAxis; - private Line distLineZAxis; - private DockingPort myDockingPort; - private DockingPort targetDockingPort; - private Vector positionMyDockingPort; - private Vector positionTargetDockingPort; - - private double DOCKING_MAX_SPEED = 3.0; - private double SAFE_DISTANCE = 25.0; - private double currentXAxisSpeed = 0.0; - private double currentYAxisSpeed = 0.0; - private double currentZAxisSpeed = 0.0; - private double lastXTargetPos = 0.0; - private double lastYTargetPos = 0.0; - private double lastZTargetPos = 0.0; - private long sleepTime = 25; - private DOCKING_STEPS dockingStep; - - public DockingController(Map commands) { - super(); - this.commands = commands; - initializeParameters(); - } - - private void initializeParameters() { - try { - DOCKING_MAX_SPEED = Double.parseDouble(commands.get(Module.MAX_SPEED.get())); - SAFE_DISTANCE = Double.parseDouble(commands.get(Module.SAFE_DISTANCE.get())); - drawing = Drawing.newInstance(getConnection()); - targetVessel = getSpaceCenter().getTargetVessel(); - control = getActiveVessel().getControl(); - vesselRefFrame = getActiveVessel().getReferenceFrame(); - orbitalRefVessel = getActiveVessel().getOrbitalReferenceFrame(); - - myDockingPort = getActiveVessel().getParts().getDockingPorts().get(0); - targetDockingPort = targetVessel.getParts().getDockingPorts().get(0); - dockingStep = DOCKING_STEPS.STOP_RELATIVE_SPEED; - - positionMyDockingPort = new Vector(myDockingPort.position(orbitalRefVessel)); - positionTargetDockingPort = new Vector(targetDockingPort.position(orbitalRefVessel)); - } catch (RPCException ignored) { - } - } - - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { - startDocking(); - } + private enum DockingPhase { + SETUP, + ORIENT_TO_TARGET, + APPROACH_TARGET, + ORIENT_TO_PORT, + FINAL_APPROACH, + FINISHED + } + + private enum DOCKING_STEPS { + APPROACH, + STOP_RELATIVE_SPEED, + LINE_UP_WITH_TARGET, + GO_IN_FRONT_OF_TARGET + } + + private Drawing drawing; + private Vessel targetVessel; + + private Control control; + private KRPC krpc; + private ReferenceFrame orbitalRefVessel; + private ReferenceFrame vesselRefFrame; + private Line distanceLine; + private Line distLineXAxis; + private Line distLineYAxis; + private Line distLineZAxis; + private DockingPort myDockingPort; + private DockingPort targetDockingPort; + + private Vector positionMyDockingPort; + private Vector positionTargetDockingPort; + private double DOCKING_MAX_SPEED = 3.0; + private double SAFE_DISTANCE = 25.0; + private double currentXAxisSpeed = 0.0; + private double currentYAxisSpeed = 0.0; + private double currentZAxisSpeed = 0.0; + private double lastXTargetPos = 0.0; + private double lastYTargetPos = 0.0; + private double lastZTargetPos = 0.0; + private long sleepTime = 25; + + private DOCKING_STEPS dockingStep; + private boolean isOriented = false; + private final Map commands; + + private Stream errorStream; + + private int errorCallbackTag; + + private DockingPhase currentPhase; + private ScheduledExecutorService dockingExecutor; + + public DockingController(ActiveVessel vessel, Map commands) { + super(vessel); + this.commands = commands; + initializeParameters(); + } + + @Override + public void run() { + if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { + try { + startDocking(); + } catch (RPCException | StreamException e) { + cleanup(); + setCurrentStatus("Docking failed: " + e.getMessage()); + } } - - private void pointToTarget(Vector targetDirection) throws RPCException, InterruptedException { - getActiveVessel().getAutoPilot().setReferenceFrame(orbitalRefVessel); - getActiveVessel().getAutoPilot().setTargetDirection(targetDirection.toTriplet()); - getActiveVessel().getAutoPilot().setTargetRoll(90); - getActiveVessel().getAutoPilot().engage(); - // Fazer a nave apontar usando o piloto automático, na marra - while (Math.abs(getActiveVessel().getAutoPilot().getError()) > 3) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Thread.sleep(100); - System.out.println(getActiveVessel().getAutoPilot().getError()); - } - getActiveVessel().getAutoPilot().disengage(); - control.setSAS(true); - control.setSASMode(SASMode.STABILITY_ASSIST); - } - - private void getCloserToTarget(Vector targetPosition) throws InterruptedException, RPCException { - lastXTargetPos = targetPosition.x; - lastYTargetPos = targetPosition.y; - lastZTargetPos = targetPosition.z; - - while (Math.abs(lastYTargetPos) >= SAFE_DISTANCE) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - targetPosition = new Vector(targetVessel.position(vesselRefFrame)); - controlShipRCS(targetPosition, SAFE_DISTANCE); - Thread.sleep(sleepTime); - } - + } + + public void startDocking() throws RPCException, StreamException { + krpc = KRPC.newInstance(vessel.getConnectionManager().getConnection()); + currentPhase = DockingPhase.SETUP; + dockingExecutor = Executors.newSingleThreadScheduledExecutor(); + dockingExecutor.scheduleAtFixedRate(this::updateDockingState, 0, 100, TimeUnit.MILLISECONDS); + } + + private void initializeParameters() { + try { + DOCKING_MAX_SPEED = Double.parseDouble(commands.get(Module.MAX_SPEED.get())); + SAFE_DISTANCE = Double.parseDouble(commands.get(Module.SAFE_DISTANCE.get())); + drawing = Drawing.newInstance(vessel.getConnectionManager().getConnection()); + targetVessel = vessel.getConnectionManager().getSpaceCenter().getTargetVessel(); + control = vessel.getActiveVessel().getControl(); + vesselRefFrame = vessel.getActiveVessel().getReferenceFrame(); + orbitalRefVessel = vessel.getActiveVessel().getOrbitalReferenceFrame(); + + myDockingPort = vessel.getActiveVessel().getParts().getDockingPorts().get(0); + targetDockingPort = targetVessel.getParts().getDockingPorts().get(0); + dockingStep = DOCKING_STEPS.STOP_RELATIVE_SPEED; + + positionMyDockingPort = new Vector(myDockingPort.position(orbitalRefVessel)); + positionTargetDockingPort = new Vector(targetDockingPort.position(orbitalRefVessel)); + } catch (RPCException ignored) { } - - public void startDocking() { - try { - // Setting up the control - control.setSAS(true); - control.setRCS(false); - control.setSASMode(SASMode.STABILITY_ASSIST); - createLines(positionMyDockingPort, positionTargetDockingPort); - - // PRIMEIRA PARTE DO DOCKING: APROXIMAÇÃO - Vector targetPosition = new Vector(targetVessel.position(vesselRefFrame)); - if (targetPosition.magnitude() > SAFE_DISTANCE) { - // Apontar para o alvo: - Vector targetDirection = new Vector(getActiveVessel().position(orbitalRefVessel)) - .subtract(new Vector(targetVessel.position(orbitalRefVessel))).multiply(-1); - pointToTarget(targetDirection); - - control.setRCS(true); - - getCloserToTarget(targetPosition); - } - - control.setSAS(false); - control.setRCS(false); - - // SEGUNDA PARTE FICAR DE FRENTE COM A DOCKING PORT: - Vector targetDockingPortDirection = new Vector(targetDockingPort.direction(orbitalRefVessel)).multiply(-1); - pointToTarget(targetDockingPortDirection); - - Thread.sleep(1000); + } + + private void updateDockingState() { + try { + if (!isRunning()) { + cleanup(); + return; + } + Vector targetPosition; + switch (currentPhase) { + case SETUP: + setCurrentStatus("Setting up for docking..."); + control.setSAS(true); + control.setRCS(false); + control.setSASMode(SASMode.STABILITY_ASSIST); + createLines(positionMyDockingPort, positionTargetDockingPort); + currentPhase = DockingPhase.ORIENT_TO_TARGET; + break; + case ORIENT_TO_TARGET: + setCurrentStatus("Orienting to target vessel..."); + targetPosition = new Vector(targetVessel.position(vesselRefFrame)); + if (targetPosition.magnitude() > SAFE_DISTANCE) { + Vector targetDirection = + new Vector(vessel.getActiveVessel().position(orbitalRefVessel)) + .subtract(new Vector(targetVessel.position(orbitalRefVessel))) + .multiply(-1); + pointToTarget(targetDirection); control.setRCS(true); - double safeDistance = 10; - while (true) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - targetPosition = new Vector(targetDockingPort.position(vesselRefFrame)) - .subtract(new Vector(myDockingPort.position(vesselRefFrame))); - if (targetPosition.magnitude() < safeDistance) { - safeDistance = 1; - } - controlShipRCS(targetPosition, safeDistance); - Thread.sleep(sleepTime); - } - } catch (RPCException | InterruptedException | IllegalArgumentException e) { - StatusJPanel.setStatusMessage("Docking interrupted."); - } + currentPhase = DockingPhase.APPROACH_TARGET; + } else { + currentPhase = DockingPhase.ORIENT_TO_PORT; + } + break; + case APPROACH_TARGET: + setCurrentStatus("Approaching target vessel..."); + targetPosition = new Vector(targetVessel.position(vesselRefFrame)); + if (targetPosition.magnitude() > SAFE_DISTANCE) { + controlShipRCS(targetPosition, SAFE_DISTANCE); + } else { + currentPhase = DockingPhase.ORIENT_TO_PORT; + } + break; + case ORIENT_TO_PORT: + setCurrentStatus("Orienting to docking port..."); + control.setSAS(false); + control.setRCS(false); + Vector targetDockingPortDirection = + new Vector(targetDockingPort.direction(orbitalRefVessel)).multiply(-1); + pointToTarget(targetDockingPortDirection); + control.setRCS(true); + if (isOriented) { + currentPhase = DockingPhase.FINAL_APPROACH; + } + break; + case FINAL_APPROACH: + setCurrentStatus("Final approach..."); + targetPosition = + new Vector(targetDockingPort.position(vesselRefFrame)) + .subtract(new Vector(myDockingPort.position(vesselRefFrame))); + double safeDistance = targetPosition.magnitude() < 10 ? 1 : 10; + controlShipRCS(targetPosition, safeDistance); + if (myDockingPort.getState() == DockingPortState.DOCKED) { + setCurrentStatus("Docking successful!"); + currentPhase = DockingPhase.FINISHED; + } + break; + case FINISHED: + stop(); + break; + } + } catch (RPCException | StreamException | InterruptedException e) { + setCurrentStatus("Docking failed: " + e.getMessage()); + cleanup(); } - - /* - * Possibilidades do docking: primeiro: a nave ta na orientação certa, e só - * precisa seguir em frente X e Z = 0, Y positivo segundo: a nave ta na - * orientação certa, mas precisa corrigir a posição X e Z, Y positivo terceiro: - * a nave está atrás da docking port, precisa corrigir Y primeiro, Y negativo - * quarto: a nave está atrás da docking port, precisa afastar X e Z longe da - * nave primeiro, Y negativo - */ - - private enum DOCKING_STEPS { - APPROACH, STOP_RELATIVE_SPEED, LINE_UP_WITH_TARGET, GO_IN_FRONT_OF_TARGET + } + + /* + * Possibilidades do docking: primeiro: a nave ta na orientação certa, e só precisa seguir em + * frente X e Z = 0, Y positivo segundo: a nave ta na orientação certa, mas precisa corrigir a + * posição X e Z, Y positivo terceiro: a nave está atrás da docking port, precisa corrigir Y + * primeiro, Y negativo quarto: a nave está atrás da docking port, precisa afastar X e Z longe da + * nave primeiro, Y negativo + */ + + private void pointToTarget(Vector targetDirection) + throws RPCException, InterruptedException, StreamException { + vessel.getActiveVessel().getAutoPilot().setReferenceFrame(orbitalRefVessel); + vessel.getActiveVessel().getAutoPilot().setTargetDirection(targetDirection.toTriplet()); + vessel.getActiveVessel().getAutoPilot().setTargetRoll(90); + vessel.getActiveVessel().getAutoPilot().engage(); + errorStream = vessel.connection.addStream(vessel.ap, "getError"); + errorCallbackTag = + errorStream.addCallback( + (error) -> { + if (error < 3.0) { + isOriented = true; + errorStream.removeCallback(errorCallbackTag); + } + }); + errorStream.start(); + vessel.getActiveVessel().getAutoPilot().disengage(); + control.setSAS(true); + control.setSASMode(SASMode.STABILITY_ASSIST); + } + + private void controlShipRCS(Vector targetPosition, double forwardsDistanceLimit) { + try { + // Atualizar posições para linhas + positionMyDockingPort = new Vector(myDockingPort.position(vesselRefFrame)); + updateLines(positionMyDockingPort, targetPosition); + + // Calcular velocidade de cada eixo: + currentXAxisSpeed = (targetPosition.x - lastXTargetPos) * sleepTime; + currentYAxisSpeed = (targetPosition.y - lastYTargetPos) * sleepTime; + currentZAxisSpeed = (targetPosition.z - lastZTargetPos) * sleepTime; + + double sidewaysDistance = Math.abs(targetPosition.x); + double upwardsDistance = Math.abs(targetPosition.z); + boolean isInFrontOfTarget = Math.signum(targetPosition.y) == 1; + boolean isOnTheBackOfTarget = + Math.signum(targetPosition.y) == -1 && targetPosition.y < forwardsDistanceLimit; + float forwardsError, upwardsError, sidewaysError = 0; + + switch (dockingStep) { + case APPROACH: + // Calcular a aceleração para cada eixo no RCS: + forwardsError = + calculateThrottle( + forwardsDistanceLimit, + forwardsDistanceLimit * 3, + currentYAxisSpeed, + targetPosition.y, + DOCKING_MAX_SPEED); + sidewaysError = + calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, DOCKING_MAX_SPEED); + upwardsError = + calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, DOCKING_MAX_SPEED); + control.setForward(forwardsError); + control.setRight(sidewaysError); + control.setUp(-upwardsError); + DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_approach")); + break; + case LINE_UP_WITH_TARGET: + forwardsError = + calculateThrottle( + forwardsDistanceLimit, + forwardsDistanceLimit * 3, + currentYAxisSpeed, + targetPosition.y, + 0); + sidewaysError = + calculateThrottle(0, 10, currentXAxisSpeed, targetPosition.x, DOCKING_MAX_SPEED); + upwardsError = + calculateThrottle(0, 10, currentZAxisSpeed, targetPosition.z, DOCKING_MAX_SPEED); + control.setForward(forwardsError); + control.setRight(sidewaysError); + control.setUp(-upwardsError); + DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_line_up_with_target")); + break; + case GO_IN_FRONT_OF_TARGET: + forwardsError = + calculateThrottle(-20, -10, currentYAxisSpeed, targetPosition.y, DOCKING_MAX_SPEED); + sidewaysError = calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, 0); + upwardsError = calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, 0); + control.setForward(forwardsError); + control.setRight(sidewaysError); + control.setUp(-upwardsError); + if (isInFrontOfTarget) { + dockingStep = DOCKING_STEPS.STOP_RELATIVE_SPEED; + break; + } + DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_go_in_front_of_target")); + break; + case STOP_RELATIVE_SPEED: + forwardsError = calculateThrottle(0, 5, currentYAxisSpeed, targetPosition.y, 0); + sidewaysError = calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, 0); + upwardsError = calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, 0); + control.setForward(forwardsError); + control.setRight(sidewaysError); + control.setUp(-upwardsError); + if ((Math.abs(currentXAxisSpeed) < 1) + && (Math.abs(currentYAxisSpeed) < 1) + && (Math.abs(currentZAxisSpeed) < 1)) { + dockingStep = DOCKING_STEPS.APPROACH; + break; + } + DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_stop_relative_speed")); + break; + } + System.out.println(dockingStep); + + // Guardar últimas posições: + lastXTargetPos = targetPosition.x; + lastYTargetPos = targetPosition.y; + lastZTargetPos = targetPosition.z; + } catch (RPCException ignored) { } - - private void controlShipRCS(Vector targetPosition, double forwardsDistanceLimit) { - try { - // Atualizar posições para linhas - positionMyDockingPort = new Vector(myDockingPort.position(vesselRefFrame)); - updateLines(positionMyDockingPort, targetPosition); - - // Calcular velocidade de cada eixo: - currentXAxisSpeed = (targetPosition.x - lastXTargetPos) * sleepTime; - currentYAxisSpeed = (targetPosition.y - lastYTargetPos) * sleepTime; - currentZAxisSpeed = (targetPosition.z - lastZTargetPos) * sleepTime; - - double sidewaysDistance = Math.abs(targetPosition.x); - double upwardsDistance = Math.abs(targetPosition.z); - boolean isInFrontOfTarget = Math.signum(targetPosition.y) == 1; - boolean isOnTheBackOfTarget = Math.signum(targetPosition.y) == -1 - && targetPosition.y < forwardsDistanceLimit; - float forwardsError, upwardsError, sidewaysError = 0; - - switch (dockingStep) { - case APPROACH : - // Calcular a aceleração para cada eixo no RCS: - forwardsError = calculateThrottle(forwardsDistanceLimit, forwardsDistanceLimit * 3, - currentYAxisSpeed, targetPosition.y, DOCKING_MAX_SPEED); - sidewaysError = calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, DOCKING_MAX_SPEED); - upwardsError = calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, DOCKING_MAX_SPEED); - control.setForward(forwardsError); - control.setRight(sidewaysError); - control.setUp(-upwardsError); - DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_approach")); - break; - case LINE_UP_WITH_TARGET : - forwardsError = calculateThrottle(forwardsDistanceLimit, forwardsDistanceLimit * 3, - currentYAxisSpeed, targetPosition.y, 0); - sidewaysError = calculateThrottle(0, 10, currentXAxisSpeed, targetPosition.x, DOCKING_MAX_SPEED); - upwardsError = calculateThrottle(0, 10, currentZAxisSpeed, targetPosition.z, DOCKING_MAX_SPEED); - control.setForward(forwardsError); - control.setRight(sidewaysError); - control.setUp(-upwardsError); - DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_line_up_with_target")); - break; - case GO_IN_FRONT_OF_TARGET : - forwardsError = calculateThrottle(-20, -10, currentYAxisSpeed, targetPosition.y, DOCKING_MAX_SPEED); - sidewaysError = calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, 0); - upwardsError = calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, 0); - control.setForward(forwardsError); - control.setRight(sidewaysError); - control.setUp(-upwardsError); - if (isInFrontOfTarget) { - dockingStep = DOCKING_STEPS.STOP_RELATIVE_SPEED; - break; - } - DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_go_in_front_of_target")); - break; - case STOP_RELATIVE_SPEED : - forwardsError = calculateThrottle(0, 5, currentYAxisSpeed, targetPosition.y, 0); - sidewaysError = calculateThrottle(0, 5, currentXAxisSpeed, targetPosition.x, 0); - upwardsError = calculateThrottle(0, 5, currentZAxisSpeed, targetPosition.z, 0); - control.setForward(forwardsError); - control.setRight(sidewaysError); - control.setUp(-upwardsError); - if ((Math.abs(currentXAxisSpeed) < 1) && (Math.abs(currentYAxisSpeed) < 1) - && (Math.abs(currentZAxisSpeed) < 1)) { - dockingStep = DOCKING_STEPS.APPROACH; - break; - } - DockingJPanel.setDockingStep(Bundle.getString("pnl_docking_step_stop_relative_speed")); - break; - } - System.out.println(dockingStep); - - // Guardar últimas posições: - lastXTargetPos = targetPosition.x; - lastYTargetPos = targetPosition.y; - lastZTargetPos = targetPosition.z; - } catch (RPCException ignored) { - } + } + + private float calculateThrottle( + double minDistance, + double maxDistance, + double currentSpeed, + double currentPosition, + double speedLimit) { + double limiter = + Utilities.remap(minDistance, maxDistance, 0, 1, Math.abs(currentPosition), true); + double change = + (Utilities.remap( + -speedLimit, + speedLimit, + -1.0, + 1.0, + currentSpeed + (Math.signum(currentPosition) * (limiter * speedLimit)), + true)); + return (float) change; + } + + private void createLines(Vector start, Vector end) { + try { + distanceLine = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); + distLineXAxis = + drawing.addLine( + start.toTriplet(), new Vector(end.x, 0.0, 0.0).toTriplet(), vesselRefFrame, true); + distLineYAxis = + drawing.addLine( + start.toTriplet(), new Vector(end.x, end.y, 0.0).toTriplet(), vesselRefFrame, true); + distLineZAxis = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); + distanceLine.setThickness(0.5f); + distLineXAxis.setThickness(0.25f); + distLineYAxis.setThickness(0.25f); + distLineZAxis.setThickness(0.25f); + distLineXAxis.setColor(new Vector(1.0, 0.0, 0.0).toTriplet()); + distLineYAxis.setColor(new Vector(0.0, 1.0, 0.0).toTriplet()); + distLineZAxis.setColor(new Vector(0.0, 0.0, 1.0).toTriplet()); + } catch (RPCException e) { } - - private float calculateThrottle(double minDistance, double maxDistance, double currentSpeed, double currentPosition, - double speedLimit) { - double limiter = Utilities.remap(minDistance, maxDistance, 0, 1, Math.abs(currentPosition), true); - double change = (Utilities.remap(-speedLimit, speedLimit, -1.0, 1.0, - currentSpeed + (Math.signum(currentPosition) * (limiter * speedLimit)), true)); - return (float) change; + } + + private void updateLines(Vector start, Vector end) { + // Updating drawing lines: + try { + distanceLine.setStart(start.toTriplet()); + distanceLine.setEnd(end.toTriplet()); + distLineXAxis.setStart(start.toTriplet()); + distLineXAxis.setEnd(new Vector(end.x, 0.0, 0.0).toTriplet()); + distLineYAxis.setStart(distLineXAxis.getEnd()); + distLineYAxis.setEnd(new Vector(end.x, end.y, 0.0).toTriplet()); + distLineZAxis.setStart(distLineYAxis.getEnd()); + distLineZAxis.setEnd(end.toTriplet()); + } catch (RPCException e) { } + } - private void createLines(Vector start, Vector end) { - try { - distanceLine = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); - distLineXAxis = drawing.addLine(start.toTriplet(), new Vector(end.x, 0.0, 0.0).toTriplet(), vesselRefFrame, - true); - distLineYAxis = drawing.addLine(start.toTriplet(), new Vector(end.x, end.y, 0.0).toTriplet(), - vesselRefFrame, true); - distLineZAxis = drawing.addLine(start.toTriplet(), end.toTriplet(), vesselRefFrame, true); - distanceLine.setThickness(0.5f); - distLineXAxis.setThickness(0.25f); - distLineYAxis.setThickness(0.25f); - distLineZAxis.setThickness(0.25f); - distLineXAxis.setColor(new Vector(1.0, 0.0, 0.0).toTriplet()); - distLineYAxis.setColor(new Vector(0.0, 1.0, 0.0).toTriplet()); - distLineZAxis.setColor(new Vector(0.0, 0.0, 1.0).toTriplet()); - } catch (RPCException e) { - } + private void cleanup() { + if (dockingExecutor != null && !dockingExecutor.isShutdown()) { + dockingExecutor.shutdownNow(); } - - private void updateLines(Vector start, Vector end) { - // Updating drawing lines: - try { - distanceLine.setStart(start.toTriplet()); - distanceLine.setEnd(end.toTriplet()); - distLineXAxis.setStart(start.toTriplet()); - distLineXAxis.setEnd(new Vector(end.x, 0.0, 0.0).toTriplet()); - distLineYAxis.setStart(distLineXAxis.getEnd()); - distLineYAxis.setEnd(new Vector(end.x, end.y, 0.0).toTriplet()); - distLineZAxis.setStart(distLineYAxis.getEnd()); - distLineZAxis.setEnd(end.toTriplet()); - } catch (RPCException e) { - } + try { + if (errorStream != null) { + errorStream.remove(); + } + distanceLine.remove(); + distLineXAxis.remove(); + distLineYAxis.remove(); + distLineZAxis.remove(); + vessel.ap.disengage(); + vessel.throttle(0); + } catch (RPCException | NullPointerException e) { + // ignore } - + } } diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index 57cdc6f..12bf23b 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -1,284 +1,436 @@ package com.pesterenan.controllers; -import static com.pesterenan.MechPeste.getSpaceCenter; - -import java.util.Map; - +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Utilities; - +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import krpc.client.RPCException; +import krpc.client.Stream; import krpc.client.StreamException; +import krpc.client.services.SpaceCenter.Engine; import krpc.client.services.SpaceCenter.VesselSituation; public class LandingController extends Controller { - public static final double MAX_VELOCITY = 5; - private static final long sleepTime = 50; - private static final double velP = 0.05; - private static final double velI = 0.005; - private static final double velD = 0.001; - private ControlePID altitudeCtrl; - private ControlePID velocityCtrl; - private Navigation navigation; - private final int HUNDRED_PERCENT = 100; - private double hoverAltitude; - private boolean hoveringMode; - private boolean hoverAfterApproximation; - private boolean landingMode; - private MODE currentMode; - private double altitudeErrorPercentage; - private float maxTWR; - - public LandingController(Map commands) { - super(); - this.commands = commands; - this.navigation = new Navigation(getActiveVessel()); - this.initializeParameters(); - } + private enum MODE { + APPROACHING, + DEORBITING, + ORIENTING_DEORBIT, + EXECUTING_DEORBIT_BURN, + GOING_DOWN, + GOING_UP, + HOVERING, + LANDING, + WAITING + } - private void initializeParameters() { - altitudeCtrl = new ControlePID(getSpaceCenter(), sleepTime); - velocityCtrl = new ControlePID(getSpaceCenter(), sleepTime); - altitudeCtrl.setOutput(0, 1); - velocityCtrl.setOutput(0, 1); - } + public static final double MAX_VELOCITY = 5; + private static final double velP = 0.05; + private static final double velI = 0.000001; + private static final double velD = 0.001; + private ControlePID altitudeCtrl; + private ControlePID velocityCtrl; + private Navigation navigation; + private final int HUNDRED_PERCENT = 100; + private double altitudeErrorPercentage, hoverAltitude, targetPeriapsis; + private boolean hoverAfterApproximation, landingMode; + private MODE currentMode; + private float maxTWR; + private final Map commands; - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { - hoverArea(); - } - if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { - autoLanding(); - } + private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling, wasAirborne = false; + private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag; + private Stream apErrorStream; + private ScheduledExecutorService landingExecutor; + + public LandingController(ActiveVessel vessel, Map commands) { + super(vessel); + this.commands = commands; + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); + this.initializeParameters(); + } + + @Override + public void run() { + try { + if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { + hoverArea(); + } else if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { + autoLanding(); + } + landingExecutor = Executors.newSingleThreadScheduledExecutor(); + landingExecutor.scheduleAtFixedRate(this::landingStateMachine, 0, 100, TimeUnit.MILLISECONDS); + } catch (RPCException | StreamException e) { + System.err.println("Erro no run do landingController:" + e.getMessage()); + setCurrentStatus(Bundle.getString("status_data_unavailable")); + cleanup(); } + } - private void hoverArea() { - try { - hoverAltitude = Double.parseDouble(commands.get(Module.HOVER_ALTITUDE.get())); - hoveringMode = true; - ap.engage(); - tuneAutoPilot(); - while (hoveringMode) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - try { - altitudeErrorPercentage = surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; - // Select which mode depending on altitude error: - if (altitudeErrorPercentage > HUNDRED_PERCENT) { - currentMode = MODE.GOING_DOWN; - } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.9) { - currentMode = MODE.GOING_UP; - } else { - currentMode = MODE.HOVERING; - } - changeControlMode(); - } catch (RPCException | StreamException ignored) { - } - Thread.sleep(sleepTime); - } - } catch (InterruptedException | RPCException ignored) { - // disengageAfterException(Bundle.getString("status_liftoff_abort")); + private void landingStateMachine() { + try { + if (!isRunning()) { + cleanup(); + return; + } + if (landingMode) { + executeLandingStep(); + } else { + altitudeErrorPercentage = vessel.surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; + if (altitudeErrorPercentage > HUNDRED_PERCENT * 1.1) { + currentMode = MODE.GOING_DOWN; + } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.9) { + currentMode = MODE.GOING_UP; + } else { + currentMode = MODE.HOVERING; } + executeHoverStep(); + } + } catch (Exception e) { + System.err.println("Landing state machine error: " + e.getMessage()); + cleanup(); } + } - private void autoLanding() { - try { - landingMode = true; - maxTWR = Float.parseFloat(commands.get(Module.MAX_TWR.get())); - hoverAfterApproximation = Boolean.parseBoolean(commands.get(Module.HOVER_AFTER_LANDING.get())); - hoverAltitude = Double.parseDouble(commands.get(Module.HOVER_ALTITUDE.get())); - if (!hoverAfterApproximation) { - hoverAltitude = 100; - } - setCurrentStatus(Bundle.getString("status_starting_landing_at") + " " + currentBody.getName()); - currentMode = MODE.DEORBITING; - ap.engage(); - changeControlMode(); - tuneAutoPilot(); - setCurrentStatus(Bundle.getString("status_starting_landing")); - while (landingMode) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - getActiveVessel().getControl().setBrakes(true); - changeControlMode(); - Thread.sleep(sleepTime); - } - } catch (RPCException | StreamException | InterruptedException e) { - setCurrentStatus(Bundle.getString("status_ready")); - } + private void initializeParameters() { + maxTWR = Float.parseFloat(commands.get(Module.MAX_TWR.get())); + hoverAltitude = Double.parseDouble(commands.get(Module.HOVER_ALTITUDE.get())); + altitudeCtrl = new ControlePID(vessel.spaceCenter); + velocityCtrl = new ControlePID(vessel.spaceCenter); + altitudeCtrl.setOutput(0, 1); + velocityCtrl.setOutput(0, 1); + } + + private void hoverArea() throws RPCException, StreamException { + landingMode = false; + vessel.ap.engage(); + vessel.tuneAutoPilot(); + setupCallbacks(); + } + + private void autoLanding() throws RPCException, StreamException { + landingMode = true; + hoverAfterApproximation = Boolean.parseBoolean(commands.get(Module.HOVER_AFTER_LANDING.get())); + if (!hoverAfterApproximation) { + hoverAltitude = 100; } + altitudeCtrl.reset(); + velocityCtrl.reset(); + setCurrentStatus( + Bundle.getString("status_starting_landing_at") + " " + vessel.currentBody.getName()); + currentMode = MODE.DEORBITING; + vessel.ap.engage(); + vessel.tuneAutoPilot(); + vessel.getActiveVessel().getControl().setBrakes(true); + setCurrentStatus(Bundle.getString("status_starting_landing")); + setupCallbacks(); + } - private void changeControlMode() throws RPCException, StreamException, InterruptedException { - adjustPIDbyTWR(); - double velPID, altPID = 0; - // Change vessel behavior depending on which mode is active - switch (currentMode) { - case DEORBITING : - deOrbitShip(); - currentMode = MODE.WAITING; - break; - case WAITING : - if (verticalVelocity.get() > 0) { - setCurrentStatus(Bundle.getString("status_waiting_for_landing")); - throttle(0.0f); - } else { - currentMode = MODE.APPROACHING; - } - break; - case APPROACHING : - altitudeCtrl.reset(); - velocityCtrl.reset(); - altitudeCtrl.setOutput(0, 1); - velocityCtrl.setOutput(0, 1); - double currentVelocity = calculateCurrentVelocityMagnitude(); - double zeroVelocity = calculateZeroVelocityMagnitude(); - double landingDistanceThreshold = Math.max(hoverAltitude, getMaxAcel(maxTWR) * 3); - double threshold = Utilities.clamp( - ((currentVelocity + zeroVelocity) - landingDistanceThreshold) / landingDistanceThreshold, 0, 1); - altPID = altitudeCtrl.calculate(currentVelocity / sleepTime, zeroVelocity / sleepTime); - velPID = velocityCtrl.calculate(verticalVelocity.get() / sleepTime, - (-Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20) / sleepTime)); - throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); - navigation.aimForLanding(); - if (threshold < 0.15 || surfaceAltitude.get() < landingDistanceThreshold) { - hoverAltitude = landingDistanceThreshold; - getActiveVessel().getControl().setGear(true); - if (hoverAfterApproximation) { - landingMode = false; - hoverArea(); - break; - } - currentMode = MODE.LANDING; + private void setupCallbacks() throws RPCException, StreamException { + // Callback for ship orientation + apErrorStream = vessel.connection.addStream(vessel.ap, "getError"); + isOrientedCallbackTag = + apErrorStream.addCallback( + (error) -> { + if (error <= 5.0) { + isOrientedForDeorbit = true; + apErrorStream.removeCallback(isOrientedCallbackTag); + } + }); + + // Callback for de-orbit burn completion + isDeorbitBurnDoneCallbackTag = + vessel.periapsis.addCallback( + p -> { + try { + if (isDeorbitBurnDone) return; // Stop if flag is already set + + if (currentMode == MODE.EXECUTING_DEORBIT_BURN) { + // During de-orbit, actively control throttle + if (p <= targetPeriapsis) { + isDeorbitBurnDone = true; + vessel.throttle(0.0f); + vessel.periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); + } else { + vessel.throttle(altitudeCtrl.calculate(targetPeriapsis, p)); + } } - setCurrentStatus("Se aproximando do momento do pouso..."); - break; - case GOING_UP : - altitudeCtrl.reset(); - velocityCtrl.reset(); - altitudeCtrl.setOutput(-0.5, 0.5); - velocityCtrl.setOutput(-0.5, 0.5); - altPID = altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT); - velPID = velocityCtrl.calculate(verticalVelocity.get(), MAX_VELOCITY); - throttle(altPID + velPID); - navigation.aimAtRadialOut(); - setCurrentStatus("Subindo altitude..."); - break; - case GOING_DOWN : - altitudeCtrl.reset(); - velocityCtrl.reset(); - controlThrottleByMatchingVerticalVelocity(-MAX_VELOCITY); - navigation.aimAtRadialOut(); - setCurrentStatus("Baixando altitude..."); - break; - case LANDING : - altitudeCtrl.reset(); - velocityCtrl.reset(); - controlThrottleByMatchingVerticalVelocity( - horizontalVelocity.get() > 4 ? 0 : -Utilities.clamp(surfaceAltitude.get() * 0.1, 1, 10)); - navigation.aimForLanding(); - setCurrentStatus("Pousando..."); - hasTheVesselLanded(); - break; - case HOVERING : - altitudeCtrl.reset(); - velocityCtrl.reset(); - altitudeCtrl.setOutput(-0.5, 0.5); - velocityCtrl.setOutput(-0.5, 0.5); - altPID = altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT); - velPID = velocityCtrl.calculate(verticalVelocity.get(), 0); - throttle(altPID + velPID); - navigation.aimAtRadialOut(); - setCurrentStatus("Sobrevoando area..."); - break; - } - } + } catch (RPCException e) { + System.err.println("isDeorbitBurnDoneCallbackTag error: " + e.getMessage()); + } + }); - private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) - throws RPCException, StreamException { - velocityCtrl.setOutput(0, 1); - throttle(velocityCtrl.calculate(verticalVelocity.get(), velocityToMatch + horizontalVelocity.get())); - } + // Callback for when the ship starts falling back to the ground + isFallingCallbackTag = + vessel.verticalVelocity.addCallback( + (vv) -> { + if (vv <= 0) { + isFalling = true; + vessel.verticalVelocity.removeCallback(isFallingCallbackTag); + } + }); + + // Start all streams + apErrorStream.start(); + vessel.apoapsis.start(); + vessel.periapsis.start(); + vessel.verticalVelocity.start(); + } - private void deOrbitShip() throws RPCException, StreamException, InterruptedException { - throttle(0.0f); - if (getActiveVessel().getSituation().equals(VesselSituation.ORBITING) - || getActiveVessel().getSituation().equals(VesselSituation.SUB_ORBITAL)) { - setCurrentStatus(Bundle.getString("status_going_suborbital")); - ap.engage(); - getActiveVessel().getControl().setRCS(true); - while (ap.getError() > 5) { - navigation.aimForLanding(); - setCurrentStatus(Bundle.getString("status_orienting_ship")); - ap.wait_(); - Thread.sleep(sleepTime); - } - double targetPeriapsis = currentBody.getAtmosphereDepth(); - targetPeriapsis = targetPeriapsis > 0 ? targetPeriapsis / 2 : -currentBody.getEquatorialRadius() / 2; - while (periapsis.get() > -apoapsis.get()) { - navigation.aimForLanding(); - throttle(altitudeCtrl.calculate(targetPeriapsis, periapsis.get())); - setCurrentStatus(Bundle.getString("status_lowering_periapsis")); - Thread.sleep(sleepTime); - } - getActiveVessel().getControl().setRCS(false); - throttle(0.0f); + private void executeLandingStep() throws RPCException, StreamException, InterruptedException { + adjustLandingPID(); + // Change vessel behavior depending on which mode is active + switch (currentMode) { + case DEORBITING: + if (vessel.getActiveVessel().getSituation().equals(VesselSituation.ORBITING) + || vessel.getActiveVessel().getSituation().equals(VesselSituation.SUB_ORBITAL)) { + setCurrentStatus(Bundle.getString("status_going_suborbital")); + vessel.getActiveVessel().getControl().setRCS(true); + vessel.throttle(0.0f); + targetPeriapsis = vessel.currentBody.getAtmosphereDepth(); + targetPeriapsis = + targetPeriapsis > 0 + ? targetPeriapsis / 2 + : -vessel.currentBody.getEquatorialRadius() / 2; + currentMode = MODE.ORIENTING_DEORBIT; + } else { + currentMode = MODE.APPROACHING; } - } + break; + case ORIENTING_DEORBIT: + setCurrentStatus(Bundle.getString("status_orienting_ship")); + navigation.aimForDeorbit(); + if (isOrientedForDeorbit) { + currentMode = MODE.EXECUTING_DEORBIT_BURN; + } + break; + case EXECUTING_DEORBIT_BURN: + setCurrentStatus(Bundle.getString("status_lowering_periapsis")); + if (isDeorbitBurnDone) { + vessel.getActiveVessel().getControl().setRCS(false); + vessel.throttle(0.0f); + currentMode = MODE.WAITING; + } + break; + case WAITING: + if (isFalling) { + currentMode = MODE.APPROACHING; + } else { + setCurrentStatus(Bundle.getString("status_waiting_for_landing")); + vessel.throttle(0.0f); + } + break; + case APPROACHING: + for (Engine engine : vessel.getActiveVessel().getParts().getEngines()) { + if (engine.getPart().getStage() == vessel.getActiveVessel().getControl().getCurrentStage() + && !engine.getActive()) { + engine.setActive(true); + } + } + altitudeCtrl.setOutput(0, 1); + velocityCtrl.setOutput(0, 1); - /** - * Adjust altitude and velocity PID gains according to current ship TWR: - */ - private void adjustPIDbyTWR() throws RPCException, StreamException { - double currentTWR = Math.min(getTWR(), maxTWR); - // double currentTWR = getMaxAcel(maxTWR); - double pGain = currentTWR / (sleepTime); - System.out.println(pGain); - altitudeCtrl.setPIDValues(pGain * 0.1, 0.0002, pGain * 0.1 * 2); - velocityCtrl.setPIDValues(pGain * 0.1, 0.1, 0.001); - } + double brakingDistance = calculateBrakingDistance(); + double surfaceAltitude = vessel.surfaceAltitude.get(); + // The threshold is a ratio of our current altitude versus the altitude needed to brake. + // It's 1 when we are much higher than the braking distance, and drops to 0 as we approach + // it. + double threshold = + (brakingDistance > 1) + ? Utilities.clamp(surfaceAltitude / brakingDistance - 1, 0, 1) + : 0; - private boolean hasTheVesselLanded() throws RPCException { - if (getActiveVessel().getSituation().equals(VesselSituation.LANDED) - || getActiveVessel().getSituation().equals(VesselSituation.SPLASHED)) { - setCurrentStatus(Bundle.getString("status_landed")); - hoveringMode = false; + double altPID = altitudeCtrl.calculate(surfaceAltitude, brakingDistance); + double velPID = + velocityCtrl.calculate( + vessel.verticalVelocity.get(), + (-Utilities.clamp(vessel.surfaceAltitude.get() * 0.1, 3, 20))); + vessel.throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); + navigation.aimForLanding(); + + double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 3); + if (surfaceAltitude < landingDistanceThreshold) { + vessel.getActiveVessel().getControl().setGear(true); + if (hoverAfterApproximation) { landingMode = false; - throttle(0.0f); - getActiveVessel().getControl().setSAS(true); - getActiveVessel().getControl().setRCS(true); - getActiveVessel().getControl().setBrakes(false); - ap.disengage(); - return true; + hoverArea(); + break; + } + currentMode = MODE.LANDING; } - return false; + setCurrentStatus("Se aproximando do momento do pouso..."); + break; + + case LANDING: + if (hasTheVesselLanded()) break; + controlThrottleByMatchingVerticalVelocity( + vessel.horizontalVelocity.get() > 5 + ? 0 + : -Utilities.clamp(vessel.surfaceAltitude.get() * 0.1, 3, 20)); + navigation.aimForLanding(); + setCurrentStatus("Pousando..."); + break; + default: + break; } + } - private double calculateCurrentVelocityMagnitude() throws RPCException, StreamException { - double timeToGround = surfaceAltitude.get() / verticalVelocity.get(); - double horizontalDistance = horizontalVelocity.get() * timeToGround; - return calculateEllipticTrajectory(horizontalDistance, surfaceAltitude.get()); + private void executeHoverStep() throws RPCException, StreamException, InterruptedException { + if (hasTheVesselLanded()) { + stop(); + return; } + adjustHoverPID(); + altitudeCtrl.setOutput(-0.5, 0.5); + velocityCtrl.setOutput(-0.5, 0.5); + switch (currentMode) { + case GOING_UP: + vessel.throttle( + altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) + + velocityCtrl.calculate(vessel.verticalVelocity.get(), MAX_VELOCITY)); + navigation.aimAtRadialOut(); + setCurrentStatus("Subindo altitude..."); + break; + case GOING_DOWN: + controlThrottleByMatchingVerticalVelocity(-MAX_VELOCITY); + navigation.aimAtRadialOut(); + setCurrentStatus("Baixando altitude..."); + break; + case HOVERING: + vessel.throttle( + altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) + + velocityCtrl.calculate(0, -vessel.verticalVelocity.get())); + navigation.aimAtRadialOut(); + setCurrentStatus("Sobrevoando area..."); + break; + default: + break; + } + } + + private void adjustLandingPID() throws RPCException, StreamException { + double maxAccel = vessel.getMaxAcel(maxTWR); + double currentTWR = Math.min(vessel.getTWR(), maxTWR); + + if (currentMode == MODE.APPROACHING) { + // 1. Calcula a distância de trajetória restante + double trajectoryLength; + double timeToGround = 0; + if (vessel.verticalVelocity.get() < -0.1) { // Apenas calcula se estiver descendo + timeToGround = vessel.surfaceAltitude.get() / -vessel.verticalVelocity.get(); + } + if (timeToGround > 0) { + double horizontalDistance = vessel.horizontalVelocity.get() * timeToGround; + trajectoryLength = + calculateEllipticTrajectory(horizontalDistance, vessel.surfaceAltitude.get()); + } else { + trajectoryLength = vessel.surfaceAltitude.get(); // Usa apenas a altitude como fallback + } + + // 2. Calcula a velocidade total (vetorial) + double totalVelocity = + Math.sqrt( + Math.pow(vessel.verticalVelocity.get(), 2) + + Math.pow(vessel.horizontalVelocity.get(), 2)); - private double calculateZeroVelocityMagnitude() throws RPCException, StreamException { - double zeroVelocityDistance = calculateEllipticTrajectory(horizontalVelocity.get(), verticalVelocity.get()); - double zeroVelocityBurnTime = zeroVelocityDistance / getMaxAcel(maxTWR); - return zeroVelocityDistance * zeroVelocityBurnTime; + // 3. Calcula o tempo de impacto baseado na TRAJETÓRIA + double timeToImpact = 10.0; // Default seguro + if (totalVelocity > 1) { // Evita divisão por zero + timeToImpact = Utilities.clamp(trajectoryLength / totalVelocity, 0.5, 20); + } + + // O resto da lógica permanece o mesmo, mas agora usando o novo timeToImpact + double Kp = (currentTWR * velP) / timeToImpact; + double Kd = Kp * (velD / velP); + double Ki = 0.0001; + altitudeCtrl.setPIDValues(Kp, Ki, Kd); + } else { + // Para outros modos, usa um PID mais simples e estável + altitudeCtrl.setPIDValues(maxAccel * velP, velI, velD); } - private double calculateEllipticTrajectory(double a, double b) { - double semiMajor = Math.max(a * 2, b * 2); - double semiMinor = Math.min(a * 2, b * 2); - return Math.PI * Math.sqrt((semiMajor * semiMajor + semiMinor * semiMinor)) / 4; + // O controlador de velocidade pode usar uma sintonia mais simples + velocityCtrl.setPIDValues(currentTWR * velP, velI, velD); + } + + private void adjustHoverPID() throws RPCException, StreamException { + double currentTWR = Math.min(vessel.getTWR(), maxTWR); + altitudeCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); + velocityCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); + } + + private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) + throws RPCException, StreamException { + velocityCtrl.setOutput(0, 1); + vessel.throttle( + velocityCtrl.calculate( + vessel.verticalVelocity.get(), velocityToMatch + vessel.horizontalVelocity.get())); + } + + private boolean hasTheVesselLanded() throws RPCException { + VesselSituation situation = vessel.getActiveVessel().getSituation(); + if (wasAirborne + && (situation.equals(VesselSituation.LANDED) + || situation.equals(VesselSituation.SPLASHED))) { + setCurrentStatus(Bundle.getString("status_landed")); + stop(); + vessel.throttle(0.0f); + vessel.getActiveVessel().getControl().setSAS(true); + vessel.getActiveVessel().getControl().setRCS(true); + vessel.getActiveVessel().getControl().setBrakes(false); + vessel.ap.disengage(); + return true; + } + // If we are not landed, we must be airborne + if (!situation.equals(VesselSituation.LANDED) + && !situation.equals(VesselSituation.SPLASHED) + && !situation.equals(VesselSituation.PRE_LAUNCH)) { + wasAirborne = true; + } + return false; + } + + private double calculateBrakingDistance() throws RPCException, StreamException { + double totalVelocity = + Math.sqrt( + Math.pow(vessel.verticalVelocity.get(), 2) + + Math.pow(vessel.horizontalVelocity.get(), 2)); + // Braking distance formula: d = v^2 / (2 * a) + // where a is the effective deceleration (max engine acceleration - gravity) + double maxDeceleration = vessel.getMaxAcel(maxTWR); + if (maxDeceleration <= 0.1) { // Avoid division by zero or tiny numbers + return Double.POSITIVE_INFINITY; } + return Math.pow(totalVelocity, 2) / (2 * maxDeceleration); + } + + private double calculateEllipticTrajectory(double a, double b) { + double semiMajor = Math.max(a * 2, b * 2); + double semiMinor = Math.min(a * 2, b * 2); + return Math.PI * Math.sqrt((semiMajor * semiMajor + semiMinor * semiMinor)) / 4; + } - private enum MODE { - DEORBITING, APPROACHING, GOING_UP, HOVERING, GOING_DOWN, LANDING, WAITING + private void cleanup() { + if (landingExecutor != null && !landingExecutor.isShutdown()) { + landingExecutor.shutdownNow(); + } + try { + if (vessel.ap != null) { + vessel.ap.disengage(); + } + if (apErrorStream != null) { + apErrorStream.remove(); + } + vessel.throttle(0); + setCurrentStatus(Bundle.getString("status_ready")); + } catch (RPCException | NullPointerException e) { + System.err.println("Erro durante a limpeza do LandingController: " + e.getMessage()); } + } } diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index 2901639..2c9139d 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -1,220 +1,286 @@ package com.pesterenan.controllers; -import static com.pesterenan.MechPeste.getSpaceCenter; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.pesterenan.MechPeste; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Utilities; - +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import krpc.client.RPCException; +import krpc.client.Stream; import krpc.client.StreamException; import krpc.client.services.SpaceCenter.Engine; import krpc.client.services.SpaceCenter.Fairing; +import krpc.client.services.SpaceCenter.VesselSituation; public class LiftoffController extends Controller { - private static final float PITCH_UP = 90; - private ControlePID thrControl; - private float currentPitch; - private float finalApoapsisAlt; - private float heading; - private float roll; - private float maxTWR; - - private boolean willDecoupleStages, willOpenPanelsAndAntenna; - private String gravityCurveModel = Module.CIRCULAR.get(); - private Navigation navigation; - - public LiftoffController(Map commands) { - super(); - this.commands = commands; - this.navigation = new Navigation(getActiveVessel()); - initializeParameters(); - } + private enum LIFTOFF_MODE { + GRAVITY_TURN, + FINALIZE_ORBIT, + CIRCULARIZE + } - private void initializeParameters() { - currentPitch = PITCH_UP; - setFinalApoapsisAlt(Float.parseFloat(commands.get(Module.APOAPSIS.get()))); - setHeading(Float.parseFloat(commands.get(Module.DIRECTION.get()))); - setRoll(Float.parseFloat(commands.get(Module.ROLL.get()))); - maxTWR = (float) Utilities.clamp(Float.parseFloat(commands.get(Module.MAX_TWR.get())), 1.2, 5.0); - setGravityCurveModel(commands.get(Module.INCLINATION.get())); - willOpenPanelsAndAntenna = Boolean.parseBoolean(commands.get(Module.OPEN_PANELS.get())); - willDecoupleStages = Boolean.parseBoolean(commands.get(Module.STAGE.get())); - thrControl = new ControlePID(getSpaceCenter(), 25); - thrControl.setOutput(0.0, 1.0); - } + private static final float PITCH_UP = 90; + private ControlePID thrControl; + private float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; + private volatile boolean targetApoapsisReached, dynamicPressureLowEnough; + private int apoapsisCallbackTag, pressureCallbackTag; + private Stream pressureStream; + private boolean willDecoupleStages, willOpenPanelsAndAntenna; + private String gravityCurveModel = Module.CIRCULAR.get(); + private Navigation navigation; + private LIFTOFF_MODE liftoffMode; + private double startCurveAlt; + private final Map commands; + private ScheduledExecutorService liftoffExecutor; - @Override - public void run() { - try { - tuneAutoPilot(); - liftoff(); - gravityCurve(); - finalizeCurve(); - circularizeOrbitOnApoapsis(); - } catch (RPCException | InterruptedException | StreamException ignored) { - setCurrentStatus(Bundle.getString("status_ready")); - } - } + public LiftoffController(ActiveVessel vessel, Map commands) { + super(vessel); + this.commands = commands; + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); + initializeParameters(); + } - private void gravityCurve() throws RPCException, StreamException, InterruptedException { - ap.setReferenceFrame(surfaceReferenceFrame); - ap.targetPitchAndHeading(currentPitch, getHeading()); - ap.setTargetRoll(getRoll()); - ap.engage(); - throttle(getMaxThrottleForTWR(maxTWR)); - double startCurveAlt = altitude.get(); - - while (currentPitch > 1) { - if (apoapsis.get() > getFinalApoapsis()) { - throttle(0); - break; - } - double altitudeProgress = Utilities.remap(startCurveAlt, getFinalApoapsis(), 1, 0.01, altitude.get(), - false); - currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); - double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); - ap.setTargetPitch(currentPitch); - throttle(Math.min(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000), - getMaxThrottleForTWR(currentMaxTWR))); - - if (willDecoupleStages && isCurrentStageWithoutFuel()) { - decoupleStage(); - } - setCurrentStatus(String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); - - Thread.sleep(25); - if (Thread.interrupted()) { - throw new InterruptedException(); - } + @Override + public void run() { + try { + // Part 1: Blocking Countdown and Launch + if (vessel.getActiveVessel().getSituation().equals(VesselSituation.PRE_LAUNCH)) { + vessel.throttleUp(vessel.getMaxThrottleForTWR(1.4), 1); + for (double count = 5.0; count >= 0; count -= 0.1) { + if (!isRunning()) return; + setCurrentStatus(String.format(Bundle.getString("status_launching_in"), count)); + Thread.sleep(100); } - } + setCurrentStatus(Bundle.getString("status_liftoff")); + vessel.getActiveVessel().getControl().activateNextStage(); + } else { + vessel.throttle(1.0f); + } - private double calculateTWRBasedOnPressure(float currentPitch) throws RPCException { - float currentPressure = flightParameters.getDynamicPressure(); - if (currentPressure <= 10) { - return Utilities.remap(90.0, 0.0, maxTWR, 5.0, currentPitch, true); - } - return Utilities.remap(22000.0, 10.0, maxTWR, 5.0, currentPressure, true); - } + // Part 2: Async Gravity Turn + liftoffMode = LIFTOFF_MODE.GRAVITY_TURN; // Set initial state for async phase + setupCallbacks(); + vessel.tuneAutoPilot(); + startCurveAlt = vessel.altitude.get(); + vessel.ap.setReferenceFrame(vessel.surfaceReferenceFrame); + vessel.ap.targetPitchAndHeading(currentPitch, heading); + vessel.ap.setTargetRoll(this.roll); + vessel.ap.engage(); - private void finalizeCurve() throws RPCException, StreamException, InterruptedException { - setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); - getActiveVessel().getControl().setRCS(true); - - while (flightParameters.getDynamicPressure() > 10) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtPrograde(); - throttle(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000)); - Thread.sleep(25); - } - throttle(0.0f); - if (willDecoupleStages) { - jettisonFairings(); - } - if (willOpenPanelsAndAntenna) { - openPanelsAndAntenna(); - } - } + liftoffExecutor = Executors.newSingleThreadScheduledExecutor(); + liftoffExecutor.scheduleAtFixedRate(this::handleLiftoff, 0, 100, TimeUnit.MILLISECONDS); - private void circularizeOrbitOnApoapsis() { - setCurrentStatus(Bundle.getString("status_planning_orbit")); - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), Module.MANEUVER.get()); - commands.put(Module.FUNCTION.get(), Module.APOAPSIS.get()); - commands.put(Module.FINE_ADJUST.get(), String.valueOf(false)); - MechPeste.newInstance().startModule(commands); + } catch (RPCException | InterruptedException | StreamException e) { + cleanup(); + setCurrentStatus(Bundle.getString("status_ready")); } + } - private void jettisonFairings() throws RPCException, InterruptedException { - List fairings = getActiveVessel().getParts().getFairings(); - if (fairings.size() > 0) { - setCurrentStatus(Bundle.getString("status_jettisoning_shields")); - for (Fairing f : fairings) { - if (f.getJettisoned()) { - // Overly complicated way of getting the event from the button in the fairing - // to jettison the fairing, since the jettison method doesn't work. - String eventName = f.getPart().getModules().get(0).getEvents().get(0); - f.getPart().getModules().get(0).triggerEvent(eventName); - Thread.sleep(10000); - } - } - } - } + public void setHeading(float heading) { + final int MIN_HEADING = 0; + final int MAX_HEADING = 360; + this.heading = (float) Utilities.clamp(heading, MIN_HEADING, MAX_HEADING); + } - private void openPanelsAndAntenna() throws RPCException, InterruptedException { - getActiveVessel().getControl().setSolarPanels(true); - getActiveVessel().getControl().setRadiators(true); - getActiveVessel().getControl().setAntennas(true); - } + public void setRoll(float roll) { + final int MIN_ROLL = 0; + final int MAX_ROLL = 360; + this.roll = (float) Utilities.clamp(roll, MIN_ROLL, MAX_ROLL); + } - private double calculateCurrentPitch(double currentAltitude) { - if (gravityCurveModel.equals(Module.QUADRATIC.get())) { - return Utilities.easeInQuad(currentAltitude) * PITCH_UP; - } - if (gravityCurveModel.equals(Module.CUBIC.get())) { - return Utilities.easeInCubic(currentAltitude) * PITCH_UP; - } - if (gravityCurveModel.equals(Module.SINUSOIDAL.get())) { - return Utilities.easeInSine(currentAltitude) * PITCH_UP; - } - if (gravityCurveModel.equals(Module.EXPONENCIAL.get())) { - return Utilities.easeInExpo(currentAltitude) * PITCH_UP; - } - return Utilities.easeInCirc(currentAltitude) * PITCH_UP; - } + public void setFinalApoapsisAlt(float finalApoapsisAlt) { + final int MIN_FINAL_APOAPSIS = 10000; + final int MAX_FINAL_APOAPSIS = 2000000; + this.finalApoapsisAlt = + (float) Utilities.clamp(finalApoapsisAlt, MIN_FINAL_APOAPSIS, MAX_FINAL_APOAPSIS); + } - private boolean isCurrentStageWithoutFuel() throws RPCException { - for (Engine engine : getActiveVessel().getParts().getEngines()) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() - && !engine.getHasFuel()) { - return true; - } - } - return false; + private void initializeParameters() { + currentPitch = PITCH_UP; + setFinalApoapsisAlt(Float.parseFloat(commands.get(Module.APOAPSIS.get()))); + setHeading(Float.parseFloat(commands.get(Module.DIRECTION.get()))); + setRoll(Float.parseFloat(commands.get(Module.ROLL.get()))); + maxTWR = + (float) Utilities.clamp(Float.parseFloat(commands.get(Module.MAX_TWR.get())), 1.2, 5.0); + gravityCurveModel = commands.get(Module.INCLINATION.get()); + willOpenPanelsAndAntenna = Boolean.parseBoolean(commands.get(Module.OPEN_PANELS.get())); + willDecoupleStages = Boolean.parseBoolean(commands.get(Module.STAGE.get())); + thrControl = new ControlePID(vessel.getConnectionManager().getSpaceCenter()); + thrControl.setOutput(0.0, 1.0); + } + + private void setupCallbacks() throws RPCException, StreamException { + apoapsisCallbackTag = + vessel.apoapsis.addCallback( + (apo) -> { + if (apo >= finalApoapsisAlt) { + targetApoapsisReached = true; + } + }); + vessel.apoapsis.start(); + + pressureStream = vessel.connection.addStream(vessel.flightParameters, "getDynamicPressure"); + pressureCallbackTag = + pressureStream.addCallback( + (pressure) -> { + if (pressure <= 10) { + dynamicPressureLowEnough = true; + } + }); + pressureStream.start(); + } + + private void handleLiftoff() { + try { + if (!isRunning()) { + cleanup(); + return; + } + switch (liftoffMode) { + case GRAVITY_TURN: + gravityTurn(); + break; + case FINALIZE_ORBIT: + finalizeOrbit(); + break; + case CIRCULARIZE: + circularizeOrbitOnApoapsis(); + stop(); + break; + } + } catch (RPCException | StreamException | InterruptedException e) { + System.err.println("Liftoff error: " + e.getMessage()); + cleanup(); } + } + + private void gravityTurn() throws RPCException, StreamException, InterruptedException { + if (currentPitch > 1 && !targetApoapsisReached) { + double altitudeProgress = + Utilities.remap(startCurveAlt, finalApoapsisAlt, 1, 0.01, vessel.altitude.get(), false); + currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); + double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); + vessel.ap.setTargetPitch(currentPitch); + double throttleValue = + Math.min( + thrControl.calculate(vessel.apoapsis.get() / finalApoapsisAlt * 1000, 1000), + vessel.getMaxThrottleForTWR(currentMaxTWR)); + vessel.throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); - public float getHeading() { - return heading; + if (willDecoupleStages && isCurrentStageWithoutFuel()) { + vessel.decoupleStage(); + } + setCurrentStatus( + String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); + } else { + vessel.throttle(0); + liftoffMode = LIFTOFF_MODE.FINALIZE_ORBIT; } + } - public void setHeading(float heading) { - final int MIN_HEADING = 0; - final int MAX_HEADING = 360; - this.heading = (float) Utilities.clamp(heading, MIN_HEADING, MAX_HEADING); + private void finalizeOrbit() throws RPCException, StreamException, InterruptedException { + if (!dynamicPressureLowEnough) { + setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); + vessel.getActiveVessel().getControl().setRCS(true); + navigation.aimAtPrograde(); + vessel.throttle(thrControl.calculate(vessel.apoapsis.get() / finalApoapsisAlt * 1000, 1000)); + } else { + vessel.throttle(0.0f); + if (willDecoupleStages) { + jettisonFairings(); + } + if (willOpenPanelsAndAntenna) { + openPanelsAndAntenna(); + } + vessel.apoapsis.removeCallback(apoapsisCallbackTag); + pressureStream.removeCallback(pressureCallbackTag); + liftoffMode = LIFTOFF_MODE.CIRCULARIZE; } + } - public float getFinalApoapsis() { - return finalApoapsisAlt; + private void cleanup() { + try { + if (liftoffExecutor != null && !liftoffExecutor.isShutdown()) { + liftoffExecutor.shutdownNow(); + } + vessel.apoapsis.removeCallback(apoapsisCallbackTag); + pressureStream.removeCallback(pressureCallbackTag); + pressureStream.remove(); + vessel.ap.disengage(); + vessel.throttle(0); + } catch (RPCException | NullPointerException e) { + // ignore } + } - public float getRoll() { - return this.roll; + private double calculateTWRBasedOnPressure(float currentPitch) throws RPCException { + float currentPressure = vessel.flightParameters.getDynamicPressure(); + if (currentPressure <= 10) { + return Utilities.remap(90.0, 0.0, maxTWR, 5.0, currentPitch, true); } + return Utilities.remap(22000.0, 10.0, maxTWR, 5.0, currentPressure, true); + } - public void setRoll(float roll) { - final int MIN_ROLL = 0; - final int MAX_ROLL = 360; - this.roll = (float) Utilities.clamp(roll, MIN_ROLL, MAX_ROLL); + private void circularizeOrbitOnApoapsis() { + setCurrentStatus(Bundle.getString("status_planning_orbit")); + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), Module.MANEUVER.get()); + commands.put(Module.FUNCTION.get(), Module.APOAPSIS.get()); + commands.put(Module.FINE_ADJUST.get(), String.valueOf(false)); + vessel.getVesselManager().startModule(commands); + } + + private void jettisonFairings() throws RPCException, InterruptedException { + List fairings = vessel.getActiveVessel().getParts().getFairings(); + if (fairings.size() > 0) { + setCurrentStatus(Bundle.getString("status_jettisoning_shields")); + for (Fairing f : fairings) { + if (f.getJettisoned()) { + String eventName = f.getPart().getModules().get(0).getEvents().get(0); + f.getPart().getModules().get(0).triggerEvent(eventName); + Thread.sleep(5000); + } + } } + } - private void setGravityCurveModel(String model) { - this.gravityCurveModel = model; + private void openPanelsAndAntenna() throws RPCException, InterruptedException { + vessel.getActiveVessel().getControl().setSolarPanels(true); + vessel.getActiveVessel().getControl().setRadiators(true); + vessel.getActiveVessel().getControl().setAntennas(true); + } + + private double calculateCurrentPitch(double currentAltitude) { + if (gravityCurveModel.equals(Module.QUADRATIC.get())) { + return Utilities.easeInQuad(currentAltitude) * PITCH_UP; + } + if (gravityCurveModel.equals(Module.CUBIC.get())) { + return Utilities.easeInCubic(currentAltitude) * PITCH_UP; + } + if (gravityCurveModel.equals(Module.SINUSOIDAL.get())) { + return Utilities.easeInSine(currentAltitude) * PITCH_UP; + } + if (gravityCurveModel.equals(Module.EXPONENCIAL.get())) { + return Utilities.easeInExpo(currentAltitude) * PITCH_UP; } + return Utilities.easeInCirc(currentAltitude) * PITCH_UP; + } - public void setFinalApoapsisAlt(float finalApoapsisAlt) { - final int MIN_FINAL_APOAPSIS = 10000; - final int MAX_FINAL_APOAPSIS = 2000000; - this.finalApoapsisAlt = (float) Utilities.clamp(finalApoapsisAlt, MIN_FINAL_APOAPSIS, MAX_FINAL_APOAPSIS); + private boolean isCurrentStageWithoutFuel() throws RPCException { + for (Engine engine : vessel.getActiveVessel().getParts().getEngines()) { + if (engine.getPart().getStage() == vessel.getActiveVessel().getControl().getCurrentStage() + && !engine.getHasFuel()) { + return true; + } } + return false; + } } diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 40356e9..8ce85aa 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -1,21 +1,18 @@ package com.pesterenan.controllers; -import static com.pesterenan.MechPeste.getConnection; -import static com.pesterenan.MechPeste.getSpaceCenter; - -import java.util.List; -import java.util.Map; - -import org.javatuples.Triplet; - +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Attributes; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Vector; -import com.pesterenan.views.RunManeuverJPanel; - +import com.pesterenan.views.MainGui; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import krpc.client.RPCException; import krpc.client.Stream; import krpc.client.StreamException; @@ -26,526 +23,695 @@ import krpc.client.services.SpaceCenter.ReferenceFrame; import krpc.client.services.SpaceCenter.Vessel; import krpc.client.services.SpaceCenter.VesselSituation; +import org.javatuples.Triplet; public class ManeuverController extends Controller { - public final static float CONST_GRAV = 9.81f; - private ControlePID ctrlRCS; - private ControlePID ctrlManeuver; - private Navigation navigation; - private boolean fineAdjustment; - private double lowOrbitAltitude; - - public ManeuverController(Map commands) { - super(); - this.commands = commands; - this.navigation = new Navigation(getActiveVessel()); - initializeParameters(); + enum Compare { + INC, + AP, + PE + } + + private enum RendezvousPhase { + SETUP, + CHECK_ANGULAR_DIFFERENCE, + ADJUST_PERIAPSIS, + ADJUST_APOAPSIS, + ADJUST_UT, + DONE + } + + private static class RendezvousState { + final Node maneuverNode; + RendezvousPhase phase = RendezvousPhase.SETUP; + double targetOrbitPosition; + double maneuverAP; + double maneuverPE; + + RendezvousState(Node maneuverNode) { + this.maneuverNode = maneuverNode; } - - private void initializeParameters() { - ctrlRCS = new ControlePID(getSpaceCenter(), 25); - ctrlManeuver = new ControlePID(getSpaceCenter(), 25); - ctrlManeuver.setPIDValues(1, 0.001, 0.1); - ctrlRCS.setOutput(0.5, 1.0); - fineAdjustment = canFineAdjust(commands.get(Module.FINE_ADJUST.get())); - try { - lowOrbitAltitude = new Attributes().getLowOrbitAltitude(currentBody.getName()); - System.out.println("lowOrbitAltitude: " + lowOrbitAltitude); - } catch (RPCException e) { - } + } + + public static final float CONST_GRAV = 9.81f; + private ControlePID ctrlManeuver, ctrlRCS; + + private Navigation navigation; + private boolean fineAdjustment; + private double lowOrbitAltitude; + private Stream headingErrorStream; + private Stream pitchErrorStream; + private Stream timeToNodeStream; + + private Stream rollErrorStream; + + private Stream> remainingBurnStream; + + private double lastBurnDv = Double.MAX_VALUE; + private final Map commands; + + public ManeuverController(ActiveVessel vessel, Map commands) { + super(vessel); + this.commands = commands; + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); + initializeParameters(); + } + + @Override + public void run() { + try { + calculateManeuver(); + if (!(commands.get(Module.FUNCTION.get()).equals(Module.RENDEZVOUS.get()) + || commands.get(Module.FUNCTION.get()).equals(Module.LOW_ORBIT.get()) + || commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get()))) { + executeNextManeuver(); + } + } catch (ClassCastException | InterruptedException e) { + System.out.println("INFO: Interrompendo Controle de Manobras. Erro: " + e.getMessage()); + cleanup(); } - - @Override - public void run() { - calculateManeuver(); - if (!(commands.get(Module.FUNCTION.get()).equals(Module.RENDEZVOUS.get()) - || commands.get(Module.FUNCTION.get()).equals(Module.LOW_ORBIT.get()) - || commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get()))) { - executeNextManeuver(); - } + } + + public void calculateManeuver() { + try { + vessel.tuneAutoPilot(); + if (commands.get(Module.FUNCTION.get()).equals(Module.EXECUTE.get())) { + return; + } + if (vessel.getActiveVessel().getSituation() == VesselSituation.LANDED + || vessel.getActiveVessel().getSituation() == VesselSituation.SPLASHED) { + throw new InterruptedException(); + } + if (commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get())) { + this.alignPlanesWithTargetVessel(); + return; + } + if (commands.get(Module.FUNCTION.get()).equals(Module.RENDEZVOUS.get())) { + this.rendezvousWithTargetVessel(); + return; + } + if (commands.get(Module.FUNCTION.get()).equals(Module.LOW_ORBIT.get())) { + biEllipticTransferToOrbit( + lowOrbitAltitude, vessel.getActiveVessel().getOrbit().getTimeToPeriapsis()); + return; + } + double gravParameter = vessel.currentBody.getGravitationalParameter(); + double startingAltitutde = 0, timeUntilAltitude = 0; + if (commands.get(Module.FUNCTION.get()).equals(Module.APOAPSIS.get())) { + startingAltitutde = vessel.getActiveVessel().getOrbit().getApoapsis(); + timeUntilAltitude = vessel.getActiveVessel().getOrbit().getTimeToApoapsis(); + } + if (commands.get(Module.FUNCTION.get()).equals(Module.PERIAPSIS.get())) { + startingAltitutde = vessel.getActiveVessel().getOrbit().getPeriapsis(); + timeUntilAltitude = vessel.getActiveVessel().getOrbit().getTimeToPeriapsis(); + } + + double semiMajorAxis = vessel.getActiveVessel().getOrbit().getSemiMajorAxis(); + double currentOrbitalVelocity = + Math.sqrt(gravParameter * ((2.0 / startingAltitutde) - (1.0 / semiMajorAxis))); + double targetOrbitalVelocity = + Math.sqrt(gravParameter * ((2.0 / startingAltitutde) - (1.0 / startingAltitutde))); + double maneuverDeltaV = targetOrbitalVelocity - currentOrbitalVelocity; + double[] deltaV = {maneuverDeltaV, 0, 0}; + createManeuver(timeUntilAltitude, deltaV); + } catch (RPCException | InterruptedException e) { + setCurrentStatus(Bundle.getString("status_maneuver_not_possible")); } + } + + public void executeNextManeuver() throws InterruptedException { + try { + Node maneuverNode = vessel.getActiveVessel().getControl().getNodes().get(0); + double burnTime = calculateBurnTime(maneuverNode); + executeBurn(maneuverNode, burnTime); + } catch (UnsupportedOperationException e) { + System.err.println("Manobras ainda não desbloqueadas: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); + } catch (IndexOutOfBoundsException e) { + System.err.println("Erro de index: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); + } catch (RPCException e) { + System.err.println("Erro de Stream ou RPC: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_data_unavailable")); + } catch (InterruptedException e) { + System.err.println("Cancelando executar manobra"); + setCurrentStatus(Bundle.getString("status_couldnt_orient")); + throw e; + } + } - private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart) { - double[] totalDv = {0, 0, 0}; - try { - Orbit currentOrbit = getActiveVessel().getOrbit(); - double startingRadius = currentOrbit.getApoapsis(); - double gravParameter = currentBody.getGravitationalParameter(); - - // Delta-v required to leave the current orbit - double deltaV1 = Math.sqrt(2 * gravParameter / startingRadius) - Math.sqrt(gravParameter / startingRadius); - - // Calculate the intermediate radius for the intermediate orbit - double intermediateRadius = currentBody.getEquatorialRadius() + targetAltitude; - - // Delta-v required to enter the intermediate orbit - double deltaV2 = Math.sqrt(gravParameter / intermediateRadius) - - Math.sqrt(2 * gravParameter / intermediateRadius); + public void orientToManeuverNode(Node maneuverNode) + throws RPCException, StreamException, InterruptedException { + setCurrentStatus(Bundle.getString("status_orienting_ship")); + vessel.ap.engage(); + vessel.ap.setReferenceFrame(maneuverNode.getReferenceFrame()); - // Calculate the final radius for the target orbit - double targetRadius = currentBody.getEquatorialRadius() + targetAltitude; + // --- STAGE 1: ORIENT TO MANEUVER (PITCH/HEADING) --- + try { + setCurrentStatus(Bundle.getString("status_orienting_to_maneuver")); + vessel.ap.setTargetDirection(new Triplet<>(0.0, 1.0, 0.0)); // Prograde in node's frame + vessel.ap.setTargetRoll(Float.NaN); // Disable roll control - // Delta-v required to leave the intermediate orbit and enter the target orbit - double deltaV3 = Math.sqrt(2 * gravParameter / intermediateRadius) - - Math.sqrt(gravParameter / intermediateRadius); - double deltaV4 = Math.sqrt(gravParameter / targetRadius) - Math.sqrt(2 * gravParameter / targetRadius); + final CountDownLatch directionLatch = new CountDownLatch(1); + this.headingErrorStream = vessel.connection.addStream(vessel.ap, "getHeadingError"); + this.pitchErrorStream = vessel.connection.addStream(vessel.ap, "getPitchError"); - // Total delta-v for the bi-elliptic transfer - totalDv[0] = deltaV1 + deltaV2 + deltaV3 + deltaV4; - } catch (RPCException e) { - // Handle the exception - } - return createManeuver(timeToStart, totalDv); - } + final Stream finalHeadingErrorStream = this.headingErrorStream; + final Stream finalPitchErrorStream = this.pitchErrorStream; - public void calculateManeuver() { - try { - tuneAutoPilot(); - System.out.println(commands + " calculate maneuvers"); - if (commands.get(Module.FUNCTION.get()).equals(Module.EXECUTE.get())) { - return; - } - if (getActiveVessel().getSituation() == VesselSituation.LANDED - || getActiveVessel().getSituation() == VesselSituation.SPLASHED) { - throw new InterruptedException(); - } - if (commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get())) { - this.alignPlanesWithTargetVessel(); - return; - } - if (commands.get(Module.FUNCTION.get()).equals(Module.RENDEZVOUS.get())) { - this.rendezvousWithTargetVessel(); - return; - } - if (commands.get(Module.FUNCTION.get()).equals(Module.LOW_ORBIT.get())) { - biEllipticTransferToOrbit(lowOrbitAltitude, getActiveVessel().getOrbit().getTimeToPeriapsis()); - return; - } - double gravParameter = currentBody.getGravitationalParameter(); - double startingAltitutde = 0, timeUntilAltitude = 0; - if (commands.get(Module.FUNCTION.get()).equals(Module.APOAPSIS.get())) { - startingAltitutde = getActiveVessel().getOrbit().getApoapsis(); - timeUntilAltitude = getActiveVessel().getOrbit().getTimeToApoapsis(); - } - if (commands.get(Module.FUNCTION.get()).equals(Module.PERIAPSIS.get())) { - startingAltitutde = getActiveVessel().getOrbit().getPeriapsis(); - timeUntilAltitude = getActiveVessel().getOrbit().getTimeToPeriapsis(); + Runnable checkOrientation = + () -> { + try { + if (directionLatch.getCount() > 0 + && Math.abs(finalHeadingErrorStream.get()) < 1 + && Math.abs(finalPitchErrorStream.get()) < 1) { + directionLatch.countDown(); + } + } catch (Exception e) { + e.printStackTrace(); + directionLatch.countDown(); } - - double semiMajorAxis = getActiveVessel().getOrbit().getSemiMajorAxis(); - double currentOrbitalVelocity = Math - .sqrt(gravParameter * ((2.0 / startingAltitutde) - (1.0 / semiMajorAxis))); - double targetOrbitalVelocity = Math - .sqrt(gravParameter * ((2.0 / startingAltitutde) - (1.0 / startingAltitutde))); - double maneuverDeltaV = targetOrbitalVelocity - currentOrbitalVelocity; - double[] deltaV = {maneuverDeltaV, 0, 0}; - createManeuver(timeUntilAltitude, deltaV); - } catch (RPCException | InterruptedException e) { - setCurrentStatus(Bundle.getString("status_maneuver_not_possible")); - } + }; + + this.headingErrorStream.addCallback(v -> checkOrientation.run()); + this.pitchErrorStream.addCallback(v -> checkOrientation.run()); + this.headingErrorStream.start(); + this.pitchErrorStream.start(); + + if (!directionLatch.await(60, TimeUnit.SECONDS)) { + System.err.println("Timeout waiting for direction stabilization."); + } + } finally { + if (this.headingErrorStream != null) { + this.headingErrorStream.remove(); + } + if (this.pitchErrorStream != null) { + this.pitchErrorStream.remove(); + } } - public void matchOrbitApoapsis() { - try { - Orbit targetOrbit = getTargetOrbit(); - System.out.println(targetOrbit.getApoapsis() + "-- APO"); - Node maneuver = biEllipticTransferToOrbit(targetOrbit.getApoapsis(), - getActiveVessel().getOrbit().getTimeToPeriapsis()); - while (true) { - if (Thread.interrupted()) { - throw new InterruptedException(); + // --- STAGE 2: STABILIZE ROLL --- + try { + setCurrentStatus(Bundle.getString("status_stabilizing_roll")); + vessel.ap.setTargetRoll(0.0f); + final CountDownLatch rollLatch = new CountDownLatch(1); + this.rollErrorStream = vessel.connection.addStream(vessel.ap, "getRollError"); + final Stream finalRollErrorStream = this.rollErrorStream; + final int[] rollCallbackTag = new int[1]; + rollCallbackTag[0] = + finalRollErrorStream.addCallback( + error -> { + try { + if (Math.abs(error) < 1.0) { + rollLatch.countDown(); + finalRollErrorStream.removeCallback(rollCallbackTag[0]); + } + } catch (Exception e) { + e.printStackTrace(); + rollLatch.countDown(); } - double currentDeltaApo = compareOrbitParameter(maneuver.getOrbit(), targetOrbit, Compare.AP); - String deltaApoFormatted = String.format("%.2f", currentDeltaApo); - System.out.println(deltaApoFormatted); - if (deltaApoFormatted.equals(String.format("%.2f", 0.00))) { - break; - } - double dvPrograde = maneuver.getPrograde(); - double ctrlOutput = ctrlManeuver.calculate(currentDeltaApo, 0); - - maneuver.setPrograde(dvPrograde - (ctrlOutput)); - Thread.sleep(25); - } - } catch (Exception e) { - setCurrentStatus("Não foi possivel ajustar a inclinação"); - } + }); + this.rollErrorStream.start(); + if (!rollLatch.await(20, TimeUnit.SECONDS)) { + System.err.println("Timeout waiting for roll stabilization."); + } + } finally { + if (this.rollErrorStream != null) { + this.rollErrorStream.remove(); + } } - - private Node createManeuverAtClosestIncNode(Orbit targetOrbit) { - double uTatClosestNode = 1; - double[] dv = {0, 0, 0}; - try { - double[] incNodesUt = getTimeToIncNodes(targetOrbit); - uTatClosestNode = Math.min(incNodesUt[0], incNodesUt[1]) - getSpaceCenter().getUT(); - } catch (Exception ignored) { + } + + public double calculateBurnTime(Node maneuverNode) { + try { + List engines = vessel.getActiveVessel().getParts().getEngines(); + for (Engine engine : engines) { + if (engine.getPart().getStage() == vessel.getActiveVessel().getControl().getCurrentStage() + && !engine.getActive()) { + engine.setActive(true); } - return createManeuver(uTatClosestNode, dv); + } + } catch (RPCException e) { + System.err.println("Não foi possível ativar os motores." + e.getMessage()); } - - private double[] getTimeToIncNodes(Orbit targetOrbit) throws RPCException { - Orbit vesselOrbit = getActiveVessel().getOrbit(); - double ascendingNode = vesselOrbit.trueAnomalyAtAN(targetOrbit); - double descendingNode = vesselOrbit.trueAnomalyAtDN(targetOrbit); - return new double[]{vesselOrbit.uTAtTrueAnomaly(ascendingNode), vesselOrbit.uTAtTrueAnomaly(descendingNode)}; + double burnDuration = 0; + try { + double thrust = vessel.getActiveVessel().getAvailableThrust(); + double isp = vessel.getActiveVessel().getSpecificImpulse() * CONST_GRAV; + double totalMass = vessel.getActiveVessel().getMass(); + double dryMass = totalMass / Math.exp(maneuverNode.getDeltaV() / isp); + double burnRatio = thrust / isp; + burnDuration = (totalMass - dryMass) / burnRatio; + } catch (RPCException e) { + System.err.println("Não foi possível calcular o tempo de queima." + e.getMessage()); } - - private void alignPlanesWithTargetVessel() { - try { - Vessel vessel = getActiveVessel(); - Orbit vesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = getSpaceCenter().getTargetVessel().getOrbit(); - boolean hasManeuverNodes = vessel.getControl().getNodes().size() > 0; - System.out.println("hasManeuverNodes: " + hasManeuverNodes); - if (!hasManeuverNodes) { - RunManeuverJPanel.createManeuver(); + setCurrentStatus("Tempo de Queima da Manobra: " + burnDuration + " segundos."); + return burnDuration; + } + + public void executeBurn(Node maneuverNode, double burnDuration) throws InterruptedException { + try { + // 1. PRE-WARP ORIENTATION + orientToManeuverNode(maneuverNode); + + // 2. WARP AND COUNTDOWN + // Wait until it's time to burn + final CountDownLatch timeToBurnLatch = new CountDownLatch(1); + timeToNodeStream = vessel.connection.addStream(maneuverNode, "getTimeTo"); + timeToNodeStream.addCallback( + (time) -> { + // Countdown for the last 5 seconds of warp + double timeToWarp = time - (burnDuration / 2.0) - 30; + if (timeToWarp > 0) { + setCurrentStatus(String.format("Warping in: %.1f seconds...", timeToWarp)); + return; } - java.util.List currentManeuvers = vessel.getControl().getNodes(); - Node currentManeuver = currentManeuvers.get(0); - double[] incNodesUt = {vesselOrbit.uTAtTrueAnomaly(vesselOrbit.trueAnomalyAtAN(targetVesselOrbit)), - vesselOrbit.uTAtTrueAnomaly(vesselOrbit.trueAnomalyAtDN(targetVesselOrbit))}; - boolean closestIsAN = incNodesUt[0] < incNodesUt[1]; - RunManeuverJPanel.positionManeuverAt(closestIsAN ? "ascending" : "descending"); - double currentInclination = Math - .toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - ctrlManeuver.setTimeSample(25); - while (currentInclination > 0.05) { - currentInclination = Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); - currentManeuver.setNormal(currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); - Thread.sleep(25); + // Countdown for ignition + double timeToBurnStart = time - (burnDuration / 2.0); + setCurrentStatus( + String.format(Bundle.getString("status_maneuver_ignition_in"), timeToBurnStart)); + if (timeToBurnStart <= 0) { + try { + timeToBurnLatch.countDown(); + timeToNodeStream.remove(); + } catch (RPCException e) { + } } - } catch (Exception err) { - System.err.println(err); - } - } + }); + timeToNodeStream.start(); + + double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - 30; + if (burnStartTime > 0) { + setCurrentStatus(Bundle.getString("status_maneuver_warp")); + vessel + .getConnectionManager() + .getSpaceCenter() + .warpTo( + (vessel.getConnectionManager().getSpaceCenter().getUT() + burnStartTime), + 100000, + 4); + } + + // 2. ORIENT (AFTER WARP) + orientToManeuverNode(maneuverNode); + + // 3. FINAL COUNTDOWN + while (timeToBurnLatch.getCount() > 0) { + if (Thread.interrupted()) throw new InterruptedException(); + if (timeToBurnLatch.await(100, TimeUnit.MILLISECONDS)) break; + } + + // 4. EXECUTE THE BURN + final CountDownLatch burnCompleteLatch = new CountDownLatch(1); + remainingBurnStream = + vessel.connection.addStream( + maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); + remainingBurnStream.addCallback( + (burn) -> { + try { + double burnDvLeft = burn.getValue1(); - private void rendezvousWithTargetVessel() { - try { - boolean hasManeuverNodes = getActiveVessel().getControl().getNodes().size() > 0; - List currentManeuvers = getActiveVessel().getControl().getNodes(); - Node lastManeuverNode; - double lastManeuverNodeUT = 60; - if (hasManeuverNodes) { - currentManeuvers = getActiveVessel().getControl().getNodes(); - lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); - lastManeuverNodeUT += lastManeuverNode.getUT(); - RunManeuverJPanel.createManeuver(lastManeuverNodeUT); - } else { - RunManeuverJPanel.createManeuver(); - } - currentManeuvers = getActiveVessel().getControl().getNodes(); - lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); - - Orbit activeVesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = getSpaceCenter().getTargetVessel().getOrbit(); - ReferenceFrame currentBodyRefFrame = activeVesselOrbit.getBody().getNonRotatingReferenceFrame(); - - double angularDiff = 10; - while (angularDiff >= 0.005) { - double maneuverUT = lastManeuverNode.getUT(); - double targetOrbitPosition = new Vector(targetVesselOrbit.positionAt(maneuverUT, currentBodyRefFrame)) - .magnitude(); - double maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); - double maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); - ctrlManeuver.setPIDValues(0.25, 0.0, 0.01); - ctrlManeuver.setOutput(-100, 100); - - if (targetOrbitPosition < maneuverPE) { - while (Math.floor(targetOrbitPosition) != Math.floor(maneuverPE)) { - lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverPE / targetOrbitPosition * 1000, 1000)); - maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); - Thread.sleep(25); - } - } + // SAFETY STOP: Check if dV is increasing + if (burnDvLeft > lastBurnDv + 0.1) { // Using a 0.1m/s tolerance + System.err.println("Maneuver failed: Delta-V increasing. Aborting burn."); + vessel.throttle(0); + burnCompleteLatch.countDown(); + return; + } + lastBurnDv = burnDvLeft; // Update last known dV - if (targetOrbitPosition > maneuverAP) { - while (Math.floor(targetOrbitPosition) != Math.floor(maneuverAP)) { - lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverAP / targetOrbitPosition * 1000, 1000)); - maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); - Thread.sleep(25); - } - } - angularDiff = calculatePhaseAngle( - lastManeuverNode.getOrbit().positionAt(maneuverUT, currentBodyRefFrame), - getSpaceCenter().getTargetVessel().getOrbit().positionAt(maneuverUT, currentBodyRefFrame)); - maneuverUT = lastManeuverNode.getUT(); - lastManeuverNode.setUT(lastManeuverNode.getUT() + ctrlManeuver.calculate(-angularDiff * 100, 0)); - System.out.println(angularDiff); - Thread.sleep(25); + if (burnDvLeft < (fineAdjustment ? 2 : 0.75)) { + burnCompleteLatch.countDown(); + return; + } + navigation.aimAtManeuverNode(maneuverNode); + vessel.throttle(ctrlManeuver.calculate(0, burnDvLeft / 100.0)); + } catch (Exception e) { + e.printStackTrace(); + burnCompleteLatch.countDown(); } - // double mu = currentBody.getGravitationalParameter(); - // double time = 1000; - // - // double hohmannTransferDistance = - // lastManeuverNode.getOrbit().getSemiMajorAxis(); - // double timeOfFlight = Math.PI * Math.sqrt(Math.pow(hohmannTransferDistance, - // 3) / mu); - // double angle = activeVesselOrbit.getMeanAnomalyAtEpoch(); - // double omegaInterceptor = Math - // .sqrt(mu / - // Math.pow(activeVesselOrbit.radiusAt(getSpaceCenter().getUT()), 3)); - // // rad/s - // double omegaTarget = Math.sqrt(mu / - // Math.pow(targetVesselOrbit.radiusAt(getSpaceCenter().getUT()), 3)); // rad/s - // // double leadAngle = omegaTarget * timeOfFlight; // rad - // double leadAngle = targetVesselOrbit.getMeanAnomalyAtEpoch(); // rad - // double phaseAngle = Math.PI - leadAngle; // rad - // double calcAngle = (phaseAngle - angle); - // calcAngle = calcAngle < 0 ? calcAngle + (Math.PI * 2) : calcAngle; - // double waitTime = calcAngle / (omegaTarget - omegaInterceptor); - // time = waitTime; - // - // lastManeuverNode.setUT(getSpaceCenter().getUT() + time); - // ctrlManeuver.setOutput(-100, 100); - // ctrlManeuver.setPIDValues(0.05, 0.1, 0.01); - // double closestApproach = - // lastManeuverNode.getOrbit().distanceAtClosestApproach(targetVesselOrbit); - // System.out.println(closestApproach); - // System.out.println("Ajustando tempo de Rendezvous..."); - // while (Math.round(closestApproach) > 100) { - // if (closestApproach < 100000) { - // ctrlManeuver.setOutput(-10, 10); - // } else if (closestApproach < 10000) { - // ctrlManeuver.setOutput(-1, 1); - // } else { - // ctrlManeuver.setOutput(-100, 100); - // } - // maneuverUT = ctrlManeuver.calculate(-closestApproach, 0); - // lastManeuverNode.setUT(lastManeuverNode.getUT() + maneuverUT); - // System.out.println("Closest " + (closestApproach)); - // closestApproach = - // targetVesselOrbit.distanceAtClosestApproach(lastManeuverNode.getOrbit()); - // Thread.sleep(25); - // } - // lastManeuverNode.setUT(lastManeuverNode.getUT() - - // lastManeuverNode.getOrbit().getPeriod() / 2); - } catch ( - - Exception err) { - } + }); + remainingBurnStream.start(); + + setCurrentStatus(Bundle.getString("status_maneuver_executing")); + burnCompleteLatch.await(); + vessel.throttle(0.0f); + remainingBurnStream.remove(); + + if (fineAdjustment) { + adjustManeuverWithRCS(maneuverNode); + } + + vessel.ap.setReferenceFrame(vessel.surfaceReferenceFrame); + vessel.ap.disengage(); + vessel.getActiveVessel().getControl().setSAS(true); + vessel.getActiveVessel().getControl().setRCS(false); + maneuverNode.remove(); + setCurrentStatus(Bundle.getString("status_ready")); + } catch (RPCException | StreamException e) { + setCurrentStatus(Bundle.getString("status_data_unavailable")); + } catch (InterruptedException e) { + setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); + throw e; } - - private double compareOrbitParameter(Orbit maneuverOrbit, Orbit targetOrbit, Compare parameter) { - double maneuverParameter; - double targetParameter; - double delta = 0; - try { - switch (parameter) { - case INC : - maneuverParameter = maneuverOrbit.getInclination(); - System.out.println(maneuverParameter + " maneuver"); - targetParameter = targetOrbit.getInclination(); - System.out.println(targetParameter + " target"); - delta = (maneuverParameter / targetParameter) * 10; - break; - case AP : - maneuverParameter = Math.round(maneuverOrbit.getApoapsis() / 100000.0); - targetParameter = Math.round(targetOrbit.getApoapsis() / 100000.0); - delta = (targetParameter - maneuverParameter); - break; - case PE : - maneuverParameter = Math.round(maneuverOrbit.getPeriapsis()) / 100.0; - targetParameter = Math.round(targetOrbit.getPeriapsis()) / 100.0; - delta = (targetParameter - maneuverParameter); - break; - default : - break; - } - - } catch (RPCException e) { - e.printStackTrace(); - } - return delta; + } + + private void initializeParameters() { + ctrlRCS = new ControlePID(vessel.getConnectionManager().getSpaceCenter()); + ctrlManeuver = new ControlePID(vessel.getConnectionManager().getSpaceCenter()); + ctrlManeuver.setPIDValues(1, 0.001, 0.1); + ctrlManeuver.setOutput(0.1, 1.0); + ctrlRCS.setOutput(0.5, 1.0); + fineAdjustment = canFineAdjust(commands.get(Module.FINE_ADJUST.get())); + try { + lowOrbitAltitude = new Attributes().getLowOrbitAltitude(vessel.currentBody.getName()); + } catch (RPCException e) { } - - private Orbit getTargetOrbit() throws RPCException { - if (getSpaceCenter().getTargetBody() != null) { - return getSpaceCenter().getTargetBody().getOrbit(); - } - if (getSpaceCenter().getTargetVessel() != null) { - return getSpaceCenter().getTargetVessel().getOrbit(); - } - return null; + } + + private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart) { + double[] totalDv = {0, 0, 0}; + try { + Orbit currentOrbit = vessel.getActiveVessel().getOrbit(); + double startingRadius = currentOrbit.getApoapsis(); + double gravParameter = vessel.currentBody.getGravitationalParameter(); + double deltaV1 = + Math.sqrt(2 * gravParameter / startingRadius) - Math.sqrt(gravParameter / startingRadius); + double intermediateRadius = vessel.currentBody.getEquatorialRadius() + targetAltitude; + double deltaV2 = + Math.sqrt(gravParameter / intermediateRadius) + - Math.sqrt(2 * gravParameter / intermediateRadius); + double targetRadius = vessel.currentBody.getEquatorialRadius() + targetAltitude; + double deltaV3 = + Math.sqrt(2 * gravParameter / intermediateRadius) + - Math.sqrt(gravParameter / intermediateRadius); + double deltaV4 = + Math.sqrt(gravParameter / targetRadius) - Math.sqrt(2 * gravParameter / targetRadius); + totalDv[0] = deltaV1 + deltaV2 + deltaV3 + deltaV4; + } catch (RPCException e) { + System.err.println("ERRO: Cálculo de Transferência de Órbita" + e.getMessage()); } - - private Node createManeuver(double laterTime, double[] deltaV) { - Node maneuverNode = null; - try { - getActiveVessel().getControl().addNode(getSpaceCenter().getUT() + laterTime, (float) deltaV[0], - (float) deltaV[1], (float) deltaV[2]); - List currentNodes = getActiveVessel().getControl().getNodes(); - maneuverNode = currentNodes.get(currentNodes.size() - 1); - } catch (UnsupportedOperationException | RPCException e) { - setCurrentStatus(Bundle.getString("status_maneuver_not_possible")); - } - return maneuverNode; + return createManeuver(timeToStart, totalDv); + } + + private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { + final int[] utCallbackTag = new int[1]; + try { + Vessel vesselObj = this.vessel.getActiveVessel(); + Orbit vesselOrbit = this.vessel.getActiveVessel().getOrbit(); + Orbit targetVesselOrbit = + this.vessel.getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + if (vesselObj.getControl().getNodes().isEmpty()) { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + } + java.util.List currentManeuvers = vesselObj.getControl().getNodes(); + Node currentManeuver = currentManeuvers.get(0); + double[] incNodesUt = { + vesselOrbit.uTAtTrueAnomaly(vesselOrbit.trueAnomalyAtAN(targetVesselOrbit)), + vesselOrbit.uTAtTrueAnomaly(vesselOrbit.trueAnomalyAtDN(targetVesselOrbit)) + }; + boolean closestIsAN = incNodesUt[0] < incNodesUt[1]; + MainGui.newInstance() + .getCreateManeuverPanel() + .positionManeuverAt(closestIsAN ? "ascending" : "descending"); + + ctrlManeuver.setTimeSample(25); + + final CountDownLatch latch = new CountDownLatch(1); + utCallbackTag[0] = + vessel.missionTime.addCallback( + (ut) -> { + try { + double currentInclination = + Math.toDegrees( + currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); + if (currentInclination <= 0.05) { + latch.countDown(); + return; + } + double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); + currentManeuver.setNormal( + currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); + } catch (Exception e) { + e.printStackTrace(); + latch.countDown(); + } + }); + latch.await(); // Wait until the alignment is done + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; + } + System.err.println("Error aligning planes: " + err); + } finally { + vessel.missionTime.removeCallback(utCallbackTag[0]); } - - public void executeNextManeuver() { - try { - Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); - double burnTime = calculateBurnTime(maneuverNode); - orientToManeuverNode(maneuverNode); - executeBurn(maneuverNode, burnTime); - } catch (UnsupportedOperationException e) { - setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); - } catch (IndexOutOfBoundsException e) { - setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); - } catch (RPCException e) { - setCurrentStatus(Bundle.getString("status_data_unavailable")); - } catch (InterruptedException e) { - setCurrentStatus(Bundle.getString("status_couldnt_orient")); - } + } + + private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { + final int[] utCallbackTag = new int[1]; + try { + List currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); + Node lastManeuverNode; + if (currentManeuvers.isEmpty()) { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); + } else { + double lastManeuverNodeUT = 60 + currentManeuvers.get(currentManeuvers.size() - 1).getUT(); + MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); + currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); + } + lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); + + final CountDownLatch latch = new CountDownLatch(1); + final RendezvousState state = new RendezvousState(lastManeuverNode); + + utCallbackTag[0] = + vessel.missionTime.addCallback( + ut -> { + try { + updateRendezvousState(state, latch, utCallbackTag[0]); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Error in rendezvous update: " + e); + latch.countDown(); + } + }); + latch.await(); + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; + } + System.err.println("Error during rendezvous: " + err); + } finally { + vessel.missionTime.removeCallback(utCallbackTag[0]); } - - public void orientToManeuverNode(Node maneuverNode) throws InterruptedException, RPCException { - setCurrentStatus(Bundle.getString("status_orienting_ship")); - ap.engage(); - while (ap.getHeadingError() > 3 || ap.getPitchError() > 3) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - Thread.sleep(100); + } + + private void updateRendezvousState( + RendezvousState state, CountDownLatch latch, int callbackTag) + throws RPCException, IOException { + Orbit targetVesselOrbit = + vessel.getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + ReferenceFrame currentBodyRefFrame = + vessel.getActiveVessel().getOrbit().getBody().getNonRotatingReferenceFrame(); + + switch (state.phase) { + case SETUP: + ctrlManeuver.setPIDValues(1, 0.001, 0.01); + ctrlManeuver.setOutput(-100, 100); + state.phase = RendezvousPhase.CHECK_ANGULAR_DIFFERENCE; + break; + + case CHECK_ANGULAR_DIFFERENCE: + double maneuverUT = state.maneuverNode.getUT(); + double angularDiff = + calculatePhaseAngle( + state.maneuverNode.getOrbit().positionAt(maneuverUT, currentBodyRefFrame), + targetVesselOrbit.positionAt(maneuverUT, currentBodyRefFrame)); + if (angularDiff < 0.005) { + state.phase = RendezvousPhase.DONE; + break; } - - } - - public double calculateBurnTime(Node maneuverNode) throws RPCException { - - List engines = getActiveVessel().getParts().getEngines(); - for (Engine engine : engines) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() - && !engine.getActive()) { - engine.setActive(true); - } + state.targetOrbitPosition = + new Vector(targetVesselOrbit.positionAt(maneuverUT, currentBodyRefFrame)).magnitude(); + state.maneuverAP = state.maneuverNode.getOrbit().getApoapsis(); + state.maneuverPE = state.maneuverNode.getOrbit().getPeriapsis(); + + if (state.targetOrbitPosition < state.maneuverPE) { + state.phase = RendezvousPhase.ADJUST_PERIAPSIS; + } else if (state.targetOrbitPosition > state.maneuverAP) { + state.phase = RendezvousPhase.ADJUST_APOAPSIS; + } else { + state.phase = RendezvousPhase.ADJUST_UT; } - double thrust = getActiveVessel().getAvailableThrust(); - double isp = getActiveVessel().getSpecificImpulse() * CONST_GRAV; - double totalMass = getActiveVessel().getMass(); - double dryMass = totalMass / Math.exp(maneuverNode.getDeltaV() / isp); - double burnRatio = thrust / isp; - double burnDuration = (totalMass - dryMass) / burnRatio; - - setCurrentStatus("Tempo de Queima da Manobra: " + burnDuration + " segundos"); - return burnDuration; - } + break; - public void executeBurn(Node maneuverNode, double burnDuration) { - try { - double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - (fineAdjustment ? 5 : 0); - setCurrentStatus(Bundle.getString("status_maneuver_warp")); - if (burnStartTime > 30) { - getSpaceCenter().warpTo((getSpaceCenter().getUT() + burnStartTime - 10), 100000, 4); - } - // Mostrar tempo de ignição: - setCurrentStatus(String.format(Bundle.getString("status_maneuver_duration"), burnDuration)); - while (burnStartTime > 0) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - burnStartTime = Math.max(maneuverNode.getTimeTo() - (burnDuration / 2.0), 0.0); - navigation.aimAtManeuver(maneuverNode); - setCurrentStatus(String.format(Bundle.getString("status_maneuver_ignition_in"), burnStartTime)); - Thread.sleep(100); - } - // Executar a manobra: - Stream> remainingBurn = getConnection().addStream(maneuverNode, - "remainingBurnVector", maneuverNode.getReferenceFrame()); - setCurrentStatus(Bundle.getString("status_maneuver_executing")); - while (maneuverNode != null) { - double burnDvLeft = remainingBurn.get().getValue1(); - if (Thread.interrupted()) { - throw new InterruptedException(); - } - if (burnDvLeft < (fineAdjustment ? 2 : 0.5)) { - throttle(0.0f); - break; - } - navigation.aimAtManeuver(maneuverNode); - float limitValue = burnDvLeft > 100 ? 1000 : 100; - throttle(ctrlManeuver.calculate( - (maneuverNode.getDeltaV() - Math.floor(burnDvLeft)) / maneuverNode.getDeltaV() * limitValue, - limitValue)); - Thread.sleep(25); - } - throttle(0.0f); - if (fineAdjustment) { - adjustManeuverWithRCS(remainingBurn); - } - ap.setReferenceFrame(surfaceReferenceFrame); - ap.disengage(); - getActiveVessel().getControl().setSAS(true); - getActiveVessel().getControl().setRCS(false); - remainingBurn.remove(); - maneuverNode.remove(); - setCurrentStatus(Bundle.getString("status_ready")); - } catch (StreamException | RPCException e) { - setCurrentStatus(Bundle.getString("status_data_unavailable")); - } catch (InterruptedException e) { - setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); + case ADJUST_PERIAPSIS: + if (Math.floor(state.targetOrbitPosition) == Math.floor(state.maneuverPE)) { + state.phase = RendezvousPhase.ADJUST_UT; + break; } - } - - private void adjustManeuverWithRCS(Stream> remainingDeltaV) - throws RPCException, StreamException, InterruptedException { - getActiveVessel().getControl().setRCS(true); - while (Math.floor(remainingDeltaV.get().getValue1()) > 0.2) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - getActiveVessel().getControl() - .setForward((float) ctrlRCS.calculate(-remainingDeltaV.get().getValue1() * 10, 0)); - Thread.sleep(25); + state.maneuverNode.setPrograde( + state.maneuverNode.getPrograde() + + ctrlManeuver.calculate( + state.maneuverPE / state.targetOrbitPosition * 1000, 1000)); + state.maneuverPE = state.maneuverNode.getOrbit().getPeriapsis(); + break; + + case ADJUST_APOAPSIS: + if (Math.floor(state.targetOrbitPosition) == Math.floor(state.maneuverAP)) { + state.phase = RendezvousPhase.ADJUST_UT; + break; } - getActiveVessel().getControl().setForward(0); + state.maneuverNode.setPrograde( + state.maneuverNode.getPrograde() + + ctrlManeuver.calculate( + state.maneuverAP / state.targetOrbitPosition * 1000, 1000)); + state.maneuverAP = state.maneuverNode.getOrbit().getApoapsis(); + break; + + case ADJUST_UT: + double maneuverUT_adjust = state.maneuverNode.getUT(); + double angularDiff_adjust = + calculatePhaseAngle( + state.maneuverNode.getOrbit().positionAt(maneuverUT_adjust, currentBodyRefFrame), + targetVesselOrbit.positionAt(maneuverUT_adjust, currentBodyRefFrame)); + state.maneuverNode.setUT( + state.maneuverNode.getUT() + ctrlManeuver.calculate(-angularDiff_adjust * 100, 0)); + state.phase = RendezvousPhase.CHECK_ANGULAR_DIFFERENCE; + break; + + case DONE: + latch.countDown(); + break; } - - private boolean canFineAdjust(String string) { - if (string.equals("true")) { - try { - List rcsEngines = getActiveVessel().getParts().getRCS(); - if (rcsEngines.size() > 0) { - for (RCS rcs : rcsEngines) { - if (rcs.getHasFuel()) { - return true; - } - } + } + + private Node createManeuver(double laterTime, double[] deltaV) { + Node maneuverNode = null; + try { + vessel + .getActiveVessel() + .getControl() + .addNode( + vessel.getConnectionManager().getSpaceCenter().getUT() + laterTime, + (float) deltaV[0], + (float) deltaV[1], + (float) deltaV[2]); + List currentNodes = vessel.getActiveVessel().getControl().getNodes(); + maneuverNode = currentNodes.get(currentNodes.size() - 1); + } catch (UnsupportedOperationException | RPCException e) { + setCurrentStatus(Bundle.getString("status_maneuver_not_possible")); + } + return maneuverNode; + } + + private void cleanup() { + try { + if (headingErrorStream != null) headingErrorStream.remove(); + if (pitchErrorStream != null) pitchErrorStream.remove(); + if (rollErrorStream != null) rollErrorStream.remove(); + if (timeToNodeStream != null) timeToNodeStream.remove(); + if (remainingBurnStream != null) remainingBurnStream.remove(); + if (vessel.ap != null) vessel.ap.disengage(); + vessel.throttle(0); + } catch (RPCException | NullPointerException e) { + // ignore + } + } + + private void adjustManeuverWithRCS(Node maneuverNode) + throws RPCException, StreamException, InterruptedException { + setCurrentStatus("Fine tuning with RCS..."); + vessel.getActiveVessel().getControl().setRCS(true); + final CountDownLatch rcsLatch = new CountDownLatch(1); + + Stream> rcsStream = null; + try { + rcsStream = + vessel.connection.addStream( + maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); + final Stream> finalRcsStream = rcsStream; + final int[] rcsCallbackTag = new int[1]; + + rcsCallbackTag[0] = + finalRcsStream.addCallback( + (burn) -> { + try { + double progradeDv = burn.getValue1(); + if (progradeDv <= 0.1) { + rcsLatch.countDown(); + finalRcsStream.removeCallback(rcsCallbackTag[0]); + return; + } + // Use the RCS PID controller to gently burn off remaining dV + vessel + .getActiveVessel() + .getControl() + .setForward((float) ctrlRCS.calculate(-progradeDv, 0)); + } catch (Exception e) { + e.printStackTrace(); + rcsLatch.countDown(); } - return false; - } catch (RPCException ignored) { + }); + rcsStream.start(); + + // Wait for RCS burn to complete, with a timeout + if (!rcsLatch.await(60, TimeUnit.SECONDS)) { + System.err.println("Timeout during RCS fine tuning."); + } + } finally { + vessel.getActiveVessel().getControl().setForward(0); + if (rcsStream != null) { + rcsStream.remove(); + } + } + } + + private boolean canFineAdjust(String string) { + if ("true".equals(string)) { + try { + List rcsEngines = vessel.getActiveVessel().getParts().getRCS(); + if (rcsEngines.size() > 0) { + for (RCS rcs : rcsEngines) { + if (rcs.getHasFuel()) { + return true; } + } } return false; + } catch (RPCException e) { + System.err.println("ERRO: Ajuste fino de manobra." + e.getMessage()); + } } - - private double calculatePhaseAngle(Triplet startPos, Triplet endPos) - throws RPCException, InterruptedException { - double targetPhaseAngle = 10; - double angularDifference = 15; - Vector startPosition = new Vector(startPos); - Vector endPosition = new Vector(endPos); - - // Phase angle - double dot = endPosition.dotProduct(startPosition); - double det = endPosition.determinant(startPosition); - targetPhaseAngle = Math.atan2(det, dot); - - double targetOrbit = endPosition.magnitude(); - - double activeVesselSMA = getActiveVessel().getOrbit().getSemiMajorAxis(); - angularDifference = targetPhaseAngle + Math.PI - * (1 - (1 / (2 * Math.sqrt(2))) * Math.sqrt(Math.pow((activeVesselSMA / targetOrbit + 1), 3))); - - return Math.abs(angularDifference); - } - - enum Compare { - INC, AP, PE - } - + return false; + } + + private double calculatePhaseAngle( + Triplet startPos, Triplet endPos) + throws RPCException { + double targetPhaseAngle = 10; + double angularDifference = 15; + Vector startPosition = new Vector(startPos); + Vector endPosition = new Vector(endPos); + + // Phase angle + double dot = endPosition.dotProduct(startPosition); + double det = endPosition.determinant(startPosition); + targetPhaseAngle = Math.atan2(det, dot); + + double targetOrbit = endPosition.magnitude(); + + double activeVesselSMA = vessel.getActiveVessel().getOrbit().getSemiMajorAxis(); + angularDifference = + targetPhaseAngle + + Math.PI + * (1 + - (1 / (2 * Math.sqrt(2))) + * Math.sqrt(Math.pow((activeVesselSMA / targetOrbit + 1), 3))); + + return Math.abs(angularDifference); + } } diff --git a/src/main/java/com/pesterenan/controllers/RoverController.java b/src/main/java/com/pesterenan/controllers/RoverController.java index 64ad213..4fb2e9c 100644 --- a/src/main/java/com/pesterenan/controllers/RoverController.java +++ b/src/main/java/com/pesterenan/controllers/RoverController.java @@ -1,12 +1,21 @@ package com.pesterenan.controllers; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; import com.pesterenan.utils.PathFinding; import com.pesterenan.utils.Utilities; import com.pesterenan.utils.Vector; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import krpc.client.RPCException; +import krpc.client.Stream; import krpc.client.StreamException; import krpc.client.services.SpaceCenter.ReferenceFrame; import krpc.client.services.SpaceCenter.SolarPanel; @@ -14,291 +23,396 @@ import org.javatuples.Pair; import org.javatuples.Triplet; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static com.pesterenan.MechPeste.getSpaceCenter; - public class RoverController extends Controller { - private final ControlePID sterringCtrl = new ControlePID(); - private final ControlePID acelCtrl = new ControlePID(); - float distanceFromTargetLimit = 50; - private float maxSpeed = 3; - private ReferenceFrame roverReferenceFrame; - private boolean isAutoRoverRunning; - private PathFinding pathFinding; - private Vector targetPoint = new Vector(); - private Vector roverDirection = new Vector(); - private MODE currentMode; - - public RoverController(Map commands) { - super(); - this.commands = commands; - initializeParameters(); + private final ControlePID sterringCtrl = new ControlePID(); + private final ControlePID acelCtrl = new ControlePID(); + float distanceFromTargetLimit = 50; + private float maxSpeed = 3; + private ReferenceFrame roverReferenceFrame; + private PathFinding pathFinding; + private Vector targetPoint = new Vector(); + private Vector roverDirection = new Vector(); + private MODE currentMode; + private final Map commands; + + private volatile double currentDistanceToTarget = 0; + private volatile float currentChargePercentage = 100; + private volatile float currentChargeAmount = 0; + private int distanceCallbackTag, chargeCallbackTag; + private Stream> positionStream; + private Stream chargeAmountStream; + private ScheduledExecutorService roverExecutor; + + public RoverController(ActiveVessel vessel, Map commands) { + super(vessel); + this.commands = commands; + initializeParameters(); + } + + private void initializeParameters() { + try { + maxSpeed = Float.parseFloat(commands.get(Module.MAX_SPEED.get())); + roverReferenceFrame = vessel.getActiveVessel().getReferenceFrame(); + roverDirection = new Vector(vessel.getActiveVessel().direction(roverReferenceFrame)); + pathFinding = new PathFinding(vessel.getConnectionManager(), vessel.getVesselManager()); + acelCtrl.setOutput(0, 1); + sterringCtrl.setOutput(-1, 1); + } catch (RPCException ignored) { } + } - private void initializeParameters() { - try { - maxSpeed = Float.parseFloat(commands.get(Module.MAX_SPEED.get())); - roverReferenceFrame = getActiveVessel().getReferenceFrame(); - roverDirection = new Vector(getActiveVessel().direction(roverReferenceFrame)); - pathFinding = new PathFinding(); - acelCtrl.setOutput(0, 1); - sterringCtrl.setOutput(-1, 1); - isAutoRoverRunning = true; - } catch (RPCException ignored) { - } + private boolean isSolarPanelNotBroken(SolarPanel sp) { + try { + return sp.getState() != SolarPanelState.BROKEN; + } catch (RPCException e) { + return false; } - - private boolean isSolarPanelNotBroken(SolarPanel sp) { - try { - return sp.getState() != SolarPanelState.BROKEN; - } catch (RPCException e) { - return false; - } - } - - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.ROVER.get())) { - setTarget(); - driveRoverToTarget(); - } - } - - private void setTarget() { - try { - if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.MAP_MARKER.get())) { - pathFinding.addWaypointsOnSameBody(commands.get(Module.MARKER_NAME.get())); - setCurrentStatus("Calculando rota até o alvo..."); - pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); - } - if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.TARGET_VESSEL.get())) { - Vector targetVesselPosition = new Vector( - getSpaceCenter().getTargetVessel().position(orbitalReferenceFrame)); - setCurrentStatus("Calculando rota até o alvo..."); - pathFinding.buildPathToTarget(targetVesselPosition); - } - } catch (RPCException | IOException | InterruptedException ignored) { - } - } - - private void changeControlMode() throws RPCException, IOException, StreamException, InterruptedException { - switch (currentMode) { - case DRIVE : - driveRover(); - break; - case CHARGING : - rechargeRover(); - break; - case NEXT_POINT : - setNextPointInPath(); - break; - } + } + + @Override + public void run() { + if (commands.get(Module.MODULO.get()).equals(Module.ROVER.get())) { + setTarget(); + currentMode = MODE.NEXT_POINT; + try { + setupCallbacks(); + roverExecutor = Executors.newSingleThreadScheduledExecutor(); + roverExecutor.scheduleAtFixedRate(this::roverStateMachine, 0, 100, TimeUnit.MILLISECONDS); + } catch (IOException | RPCException | StreamException e) { + cleanup(); + } } - - private void driveRoverToTarget() { + } + + private void roverStateMachine() { + try { + if (!isRunning()) { + cleanup(); + return; + } + changeControlMode(); + if (isFarFromTarget()) { + currentMode = needToChargeBatteries() ? MODE.CHARGING : MODE.DRIVE; + } else { // Rover arrived at destiny currentMode = MODE.NEXT_POINT; - try { - while (isAutoRoverRunning) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - changeControlMode(); - if (isFarFromTarget()) { - currentMode = needToChargeBatteries() ? MODE.CHARGING : MODE.DRIVE; - } else { // Rover arrived at destiny - currentMode = MODE.NEXT_POINT; - } - Thread.sleep(100); - } - } catch (InterruptedException | RPCException | IOException | StreamException ignored) { - try { - getActiveVessel().getControl().setBrakes(true); - pathFinding.removeDrawnPath(); - isAutoRoverRunning = false; - setCurrentStatus(Bundle.getString("lbl_stat_ready")); - } catch (RPCException ignored2) { - } - } + } + } catch (RPCException | IOException | StreamException | InterruptedException e) { + cleanup(); } - - private void setNextPointInPath() throws RPCException, IOException, InterruptedException { - pathFinding.removePathsCurrentPoint(); - getActiveVessel().getControl().setBrakes(true); - if (pathFinding.isPathToTargetEmpty()) { - if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.MAP_MARKER.get())) { - pathFinding.removeWaypointFromList(); - if (pathFinding.isWaypointsToReachEmpty()) { - throw new InterruptedException(); - } - pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); - } - - } else { - targetPoint = pathFinding.getPathsFirstPoint(); - } + } + + private void setupCallbacks() throws RPCException, IOException, StreamException { + positionStream = + vessel.connection.addStream( + vessel.getActiveVessel(), "position", vessel.orbitalReferenceFrame); + distanceCallbackTag = + positionStream.addCallback( + (pos) -> { + currentDistanceToTarget = Vector.distance(new Vector(pos), targetPoint); + }); + positionStream.start(); + + chargeAmountStream = + vessel.connection.addStream( + vessel.getActiveVessel().getResources(), "amount", "ElectricCharge"); + chargeCallbackTag = + chargeAmountStream.addCallback( + (amount) -> { + try { + currentChargeAmount = amount; + float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); + currentChargePercentage = (float) Math.ceil(amount * 100 / totalCharge); + } catch (RPCException e) { + } + }); + chargeAmountStream.start(); + } + + private void setTarget() { + try { + if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.MAP_MARKER.get())) { + pathFinding.addWaypointsOnSameBody(commands.get(Module.MARKER_NAME.get())); + setCurrentStatus("Calculando rota até o alvo..."); + pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); + } + if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.TARGET_VESSEL.get())) { + Vector targetVesselPosition = + new Vector( + vessel + .getConnectionManager() + .getSpaceCenter() + .getTargetVessel() + .position(vessel.orbitalReferenceFrame)); + setCurrentStatus("Calculando rota até o alvo..."); + pathFinding.buildPathToTarget(targetVesselPosition); + } + } catch (RPCException | IOException | InterruptedException ignored) { } - - private boolean isFarFromTarget() throws RPCException { - double distance = Vector.distance(new Vector(getActiveVessel().position(orbitalReferenceFrame)), targetPoint); - return distance > distanceFromTargetLimit; + } + + private void changeControlMode() + throws RPCException, IOException, StreamException, InterruptedException { + switch (currentMode) { + case DRIVE: + driveRover(); + break; + case CHARGING: + rechargeRover(); + break; + case NEXT_POINT: + setNextPointInPath(); + break; } - - private boolean needToChargeBatteries() throws RPCException, IOException, StreamException, InterruptedException { - float totalCharge = getActiveVessel().getResources().max("ElectricCharge"); - float currentCharge = getActiveVessel().getResources().amount("ElectricCharge"); - float minChargeLevel = 10.0f; - float chargePercentage = (float) Math.ceil(currentCharge * 100 / totalCharge); - return (chargePercentage < minChargeLevel); + } + + private void cleanup() { + try { + if (roverExecutor != null && !roverExecutor.isShutdown()) { + roverExecutor.shutdownNow(); + } + vessel.getActiveVessel().getControl().setBrakes(true); + pathFinding.removeDrawnPath(); + if (positionStream != null) { + positionStream.removeCallback(distanceCallbackTag); + positionStream.remove(); + } + if (chargeAmountStream != null) { + chargeAmountStream.removeCallback(chargeCallbackTag); + chargeAmountStream.remove(); + } + setCurrentStatus(Bundle.getString("lbl_stat_ready")); + } catch (RPCException ignored) { } - - private void rechargeRover() throws RPCException, StreamException, InterruptedException { - - float totalCharge = getActiveVessel().getResources().max("ElectricCharge"); - float currentCharge = getActiveVessel().getResources().amount("ElectricCharge"); - - setRoverThrottle(0); - getActiveVessel().getControl().setLights(false); - getActiveVessel().getControl().setBrakes(true); - - if (horizontalVelocity.get() < 1 && getActiveVessel().getControl().getBrakes()) { - Thread.sleep(1000); - double chargeTime; - double totalEnergyFlow = 0; - List solarPanels = getActiveVessel().getParts().getSolarPanels().stream() - .filter(this::isSolarPanelNotBroken).collect(Collectors.toList()); - - for (SolarPanel sp : solarPanels) { - totalEnergyFlow += sp.getEnergyFlow(); - } - chargeTime = ((totalCharge - currentCharge) / totalEnergyFlow); - setCurrentStatus("Segundos de Carga: " + chargeTime); - if (chargeTime < 1 || chargeTime > 21600) { - chargeTime = 3600; - } - getSpaceCenter().warpTo((getSpaceCenter().getUT() + chargeTime), 10000, 4); - getActiveVessel().getControl().setLights(true); + } + + private void setNextPointInPath() throws RPCException, IOException { + pathFinding.removePathsCurrentPoint(); + vessel.getActiveVessel().getControl().setBrakes(true); + if (pathFinding.isPathToTargetEmpty()) { + if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.MAP_MARKER.get())) { + pathFinding.removeWaypointFromList(); + if (pathFinding.isWaypointsToReachEmpty()) { + stop(); + return; } - } - - private void driveRover() throws RPCException, IOException, StreamException { - Vector targetDirection = posSurfToRover(posOrbToSurf(targetPoint)).normalize(); - Vector radarSourcePosition = posRoverToSurf( - new Vector(getActiveVessel().position(roverReferenceFrame)).sum(new Vector(0.0, 3.0, 0.0))); - - double roverAngle = (roverDirection.heading()); - // fazer um raycast pra frente e verificar a distancia - double obstacleAhead = pathFinding.raycastDistance(radarSourcePosition, transformDirection(roverDirection), - surfaceReferenceFrame, 30); - double steeringPower = Utilities.remap(3, 30, 0.1, 0.5, obstacleAhead, true); - // usar esse valor pra muiltiplicar a direcao alvo - double targetAndRadarAngle = (targetDirection.multiply(steeringPower) - .sum(directionFromRadar(getActiveVessel().boundingBox(roverReferenceFrame))).normalize()).heading(); - double deltaAngle = Math.abs(targetAndRadarAngle - roverAngle); - getActiveVessel().getControl().setSAS(deltaAngle < 1); - // Control Rover Throttle - setRoverThrottle(acelCtrl.calculate(horizontalVelocity.get() / maxSpeed * 50, 50)); - // Control Rover Steering - if (deltaAngle > 1) { - setRoverSteering(sterringCtrl.calculate(roverAngle / (targetAndRadarAngle) * 100, 100)); - } else { - setRoverSteering(0.0f); + try { + pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); + } catch (InterruptedException e) { + stop(); } - setCurrentStatus("Driving... " + deltaAngle); - } + } - private Vector directionFromRadar( - Pair,Triplet> boundingBox) - throws RPCException, IOException { - // PONTO REF ROVER: X = DIREITA, Y = FRENTE, Z = BAIXO; - // Bounding box points from rover (LBU: Left, Back, Up - RFD: Right, Front, - // Down): - Vector LBU = new Vector(boundingBox.getValue0()); - Vector RFD = new Vector(boundingBox.getValue1()); - - // Pre-calculated bbox positions - Vector lateralLeft = new Vector(LBU.x, LBU.y * 0.5 + RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); - Vector latFrontLeft = new Vector(LBU.x, RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); - Vector frontLeft = new Vector(LBU.x, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); - Vector frontLeft2 = new Vector(LBU.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); - Vector front = new Vector(LBU.x * 0.5 + RFD.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); - Vector frontRight2 = new Vector(RFD.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); - Vector frontRight = new Vector(RFD.x, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); - Vector latFrontRight = new Vector(RFD.x, RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); - Vector lateralRight = new Vector(RFD.x, LBU.y * 0.5 + RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); - - // Pre-calculated bbox directions - Vector lateralLeftAngle = new Vector(-Math.sin(Math.toRadians(90)), Math.cos(Math.toRadians(90)), 0.0); - Vector latFrontLeftAngle = new Vector(-Math.sin(Math.toRadians(67.5)), Math.cos(Math.toRadians(67.5)), 0.0); - Vector frontLeftAngle = new Vector(-Math.sin(Math.toRadians(45)), Math.cos(Math.toRadians(45)), 0.0); - Vector frontLeftAngle2 = new Vector(-Math.sin(Math.toRadians(22.5)), Math.cos(Math.toRadians(22.5)), 0.0); - Vector frontAngle = new Vector(0.0, 1.0, 0.0); - Vector frontRightAngle2 = new Vector(Math.sin(Math.toRadians(22.5)), Math.cos(Math.toRadians(22.5)), 0.0); - Vector frontRightAngle = new Vector(Math.sin(Math.toRadians(45)), Math.cos(Math.toRadians(45)), 0.0); - Vector latFrontRightAngle = new Vector(Math.sin(Math.toRadians(67.5)), Math.cos(Math.toRadians(67.5)), 0.0); - Vector lateralRightAngle = new Vector(Math.sin(Math.toRadians(90)), Math.cos(Math.toRadians(90)), 0.0); - - // Raytracing distance from points: - Vector lateralLeftRay = calculateRaycastDirection(lateralLeft, lateralLeftAngle, 15); - Vector lateralFrontLeftRay = calculateRaycastDirection(latFrontLeft, latFrontLeftAngle, 19); - Vector frontLeftRay = calculateRaycastDirection(frontLeft, frontLeftAngle, 23); - Vector frontLeftRay2 = calculateRaycastDirection(frontLeft2, frontLeftAngle2, 27); - Vector frontRay = calculateRaycastDirection(front, frontAngle, 35); - Vector frontRightRay2 = calculateRaycastDirection(frontRight2, frontRightAngle2, 27); - Vector frontRightRay = calculateRaycastDirection(frontRight, frontRightAngle, 23); - Vector lateralFrontRightRay = calculateRaycastDirection(latFrontRight, latFrontRightAngle, 19); - Vector lateralRightRay = calculateRaycastDirection(lateralRight, lateralRightAngle, 15); - - Vector calculatedDirection = new Vector().sum(lateralLeftRay).sum(lateralFrontLeftRay).sum(frontLeftRay) - .sum(frontLeftRay2).sum(frontRay).sum(frontRightRay2).sum(frontRightRay).sum(lateralFrontRightRay) - .sum(lateralRightRay); - - return (calculatedDirection.normalize()); + } else { + targetPoint = pathFinding.getPathsFirstPoint(); } - - private Vector calculateRaycastDirection(Vector point, Vector direction, double distance) throws RPCException { - double raycast = pathFinding.raycastDistance(posRoverToSurf(point), transformDirection(direction), - surfaceReferenceFrame, distance); - return direction.multiply(raycast); - } - - private Vector transformDirection(Vector vector) throws RPCException { - return new Vector( - getSpaceCenter().transformDirection(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); - } - - private Vector posSurfToRover(Vector vector) throws RPCException { - return new Vector( - getSpaceCenter().transformPosition(vector.toTriplet(), surfaceReferenceFrame, roverReferenceFrame)); - } - - private Vector posRoverToSurf(Vector vector) throws RPCException { - return new Vector( - getSpaceCenter().transformPosition(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); + } + + private boolean isFarFromTarget() { + return currentDistanceToTarget > distanceFromTargetLimit; + } + + private boolean needToChargeBatteries() { + float minChargeLevel = 10.0f; + return (currentChargePercentage < minChargeLevel); + } + + private void rechargeRover() throws RPCException, StreamException, InterruptedException { + + float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); + + setRoverThrottle(0); + vessel.getActiveVessel().getControl().setLights(false); + vessel.getActiveVessel().getControl().setBrakes(true); + + if (vessel.horizontalVelocity.get() < 1 && vessel.getActiveVessel().getControl().getBrakes()) { + Thread.sleep(1000); + double chargeTime; + double totalEnergyFlow = 0; + List solarPanels = + vessel.getActiveVessel().getParts().getSolarPanels().stream() + .filter(this::isSolarPanelNotBroken) + .collect(Collectors.toList()); + + for (SolarPanel sp : solarPanels) { + totalEnergyFlow += sp.getEnergyFlow(); + } + chargeTime = ((totalCharge - currentChargeAmount) / totalEnergyFlow); + setCurrentStatus("Segundos de Carga: " + chargeTime); + if (chargeTime < 1 || chargeTime > 21600) { + chargeTime = 3600; + } + vessel + .getConnectionManager() + .getSpaceCenter() + .warpTo((vessel.getConnectionManager().getSpaceCenter().getUT() + chargeTime), 10000, 4); + vessel.getActiveVessel().getControl().setLights(true); } - - private Vector posOrbToSurf(Vector vector) throws RPCException { - return new Vector( - getSpaceCenter().transformPosition(vector.toTriplet(), orbitalReferenceFrame, surfaceReferenceFrame)); + } + + private void driveRover() throws RPCException, IOException, StreamException { + Vector targetDirection = posSurfToRover(posOrbToSurf(targetPoint)).normalize(); + Vector radarSourcePosition = + posRoverToSurf( + new Vector(vessel.getActiveVessel().position(roverReferenceFrame)) + .sum(new Vector(0.0, 3.0, 0.0))); + + double roverAngle = (roverDirection.heading()); + // fazer um raycast pra frente e verificar a distancia + double obstacleAhead = + pathFinding.raycastDistance( + radarSourcePosition, + transformDirection(roverDirection), + vessel.surfaceReferenceFrame, + 30); + double steeringPower = Utilities.remap(3, 30, 0.1, 0.5, obstacleAhead, true); + // usar esse valor pra muiltiplicar a direcao alvo + double targetAndRadarAngle = + (targetDirection + .multiply(steeringPower) + .sum(directionFromRadar(vessel.getActiveVessel().boundingBox(roverReferenceFrame))) + .normalize()) + .heading(); + double deltaAngle = Math.abs(targetAndRadarAngle - roverAngle); + vessel.getActiveVessel().getControl().setSAS(deltaAngle < 1); + // Control Rover Throttle + setRoverThrottle(acelCtrl.calculate(vessel.horizontalVelocity.get() / maxSpeed * 50, 50)); + // Control Rover Steering + if (deltaAngle > 1) { + setRoverSteering(sterringCtrl.calculate(roverAngle / (targetAndRadarAngle) * 100, 100)); + } else { + setRoverSteering(0.0f); } - - private void setRoverThrottle(double throttle) throws RPCException, StreamException { - if (horizontalVelocity.get() < (maxSpeed * 1.01)) { - getActiveVessel().getControl().setBrakes(false); - getActiveVessel().getControl().setWheelThrottle((float) throttle); - } else { - getActiveVessel().getControl().setBrakes(true); - } + setCurrentStatus("Driving... " + deltaAngle); + } + + private Vector directionFromRadar( + Pair, Triplet> boundingBox) + throws RPCException, IOException { + // PONTO REF ROVER: X = DIREITA, Y = FRENTE, Z = BAIXO; + // Bounding box points from rover (LBU: Left, Back, Up - RFD: Right, Front, + // Down): + Vector LBU = new Vector(boundingBox.getValue0()); + Vector RFD = new Vector(boundingBox.getValue1()); + + // Pre-calculated bbox positions + Vector lateralLeft = new Vector(LBU.x, LBU.y * 0.5 + RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); + Vector latFrontLeft = new Vector(LBU.x, RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); + Vector frontLeft = new Vector(LBU.x, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); + Vector frontLeft2 = new Vector(LBU.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); + Vector front = new Vector(LBU.x * 0.5 + RFD.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); + Vector frontRight2 = new Vector(RFD.x * 0.5, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); + Vector frontRight = new Vector(RFD.x, RFD.y, LBU.z * 0.5 + RFD.z * 0.5); + Vector latFrontRight = new Vector(RFD.x, RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); + Vector lateralRight = new Vector(RFD.x, LBU.y * 0.5 + RFD.y * 0.5, LBU.z * 0.5 + RFD.z * 0.5); + + // Pre-calculated bbox directions + Vector lateralLeftAngle = + new Vector(-Math.sin(Math.toRadians(90)), Math.cos(Math.toRadians(90)), 0.0); + Vector latFrontLeftAngle = + new Vector(-Math.sin(Math.toRadians(67.5)), Math.cos(Math.toRadians(67.5)), 0.0); + Vector frontLeftAngle = + new Vector(-Math.sin(Math.toRadians(45)), Math.cos(Math.toRadians(45)), 0.0); + Vector frontLeftAngle2 = + new Vector(-Math.sin(Math.toRadians(22.5)), Math.cos(Math.toRadians(22.5)), 0.0); + Vector frontAngle = new Vector(0.0, 1.0, 0.0); + Vector frontRightAngle2 = + new Vector(Math.sin(Math.toRadians(22.5)), Math.cos(Math.toRadians(22.5)), 0.0); + Vector frontRightAngle = + new Vector(Math.sin(Math.toRadians(45)), Math.cos(Math.toRadians(45)), 0.0); + Vector latFrontRightAngle = + new Vector(Math.sin(Math.toRadians(67.5)), Math.cos(Math.toRadians(67.5)), 0.0); + Vector lateralRightAngle = + new Vector(Math.sin(Math.toRadians(90)), Math.cos(Math.toRadians(90)), 0.0); + + // Raytracing distance from points: + Vector lateralLeftRay = calculateRaycastDirection(lateralLeft, lateralLeftAngle, 15); + Vector lateralFrontLeftRay = calculateRaycastDirection(latFrontLeft, latFrontLeftAngle, 19); + Vector frontLeftRay = calculateRaycastDirection(frontLeft, frontLeftAngle, 23); + Vector frontLeftRay2 = calculateRaycastDirection(frontLeft2, frontLeftAngle2, 27); + Vector frontRay = calculateRaycastDirection(front, frontAngle, 35); + Vector frontRightRay2 = calculateRaycastDirection(frontRight2, frontRightAngle2, 27); + Vector frontRightRay = calculateRaycastDirection(frontRight, frontRightAngle, 23); + Vector lateralFrontRightRay = calculateRaycastDirection(latFrontRight, latFrontRightAngle, 19); + Vector lateralRightRay = calculateRaycastDirection(lateralRight, lateralRightAngle, 15); + + Vector calculatedDirection = + new Vector() + .sum(lateralLeftRay) + .sum(lateralFrontLeftRay) + .sum(frontLeftRay) + .sum(frontLeftRay2) + .sum(frontRay) + .sum(frontRightRay2) + .sum(frontRightRay) + .sum(lateralFrontRightRay) + .sum(lateralRightRay); + + return (calculatedDirection.normalize()); + } + + private Vector calculateRaycastDirection(Vector point, Vector direction, double distance) + throws RPCException { + double raycast = + pathFinding.raycastDistance( + posRoverToSurf(point), + transformDirection(direction), + vessel.surfaceReferenceFrame, + distance); + return direction.multiply(raycast); + } + + private Vector transformDirection(Vector vector) throws RPCException { + return new Vector( + vessel + .getConnectionManager() + .getSpaceCenter() + .transformDirection( + vector.toTriplet(), roverReferenceFrame, vessel.surfaceReferenceFrame)); + } + + private Vector posSurfToRover(Vector vector) throws RPCException { + return new Vector( + vessel + .getConnectionManager() + .getSpaceCenter() + .transformPosition( + vector.toTriplet(), vessel.surfaceReferenceFrame, roverReferenceFrame)); + } + + private Vector posRoverToSurf(Vector vector) throws RPCException { + return new Vector( + vessel + .getConnectionManager() + .getSpaceCenter() + .transformPosition( + vector.toTriplet(), roverReferenceFrame, vessel.surfaceReferenceFrame)); + } + + private Vector posOrbToSurf(Vector vector) throws RPCException { + return new Vector( + vessel + .getConnectionManager() + .getSpaceCenter() + .transformPosition( + vector.toTriplet(), vessel.orbitalReferenceFrame, vessel.surfaceReferenceFrame)); + } + + private void setRoverThrottle(double throttle) throws RPCException, StreamException { + if (vessel.horizontalVelocity.get() < (maxSpeed * 1.01)) { + vessel.getActiveVessel().getControl().setBrakes(false); + vessel.getActiveVessel().getControl().setWheelThrottle((float) throttle); + } else { + vessel.getActiveVessel().getControl().setBrakes(true); } + } - private void setRoverSteering(double steering) throws RPCException { - getActiveVessel().getControl().setWheelSteering((float) steering); - } + private void setRoverSteering(double steering) throws RPCException { + vessel.getActiveVessel().getControl().setWheelSteering((float) steering); + } - private enum MODE { - DRIVE, NEXT_POINT, CHARGING - } + private enum MODE { + DRIVE, + NEXT_POINT, + CHARGING + } } diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 849d94f..9a65eee 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -1,19 +1,22 @@ package com.pesterenan.model; -import com.pesterenan.MechPeste; import com.pesterenan.controllers.Controller; +import com.pesterenan.controllers.DockingController; import com.pesterenan.controllers.LandingController; import com.pesterenan.controllers.LiftoffController; import com.pesterenan.controllers.ManeuverController; import com.pesterenan.controllers.RoverController; -import com.pesterenan.controllers.DockingController; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; import com.pesterenan.utils.Telemetry; import com.pesterenan.utils.Vector; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import krpc.client.Connection; import krpc.client.RPCException; import krpc.client.Stream; import krpc.client.StreamException; +import krpc.client.services.SpaceCenter; import krpc.client.services.SpaceCenter.AutoPilot; import krpc.client.services.SpaceCenter.CelestialBody; import krpc.client.services.SpaceCenter.Flight; @@ -21,218 +24,298 @@ import krpc.client.services.SpaceCenter.Vessel; import krpc.client.services.SpaceCenter.VesselSituation; -import java.util.HashMap; -import java.util.Map; +public class ActiveVessel { -import static com.pesterenan.MechPeste.getConnection; -import static com.pesterenan.MechPeste.getSpaceCenter; + public Vessel activeVessel; + public SpaceCenter spaceCenter; + public Connection connection; + private final Map telemetryData = new ConcurrentHashMap<>(); + public AutoPilot ap; + public Flight flightParameters; + public ReferenceFrame orbitalReferenceFrame; + public Stream totalMass; + public ReferenceFrame surfaceReferenceFrame; + public float totalCharge; + public double gravityAcel; + public CelestialBody currentBody; + public Stream altitude; + public Stream surfaceAltitude; + public Stream apoapsis; + public Stream periapsis; + public Stream verticalVelocity; + public Stream missionTime; + public Stream horizontalVelocity; + public Map commands; + public int currentVesselId = 0; + public Thread controllerThread = null; + public Controller controller; + public long timer = 0; + private String currentStatus = Bundle.getString("status_ready"); + private boolean runningModule; + private ConnectionManager connectionManager; + private VesselManager vesselManager; -public class ActiveVessel { + public ActiveVessel(ConnectionManager connectionManager, VesselManager vesselManager) { + this.connectionManager = connectionManager; + this.vesselManager = vesselManager; + this.connection = connectionManager.getConnection(); + this.spaceCenter = connectionManager.getSpaceCenter(); + reinitialize(); + } - protected Vessel activeVessel; - private final Map telemetryData = new HashMap<>(); - public AutoPilot ap; - public Flight flightParameters; - public ReferenceFrame orbitalReferenceFrame; - protected Stream totalMass; - public ReferenceFrame surfaceReferenceFrame; - public float totalCharge; - public double gravityAcel; - public CelestialBody currentBody; - public Stream altitude; - public Stream surfaceAltitude; - public Stream apoapsis; - public Stream periapsis; - public Stream verticalVelocity; - public Stream missionTime; - public Stream horizontalVelocity; - public Map commands; - protected int currentVesselId = 0; - protected Thread controllerThread = null; - protected Controller controller; - protected long timer = 0; - private String currentStatus = Bundle.getString("status_ready"); - private boolean runningModule; - - public ActiveVessel() { - initializeParameters(); - } + public ConnectionManager getConnectionManager() { + return connectionManager; + } - private void initializeParameters() { - try { - setActiveVessel(getSpaceCenter().getActiveVessel()); - currentVesselId = getActiveVessel().hashCode(); - ap = getActiveVessel().getAutoPilot(); - currentBody = getActiveVessel().getOrbit().getBody(); - gravityAcel = currentBody.getSurfaceGravity(); - orbitalReferenceFrame = currentBody.getReferenceFrame(); - surfaceReferenceFrame = getActiveVessel().getSurfaceReferenceFrame(); - flightParameters = getActiveVessel().flight(orbitalReferenceFrame); - totalMass = getConnection().addStream(getActiveVessel(), "getMass"); - altitude = getConnection().addStream(flightParameters, "getMeanAltitude"); - surfaceAltitude = getConnection().addStream(flightParameters, "getSurfaceAltitude"); - apoapsis = getConnection().addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); - periapsis = getConnection().addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); - verticalVelocity = getConnection().addStream(flightParameters, "getVerticalSpeed"); - horizontalVelocity = getConnection().addStream(flightParameters, "getHorizontalSpeed"); - } catch (RPCException | StreamException e) { - MechPeste.newInstance().checkConnection(); - } - } + public VesselManager getVesselManager() { + return vesselManager; + } - public String getCurrentStatus() { - if (controller != null) { - return controller.getCurrentStatus(); - } - return currentStatus; + public String getCurrentStatus() { + if (controller != null) { + return controller.getCurrentStatus(); } + return currentStatus; + } - public void setCurrentStatus(String status) { - currentStatus = status; + public void setCurrentStatus(String status) { + if (controller != null) { + controller.setCurrentStatus(status); + } else { + this.currentStatus = status; } + } - public int getCurrentVesselId() { - return currentVesselId; - } + public int getCurrentVesselId() { + return currentVesselId; + } - public Vessel getActiveVessel() { - return activeVessel; - } + public Vessel getActiveVessel() { + return activeVessel; + } - public void setActiveVessel(Vessel currentVessel) { - activeVessel = currentVessel; - } + public void setActiveVessel(Vessel currentVessel) { + activeVessel = currentVessel; + } - public void throttle(float acel) throws RPCException { - getActiveVessel().getControl().setThrottle(Math.min(acel, 1.0f)); - } + public void throttle(float acel) throws RPCException { + getActiveVessel().getControl().setThrottle(Math.min(acel, 1.0f)); + } - public void throttle(double acel) throws RPCException { - throttle((float) acel); - } + public void throttle(double acel) throws RPCException { + throttle((float) acel); + } - public void tuneAutoPilot() throws RPCException { - ap.setTimeToPeak(new Vector(2, 2, 2).toTriplet()); - ap.setDecelerationTime(new Vector(5, 5, 5).toTriplet()); - } + public void tuneAutoPilot() throws RPCException { + ap.setTimeToPeak(new Vector(2, 2, 2).toTriplet()); + ap.setDecelerationTime(new Vector(5, 5, 5).toTriplet()); + } - public void liftoff() { - try { - getActiveVessel().getControl().setSAS(true); - throttleUp(getMaxThrottleForTWR(2.0), 1); - if (getActiveVessel().getSituation().equals(VesselSituation.PRE_LAUNCH)) { - for (double count = 5.0; count >= 0; count -= 0.1) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - setCurrentStatus(String.format(Bundle.getString("status_launching_in"), count)); - Thread.sleep(100); - } - getSpaceCenter().setActiveVessel(activeVessel); - getActiveVessel().getControl().activateNextStage(); - } - setCurrentStatus(Bundle.getString("status_liftoff")); - } catch (RPCException | InterruptedException | StreamException ignored) { - setCurrentStatus(Bundle.getString("status_liftoff_abort")); + public void liftoff() { + try { + getActiveVessel().getControl().setSAS(true); + throttleUp(getMaxThrottleForTWR(2.0), 1); + if (getActiveVessel().getSituation().equals(VesselSituation.PRE_LAUNCH)) { + for (double count = 5.0; count >= 0; count -= 0.1) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + setCurrentStatus(String.format(Bundle.getString("status_launching_in"), count)); + Thread.sleep(100); } - } - - protected void decoupleStage() throws RPCException, InterruptedException { - setCurrentStatus(Bundle.getString("status_separating_stage")); - MechPeste.getSpaceCenter().setActiveVessel(getActiveVessel()); - double currentThrottle = getActiveVessel().getControl().getThrottle(); - throttle(0); - Thread.sleep(1000); + spaceCenter.setActiveVessel(activeVessel); getActiveVessel().getControl().activateNextStage(); - throttleUp(currentThrottle, 1); + } + setCurrentStatus(Bundle.getString("status_liftoff")); + } catch (RPCException | InterruptedException | StreamException ignored) { + setCurrentStatus(Bundle.getString("status_liftoff_abort")); } + } - protected void throttleUp(double throttleAmount, double seconds) throws RPCException, InterruptedException { - double secondsElapsed = 0; - while (secondsElapsed < seconds) { - throttle(secondsElapsed / seconds * throttleAmount); - secondsElapsed += 0.1; - Thread.sleep(100); - } - } + public double getTWR() throws RPCException, StreamException { + return getActiveVessel().getAvailableThrust() / (totalMass.get() * gravityAcel); + } - public double getTWR() throws RPCException, StreamException { - return getActiveVessel().getAvailableThrust() / (totalMass.get() * gravityAcel); - } + public double getMaxThrottleForTWR(double targetTWR) throws RPCException, StreamException { + return targetTWR / getTWR(); + } - public double getMaxThrottleForTWR(double targetTWR) throws RPCException, StreamException { - return targetTWR / getTWR(); - } + public double getMaxAcel(float maxTWR) throws RPCException, StreamException { + return Math.min(maxTWR, getTWR()) * gravityAcel - gravityAcel; + } - public double getMaxAcel(float maxTWR) throws RPCException, StreamException { - return Math.min(maxTWR, getTWR()) * gravityAcel - gravityAcel; + public void startModule(Map commands) { + String currentFunction = commands.get(Module.MODULO.get()); + if (controllerThread != null) { + controllerThread.interrupt(); + runningModule = false; + try { + controllerThread.join(); // Wait for the previous thread to finish + } catch (InterruptedException e) { + e.printStackTrace(); + } } - - public void startModule(Map commands) { - String currentFunction = commands.get(Module.MODULO.get()); - if (controllerThread != null) { - controllerThread.interrupt(); - runningModule = false; - } - if (currentFunction.equals(Module.LIFTOFF.get())) { - controller = new LiftoffController(commands); - runningModule = true; - } - if (currentFunction.equals(Module.HOVERING.get()) || currentFunction.equals(Module.LANDING.get())) { - controller = new LandingController(commands); - runningModule = true; - } - if (currentFunction.equals(Module.MANEUVER.get())) { - controller = new ManeuverController(commands); - runningModule = true; - } - if (currentFunction.equals(Module.ROVER.get())) { - controller = new RoverController(commands); - runningModule = true; - } - if (currentFunction.equals(Module.DOCKING.get())) { - controller = new DockingController(commands); - System.out.println("escolheu modulo docking"); - runningModule = true; - } - controllerThread = new Thread(controller, currentVesselId + " - " + currentFunction); - controllerThread.start(); + if (currentFunction.equals(Module.LIFTOFF.get())) { + controller = new LiftoffController(this, commands); + runningModule = true; } + if (currentFunction.equals(Module.HOVERING.get()) + || currentFunction.equals(Module.LANDING.get())) { + controller = new LandingController(this, commands); + runningModule = true; + } + if (currentFunction.equals(Module.MANEUVER.get())) { + controller = new ManeuverController(this, commands); + runningModule = true; + } + if (currentFunction.equals(Module.ROVER.get())) { + controller = new RoverController(this, commands); + runningModule = true; + } + if (currentFunction.equals(Module.DOCKING.get())) { + controller = new DockingController(this, commands); + runningModule = true; + } + String controllerThreadName = "MP_CTRL_" + currentFunction + "_" + currentVesselId; + controllerThread = new Thread(controller, controllerThreadName); + controllerThread.start(); + } - public void recordTelemetryData() throws RPCException { - if (getActiveVessel().getOrbit().getBody() != currentBody) { - initializeParameters(); - } - synchronized (telemetryData) { - try { - telemetryData.put(Telemetry.ALTITUDE, altitude.get() < 0 ? 0 : altitude.get()); - telemetryData.put(Telemetry.ALT_SURF, surfaceAltitude.get() < 0 ? 0 : surfaceAltitude.get()); - telemetryData.put(Telemetry.APOAPSIS, apoapsis.get() < 0 ? 0 : apoapsis.get()); - telemetryData.put(Telemetry.PERIAPSIS, periapsis.get() < 0 ? 0 : periapsis.get()); - telemetryData.put(Telemetry.VERT_SPEED, verticalVelocity.get()); - telemetryData.put(Telemetry.HORZ_SPEED, horizontalVelocity.get() < 0 ? 0 : horizontalVelocity.get()); - } catch (RPCException | StreamException ignored) { - } - } + public Thread getControllerThread() { + return controllerThread; + } + + public void cancelControl() { + if (controller != null) { + controller.stop(); + runningModule = false; } + } - public Map getTelemetryData() { - return telemetryData; + public void removeStreams() { + // Interrupt any running controller thread + if (controllerThread != null && controllerThread.isAlive()) { + controllerThread.interrupt(); + runningModule = false; + } + // Close all streams safely + synchronized (connection) { + try { + if (totalMass != null) totalMass.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (altitude != null) altitude.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (surfaceAltitude != null) surfaceAltitude.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (apoapsis != null) apoapsis.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (periapsis != null) periapsis.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (verticalVelocity != null) verticalVelocity.remove(); + } catch (Exception e) { + /* ignore */ + } + try { + if (horizontalVelocity != null) horizontalVelocity.remove(); + } catch (Exception e) { + /* ignore */ + } } + // Clear the telemetry data map + telemetryData.clear(); + } - public void cancelControl() { - try { - ap.disengage(); - throttle(0); - if (controllerThread != null) { - controllerThread.interrupt(); - runningModule = false; - } - } catch (RPCException ignored) { - } + public Map getTelemetryData() { + return telemetryData; + } + + public boolean hasModuleRunning() { + return runningModule; + } + + public void decoupleStage() throws RPCException, InterruptedException { + setCurrentStatus(Bundle.getString("status_separating_stage")); + spaceCenter.setActiveVessel(getActiveVessel()); + double currentThrottle = getActiveVessel().getControl().getThrottle(); + throttle(0); + Thread.sleep(1000); + getActiveVessel().getControl().activateNextStage(); + throttleUp(currentThrottle, 1); + } + + public void throttleUp(double throttleAmount, double seconds) + throws RPCException, InterruptedException { + double secondsElapsed = 0; + while (secondsElapsed < seconds) { + throttle(secondsElapsed / seconds * throttleAmount); + secondsElapsed += 0.1; + Thread.sleep(100); } + } + + public void reinitialize() { + // Clean up any existing streams or threads before creating new ones + removeStreams(); + + System.out.println("DEBUG: Re-initializing ActiveVessel parameters..."); + try { + setActiveVessel(spaceCenter.getActiveVessel()); + currentVesselId = getActiveVessel().hashCode(); + ap = getActiveVessel().getAutoPilot(); + currentBody = getActiveVessel().getOrbit().getBody(); + gravityAcel = currentBody.getSurfaceGravity(); + orbitalReferenceFrame = currentBody.getReferenceFrame(); + surfaceReferenceFrame = getActiveVessel().getSurfaceReferenceFrame(); + flightParameters = getActiveVessel().flight(orbitalReferenceFrame); + + System.out.println("DEBUG: Basic parameters set. Creating streams..."); + synchronized (connection) { + altitude = connection.addStream(flightParameters, "getMeanAltitude"); + apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); + horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); + periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); + surfaceAltitude = connection.addStream(flightParameters, "getSurfaceAltitude"); + totalMass = connection.addStream(getActiveVessel(), "getMass"); + verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); + missionTime = connection.addStream(spaceCenter.getClass(), "getUT"); + + altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); + apoapsis.addCallback(val -> telemetryData.put(Telemetry.APOAPSIS, val < 0 ? 0 : val)); + horizontalVelocity.addCallback( + val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); + periapsis.addCallback(val -> telemetryData.put(Telemetry.PERIAPSIS, val < 0 ? 0 : val)); + surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); + verticalVelocity.addCallback(val -> telemetryData.put(Telemetry.VERT_SPEED, val)); - public boolean hasModuleRunning() { - return runningModule; + altitude.start(); + apoapsis.start(); + horizontalVelocity.start(); + periapsis.start(); + surfaceAltitude.start(); + totalMass.start(); + verticalVelocity.start(); + missionTime.start(); + } + System.out.println("DEBUG: All streams created successfully."); + } catch (RPCException | StreamException e) { + System.err.println( + "DEBUG: CRITICAL ERROR while re-initializing parameters for active vessel: " + + e.getMessage()); } + } } diff --git a/src/main/java/com/pesterenan/model/ConnectionListener.java b/src/main/java/com/pesterenan/model/ConnectionListener.java new file mode 100644 index 0000000..707d869 --- /dev/null +++ b/src/main/java/com/pesterenan/model/ConnectionListener.java @@ -0,0 +1,8 @@ +package com.pesterenan.model; + +import krpc.client.Connection; +import krpc.client.services.SpaceCenter; + +public interface ConnectionListener { + void onConnectionChanged(Connection newConnection, SpaceCenter newSpaceCenter); +} diff --git a/src/main/java/com/pesterenan/model/ConnectionManager.java b/src/main/java/com/pesterenan/model/ConnectionManager.java new file mode 100644 index 0000000..e961598 --- /dev/null +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -0,0 +1,136 @@ +package com.pesterenan.model; + +import com.pesterenan.resources.Bundle; +import com.pesterenan.views.StatusDisplay; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import javax.swing.SwingUtilities; +import krpc.client.Connection; +import krpc.client.RPCException; +import krpc.client.services.KRPC; +import krpc.client.services.SpaceCenter; + +public class ConnectionManager { + private KRPC krpc; + private Connection connection; + private SpaceCenter spaceCenter; + private StatusDisplay statusDisplay; + private volatile boolean isConnecting = false; + private ScheduledExecutorService connectionMonitor; + private String connectionName; + private final List listeners = new ArrayList<>(); + + public ConnectionManager(final String connectionName, final StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + this.connectionName = connectionName; + startMonitoring(); + } + + public void addListener(ConnectionListener listener) { + listeners.add(listener); + } + + private void notifyListeners() { + for (ConnectionListener listener : listeners) { + listener.onConnectionChanged(connection, spaceCenter); + } + } + + public Connection getConnection() { + return connection; + } + + public SpaceCenter getSpaceCenter() { + return spaceCenter; + } + + public KRPC getKRPC() { + return krpc; + } + + public void connect() { + attemptConnection(); + } + + public void disconnect() { + try { + if (connection != null) { + connection.close(); + } + } catch (IOException e) { + System.err.println("Error closing connection: " + e.getMessage()); + } + if (connectionMonitor != null && !connectionMonitor.isShutdown()) { + connectionMonitor.shutdown(); + } + } + + private void startMonitoring() { + if (connectionMonitor != null && !connectionMonitor.isShutdown()) return; + connectionMonitor = + Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "MP_CONNECTION_MONITOR")); + connectionMonitor.scheduleAtFixedRate( + () -> { + if (!isConnectionAlive()) { + attemptConnection(); + } + }, + 0, + 5, + TimeUnit.SECONDS); + } + + private void attemptConnection() { + if (isConnecting) return; + try { + isConnecting = true; + SwingUtilities.invokeLater( + () -> { + statusDisplay.setStatusMessage(Bundle.getString("status_connecting")); + statusDisplay.setBtnConnectVisible(false); + }); + + connection = Connection.newInstance(connectionName); + krpc = KRPC.newInstance(connection); + spaceCenter = SpaceCenter.newInstance(connection); + notifyListeners(); // Notify listeners of the new connection + + SwingUtilities.invokeLater( + () -> statusDisplay.setStatusMessage(Bundle.getString("status_connected"))); + } catch (IOException e) { + connection = null; + krpc = null; + spaceCenter = null; + SwingUtilities.invokeLater( + () -> { + statusDisplay.setStatusMessage(Bundle.getString("status_error_connection")); + statusDisplay.setBtnConnectVisible(true); + }); + } finally { + isConnecting = false; + } + } + + public boolean isConnectionAlive() { + try { + if (krpc == null || connection == null) return false; + krpc.getStatus(); + return true; + } catch (RPCException e) { + return false; + } + } + + public boolean isOnFlightScene() { + try { + if (krpc == null) return false; + return krpc.getCurrentGameScene().equals(KRPC.GameScene.FLIGHT); + } catch (final RPCException e) { + return false; + } + } +} diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java new file mode 100644 index 0000000..08b8390 --- /dev/null +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -0,0 +1,245 @@ +package com.pesterenan.model; + +import com.pesterenan.resources.Bundle; +import com.pesterenan.utils.Vector; +import com.pesterenan.views.FunctionsAndTelemetryJPanel; +import com.pesterenan.views.CreateManeuverJPanel; +import com.pesterenan.views.MainGui; +import com.pesterenan.views.StatusDisplay; +import java.awt.event.ActionEvent; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import javax.swing.DefaultListModel; +import javax.swing.ListModel; +import javax.swing.SwingUtilities; +import krpc.client.Connection; +import krpc.client.RPCException; +import krpc.client.services.SpaceCenter; +import krpc.client.services.SpaceCenter.Node; +import krpc.client.services.SpaceCenter.Vessel; + +public class VesselManager implements ConnectionListener { + private ActiveVessel currentVessel; + private ConnectionManager connectionManager; + private FunctionsAndTelemetryJPanel telemetryPanel; + private StatusDisplay statusDisplay; + private SpaceCenter spaceCenter; + private int currentVesselId = -1; + private ScheduledExecutorService telemetryMonitor; + + public VesselManager( + final ConnectionManager connectionManager, + final StatusDisplay statusDisplay, + final FunctionsAndTelemetryJPanel telemetryPanel) { + this.connectionManager = connectionManager; + this.statusDisplay = statusDisplay; + this.telemetryPanel = telemetryPanel; + this.connectionManager.addListener(this); + } + + @Override + public void onConnectionChanged(Connection newConnection, SpaceCenter newSpaceCenter) { + this.spaceCenter = newSpaceCenter; + clearVessel(); + System.out.println("DEBUG: Connection changed. Vessel cleared."); + } + + public Connection getConnection() { + return connectionManager.getConnection(); + } + + public SpaceCenter getSpaceCenter() { + return spaceCenter; + } + + public ActiveVessel getCurrentVessel() { + return currentVessel; + } + + public int getCurrentVesselId() { + return currentVesselId; + } + + public void startTelemetryLoop() { + if (telemetryMonitor != null && !telemetryMonitor.isShutdown()) return; + telemetryMonitor = + Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "MP_TELEMETRY_UPDATER")); + telemetryMonitor.scheduleAtFixedRate( + () -> { + if (connectionManager.isConnectionAlive()) { + checkAndUpdateActiveVessel(); + } + if (currentVessel != null && connectionManager.isOnFlightScene()) { + updateUI(); + } + }, + 0, + 100, + TimeUnit.MILLISECONDS); + } + + public ListModel getCurrentManeuvers() { + DefaultListModel list = new DefaultListModel<>(); + try { + List maneuvers = getSpaceCenter().getActiveVessel().getControl().getNodes(); + maneuvers.forEach( + m -> { + try { + String maneuverStr = + String.format( + "%d - Dv: %.1f {P: %.1f, N: %.1f, R: %.1f} AP: %.1f, PE: %.1f", + maneuvers.indexOf(m) + 1, + m.getDeltaV(), + m.getPrograde(), + m.getNormal(), + m.getRadial(), + m.getOrbit().getApoapsisAltitude(), + m.getOrbit().getPeriapsisAltitude()); + list.addElement(maneuverStr); + } catch (RPCException ignored) { + } + }); + } catch (RPCException | NullPointerException | UnsupportedOperationException e) { + System.err.println("ERRO: Não foi possivel atualizar as manobras atuais. " + e.getClass().getSimpleName()); + } + return list; + } + + public void startModule(Map commands) { + currentVessel.startModule(commands); + } + + public ListModel getActiveVessels(String search) { + DefaultListModel list = new DefaultListModel<>(); + try { + List vessels = getSpaceCenter().getVessels(); + vessels = vessels.stream().filter(v -> filterVessels(v, search)).collect(Collectors.toList()); + vessels.forEach( + v -> { + try { + String vesselName = v.hashCode() + " - \t" + v.getName(); + list.addElement(vesselName); + } catch (RPCException e) { + System.err.println( + "ERRO: Não foi possível adicionar nave na lista. " + e.getMessage()); + } + }); + } catch (RPCException | NullPointerException e) { + System.err.println("ERRO: Não foi possível buscar lista de naves. " + e.getMessage()); + } + return list; + } + + public String getVesselInfo(int selectedIndex) { + try { + Vessel activeVessel = + getSpaceCenter().getVessels().stream() + .filter(v -> v.hashCode() == selectedIndex) + .findFirst() + .get(); + String name = + activeVessel.getName().length() > 40 + ? activeVessel.getName().substring(0, 40) + "..." + : activeVessel.getName(); + String vesselInfo = + String.format( + "Nome: %s\t\t\t | Corpo: %s", name, activeVessel.getOrbit().getBody().getName()); + return vesselInfo; + } catch (RPCException | NullPointerException e) { + System.err.println("ERRO: Não foi possível buscar informações da nave. " + e.getMessage()); + } + return ""; + } + + public void changeToVessel(int selectedIndex) { + try { + Vessel activeVessel = + getSpaceCenter().getVessels().stream() + .filter(v -> v.hashCode() == selectedIndex) + .findFirst() + .get(); + getSpaceCenter().setActiveVessel(activeVessel); + } catch (RPCException | NullPointerException e) { + System.out.println(Bundle.getString("status_couldnt_switch_vessel")); + } + } + + public void cancelControl(ActionEvent e) { + if (currentVessel != null) { + currentVessel.cancelControl(); + } + } + + private void checkAndUpdateActiveVessel() { + try { + int activeVesselId = getSpaceCenter().getActiveVessel().hashCode(); + if (currentVesselId != activeVesselId) { + System.out.println("DEBUG: Active vessel changed. Re-initializing..."); + if (currentVessel != null) { + currentVessel.reinitialize(); + } else { + currentVessel = new ActiveVessel(connectionManager, this); + } + currentVesselId = currentVessel.getCurrentVesselId(); + MainGui.getInstance().setVesselManager(this); + } + } catch (IllegalArgumentException e) { + } catch (RPCException e) { + System.out.println("DEBUG: Could not get active vessel. Cleaning up."); + clearVessel(); + } + } + + public void clearVessel() { + if (currentVessel != null) { + currentVessel.removeStreams(); + } + currentVessel = null; + currentVesselId = -1; + } + + private void updateUI() { + SwingUtilities.invokeLater( + () -> { + if (currentVessel != null && telemetryPanel != null) { + telemetryPanel.updateTelemetry(currentVessel.getTelemetryData()); + CreateManeuverJPanel createManeuverPanel = MainGui.getInstance().getCreateManeuverPanel(); + if (createManeuverPanel.isShowing()) { + createManeuverPanel.updatePanel(getCurrentManeuvers()); + } + if (currentVessel.hasModuleRunning()) { + statusDisplay.setStatusMessage(currentVessel.getCurrentStatus()); + } + } + }); } + + private boolean filterVessels(Vessel vessel, String search) { + if ("all".equals(search)) { + return true; + } + double TEN_KILOMETERS = 10000.0; + try { + Vessel active = getSpaceCenter().getActiveVessel(); + if (vessel.getOrbit().getBody().getName().equals(active.getOrbit().getBody().getName())) { + final Vector activePos = new Vector(active.position(active.getSurfaceReferenceFrame())); + final Vector vesselPos = new Vector(vessel.position(active.getSurfaceReferenceFrame())); + final double distance = Vector.distance(activePos, vesselPos); + switch (search) { + case "closest": + if (distance < TEN_KILOMETERS) { + return true; + } + break; + case "samebody": + return true; + } + } + } catch (RPCException ignored) { + } + return false; + } +} diff --git a/src/main/java/com/pesterenan/resources/Bundle.java b/src/main/java/com/pesterenan/resources/Bundle.java index 1d4e53b..42e493d 100644 --- a/src/main/java/com/pesterenan/resources/Bundle.java +++ b/src/main/java/com/pesterenan/resources/Bundle.java @@ -5,17 +5,16 @@ import java.util.ResourceBundle; public class Bundle { - public static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("MechPesteBundle", - Locale.getDefault()); + public static final ResourceBundle RESOURCE_BUNDLE = + ResourceBundle.getBundle("MechPesteBundle", Locale.getDefault()); - private Bundle() { - } + private Bundle() {} - public static String getString(String key) { - try { - return RESOURCE_BUNDLE.getString(key); - } catch (MissingResourceException e) { - return '!' + key + '!'; - } + public static String getString(String key) { + try { + return RESOURCE_BUNDLE.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; } + } } diff --git a/src/main/java/com/pesterenan/updater/KrpcInstaller.java b/src/main/java/com/pesterenan/updater/KrpcInstaller.java index f7ca60b..3feebec 100644 --- a/src/main/java/com/pesterenan/updater/KrpcInstaller.java +++ b/src/main/java/com/pesterenan/updater/KrpcInstaller.java @@ -1,7 +1,6 @@ package com.pesterenan.updater; import com.pesterenan.views.InstallKrpcDialog; - import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -15,76 +14,77 @@ import java.util.zip.ZipInputStream; public class KrpcInstaller { - static final int BUFFER = 2048; - private static final String KRPC_GITHUB_LINK = "https://github.com/krpc/krpc/releases/download/v0.5.4/krpc-0.5.4.zip"; - private static String KSP_FOLDER = null; + static final int BUFFER = 2048; + private static final String KRPC_GITHUB_LINK = + "https://github.com/krpc/krpc/releases/download/v0.5.4/krpc-0.5.4.zip"; + private static String KSP_FOLDER = null; - public static String getKspFolder() { - return KSP_FOLDER; - } + public static String getKspFolder() { + return KSP_FOLDER; + } - public static void setKspFolder(String folderPath) { - KSP_FOLDER = folderPath; - } + public static void setKspFolder(String folderPath) { + KSP_FOLDER = folderPath; + } - public static void downloadAndInstallKrpc() { - InstallKrpcDialog.setStatus("Fazendo download do KRPC pelo Github..."); - downloadKrpc(); - InstallKrpcDialog.setStatus("Download completo!"); - InstallKrpcDialog.setStatus("Extraindo arquivos..."); - unzipKrpc(); - InstallKrpcDialog.setStatus("Instalação terminada, reinicie o KSP!"); - } + public static void downloadAndInstallKrpc() { + InstallKrpcDialog.setStatus("Fazendo download do KRPC pelo Github..."); + downloadKrpc(); + InstallKrpcDialog.setStatus("Download completo!"); + InstallKrpcDialog.setStatus("Extraindo arquivos..."); + unzipKrpc(); + InstallKrpcDialog.setStatus("Instalação terminada, reinicie o KSP!"); + } - public static void downloadKrpc() { - URL krpcLink; - try { - krpcLink = new URL(KRPC_GITHUB_LINK); - ReadableByteChannel readableByteChannel = Channels.newChannel(krpcLink.openStream()); - FileOutputStream fos = new FileOutputStream(KSP_FOLDER + "\\krpc-0.5.4.zip"); - fos.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); - fos.close(); - } catch (IOException e) { - InstallKrpcDialog.setStatus("Não foi possível fazer o download do KRPC"); - } + public static void downloadKrpc() { + URL krpcLink; + try { + krpcLink = new URL(KRPC_GITHUB_LINK); + ReadableByteChannel readableByteChannel = Channels.newChannel(krpcLink.openStream()); + FileOutputStream fos = new FileOutputStream(KSP_FOLDER + "\\krpc-0.5.4.zip"); + fos.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); + fos.close(); + } catch (IOException e) { + InstallKrpcDialog.setStatus("Não foi possível fazer o download do KRPC"); } + } - public static void unzipKrpc() { - try { - File folder = new File(KSP_FOLDER); - if (!folder.exists()) { - folder.mkdir(); - } - BufferedOutputStream dest; - // zipped input - FileInputStream fis = new FileInputStream(KSP_FOLDER + "\\krpc-0.5.4.zip"); - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis)); - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - int count; - byte[] data = new byte[BUFFER]; - String fileName = entry.getName(); - File newFile = new File(folder + File.separator + fileName); - // If directory then just create the directory (and parents if required) - if (entry.isDirectory()) { - if (!newFile.exists()) { - newFile.mkdirs(); - } - } else { - // write the files to the disk - FileOutputStream fos = new FileOutputStream(newFile); - dest = new BufferedOutputStream(fos, BUFFER); - while ((count = zis.read(data, 0, BUFFER)) != -1) { - dest.write(data, 0, count); - } - dest.flush(); - dest.close(); - } - zis.closeEntry(); - } - zis.close(); - } catch (Exception e) { - InstallKrpcDialog.setStatus("Não foi possível fazer instalar o KRPC"); + public static void unzipKrpc() { + try { + File folder = new File(KSP_FOLDER); + if (!folder.exists()) { + folder.mkdir(); + } + BufferedOutputStream dest; + // zipped input + FileInputStream fis = new FileInputStream(KSP_FOLDER + "\\krpc-0.5.4.zip"); + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis)); + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + int count; + byte[] data = new byte[BUFFER]; + String fileName = entry.getName(); + File newFile = new File(folder + File.separator + fileName); + // If directory then just create the directory (and parents if required) + if (entry.isDirectory()) { + if (!newFile.exists()) { + newFile.mkdirs(); + } + } else { + // write the files to the disk + FileOutputStream fos = new FileOutputStream(newFile); + dest = new BufferedOutputStream(fos, BUFFER); + while ((count = zis.read(data, 0, BUFFER)) != -1) { + dest.write(data, 0, count); + } + dest.flush(); + dest.close(); } + zis.closeEntry(); + } + zis.close(); + } catch (Exception e) { + InstallKrpcDialog.setStatus("Não foi possível fazer instalar o KRPC"); } + } } diff --git a/src/main/java/com/pesterenan/utils/Attributes.java b/src/main/java/com/pesterenan/utils/Attributes.java index 51174d8..2c75c39 100644 --- a/src/main/java/com/pesterenan/utils/Attributes.java +++ b/src/main/java/com/pesterenan/utils/Attributes.java @@ -5,29 +5,30 @@ public class Attributes { - private static Map safeLowOrbitAltitudes = new HashMap(); + private static Map safeLowOrbitAltitudes = new HashMap(); - public Attributes() { - safeLowOrbitAltitudes.put("Bop", 10_000.0); - safeLowOrbitAltitudes.put("Dres", 20_000.0); - safeLowOrbitAltitudes.put("Duna", 60_000.0); - safeLowOrbitAltitudes.put("Eeloo", 20_000.0); - safeLowOrbitAltitudes.put("Eve", 100_000.0); - safeLowOrbitAltitudes.put("Gilly", 10_000.0); - safeLowOrbitAltitudes.put("Ike", 20_000.0); - safeLowOrbitAltitudes.put("Jool", 220_000.0); - safeLowOrbitAltitudes.put("Kerbin", 80_000.0); - safeLowOrbitAltitudes.put("Laythe", 80_000.0); - safeLowOrbitAltitudes.put("Minmus", 10_000.0); - safeLowOrbitAltitudes.put("Moho", 35_000.0); - safeLowOrbitAltitudes.put("Mun", 10_000.0); - safeLowOrbitAltitudes.put("Pol", 10_000.0); - safeLowOrbitAltitudes.put("Sun", 350_000.0); - safeLowOrbitAltitudes.put("Tylo", 40_000.0); - safeLowOrbitAltitudes.put("Vall", 20_000.0); - } + public Attributes() { + safeLowOrbitAltitudes.put("Bop", 10_000.0); + safeLowOrbitAltitudes.put("Dres", 20_000.0); + safeLowOrbitAltitudes.put("Duna", 60_000.0); + safeLowOrbitAltitudes.put("Eeloo", 20_000.0); + safeLowOrbitAltitudes.put("Eve", 100_000.0); + safeLowOrbitAltitudes.put("Gilly", 10_000.0); + safeLowOrbitAltitudes.put("Ike", 20_000.0); + safeLowOrbitAltitudes.put("Jool", 220_000.0); + safeLowOrbitAltitudes.put("Kerbin", 80_000.0); + safeLowOrbitAltitudes.put("Laythe", 80_000.0); + safeLowOrbitAltitudes.put("Minmus", 10_000.0); + safeLowOrbitAltitudes.put("Moho", 35_000.0); + safeLowOrbitAltitudes.put("Mun", 10_000.0); + safeLowOrbitAltitudes.put("Pol", 10_000.0); + safeLowOrbitAltitudes.put("Sun", 350_000.0); + safeLowOrbitAltitudes.put("Tylo", 40_000.0); + safeLowOrbitAltitudes.put("Vall", 20_000.0); + } - public double getLowOrbitAltitude(String celestialBody) { - return safeLowOrbitAltitudes.getOrDefault(celestialBody, 10_000.0); - }; + public double getLowOrbitAltitude(String celestialBody) { + return safeLowOrbitAltitudes.getOrDefault(celestialBody, 10_000.0); + } + ; } diff --git a/src/main/java/com/pesterenan/utils/ControlePID.java b/src/main/java/com/pesterenan/utils/ControlePID.java index 1fce344..5534712 100644 --- a/src/main/java/com/pesterenan/utils/ControlePID.java +++ b/src/main/java/com/pesterenan/utils/ControlePID.java @@ -4,101 +4,115 @@ import krpc.client.services.SpaceCenter; public class ControlePID { - private SpaceCenter spaceCenter = null; - private double outputMin = -1; - private double outputMax = 1; - private double kp = 0.025; - private double ki = 0.001; - private double kd = 0.01; - private double integralTerm = 0.0; - private double previousError, previousMeasurement, lastTime = 0.0; - private double timeSample = 0.025; // 25 millisegundos - private double proportionalTerm; - private double derivativeTerm; - - public ControlePID() { + private SpaceCenter spaceCenter = null; + private double outputMin = -1; + private double outputMax = 1; + private double kp = 0.025; + private double ki = 0.001; + private double kd = 0.01; + private double integralTerm = 0.0; + private double previousError, lastTime = 0.0; + private double timeSample = 0.025; // 25 millisegundos + private double proportionalTerm; + + private double derivativeTerm; + + public ControlePID() {} + + public ControlePID(SpaceCenter spaceCenter) { + this.spaceCenter = spaceCenter; + } + + public ControlePID(double kp, double ki, double kd, double outputMin, double outputMax) { + this.kp = kp; + this.ki = ki; + this.kd = kd; + this.outputMin = outputMin; + this.outputMax = outputMax; + } + + public double getTimeSample() { + return timeSample; + } + + public void reset() { + this.previousError = 0; + this.proportionalTerm = 0; + this.integralTerm = 0; + this.derivativeTerm = 0; + } + + public double calculate(double measurement, double setPoint) { + double now = this.getCurrentTime(); + double changeInTime = now - lastTime; + + if (changeInTime >= timeSample) { + // Error signal + double error = setPoint - measurement; + // Proportional + proportionalTerm = kp * error; + + // Integral + integralTerm += 0.5 * ki * timeSample * (error + previousError); + integralTerm = limitOutput(integralTerm); + + derivativeTerm = kd * ((error - previousError) / timeSample); + previousError = error; + lastTime = now; } + return limitOutput(proportionalTerm + integralTerm + derivativeTerm); + } - public ControlePID(SpaceCenter spaceCenter, double timeSample) { - this.spaceCenter = spaceCenter; - setTimeSample(timeSample); + public void setOutput(double min, double max) { + if (min > max) { + return; } - - public ControlePID(double kp, double ki, double kd, double outputMin, double outputMax) { - this.kp = kp; - this.ki = ki; - this.kd = kd; - this.outputMin = outputMin; - this.outputMax = outputMax; - } - - public void reset() { - this.previousError = 0; - this.previousMeasurement = 0; - this.proportionalTerm = 0; - this.integralTerm = 0; - this.derivativeTerm = 0; + outputMin = min; + outputMax = max; + integralTerm = limitOutput(integralTerm); + } + + public void setPIDValues(double Kp, double Ki, double Kd) { + if (Kp > 0) { + this.kp = Kp; } - - public double calculate(double measurement, double setPoint) { - double now = this.getCurrentTime(); - double changeInTime = now - lastTime; - - if (changeInTime >= timeSample) { - // Error signal - double error = setPoint - measurement; - // Proportional - proportionalTerm = kp * error; - - // Integral - // integralTerm += 0.5f * ki * timeSample * (error + previousError); - // integralTerm += ki * (error + previousError); - integralTerm = ki * (integralTerm + (error * timeSample)); - integralTerm = limitOutput(integralTerm); - - derivativeTerm = kd * ((error - previousError) / timeSample); - previousError = error; - lastTime = now; - } - return limitOutput(proportionalTerm + integralTerm + derivativeTerm); + if (Ki >= 0) { + this.ki = Ki; } - - private double limitOutput(double value) { - return Utilities.clamp(value, outputMin, outputMax); + if (Kd >= 0) { + this.kd = Kd; } - - public void setOutput(double min, double max) { - if (min > max) { - return; - } - outputMin = min; - outputMax = max; - integralTerm = limitOutput(integralTerm); + } + + public void setTimeSample(double milliseconds) { + timeSample = milliseconds > 0 ? milliseconds / 1000 : timeSample; + } + + /** + * Tries to return the milliseconds from the game, if it fails, returns the system time + * + * @returns the current time in milisseconds + */ + public double getCurrentTime() { + try { + return spaceCenter.getUT(); + } catch (RPCException | NullPointerException ignored) { + return System.currentTimeMillis(); } - - public void setPIDValues(double Kp, double Ki, double Kd) { - if (Kp > 0) { - this.kp = Kp; - } - if (Ki >= 0) { - this.ki = Ki; - } - if (Kd >= 0) { - this.kd = Kd; - } - } - - public void setTimeSample(double milliseconds) { - timeSample = milliseconds > 0 ? milliseconds / 1000 : timeSample; - } - - private double getCurrentTime() { - try { - return spaceCenter.getUT(); - } catch (RPCException | NullPointerException ignored) { - System.err.println("Não foi possível buscar o tempo do jogo, retornando do sistema"); - return System.currentTimeMillis(); - } + } + + /** Uses busy waiting to lock the thread until time passes the time sample */ + public void waitForNextSample() throws InterruptedException { + double startTime = getCurrentTime(); + while (getCurrentTime() - startTime < getTimeSample()) { + Thread.sleep(1); + if (Thread.interrupted()) { + throw new InterruptedException(); + } } + } + private double limitOutput(double value) { + return Utilities.clamp(value, outputMin, outputMax); + } } diff --git a/src/main/java/com/pesterenan/utils/Module.java b/src/main/java/com/pesterenan/utils/Module.java index 2a6b5cd..566af1e 100644 --- a/src/main/java/com/pesterenan/utils/Module.java +++ b/src/main/java/com/pesterenan/utils/Module.java @@ -1,37 +1,51 @@ package com.pesterenan.utils; public enum Module { + ADJUST("Ajustar"), + APOAPSIS("Apoastro"), + CIRCULAR("Circular"), + CREATE_MANEUVER("CRIAR_MANOBRAS"), + CUBIC("Cúbica"), + DIRECTION("Direção"), + DOCKING("DOCKING"), + EXECUTE("Executar"), + EXPONENCIAL("Exponencial"), + FINE_ADJUST("Ajuste Fino"), + FUNCTION("Função"), + HOVERING("HOVER"), + HOVER_AFTER_LANDING("SOBREVOO PÓS POUSO"), + HOVER_ALTITUDE("Altitude Sobrevoo"), + INCLINATION("Inclinação"), + LAND("Pousar nave"), + LANDING("LANDING"), + LIFTOFF("LIFTOFF"), + LOW_ORBIT("ÓRBITA BAIXA"), + MANEUVER("MANEUVER"), + MAP_MARKER("Marcador no mapa"), + MARKER_NAME("Nome do marcador"), + MAX_SPEED("Velocidade Máxima"), + MAX_TWR("Max_TWR"), + MODULO("Módulo"), + OPEN_PANELS("Abrir Painéis"), + PERIAPSIS("Periastro"), + QUADRATIC("Quadrática"), + RENDEZVOUS("Rendezvous"), + ROLL("Rolagem"), + ROVER("ROVER"), + ROVER_TARGET_TYPE("Tipo de Alvo do Rover"), + SAFE_DISTANCE("Distância Segura"), + SINUSOIDAL("Sinusoidal"), + STAGE("Usar Estágios"), + TARGET_VESSEL("Nave alvo"), + TELEMETRY("TELEMETRY"); - ADJUST("Ajustar"), APOAPSIS("Apoastro"), CIRCULAR("Circular"), CREATE_MANEUVER("CRIAR_MANOBRAS"), CUBIC( - "Cúbica"), DIRECTION("Direção"), DOCKING("DOCKING"), EXECUTE("Executar"), EXPONENCIAL( - "Exponencial"), FINE_ADJUST("Ajuste Fino"), FUNCTION("Função"), HOVERING( - "HOVER"), HOVER_AFTER_LANDING("SOBREVOO PÓS POUSO"), HOVER_ALTITUDE( - "Altitude Sobrevoo"), INCLINATION("Inclinação"), LAND("Pousar nave"), LANDING( - "LANDING"), LIFTOFF("LIFTOFF"), LOW_ORBIT("ÓRBITA BAIXA"), MANEUVER( - "MANEUVER"), MAP_MARKER("Marcador no mapa"), MARKER_NAME( - "Nome do marcador"), MAX_SPEED( - "Velocidade Máxima"), MAX_TWR("Max_TWR"), MODULO( - "Módulo"), OPEN_PANELS( - "Abrir Painéis"), PERIAPSIS( - "Periastro"), QUADRATIC( - "Quadrática"), RENDEZVOUS( - "Rendezvous"), ROLL( - "Rolagem"), ROVER( - "ROVER"), ROVER_TARGET_TYPE( - "Tipo de Alvo do Rover"), SAFE_DISTANCE( - "Distância Segura"), SINUSOIDAL( - "Sinusoidal"), STAGE( - "Usar Estágios"), TARGET_VESSEL( - "Nave alvo"), TELEMETRY( - "TELEMETRY"); + final String t; - final String t; + Module(String t) { + this.t = t; + } - Module(String t) { - this.t = t; - } - - public String get() { - return this.t; - } + public String get() { + return this.t; + } } diff --git a/src/main/java/com/pesterenan/utils/Navigation.java b/src/main/java/com/pesterenan/utils/Navigation.java index 9e8792d..5eac8b8 100644 --- a/src/main/java/com/pesterenan/utils/Navigation.java +++ b/src/main/java/com/pesterenan/utils/Navigation.java @@ -1,154 +1,155 @@ package com.pesterenan.utils; +import static krpc.client.services.SpaceCenter.*; + +import com.pesterenan.model.ConnectionManager; import krpc.client.RPCException; import krpc.client.Stream; import krpc.client.StreamException; import org.javatuples.Triplet; -import static com.pesterenan.MechPeste.getConnection; -import static com.pesterenan.MechPeste.getSpaceCenter; -import static krpc.client.services.SpaceCenter.*; - public class Navigation { - public static final Triplet RADIAL = new Triplet<>(1.0, 0.0, 0.0); - public static final Triplet ANTI_RADIAL = new Triplet<>(-1.0, 0.0, 0.0); - public static final Triplet PROGRADE = new Triplet<>(0.0, 1.0, 0.0); - public static final Triplet RETROGRADE = new Triplet<>(0.0, -1.0, 0.0); - public static final Triplet NORMAL = new Triplet<>(0.0, 0.0, 1.0); - public static final Triplet ANTI_NORMAL = new Triplet<>(0.0, 0.0, -1.0); - // private Drawing drawing; - private Vessel currentVessel; - private Flight flightParameters; - private Stream horizontalSpeed; - private ReferenceFrame orbitalReference; - - public Navigation(Vessel currentVessel) { - this.currentVessel = currentVessel; - initializeParameters(); - } + public static final Triplet RADIAL = new Triplet<>(1.0, 0.0, 0.0); + public static final Triplet ANTI_RADIAL = new Triplet<>(-1.0, 0.0, 0.0); + public static final Triplet PROGRADE = new Triplet<>(0.0, 1.0, 0.0); + public static final Triplet RETROGRADE = new Triplet<>(0.0, -1.0, 0.0); + public static final Triplet NORMAL = new Triplet<>(0.0, 0.0, 1.0); + public static final Triplet ANTI_NORMAL = new Triplet<>(0.0, 0.0, -1.0); + // private Drawing drawing; + private Vessel currentVessel; + private Flight flightParameters; + private Stream horizontalSpeed; + private ReferenceFrame orbitalReference; + private ConnectionManager connectionManager; - private void initializeParameters() { - try { - // drawing = Drawing.newInstance(getConexao()); - orbitalReference = currentVessel.getOrbit().getBody().getReferenceFrame(); - flightParameters = currentVessel.flight(orbitalReference); - horizontalSpeed = getConnection().addStream(flightParameters, "getHorizontalSpeed"); - } catch (RPCException | StreamException ignored) { - } + public Navigation(ConnectionManager connectionManager, Vessel currentVessel) { + this.connectionManager = connectionManager; + this.currentVessel = currentVessel; + try { + // drawing = Drawing.newInstance(getConexao()); + orbitalReference = currentVessel.getOrbit().getBody().getReferenceFrame(); + flightParameters = currentVessel.flight(orbitalReference); + horizontalSpeed = + connectionManager.getConnection().addStream(flightParameters, "getHorizontalSpeed"); + } catch (RPCException | StreamException e) { + throw new IllegalStateException("ERRO: Inicialização de parâmetros de navegação.", e); } + } - public void aimAtManeuver(Node maneuver) throws RPCException { - aimAtDirection(getSpaceCenter().transformDirection(PROGRADE, maneuver.getReferenceFrame(), orbitalReference)); - } + public void aimAtManeuverNode(Node maneuver) throws RPCException { + currentVessel.getAutoPilot().setReferenceFrame(maneuver.getReferenceFrame()); + currentVessel.getAutoPilot().setTargetDirection(PROGRADE); + } - public void aimForLanding() throws RPCException, StreamException { - Vector currentPosition = new Vector(currentVessel.position(orbitalReference)); - Vector retrograde = new Vector(getSpaceCenter().transformPosition(RETROGRADE, - currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)).subtract(currentPosition); - Vector radial = new Vector(getSpaceCenter().transformDirection(RADIAL, currentVessel.getSurfaceReferenceFrame(), - orbitalReference)); - double angleLimit = Utilities.remap(0, 10, 0, 0.9, horizontalSpeed.get(), true); - Vector landingVector = Utilities.linearInterpolation(radial, retrograde, angleLimit); - aimAtDirection(landingVector.toTriplet()); - } + public void aimForLanding() throws RPCException, StreamException { + Vector retrograde = + new Vector( + transformDirection(RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame())); + Vector radial = + new Vector(transformDirection(RADIAL, currentVessel.getSurfaceReferenceFrame())); - // public void aimAtTarget() throws RPCException, StreamException, - // InterruptedException { - // Vector currentPosition = new Vector(naveAtual.position(pontoRefSuperficie)); - // Vector targetPosition = new - // Vector(centroEspacial.getTargetVessel().position(pontoRefSuperficie)); - // targetPosition.x = 0.0; - // double distanceToTarget = Vector.distance(currentPosition, targetPosition); - // - // Vector toTargetDirection = Vector.targetDirection(currentPosition, - // targetPosition); - // Vector oppositeDirection = Vector.targetOppositeDirection(currentPosition, - // targetPosition); - // Vector progradeDirection = Vector.targetDirection(currentPosition, new - // Vector( - // centroEspacial.transformPosition(PROGRADE, - // naveAtual.getSurfaceVelocityReferenceFrame(), - // pontoRefSuperficie - // ))); - // Vector retrogradeDirection = Vector.targetDirection(currentPosition, new - // Vector( - // centroEspacial.transformPosition(RETROGRADE, - // naveAtual.getSurfaceVelocityReferenceFrame(), - // pontoRefSuperficie - // ))); - // progradeDirection.x = 0.0; - // retrogradeDirection.x = 0.0; - // drawing.addDirection(toTargetDirection.toTriplet(), pontoRefSuperficie, 10, - // true); - // drawing.addDirection(oppositeDirection.toTriplet(), pontoRefSuperficie, 5, - // true); - // double pointingToTargetThreshold = Utilities.remap(0, 200, 0, 1, - // distanceToTarget, true); - // double speedThreshold = Utilities.remap(0, 20, 0, 1, horizontalSpeed.get(), - // true); - // - // Vector currentDirection = - // Utilities.linearInterpolation(oppositeDirection, toTargetDirection, - // pointingToTargetThreshold); - // double angleCurrentDirection = - // new Vector(currentDirection.z, currentDirection.y, - // currentDirection.x).heading(); - // double angleProgradeDirection = - // new Vector(progradeDirection.z, progradeDirection.y, - // progradeDirection.x).heading(); - // double deltaAngle = angleProgradeDirection - angleCurrentDirection; - // System.out.println(deltaAngle); - // if (deltaAngle > 3) { - // currentDirection.sum(progradeDirection).normalize(); - // } else if (deltaAngle < -3) { - // currentDirection.subtract(progradeDirection).normalize(); - // } - // drawing.addDirection(currentDirection.toTriplet(), pontoRefSuperficie, 25, - // true); - // - // - // Vector currentDirectionOnOrbitalRef = new Vector( - // centroEspacial.transformDirection(currentDirection.toTriplet(), - // pontoRefSuperficie, - // pontoRefOrbital)); - // Vector radial = new Vector(centroEspacial.transformDirection(RADIAL, - // pontoRefSuperficie, - // pontoRefOrbital)); - // Vector speedVector = Utilities.linearInterpolation(retrogradeDirection, - // progradeDirection, speedThreshold); - // Vector speedVectorOnOrbitalRef = new Vector( - // centroEspacial.transformDirection(speedVector.toTriplet(), - // pontoRefSuperficie, - // pontoRefOrbital)); - // Vector pointingVector = - // Utilities.linearInterpolation(currentDirectionOnOrbitalRef, - // radial.sum(speedVectorOnOrbitalRef), - // speedThreshold - // ); - // Thread.sleep(50); - // drawing.clear(false); - // aimAtDirection(pointingVector.toTriplet()); - // } - - public void aimAtPrograde() throws RPCException { - aimAtDirection(getSpaceCenter().transformDirection(PROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), - orbitalReference)); - } + double angleLimit = Utilities.remap(0, 10, 0, 0.9, horizontalSpeed.get(), true); + Vector landingVector = Utilities.linearInterpolation(radial, retrograde, angleLimit); + aimAtDirection(landingVector.toTriplet()); + } - public void aimAtRadialOut() throws RPCException { - aimAtDirection(getSpaceCenter().transformDirection(RADIAL, currentVessel.getSurfaceReferenceFrame(), - orbitalReference)); - } + public void aimForDeorbit() throws RPCException { + aimAtDirection(flightParameters.getRetrograde()); + } - public void aimAtRetrograde() throws RPCException { - aimAtDirection(getSpaceCenter().transformDirection(RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), - orbitalReference)); - } + public void aimAtPrograde() throws RPCException { + transformAndAim(PROGRADE, currentVessel.getSurfaceVelocityReferenceFrame()); + } - public void aimAtDirection(Triplet currentDirection) throws RPCException { - currentVessel.getAutoPilot().setReferenceFrame(orbitalReference); - currentVessel.getAutoPilot().setTargetDirection(currentDirection); - } + public void aimAtRadialOut() throws RPCException { + transformAndAim(RADIAL, currentVessel.getSurfaceReferenceFrame()); + } + + public void aimAtRetrograde() throws RPCException { + transformAndAim(RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame()); + } + + public void aimAtDirection(Triplet currentDirection) throws RPCException { + currentVessel.getAutoPilot().setReferenceFrame(orbitalReference); + currentVessel.getAutoPilot().setTargetDirection(currentDirection); + } + + // Método auxiliar para evitar duplicação + private void transformAndAim( + Triplet direction, ReferenceFrame referenceFrame) + throws RPCException { + aimAtDirection(transformDirection(direction, referenceFrame)); + } + + // Método auxiliar sobrecarregado para clareza + private Triplet transformDirection( + Triplet direction, ReferenceFrame fromReference) throws RPCException { + return connectionManager + .getSpaceCenter() + .transformDirection(direction, fromReference, orbitalReference); + } + // public void aimAtTarget() throws RPCException, StreamException, InterruptedException { + // Vector currentPosition = new Vector(naveAtual.position(pontoRefSuperficie)); + // Vector targetPosition = + // new Vector(centroEspacial.getTargetVessel().position(pontoRefSuperficie)); + // targetPosition.x = 0.0; + // double distanceToTarget = Vector.distance(currentPosition, targetPosition); + // + // Vector toTargetDirection = Vector.targetDirection(currentPosition, targetPosition); + // Vector oppositeDirection = Vector.targetOppositeDirection(currentPosition, targetPosition); + // Vector progradeDirection = + // Vector.targetDirection( + // currentPosition, + // new Vector( + // centroEspacial.transformPosition( + // PROGRADE, naveAtual.getSurfaceVelocityReferenceFrame(), pontoRefSuperficie))); + // Vector retrogradeDirection = + // Vector.targetDirection( + // currentPosition, + // new Vector( + // centroEspacial.transformPosition( + // RETROGRADE, naveAtual.getSurfaceVelocityReferenceFrame(), pontoRefSuperficie))); + // progradeDirection.x = 0.0; + // retrogradeDirection.x = 0.0; + // drawing.addDirection(toTargetDirection.toTriplet(), pontoRefSuperficie, 10, true); + // drawing.addDirection(oppositeDirection.toTriplet(), pontoRefSuperficie, 5, true); + // double pointingToTargetThreshold = Utilities.remap(0, 200, 0, 1, distanceToTarget, true); + // double speedThreshold = Utilities.remap(0, 20, 0, 1, horizontalSpeed.get(), true); + // + // Vector currentDirection = + // Utilities.linearInterpolation( + // oppositeDirection, toTargetDirection, pointingToTargetThreshold); + // double angleCurrentDirection = + // new Vector(currentDirection.z, currentDirection.y, currentDirection.x).heading(); + // double angleProgradeDirection = + // new Vector(progradeDirection.z, progradeDirection.y, progradeDirection.x).heading(); + // double deltaAngle = angleProgradeDirection - angleCurrentDirection; + // System.out.println(deltaAngle); + // if (deltaAngle > 3) { + // currentDirection.sum(progradeDirection).normalize(); + // } else if (deltaAngle < -3) { + // currentDirection.subtract(progradeDirection).normalize(); + // } + // drawing.addDirection(currentDirection.toTriplet(), pontoRefSuperficie, 25, true); + // + // Vector currentDirectionOnOrbitalRef = + // new Vector( + // transformDirection(currentDirection.toTriplet(), pontoRefSuperficie)); + // Vector radial = + // new Vector(centroEspacial.transformDirection(RADIAL, pontoRefSuperficie, pontoRefOrbital)); + // Vector speedVector = + // Utilities.linearInterpolation(retrogradeDirection, progradeDirection, speedThreshold); + // Vector speedVectorOnOrbitalRef = + // new Vector( + // centroEspacial.transformDirection( + // speedVector.toTriplet(), pontoRefSuperficie, pontoRefOrbital)); + // Vector pointingVector = + // Utilities.linearInterpolation( + // currentDirectionOnOrbitalRef, radial.sum(speedVectorOnOrbitalRef), speedThreshold); + // Thread.sleep(50); + // drawing.clear(false); + // aimAtDirection(pointingVector.toTriplet()); + // } } diff --git a/src/main/java/com/pesterenan/utils/PathFinding.java b/src/main/java/com/pesterenan/utils/PathFinding.java index f55510f..700125d 100644 --- a/src/main/java/com/pesterenan/utils/PathFinding.java +++ b/src/main/java/com/pesterenan/utils/PathFinding.java @@ -1,216 +1,259 @@ package com.pesterenan.utils; -import com.pesterenan.controllers.Controller; -import krpc.client.RPCException; -import krpc.client.services.Drawing; -import krpc.client.services.SpaceCenter; -import krpc.client.services.SpaceCenter.Waypoint; -import krpc.client.services.SpaceCenter.WaypointManager; -import org.javatuples.Triplet; - +import com.pesterenan.model.ConnectionManager; +import com.pesterenan.model.VesselManager; import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.stream.Collectors; +import krpc.client.RPCException; +import krpc.client.services.Drawing; +import krpc.client.services.SpaceCenter; +import krpc.client.services.SpaceCenter.Waypoint; +import krpc.client.services.SpaceCenter.WaypointManager; +import org.javatuples.Triplet; -import static com.pesterenan.MechPeste.getConnection; -import static com.pesterenan.MechPeste.getSpaceCenter; +public class PathFinding { -public class PathFinding extends Controller { + private WaypointManager waypointManager; + private String waypointName; + private List waypointsToReach; + private List pathToTarget; + private Drawing drawing; + private Drawing.Polygon polygonPath; + private final ConnectionManager connectionManager; + private final VesselManager vesselManager; - private WaypointManager waypointManager; - private String waypointName; - private List waypointsToReach; - private List pathToTarget; - private Drawing drawing; - private Drawing.Polygon polygonPath; + public PathFinding(ConnectionManager connectionManager, VesselManager vesselManager) { + this.connectionManager = connectionManager; + this.vesselManager = vesselManager; + initializeParameters(); + } - public PathFinding() { - super(); - initializeParameters(); + private void initializeParameters() { + try { + waypointManager = connectionManager.getSpaceCenter().getWaypointManager(); + waypointsToReach = new ArrayList<>(); + pathToTarget = new ArrayList<>(); + drawing = Drawing.newInstance(connectionManager.getConnection()); + } catch (RPCException ignored) { } + } - private void initializeParameters() { - try { - waypointManager = getSpaceCenter().getWaypointManager(); - waypointsToReach = new ArrayList<>(); - pathToTarget = new ArrayList<>(); - drawing = Drawing.newInstance(getConnection()); - } catch (RPCException ignored) { - } - } + public void addWaypointsOnSameBody(String waypointName) throws RPCException { + this.waypointName = waypointName; + this.waypointsToReach = + waypointManager.getWaypoints().stream() + .filter(this::hasSameName) + .collect(Collectors.toList()); + } - public void addWaypointsOnSameBody(String waypointName) throws RPCException { - this.waypointName = waypointName; - this.waypointsToReach = waypointManager.getWaypoints().stream().filter(this::hasSameName) - .collect(Collectors.toList()); + private boolean hasSameName(Waypoint waypoint) { + try { + return waypoint.getName().toLowerCase().contains(waypointName.toLowerCase()) + && waypoint.getBody().equals(vesselManager.getCurrentVessel().currentBody); + } catch (RPCException e) { + return false; } + } - private boolean hasSameName(Waypoint waypoint) { - try { - return waypoint.getName().toLowerCase().contains(waypointName.toLowerCase()) - && waypoint.getBody().equals(currentBody); - } catch (RPCException e) { - return false; - } - } + public Vector findNearestWaypoint() throws RPCException, IOException, InterruptedException { + waypointsToReach = + waypointsToReach.stream() + .sorted( + (w1, w2) -> { + double w1Distance = 0; + double w2Distance = 0; + try { + w1Distance = + Vector.distance( + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position( + vesselManager.getCurrentVessel().orbitalReferenceFrame)), + waypointPosOnSurface(w1)); + w2Distance = + Vector.distance( + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position( + vesselManager.getCurrentVessel().orbitalReferenceFrame)), + waypointPosOnSurface(w2)); + } catch (RPCException e) { + } + return w1Distance > w2Distance ? 1 : -1; + }) + .collect(Collectors.toList()); + waypointsToReach.forEach(System.out::println); + Waypoint currentWaypoint = waypointsToReach.get(0); + return waypointPosOnSurface(currentWaypoint); + } - public Vector findNearestWaypoint() throws RPCException, IOException, InterruptedException { - waypointsToReach = waypointsToReach.stream().sorted((w1, w2) -> { - double w1Distance = 0; - double w2Distance = 0; - try { - w1Distance = Vector.distance(new Vector(getActiveVessel().position(orbitalReferenceFrame)), - waypointPosOnSurface(w1)); - w2Distance = Vector.distance(new Vector(getActiveVessel().position(orbitalReferenceFrame)), - waypointPosOnSurface(w2)); - } catch (RPCException e) { - } - return w1Distance > w2Distance ? 1 : -1; - }).collect(Collectors.toList()); - waypointsToReach.forEach(System.out::println); - Waypoint currentWaypoint = waypointsToReach.get(0); - // for (Waypoint waypoint : waypointsToReach) { - // double waypointDistance = Vector.distance(new - // Vector(getNaveAtual().position(orbitalReferenceFrame)), - // waypointPosOnSurface(waypoint) - // ); - // if (currentDistance > waypointDistance) { - // currentDistance = waypointDistance; - // currentWaypoint = waypoint; - // } - // } - return waypointPosOnSurface(currentWaypoint); - } + private Vector waypointPosOnSurface(Waypoint waypoint) throws RPCException { + return new Vector( + vesselManager + .getCurrentVessel() + .currentBody + .surfacePosition( + waypoint.getLatitude(), + waypoint.getLongitude(), + vesselManager.getCurrentVessel().orbitalReferenceFrame)); + } - private Vector waypointPosOnSurface(Waypoint waypoint) throws RPCException { - return new Vector( - currentBody.surfacePosition(waypoint.getLatitude(), waypoint.getLongitude(), orbitalReferenceFrame)); - } + public boolean isPathToTargetEmpty() { + return pathToTarget.isEmpty(); + } - public boolean isPathToTargetEmpty() { - return pathToTarget.isEmpty(); - } + public boolean isWaypointsToReachEmpty() { + boolean allFromContract = + waypointsToReach.stream() + .allMatch( + v -> { + try { + return v.getHasContract(); + } catch (RPCException ignored) { + } + return false; + }); + return waypointsToReach.isEmpty() || allFromContract; + } - public boolean isWaypointsToReachEmpty() { - boolean allFromContract = waypointsToReach.stream().allMatch(v -> { - try { - return v.getHasContract(); - } catch (RPCException ignored) { - } - return false; - }); - return waypointsToReach.isEmpty() || allFromContract; - } + public Vector getPathsFirstPoint() { + return pathToTarget.get(0); + } - public Vector getPathsFirstPoint() { - return pathToTarget.get(0); + public void removePathsCurrentPoint() { + if (isPathToTargetEmpty()) { + return; } + pathToTarget.remove(0); + } - public void removePathsCurrentPoint() { - if (isPathToTargetEmpty()) { - return; - } - pathToTarget.remove(0); + public void removeWaypointFromList() throws RPCException { + if (waypointsToReach.isEmpty()) { + return; } + waypointsToReach.remove(0); + } - public void removeWaypointFromList() throws RPCException { - if (waypointsToReach.isEmpty()) { - return; - } - waypointsToReach.remove(0); + public void buildPathToTarget(Vector targetPosition) + throws IOException, RPCException, InterruptedException { + Vector roverHeight = new Vector(0.2, 0.0, 0.0); + Vector currentRoverPos = + transformSurfToOrb( + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position(vesselManager.getCurrentVessel().surfaceReferenceFrame)) + .sum(roverHeight)); + double distanceToTarget = Vector.distance(currentRoverPos, targetPosition); + pathToTarget.add(currentRoverPos); + int index = 0; + while (distanceToTarget > 50) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Vector currentPoint = pathToTarget.get(index); + Vector targetDirection = + transformOrbToSurf(targetPosition).subtract(transformOrbToSurf(currentPoint)).normalize(); + Vector nextPoint = + transformSurfToOrb(calculateNextPoint(transformOrbToSurf(currentPoint), targetDirection)); + pathToTarget.add(nextPoint); + index++; + double distanceBetweenPoints = + Vector.distance(transformOrbToSurf(currentPoint), transformOrbToSurf(nextPoint)); + distanceToTarget -= distanceBetweenPoints; } + pathToTarget.add(getPosOnSurface(targetPosition)); + drawPathToTarget(pathToTarget); + } - /** - * Builds the path to the targetPosition, on the Celestial Body Reference ( - * Orbital Ref ) - * - * @param targetPosition - * the target pos to build the path to - * @throws IOException - * @throws RPCException - * @throws InterruptedException - */ - public void buildPathToTarget(Vector targetPosition) throws IOException, RPCException, InterruptedException { - // Get current rover Position on Orbital Ref, transform to Surf Ref and add 20 - // centimeters on height: - Vector roverHeight = new Vector(0.2, 0.0, 0.0); - Vector currentRoverPos = transformSurfToOrb( - new Vector(getActiveVessel().position(surfaceReferenceFrame)).sum(roverHeight)); - // Calculate distance from rover to target on Orbital Ref: - double distanceToTarget = Vector.distance(currentRoverPos, targetPosition); - // Add rover pos as first point, on Orbital Ref - pathToTarget.add(currentRoverPos); - // Calculate the next points positions and add to the list on Orbital Ref - int index = 0; - while (distanceToTarget > 50) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Vector currentPoint = pathToTarget.get(index); - Vector targetDirection = transformOrbToSurf(targetPosition).subtract(transformOrbToSurf(currentPoint)) - .normalize(); - Vector nextPoint = transformSurfToOrb( - calculateNextPoint(transformOrbToSurf(currentPoint), targetDirection)); - pathToTarget.add(nextPoint); - index++; - double distanceBetweenPoints = Vector.distance(transformOrbToSurf(currentPoint), - transformOrbToSurf(nextPoint)); - distanceToTarget -= distanceBetweenPoints; - } - pathToTarget.add(getPosOnSurface(targetPosition)); - drawPathToTarget(pathToTarget); - } + private void drawPathToTarget(List path) throws RPCException { + Deque> drawablePath = + path.stream().map(Vector::toTriplet).collect(Collectors.toCollection(ArrayDeque::new)); + drawablePath.offerFirst(new Triplet<>(0.0, 0.0, 0.0)); + drawablePath.offerLast(new Triplet<>(0.0, 0.0, 0.0)); + polygonPath = + drawing.addPolygon( + drawablePath.stream().collect(Collectors.toList()), + vesselManager.getCurrentVessel().orbitalReferenceFrame, + true); + polygonPath.setThickness(0.5f); + polygonPath.setColor(new Triplet<>(1.0, 0.5, 0.0)); + } - private void drawPathToTarget(List path) throws RPCException { - Deque> drawablePath = path.stream().map(Vector::toTriplet) - .collect(Collectors.toCollection(ArrayDeque::new)); - drawablePath.offerFirst(new Triplet<>(0.0, 0.0, 0.0)); - drawablePath.offerLast(new Triplet<>(0.0, 0.0, 0.0)); - polygonPath = drawing.addPolygon(drawablePath.stream().collect(Collectors.toList()), orbitalReferenceFrame, - true); - polygonPath.setThickness(0.5f); - polygonPath.setColor(new Triplet<>(1.0, 0.5, 0.0)); + public void removeDrawnPath() { + try { + polygonPath.remove(); + } catch (RPCException ignored) { } + } - public void removeDrawnPath() { - try { - polygonPath.remove(); - } catch (RPCException ignored) { - } - } + private Vector calculateNextPoint(Vector currentPoint, Vector targetDirection) + throws RPCException, IOException { + double stepDistance = 100.0; + Vector nextPoint = + getPosOnSurface( + transformSurfToOrb(currentPoint.sum(targetDirection.multiply(stepDistance)))); + return transformOrbToSurf(nextPoint).sum(new Vector(0.2, 0.0, 0.0)); + } - private Vector calculateNextPoint(Vector currentPoint, Vector targetDirection) throws RPCException, IOException { - // PONTO REF SUPERFICIE: X = CIMA, Y = NORTE, Z = LESTE; - double stepDistance = 100.0; - // Calculate the next point position on surface: - Vector nextPoint = getPosOnSurface( - transformSurfToOrb(currentPoint.sum(targetDirection.multiply(stepDistance)))); - return transformOrbToSurf(nextPoint).sum(new Vector(0.2, 0.0, 0.0)); - } - - public double raycastDistance(Vector currentPoint, Vector targetDirection, SpaceCenter.ReferenceFrame reference, - double searchDistance) throws RPCException { - return Math.min( - getSpaceCenter().raycastDistance(currentPoint.toTriplet(), targetDirection.toTriplet(), reference), - searchDistance); - } + public double raycastDistance( + Vector currentPoint, + Vector targetDirection, + SpaceCenter.ReferenceFrame reference, + double searchDistance) + throws RPCException { + return Math.min( + connectionManager + .getSpaceCenter() + .raycastDistance(currentPoint.toTriplet(), targetDirection.toTriplet(), reference), + searchDistance); + } - private Vector getPosOnSurface(Vector vector) throws RPCException { - return new Vector(currentBody.surfacePosition( - currentBody.latitudeAtPosition(vector.toTriplet(), orbitalReferenceFrame), - currentBody.longitudeAtPosition(vector.toTriplet(), orbitalReferenceFrame), orbitalReferenceFrame)); - } + private Vector getPosOnSurface(Vector vector) throws RPCException { + return new Vector( + vesselManager + .getCurrentVessel() + .currentBody + .surfacePosition( + vesselManager + .getCurrentVessel() + .currentBody + .latitudeAtPosition( + vector.toTriplet(), vesselManager.getCurrentVessel().orbitalReferenceFrame), + vesselManager + .getCurrentVessel() + .currentBody + .longitudeAtPosition( + vector.toTriplet(), vesselManager.getCurrentVessel().orbitalReferenceFrame), + vesselManager.getCurrentVessel().orbitalReferenceFrame)); + } - private Vector transformSurfToOrb(Vector vector) throws IOException, RPCException { - return new Vector( - getSpaceCenter().transformPosition(vector.toTriplet(), surfaceReferenceFrame, orbitalReferenceFrame)); - } + private Vector transformSurfToOrb(Vector vector) throws IOException, RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformPosition( + vector.toTriplet(), + vesselManager.getCurrentVessel().surfaceReferenceFrame, + vesselManager.getCurrentVessel().orbitalReferenceFrame)); + } - private Vector transformOrbToSurf(Vector vector) throws IOException, RPCException { - return new Vector( - getSpaceCenter().transformPosition(vector.toTriplet(), orbitalReferenceFrame, surfaceReferenceFrame)); - } + private Vector transformOrbToSurf(Vector vector) throws IOException, RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformPosition( + vector.toTriplet(), + vesselManager.getCurrentVessel().orbitalReferenceFrame, + vesselManager.getCurrentVessel().surfaceReferenceFrame)); + } } diff --git a/src/main/java/com/pesterenan/utils/Telemetry.java b/src/main/java/com/pesterenan/utils/Telemetry.java index ab46f77..96975ec 100644 --- a/src/main/java/com/pesterenan/utils/Telemetry.java +++ b/src/main/java/com/pesterenan/utils/Telemetry.java @@ -1,5 +1,10 @@ package com.pesterenan.utils; public enum Telemetry { - ALT_SURF, ALTITUDE, APOAPSIS, PERIAPSIS, HORZ_SPEED, VERT_SPEED, + ALT_SURF, + ALTITUDE, + APOAPSIS, + PERIAPSIS, + HORZ_SPEED, + VERT_SPEED, } diff --git a/src/main/java/com/pesterenan/utils/Utilities.java b/src/main/java/com/pesterenan/utils/Utilities.java index a310da6..1ef0687 100644 --- a/src/main/java/com/pesterenan/utils/Utilities.java +++ b/src/main/java/com/pesterenan/utils/Utilities.java @@ -4,79 +4,90 @@ public class Utilities { - public static double linearInterpolation(double start, double end, double value) { - return (1.0 - value) * start + value * end; - } + public static double linearInterpolation(double start, double end, double value) { + return (1.0 - value) * start + value * end; + } - public static Vector linearInterpolation(Vector start, Vector end, double value) { - double x = linearInterpolation(start.x, end.x, value); - double y = linearInterpolation(start.y, end.y, value); - double z = linearInterpolation(start.z, end.z, value); - return new Vector(x, y, z); - } + public static Vector linearInterpolation(Vector start, Vector end, double value) { + double x = linearInterpolation(start.x, end.x, value); + double y = linearInterpolation(start.y, end.y, value); + double z = linearInterpolation(start.z, end.z, value); + return new Vector(x, y, z); + } - public static double inverseLinearInterpolation(double start, double end, double value) { - return (value - start) / (end - start); - } + public static double inverseLinearInterpolation(double start, double end, double value) { + return (value - start) / (end - start); + } - public static Vector inverseLinearInterpolation(Vector start, Vector end, double value) { - double x = inverseLinearInterpolation(start.x, end.x, value); - double y = inverseLinearInterpolation(start.y, end.y, value); - double z = inverseLinearInterpolation(start.z, end.z, value); - return new Vector(x, y, z); - } + public static Vector inverseLinearInterpolation(Vector start, Vector end, double value) { + double x = inverseLinearInterpolation(start.x, end.x, value); + double y = inverseLinearInterpolation(start.y, end.y, value); + double z = inverseLinearInterpolation(start.z, end.z, value); + return new Vector(x, y, z); + } - public static double remap(double inputMin, double inputMax, double outputMin, double outputMax, double value, - boolean clampOutput) { - double between = inverseLinearInterpolation(inputMin, inputMax, value); - double remappedOutput = linearInterpolation(outputMin, outputMax, between); - return clampOutput ? clamp(remappedOutput, outputMin, outputMax) : remappedOutput; - } + public static double remap( + double inputMin, + double inputMax, + double outputMin, + double outputMax, + double value, + boolean clampOutput) { + double between = inverseLinearInterpolation(inputMin, inputMax, value); + double remappedOutput = linearInterpolation(outputMin, outputMax, between); + return clampOutput ? clamp(remappedOutput, outputMin, outputMax) : remappedOutput; + } - public static double clamp(double value, double minimum, double maximum) { - return Math.max(Math.min(value, maximum), minimum); - } + public static double clamp(double value, double minimum, double maximum) { + return Math.max(Math.min(value, maximum), minimum); + } - // Easing functions - public static double easeInCirc(double value) { - return 1 - Math.sqrt(1 - Math.pow(clamp(value, 0, 1), 2)); - } + // Easing functions + public static double easeInCirc(double value) { + return 1 - Math.sqrt(1 - Math.pow(clamp(value, 0, 1), 2)); + } - public static double easeInSine(double value) { - return 1 - Math.cos((value * Math.PI) / 2); - } + public static double easeInSine(double value) { + return 1 - Math.cos((value * Math.PI) / 2); + } - public static double easeInQuad(double value) { - return Math.pow(value, 2); - } + public static double easeInQuad(double value) { + return Math.pow(value, 2); + } - public static double easeInCubic(double value) { - return Math.pow(value, 3); - } + public static double easeInCubic(double value) { + return Math.pow(value, 3); + } - public static double easeInExpo(double value) { - return value == 0 ? 0 : Math.pow(2, 10 * value - 10); - } + public static double easeInExpo(double value) { + return value == 0 ? 0 : Math.pow(2, 10 * value - 10); + } - public static String convertToMetersMagnitudes(double meters) { - String decimalPlaces = "%.2f"; //$NON-NLS-1$ - if (meters >= 1000000000) { - return String.format(decimalPlaces + "Gm", meters / 1000000000); //$NON-NLS-1$ - } else if (meters >= 1000000) { - return String.format(decimalPlaces + "Mm", meters / 1000000); //$NON-NLS-1$ - } else if (meters >= 1000) { - return String.format(decimalPlaces + "km", meters / 1000); //$NON-NLS-1$ - } else { - return String.format(decimalPlaces + "m", meters); //$NON-NLS-1$ - } + public static String convertToMetersMagnitudes(double meters) { + String decimalPlaces = "%.2f"; // $NON-NLS-1$ + if (meters >= 1000000000) { + return String.format(decimalPlaces + "Gm", meters / 1000000000); // $NON-NLS-1$ + } else if (meters >= 1000000) { + return String.format(decimalPlaces + "Mm", meters / 1000000); // $NON-NLS-1$ + } else if (meters >= 1000) { + return String.format(decimalPlaces + "km", meters / 1000); // $NON-NLS-1$ + } else { + return String.format(decimalPlaces + "m", meters); // $NON-NLS-1$ } + } - public String formatElapsedTime(Double totalSeconds) { - int years = (totalSeconds.intValue() / 9201600); - int days = (totalSeconds.intValue() / 21600) % 426; - int hours = (totalSeconds.intValue() / 3600) % 6; - int minutes = (totalSeconds.intValue() % 3600) / 60; - int seconds = totalSeconds.intValue() % 60; - return String.format(Bundle.getString("pnl_tel_lbl_date_template"), years, days, hours, minutes, seconds); // $NON-NLS-1$ - } + public String formatElapsedTime(Double totalSeconds) { + int years = (totalSeconds.intValue() / 9201600); + int days = (totalSeconds.intValue() / 21600) % 426; + int hours = (totalSeconds.intValue() / 3600) % 6; + int minutes = (totalSeconds.intValue() % 3600) / 60; + int seconds = totalSeconds.intValue() % 60; + return String.format( + Bundle.getString("pnl_tel_lbl_date_template"), + years, + days, + hours, + minutes, + seconds); // $NON-NLS-1$ + } } diff --git a/src/main/java/com/pesterenan/utils/Vector.java b/src/main/java/com/pesterenan/utils/Vector.java index 2919433..975b8c6 100644 --- a/src/main/java/com/pesterenan/utils/Vector.java +++ b/src/main/java/com/pesterenan/utils/Vector.java @@ -4,234 +4,212 @@ public class Vector { - public double x = 0; - public double y = 0; - public double z = 0; - - public Vector() { - - } - - /** - * Cria um vetor informando valores X,Y,Z manualmente - * - * @param X - * - Valor eixo X - * @param Y - * - Valor eixo Y - * @param Z - * - Valor eixo Z - */ - public Vector(double X, double Y, double Z) { - this.x = X; - this.y = Y; - this.z = Z; - } - - /** - * Cria um vetor a partir de outro Vetor - * - * @param newVector - * - Outro Vetor - */ - public Vector(Vector newVector) { - this.x = newVector.x; - this.y = newVector.y; - this.z = newVector.z; - } - - /** - * Cria um vetor com valores de uma tupla (Triplet) - * - * @param triplet - * - Triplet com valores X,Y,Z em conjunto - */ - public Vector(Triplet triplet) { - this.x = triplet.getValue0(); - this.y = triplet.getValue1(); - this.z = triplet.getValue2(); - } - - /** - * Calcula o ângulo do vetor de direção informado - * - * @return - O ângulo da direção desse vetor, entre -180 a 180 graus. - */ - public double heading() { - return Math.toDegrees(Math.atan2(this.y, this.x)); - } - - public static double distance(Vector start, Vector end) { - return end.subtract(start).magnitude(); - } - - /** - * Calcula o Vetor da direção do ponto de origem até o alvo. - * - * @param start - * - Vetor contendo os componentes da posição do ponto de origem. - * @param end - * - Vetor contendo os componentes da posição do alvo. - * @return - Vetor com a soma dos valores do ponto de origem com os valores do - * alvo. - */ - public static Vector targetDirection(Vector start, Vector end) { - return end.subtract(start).normalize(); - } - - /** - * Calcula o Vetor da direção CONTRÁRIA do ponto de origem até o alvo. - * - * @param start - * - Tupla contendo os componentes da posição do ponto de origem. - * @param end - * - Tupla contendo os componentes da posição do alvo. - * @return - Vetor inverso, com a soma dos valores do ponto de origem com o - * negativo dos valores do alvo. - */ - public static Vector targetOppositeDirection(Vector start, Vector end) { - return end.subtract(start).multiply(-1).normalize(); - } - - /** - * Retorna um String com os valores do Vetor - * - * @return ex: "(X: 3.0, Y: 4.0, Z: 5.0)" - */ - @Override - public String toString() { - return String.format("( X: %.2f Y: %.2f Z: %.2f)", this.x, this.y, this.z); - } - - /** - * Modifica um vetor informando novos valores X,Y,Z - * - * @param X - * - Valor eixo X - * @param Y - * - Valor eixo Y - * @param Z - * - Valor eixo Z - */ - public void setVector(double X, double Y, double Z) { - this.x = X; - this.y = Y; - this.z = Z; - } - - /** - * @return Retorna um novo Vetor com os valores X e Y invertidos - */ - public Vector invertXY() { - return new Vector(y, x, z); - } - - /** - * Magnitude do Vetor - * - * @return Retorna a magnitude (comprimento) do Vetor no eixo X e Y. - */ - public double magnitudeXY() { - return Math.sqrt(x * x + y * y); - } - - /** - * Magnitude do Vetor - * - * @return Retorna a magnitude (comprimento) do Vetor em todos os eixos. - */ - public double magnitude() { - return Math.sqrt(x * x + y * y + z * z); - } - - /** - * Normalizar Vetor - * - * @return Retorna um novo Vetor normalizado (magnitude de 1). - */ - public Vector normalize() { - double m = magnitude(); - if (m != 0) { - return new Vector(x / m, y / m, z / m); - } - return new Vector(x, y, z); - } - - /** - * Soma os componentes de outro vetor com o vetor informado - * - * @param otherVector - * - Vetor para somar os componentes - * @return Novo vetor com a soma dos componentes dos dois - */ - - public double dotProduct(Vector otherVector) { - return (x * otherVector.x + y * otherVector.y + z * otherVector.z); - } - - public double determinant(Vector otherVector) { - return (x * otherVector.z - y * otherVector.y - z * otherVector.x); - } - - /** - * Soma os componentes de outro vetor com o vetor informado - * - * @param otherVector - * - Vetor para somar os componentes - * @return Novo vetor com a soma dos componentes dos dois - */ - public Vector sum(Vector otherVector) { - return new Vector(x + otherVector.x, y + otherVector.y, z + otherVector.z); - } - - /** - * Subtrai os componentes de outro vetor com o vetor informado - * - * @param otherVector - * - Vetor para subtrair os componentes - * @return Novo vetor com a subtração dos componentes dos dois - */ - public Vector subtract(Vector otherVector) { - return new Vector(x - otherVector.x, y - otherVector.y, z - otherVector.z); - } - - /** - * Multiplica os componentes desse vetor por uma escalar - * - * @param scalar - * - Fator para multiplicar os componentes - * @return Novo vetor com os componentes multiplicados pela escalar. Caso a - * escalar informada for 0, o Vetor retornado terá 0 como seus - * componentes. - */ - public Vector multiply(double scalar) { - if (scalar != 0) { - return new Vector(x * scalar, y * scalar, z * scalar); - } - return new Vector(0, 0, 0); - } - - /** - * Divide os componentes desse vetor por uma escalar - * - * @param scalar - * - Fator para dividir os componentes - * @return Novo vetor com os componentes divididos pela escalar. Caso a escalar - * informada for 0, o Vetor retornado terá 0 como seus componentes. - */ - public Vector divide(double scalar) { - if (scalar != 0) { - return new Vector(x / scalar, y / scalar, z / scalar); - } - return new Vector(0, 0, 0); - } - - /** - * Transforma um Vetor em uma tupla com os valores. - * - * @return - Nova tupla contendo os valores do vetor em seus componentes. - */ - public Triplet toTriplet() { - return new Triplet<>(this.x, this.y, this.z); - } + public double x = 0; + public double y = 0; + public double z = 0; + + public Vector() {} + + /** + * Cria um vetor informando valores X,Y,Z manualmente + * + * @param X - Valor eixo X + * @param Y - Valor eixo Y + * @param Z - Valor eixo Z + */ + public Vector(double X, double Y, double Z) { + this.x = X; + this.y = Y; + this.z = Z; + } + + /** + * Cria um vetor a partir de outro Vetor + * + * @param newVector - Outro Vetor + */ + public Vector(Vector newVector) { + this.x = newVector.x; + this.y = newVector.y; + this.z = newVector.z; + } + + /** + * Cria um vetor com valores de uma tupla (Triplet) + * + * @param triplet - Triplet com valores X,Y,Z em conjunto + */ + public Vector(Triplet triplet) { + this.x = triplet.getValue0(); + this.y = triplet.getValue1(); + this.z = triplet.getValue2(); + } + + /** + * Calcula o ângulo do vetor de direção informado + * + * @return - O ângulo da direção desse vetor, entre -180 a 180 graus. + */ + public double heading() { + return Math.toDegrees(Math.atan2(this.y, this.x)); + } + + public static double distance(Vector start, Vector end) { + return end.subtract(start).magnitude(); + } + + /** + * Calcula o Vetor da direção do ponto de origem até o alvo. + * + * @param start - Vetor contendo os componentes da posição do ponto de origem. + * @param end - Vetor contendo os componentes da posição do alvo. + * @return - Vetor com a soma dos valores do ponto de origem com os valores do alvo. + */ + public static Vector targetDirection(Vector start, Vector end) { + return end.subtract(start).normalize(); + } + + /** + * Calcula o Vetor da direção CONTRÁRIA do ponto de origem até o alvo. + * + * @param start - Tupla contendo os componentes da posição do ponto de origem. + * @param end - Tupla contendo os componentes da posição do alvo. + * @return - Vetor inverso, com a soma dos valores do ponto de origem com o negativo dos valores + * do alvo. + */ + public static Vector targetOppositeDirection(Vector start, Vector end) { + return end.subtract(start).multiply(-1).normalize(); + } + + /** + * Retorna um String com os valores do Vetor + * + * @return ex: "(X: 3.0, Y: 4.0, Z: 5.0)" + */ + @Override + public String toString() { + return String.format("( X: %.2f Y: %.2f Z: %.2f)", this.x, this.y, this.z); + } + + /** + * Modifica um vetor informando novos valores X,Y,Z + * + * @param X - Valor eixo X + * @param Y - Valor eixo Y + * @param Z - Valor eixo Z + */ + public void setVector(double X, double Y, double Z) { + this.x = X; + this.y = Y; + this.z = Z; + } + + /** + * @return Retorna um novo Vetor com os valores X e Y invertidos + */ + public Vector invertXY() { + return new Vector(y, x, z); + } + + /** + * Magnitude do Vetor + * + * @return Retorna a magnitude (comprimento) do Vetor no eixo X e Y. + */ + public double magnitudeXY() { + return Math.sqrt(x * x + y * y); + } + + /** + * Magnitude do Vetor + * + * @return Retorna a magnitude (comprimento) do Vetor em todos os eixos. + */ + public double magnitude() { + return Math.sqrt(x * x + y * y + z * z); + } + + /** + * Normalizar Vetor + * + * @return Retorna um novo Vetor normalizado (magnitude de 1). + */ + public Vector normalize() { + double m = magnitude(); + if (m != 0) { + return new Vector(x / m, y / m, z / m); + } + return new Vector(x, y, z); + } + + /** + * Soma os componentes de outro vetor com o vetor informado + * + * @param otherVector - Vetor para somar os componentes + * @return Novo vetor com a soma dos componentes dos dois + */ + public double dotProduct(Vector otherVector) { + return (x * otherVector.x + y * otherVector.y + z * otherVector.z); + } + + public double determinant(Vector otherVector) { + return (x * otherVector.z - y * otherVector.y - z * otherVector.x); + } + + /** + * Soma os componentes de outro vetor com o vetor informado + * + * @param otherVector - Vetor para somar os componentes + * @return Novo vetor com a soma dos componentes dos dois + */ + public Vector sum(Vector otherVector) { + return new Vector(x + otherVector.x, y + otherVector.y, z + otherVector.z); + } + + /** + * Subtrai os componentes de outro vetor com o vetor informado + * + * @param otherVector - Vetor para subtrair os componentes + * @return Novo vetor com a subtração dos componentes dos dois + */ + public Vector subtract(Vector otherVector) { + return new Vector(x - otherVector.x, y - otherVector.y, z - otherVector.z); + } + + /** + * Multiplica os componentes desse vetor por uma escalar + * + * @param scalar - Fator para multiplicar os componentes + * @return Novo vetor com os componentes multiplicados pela escalar. Caso a escalar informada for + * 0, o Vetor retornado terá 0 como seus componentes. + */ + public Vector multiply(double scalar) { + if (scalar != 0) { + return new Vector(x * scalar, y * scalar, z * scalar); + } + return new Vector(0, 0, 0); + } + + /** + * Divide os componentes desse vetor por uma escalar + * + * @param scalar - Fator para dividir os componentes + * @return Novo vetor com os componentes divididos pela escalar. Caso a escalar informada for 0, o + * Vetor retornado terá 0 como seus componentes. + */ + public Vector divide(double scalar) { + if (scalar != 0) { + return new Vector(x / scalar, y / scalar, z / scalar); + } + return new Vector(0, 0, 0); + } + + /** + * Transforma um Vetor em uma tupla com os valores. + * + * @return - Nova tupla contendo os valores do vetor em seus componentes. + */ + public Triplet toTriplet() { + return new Triplet<>(this.x, this.y, this.z); + } } diff --git a/src/main/java/com/pesterenan/utils/VersionUtil.java b/src/main/java/com/pesterenan/utils/VersionUtil.java index 728153b..053d9a9 100644 --- a/src/main/java/com/pesterenan/utils/VersionUtil.java +++ b/src/main/java/com/pesterenan/utils/VersionUtil.java @@ -3,28 +3,29 @@ import java.io.File; import java.io.FileReader; import java.io.InputStreamReader; - import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; public class VersionUtil { - public static String getVersion() { - String version = "N/A"; - MavenXpp3Reader reader = new MavenXpp3Reader(); - Model model; - try { - if ((new File("pom.xml")).exists()) { - model = reader.read(new FileReader("pom.xml")); - } else { - model = reader.read(new InputStreamReader( - VersionUtil.class.getResourceAsStream("/META-INF/maven/com.pesterenan/MechPeste/pom.xml"))); - } - version = model.getVersion(); - return version; - } catch (Exception e) { - e.printStackTrace(); - - } - return version; + public static String getVersion() { + String version = "N/A"; + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model; + try { + if ((new File("pom.xml")).exists()) { + model = reader.read(new FileReader("pom.xml")); + } else { + model = + reader.read( + new InputStreamReader( + VersionUtil.class.getResourceAsStream( + "/META-INF/maven/com.pesterenan/MechPeste/pom.xml"))); + } + version = model.getVersion(); + return version; + } catch (Exception e) { + e.printStackTrace(); } + return version; + } } diff --git a/src/main/java/com/pesterenan/views/AboutJFrame.java b/src/main/java/com/pesterenan/views/AboutJFrame.java index e3a5919..8c55628 100644 --- a/src/main/java/com/pesterenan/views/AboutJFrame.java +++ b/src/main/java/com/pesterenan/views/AboutJFrame.java @@ -1,83 +1,113 @@ package com.pesterenan.views; -import java.awt.Font; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.centerDialogOnScreen; +import static com.pesterenan.views.MainGui.createMarginComponent; +import com.pesterenan.utils.VersionUtil; +import java.awt.Desktop; +import java.awt.Font; +import java.io.IOException; +import java.net.URISyntaxException; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; +import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; - -import com.pesterenan.utils.VersionUtil; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.centerDialogOnScreen; -import static com.pesterenan.views.MainGui.createMarginComponent; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; public class AboutJFrame extends JDialog implements UIMethods { - private static final long serialVersionUID = 0L; - private JLabel lblMechpeste, lblAboutInfo; - private JButton btnOk; + private static final long serialVersionUID = 0L; + private JLabel lblMechpeste; + private JEditorPane editorPane; + private JButton btnOk; + + public AboutJFrame() { + initComponents(); + setupComponents(); + layoutComponents(); + } - public AboutJFrame() { - initComponents(); - setupComponents(); - layoutComponents(); - } + @Override + public void initComponents() { + // Labels: + lblMechpeste = new JLabel("MechPeste - v." + VersionUtil.getVersion()); + editorPane = new JEditorPane(); - @Override - public void initComponents() { - // Labels: - lblMechpeste = new JLabel("MechPeste - v." + VersionUtil.getVersion()); - lblAboutInfo = new JLabel( - "Esse app foi desenvolvido com o intuito de auxiliar o controle de naves
no game Kerbal Space Program.

" - + "Não há garantias sobre o controle exato do app, portanto fique atento
" - + "para retomar o controle quando necessário.

" + "Feito por: Renan Torres
" - + "Visite meu canal no Youtube! - https://www.youtube.com/@Pesterenan"); + // Buttons: + btnOk = new JButton("OK"); + } - // Buttons: - btnOk = new JButton("OK"); - } + @Override + public void setupComponents() { + // Main Panel setup: + setTitle("Sobre"); + setBounds(centerDialogOnScreen()); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setResizable(false); + setAlwaysOnTop(true); + setModalityType(ModalityType.APPLICATION_MODAL); - @Override - public void setupComponents() { - // Main Panel setup: - setTitle("MechPeste - por Pesterenan"); - setBounds(centerDialogOnScreen()); - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - setResizable(false); - setAlwaysOnTop(true); - setModalityType(ModalityType.APPLICATION_MODAL); + // Setting-up components: + lblMechpeste.setFont(new Font("Trajan Pro", Font.BOLD, 18)); + lblMechpeste.setAlignmentX(CENTER_ALIGNMENT); - // Setting-up components: - lblMechpeste.setFont(new Font("Trajan Pro", Font.BOLD, 18)); - lblMechpeste.setAlignmentX(CENTER_ALIGNMENT); - lblAboutInfo.setAlignmentX(CENTER_ALIGNMENT); + editorPane.setContentType("text/html"); + editorPane.setText( + "" + + "Esse app foi desenvolvido com o intuito de auxiliar o controle de naves
no game Kerbal Space Program.

" + + "Não há garantias sobre o controle exato do app, portanto fique atento
" + + "para retomar o controle quando necessário.

" + + "Feito por: Renan Torres
" + + "Visite meu canal no Youtube! - https://www.youtube.com/@Pesterenan

" + + "Gostou do app? Considere fazer uma doação!
" + + "LivePix: https://livepix.gg/pesterenan

" + + "Código fonte: https://github.com/Pesterenan/MechPeste-Java" + + ""); + editorPane.setEditable(false); + editorPane.setOpaque(false); + editorPane.addHyperlinkListener( + new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (IOException | URISyntaxException e1) { + e1.printStackTrace(); + } + } + } + }); - btnOk.addActionListener(e -> { - this.dispose(); + btnOk.addActionListener( + e -> { + this.dispose(); }); - btnOk.setPreferredSize(BTN_DIMENSION); - btnOk.setMaximumSize(BTN_DIMENSION); - btnOk.setAlignmentX(CENTER_ALIGNMENT); - } + btnOk.setPreferredSize(BTN_DIMENSION); + btnOk.setMaximumSize(BTN_DIMENSION); + btnOk.setAlignmentX(CENTER_ALIGNMENT); + } - @Override - public void layoutComponents() { - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); - pnlMain.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); - pnlMain.add(createMarginComponent(10, 10)); - pnlMain.add(lblMechpeste); - pnlMain.add(createMarginComponent(10, 10)); - pnlMain.add(lblAboutInfo); - pnlMain.add(Box.createVerticalGlue()); - pnlMain.add(btnOk); - pnlMain.add(createMarginComponent(10, 10)); + @Override + public void layoutComponents() { + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); + pnlMain.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); + pnlMain.add(createMarginComponent(10, 10)); + pnlMain.add(lblMechpeste); + pnlMain.add(createMarginComponent(10, 10)); + pnlMain.add(editorPane); + pnlMain.add(Box.createVerticalGlue()); + pnlMain.add(btnOk); + pnlMain.add(createMarginComponent(10, 10)); - getContentPane().add(pnlMain); - setVisible(true); - } + getContentPane().add(pnlMain); + pack(); + setVisible(true); + } } diff --git a/src/main/java/com/pesterenan/views/ChangeVesselDialog.java b/src/main/java/com/pesterenan/views/ChangeVesselDialog.java index 188801c..507986e 100644 --- a/src/main/java/com/pesterenan/views/ChangeVesselDialog.java +++ b/src/main/java/com/pesterenan/views/ChangeVesselDialog.java @@ -1,153 +1,165 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import javax.swing.*; +import com.pesterenan.model.VesselManager; +import java.awt.event.ActionEvent; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; -import java.awt.event.ActionEvent; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; public class ChangeVesselDialog extends JDialog implements UIMethods { - private static final long serialVersionUID = 1L; - private JLabel lblPanelInfo, lblVesselStatus; - private JList listActiveVessels; - private JButton btnChangeToVessel; - private JRadioButton rbClosestVessels, rbOnSameBody, rbAllVessels; - - public ChangeVesselDialog() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblPanelInfo = new JLabel("Use esse painel para verificar e trocar para as naves próximas."); - lblVesselStatus = new JLabel("Selecione uma nave na lista."); - - // Buttons: - btnChangeToVessel = new JButton("Mudar para"); - rbClosestVessels = new JRadioButton("Naves próximas (10km)"); - rbOnSameBody = new JRadioButton("No mesmo corpo celeste"); - rbAllVessels = new JRadioButton("Todas as naves"); - - // Misc: - listActiveVessels = new JList<>(MechPeste.getActiveVessels("closest")); - listActiveVessels.setToolTipText("Aqui são mostradas as naves próximas de acordo com o filtro da esquerda."); + private static final long serialVersionUID = 1L; + private JLabel lblPanelInfo, lblVesselStatus; + private JList listActiveVessels; + private JButton btnChangeToVessel; + private JRadioButton rbClosestVessels, rbOnSameBody, rbAllVessels; + private VesselManager vesselManager; + + public ChangeVesselDialog(VesselManager vesselManager) { + this.vesselManager = vesselManager; + initComponents(); + setupComponents(); + layoutComponents(); + } + + @Override + public void initComponents() { + // Labels: + lblPanelInfo = new JLabel("Use esse painel para verificar e trocar para as naves próximas."); + lblVesselStatus = new JLabel("Selecione uma nave na lista."); + + // Buttons: + btnChangeToVessel = new JButton("Mudar para"); + rbClosestVessels = new JRadioButton("Naves próximas (10km)"); + rbOnSameBody = new JRadioButton("No mesmo corpo celeste"); + rbAllVessels = new JRadioButton("Todas as naves"); + + // Misc: + listActiveVessels = new JList<>(vesselManager.getActiveVessels("closest")); + listActiveVessels.setToolTipText( + "Aqui são mostradas as naves próximas de acordo com o filtro da esquerda."); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setTitle("Troca de naves"); + setBounds(MainGui.centerDialogOnScreen()); + setModal(true); + setModalityType(ModalityType.MODELESS); + setResizable(false); + setAlwaysOnTop(true); + + // Setting-up components: + btnChangeToVessel.addActionListener(this::handleChangeToVessel); + btnChangeToVessel.setPreferredSize(BTN_DIMENSION); + btnChangeToVessel.setMaximumSize(BTN_DIMENSION); + + rbClosestVessels.setSelected(true); + rbClosestVessels.addActionListener(this::handleBuildVesselList); + rbOnSameBody.addActionListener(this::handleBuildVesselList); + rbAllVessels.addActionListener(this::handleBuildVesselList); + rbClosestVessels.setActionCommand("closest"); + rbOnSameBody.setActionCommand("samebody"); + rbAllVessels.setActionCommand("all"); + + ButtonGroup rbBtnGroup = new ButtonGroup(); + rbBtnGroup.add(rbClosestVessels); + rbBtnGroup.add(rbOnSameBody); + rbBtnGroup.add(rbAllVessels); + + listActiveVessels.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + listActiveVessels.addListSelectionListener(e -> handleListActiveVesselsValueChanged(e)); + } + + @Override + public void layoutComponents() { + + JPanel pnlSearchArea = new JPanel(); + pnlSearchArea.setBorder(new TitledBorder(null, "\u00C1rea de procura:")); + pnlSearchArea.setLayout(new BoxLayout(pnlSearchArea, BoxLayout.Y_AXIS)); + pnlSearchArea.add(rbClosestVessels); + pnlSearchArea.add(rbOnSameBody); + pnlSearchArea.add(rbAllVessels); + + JPanel pnlOptions = new JPanel(); + pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); + pnlOptions.add(pnlSearchArea); + pnlOptions.add(Box.createVerticalStrut(10)); + pnlOptions.add(btnChangeToVessel); + btnChangeToVessel.setAlignmentX(LEFT_ALIGNMENT); + pnlSearchArea.setAlignmentX(LEFT_ALIGNMENT); + + JPanel pnlScroll = new JPanel(); + pnlScroll.setLayout(new BoxLayout(pnlScroll, BoxLayout.Y_AXIS)); + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setViewportView(listActiveVessels); + pnlScroll.add(Box.createVerticalStrut(6)); + pnlScroll.add(scrollPane); + pnlScroll.add(Box.createHorizontalStrut(190)); + + JPanel pnlOptionsAndList = new JPanel(); + pnlOptionsAndList.setLayout(new BoxLayout(pnlOptionsAndList, BoxLayout.X_AXIS)); + pnlOptions.setAlignmentY(TOP_ALIGNMENT); + pnlScroll.setAlignmentY(TOP_ALIGNMENT); + pnlOptionsAndList.add(pnlOptions); + pnlOptionsAndList.add(Box.createHorizontalStrut(5)); + pnlOptionsAndList.add(pnlScroll); + + JPanel pnlStatus = new JPanel(); + pnlStatus.setLayout(new BoxLayout(pnlStatus, BoxLayout.X_AXIS)); + pnlStatus.add(lblVesselStatus); + pnlStatus.add(Box.createHorizontalGlue()); + pnlStatus.add(Box.createVerticalStrut(10)); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); + pnlMain.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); + lblPanelInfo.setAlignmentX(CENTER_ALIGNMENT); + pnlMain.add(lblPanelInfo); + pnlOptionsAndList.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlOptionsAndList); + pnlMain.add(Box.createVerticalStrut(5)); + pnlMain.add(pnlStatus); + pnlMain.add(Box.createVerticalStrut(10)); + + getContentPane().add(pnlMain); + setVisible(true); + } + + protected void handleChangeToVessel(ActionEvent e) { + if (listActiveVessels.getSelectedIndex() == -1) { + return; } - - @Override - public void setupComponents() { - // Main Panel setup: - setTitle("Troca de naves"); - setBounds(MainGui.centerDialogOnScreen()); - setModal(true); - setModalityType(ModalityType.MODELESS); - setResizable(false); - setAlwaysOnTop(true); - - // Setting-up components: - btnChangeToVessel.addActionListener(this::handleChangeToVessel); - btnChangeToVessel.setPreferredSize(BTN_DIMENSION); - btnChangeToVessel.setMaximumSize(BTN_DIMENSION); - - rbClosestVessels.setSelected(true); - rbClosestVessels.addActionListener(this::handleBuildVesselList); - rbOnSameBody.addActionListener(this::handleBuildVesselList); - rbAllVessels.addActionListener(this::handleBuildVesselList); - rbClosestVessels.setActionCommand("closest"); - rbOnSameBody.setActionCommand("samebody"); - rbAllVessels.setActionCommand("all"); - - ButtonGroup rbBtnGroup = new ButtonGroup(); - rbBtnGroup.add(rbClosestVessels); - rbBtnGroup.add(rbOnSameBody); - rbBtnGroup.add(rbAllVessels); - - listActiveVessels.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - listActiveVessels.addListSelectionListener(e -> handleListActiveVesselsValueChanged(e)); - } - - @Override - public void layoutComponents() { - - JPanel pnlSearchArea = new JPanel(); - pnlSearchArea.setBorder(new TitledBorder(null, "\u00C1rea de procura:")); - pnlSearchArea.setLayout(new BoxLayout(pnlSearchArea, BoxLayout.Y_AXIS)); - pnlSearchArea.add(rbClosestVessels); - pnlSearchArea.add(rbOnSameBody); - pnlSearchArea.add(rbAllVessels); - - JPanel pnlOptions = new JPanel(); - pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); - pnlOptions.add(pnlSearchArea); - pnlOptions.add(Box.createVerticalStrut(10)); - pnlOptions.add(btnChangeToVessel); - btnChangeToVessel.setAlignmentX(LEFT_ALIGNMENT); - pnlSearchArea.setAlignmentX(LEFT_ALIGNMENT); - - JPanel pnlScroll = new JPanel(); - pnlScroll.setLayout(new BoxLayout(pnlScroll, BoxLayout.Y_AXIS)); - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setViewportView(listActiveVessels); - pnlScroll.add(Box.createVerticalStrut(6)); - pnlScroll.add(scrollPane); - pnlScroll.add(Box.createHorizontalStrut(190)); - - JPanel pnlOptionsAndList = new JPanel(); - pnlOptionsAndList.setLayout(new BoxLayout(pnlOptionsAndList, BoxLayout.X_AXIS)); - pnlOptions.setAlignmentY(TOP_ALIGNMENT); - pnlScroll.setAlignmentY(TOP_ALIGNMENT); - pnlOptionsAndList.add(pnlOptions); - pnlOptionsAndList.add(Box.createHorizontalStrut(5)); - pnlOptionsAndList.add(pnlScroll); - - JPanel pnlStatus = new JPanel(); - pnlStatus.setLayout(new BoxLayout(pnlStatus, BoxLayout.X_AXIS)); - pnlStatus.add(lblVesselStatus); - pnlStatus.add(Box.createHorizontalGlue()); - pnlStatus.add(Box.createVerticalStrut(10)); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); - pnlMain.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); - lblPanelInfo.setAlignmentX(CENTER_ALIGNMENT); - pnlMain.add(lblPanelInfo); - pnlOptionsAndList.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlOptionsAndList); - pnlMain.add(Box.createVerticalStrut(5)); - pnlMain.add(pnlStatus); - pnlMain.add(Box.createVerticalStrut(10)); - - getContentPane().add(pnlMain); - setVisible(true); - } - - protected void handleChangeToVessel(ActionEvent e) { - if (listActiveVessels.getSelectedIndex() == -1) { - return; - } - int vesselHashCode = Integer.parseInt(listActiveVessels.getSelectedValue().split(" - ")[0]); - MechPeste.changeToVessel(vesselHashCode); - } - - protected void handleBuildVesselList(ActionEvent e) { - listActiveVessels.setModel(MechPeste.getActiveVessels(e.getActionCommand())); - } - - protected void handleListActiveVesselsValueChanged(ListSelectionEvent e) { - String selectedValue = listActiveVessels.getSelectedValue(); - if (selectedValue == null) { - lblVesselStatus.setText("Selecione uma nave da lista."); - btnChangeToVessel.setEnabled(false); - return; - } - int vesselId = Integer.parseInt(selectedValue.split(" - ")[0]); - lblVesselStatus.setText(MechPeste.getVesselInfo(vesselId)); - btnChangeToVessel.setEnabled(true); + int vesselHashCode = Integer.parseInt(listActiveVessels.getSelectedValue().split(" - ")[0]); + vesselManager.changeToVessel(vesselHashCode); + } + + protected void handleBuildVesselList(ActionEvent e) { + listActiveVessels.setModel(vesselManager.getActiveVessels(e.getActionCommand())); + } + + protected void handleListActiveVesselsValueChanged(ListSelectionEvent e) { + String selectedValue = listActiveVessels.getSelectedValue(); + if (selectedValue == null) { + lblVesselStatus.setText("Selecione uma nave da lista."); + btnChangeToVessel.setEnabled(false); + return; } + int vesselId = Integer.parseInt(selectedValue.split(" - ")[0]); + lblVesselStatus.setText(vesselManager.getVesselInfo(vesselId)); + btnChangeToVessel.setEnabled(true); + } } diff --git a/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java b/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java index a47af7e..28a1bf8 100644 --- a/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java @@ -1,442 +1,463 @@ package com.pesterenan.views; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + import com.pesterenan.MechPeste; +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; - -import krpc.client.RPCException; -import krpc.client.services.SpaceCenter.Node; -import krpc.client.services.SpaceCenter.Orbit; -import krpc.client.services.SpaceCenter.Vessel; -import krpc.client.services.SpaceCenter.VesselSituation; - -import javax.swing.*; -import javax.swing.border.TitledBorder; - -import org.javatuples.Pair; - -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; -import java.util.Hashtable; import java.util.Locale; import java.util.Map; - -import static com.pesterenan.views.MainGui.PNL_DIMENSION; -import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.border.TitledBorder; +import krpc.client.RPCException; +import krpc.client.services.SpaceCenter.Node; +import krpc.client.services.SpaceCenter.Orbit; +import krpc.client.services.SpaceCenter.Vessel; +import krpc.client.services.SpaceCenter.VesselSituation; public class CreateManeuverJPanel extends JPanel implements ActionListener, UIMethods { - private static JLabel lblManeuverInfo; - private static JButton btnCreateManeuver, btnDeleteManeuver, btnBack, btnAp, btnPe, btnAN, btnDN; - private static JButton btnIncrease, btnDecrease, btnNextOrbit, btnPrevOrbit; - private static JSlider sldScale; - private static JList listCurrentManeuvers; - private static int selectedManeuverIndex = 0; - private static JRadioButton rbPrograde, rbNormal, rbRadial, rbTime; - private static ButtonGroup bgManeuverType; - private static Map sliderValues = new HashMap<>(); - private final ControlePID ctrlManeuver = new ControlePID(); - - public CreateManeuverJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblManeuverInfo = new JLabel(""); - - // Buttons: - btnCreateManeuver = new JButton("Criar"); - btnDeleteManeuver = new JButton("Apagar"); - btnAp = new JButton("AP"); - btnPe = new JButton("PE"); - btnAN = new JButton("NA"); - btnDN = new JButton("ND"); - btnBack = new JButton(Bundle.getString("pnl_mnv_btn_back")); - btnIncrease = new JButton("+"); - btnDecrease = new JButton("-"); - btnNextOrbit = new JButton(">"); - btnPrevOrbit = new JButton("<"); - - // Radio buttons: - rbPrograde = new JRadioButton("Pro"); - rbNormal = new JRadioButton("Nor"); - rbRadial = new JRadioButton("Rad"); - rbTime = new JRadioButton("Tmp"); - - // Misc: - listCurrentManeuvers = new JList<>(); - sldScale = new JSlider(JSlider.VERTICAL, 0, 5, 2); - bgManeuverType = new ButtonGroup(); - sliderValues.put(0, 0.01f); - sliderValues.put(1, 0.1f); - sliderValues.put(2, 1f); - sliderValues.put(3, 10f); - sliderValues.put(4, 100f); - sliderValues.put(5, 1000f); - - ctrlManeuver.setOutput(-100, 100); - } - - @Override - public void setupComponents() { - // Setting-up components: - btnCreateManeuver.addActionListener(this); - btnCreateManeuver.setMaximumSize(BTN_DIMENSION); - btnCreateManeuver.setPreferredSize(BTN_DIMENSION); - btnDeleteManeuver.addActionListener(this); - btnDeleteManeuver.setMaximumSize(BTN_DIMENSION); - btnDeleteManeuver.setPreferredSize(BTN_DIMENSION); - btnAp.addActionListener(this); - btnAp.setActionCommand("apoapsis"); - btnPe.addActionListener(this); - btnPe.setActionCommand("periapsis"); - btnAN.addActionListener(this); - btnAN.setActionCommand("ascending"); - btnDN.addActionListener(this); - btnDN.setActionCommand("descending"); - btnBack.addActionListener(this); - btnBack.setMaximumSize(BTN_DIMENSION); - btnBack.setPreferredSize(BTN_DIMENSION); - btnIncrease.setActionCommand("increase"); - btnIncrease.addActionListener(this); - btnIncrease.addMouseWheelListener(e -> { - int rotation = e.getWheelRotation(); - if (rotation > 0) { - changeManeuverDeltaV(btnDecrease.getActionCommand()); - } else { - changeManeuverDeltaV(btnIncrease.getActionCommand()); - } + private static JLabel lblManeuverInfo; + private static JButton btnCreateManeuver, btnDeleteManeuver, btnBack, btnAp, btnPe, btnAN, btnDN; + private static JButton btnIncrease, btnDecrease, btnNextOrbit, btnPrevOrbit; + private static JSlider sldScale; + private static JList listCurrentManeuvers; + private static int selectedManeuverIndex = 0; + private static JRadioButton rbPrograde, rbNormal, rbRadial, rbTime; + private static ButtonGroup bgManeuverType; + private static Map sliderValues = new HashMap<>(); + private final ControlePID ctrlManeuver = new ControlePID(); + private VesselManager vesselManager; + private StatusDisplay statusDisplay; + + public CreateManeuverJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + } + + @Override + public void initComponents() { + // Labels: + lblManeuverInfo = new JLabel(""); + + // Buttons: + btnCreateManeuver = new JButton("Criar"); + btnDeleteManeuver = new JButton("Apagar"); + btnAp = new JButton("AP"); + btnPe = new JButton("PE"); + btnAN = new JButton("NA"); + btnDN = new JButton("ND"); + btnBack = new JButton(Bundle.getString("pnl_mnv_btn_back")); + btnIncrease = new JButton("+"); + btnDecrease = new JButton("-"); + btnNextOrbit = new JButton(">"); + btnPrevOrbit = new JButton("<"); + + // Radio buttons: + rbPrograde = new JRadioButton("Pro"); + rbNormal = new JRadioButton("Nor"); + rbRadial = new JRadioButton("Rad"); + rbTime = new JRadioButton("Tmp"); + + // Misc: + listCurrentManeuvers = new JList<>(); + sldScale = new JSlider(JSlider.VERTICAL, 0, 5, 2); + bgManeuverType = new ButtonGroup(); + sliderValues.put(0, 0.01f); + sliderValues.put(1, 0.1f); + sliderValues.put(2, 1f); + sliderValues.put(3, 10f); + sliderValues.put(4, 100f); + sliderValues.put(5, 1000f); + + ctrlManeuver.setOutput(-100, 100); + } + + @Override + public void setupComponents() { + // Setting-up components: + btnCreateManeuver.addActionListener(this); + btnCreateManeuver.setMaximumSize(BTN_DIMENSION); + btnCreateManeuver.setPreferredSize(BTN_DIMENSION); + btnDeleteManeuver.addActionListener(this); + btnDeleteManeuver.setMaximumSize(BTN_DIMENSION); + btnDeleteManeuver.setPreferredSize(BTN_DIMENSION); + btnAp.addActionListener(this); + btnAp.setActionCommand("apoapsis"); + btnPe.addActionListener(this); + btnPe.setActionCommand("periapsis"); + btnAN.addActionListener(this); + btnAN.setActionCommand("ascending"); + btnDN.addActionListener(this); + btnDN.setActionCommand("descending"); + btnBack.addActionListener(this); + btnBack.setMaximumSize(BTN_DIMENSION); + btnBack.setPreferredSize(BTN_DIMENSION); + btnIncrease.setActionCommand("increase"); + btnIncrease.addActionListener(this); + btnIncrease.addMouseWheelListener( + e -> { + int rotation = e.getWheelRotation(); + if (rotation > 0) { + changeManeuverDeltaV(btnDecrease.getActionCommand()); + } else { + changeManeuverDeltaV(btnIncrease.getActionCommand()); + } }); - btnIncrease.setMaximumSize(new Dimension(70, 26)); - btnIncrease.setPreferredSize(new Dimension(70, 26)); - btnIncrease.setMargin(new Insets(0, 0, 0, 0)); - btnDecrease.setActionCommand("decrease"); - btnDecrease.addActionListener(this); - btnDecrease.addMouseWheelListener(e -> { - int rotation = e.getWheelRotation(); - if (rotation > 0) { - changeManeuverDeltaV(btnIncrease.getActionCommand()); - } else { - changeManeuverDeltaV(btnDecrease.getActionCommand()); - } + btnIncrease.setMaximumSize(new Dimension(70, 26)); + btnIncrease.setPreferredSize(new Dimension(70, 26)); + btnIncrease.setMargin(new Insets(0, 0, 0, 0)); + btnDecrease.setActionCommand("decrease"); + btnDecrease.addActionListener(this); + btnDecrease.addMouseWheelListener( + e -> { + int rotation = e.getWheelRotation(); + if (rotation > 0) { + changeManeuverDeltaV(btnIncrease.getActionCommand()); + } else { + changeManeuverDeltaV(btnDecrease.getActionCommand()); + } }); - btnDecrease.setMaximumSize(new Dimension(70, 26)); - btnDecrease.setPreferredSize(new Dimension(70, 26)); - btnDecrease.setMargin(new Insets(0, 0, 0, 0)); - btnNextOrbit.setActionCommand("next_orbit"); - btnNextOrbit.addActionListener(this); - btnNextOrbit.setMaximumSize(new Dimension(35, 26)); - btnNextOrbit.setPreferredSize(new Dimension(35, 26)); - btnNextOrbit.setMargin(new Insets(0, 0, 0, 0)); - btnPrevOrbit.setActionCommand("prev_orbit"); - btnPrevOrbit.addActionListener(this); - btnPrevOrbit.setMaximumSize(new Dimension(35, 26)); - btnPrevOrbit.setPreferredSize(new Dimension(35, 26)); - btnPrevOrbit.setMargin(new Insets(0, 0, 0, 0)); - - rbPrograde.setActionCommand("prograde"); - rbPrograde.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); - rbNormal.setActionCommand("normal"); - rbNormal.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); - rbRadial.setActionCommand("radial"); - rbRadial.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); - rbTime.setActionCommand("time"); - rbTime.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); - bgManeuverType.add(rbPrograde); - bgManeuverType.add(rbNormal); - bgManeuverType.add(rbRadial); - bgManeuverType.add(rbTime); - bgManeuverType.setSelected(rbPrograde.getModel(), true); - - listCurrentManeuvers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - listCurrentManeuvers.addListSelectionListener(e -> selectedManeuverIndex = e.getFirstIndex()); - - sldScale.setSnapToTicks(true); - sldScale.setPaintTicks(true); - sldScale.setMajorTickSpacing(1); - sldScale.setMinorTickSpacing(1); - sldScale.setPaintLabels(false); - sldScale.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); - sldScale.addMouseWheelListener(e -> { - int rotation = e.getWheelRotation(); - if (rotation < 0) { - sldScale.setValue(sldScale.getValue() + sldScale.getMinorTickSpacing()); - } else { - sldScale.setValue(sldScale.getValue() - sldScale.getMinorTickSpacing()); - } + btnDecrease.setMaximumSize(new Dimension(70, 26)); + btnDecrease.setPreferredSize(new Dimension(70, 26)); + btnDecrease.setMargin(new Insets(0, 0, 0, 0)); + btnNextOrbit.setActionCommand("next_orbit"); + btnNextOrbit.addActionListener(this); + btnNextOrbit.setMaximumSize(new Dimension(35, 26)); + btnNextOrbit.setPreferredSize(new Dimension(35, 26)); + btnNextOrbit.setMargin(new Insets(0, 0, 0, 0)); + btnPrevOrbit.setActionCommand("prev_orbit"); + btnPrevOrbit.addActionListener(this); + btnPrevOrbit.setMaximumSize(new Dimension(35, 26)); + btnPrevOrbit.setPreferredSize(new Dimension(35, 26)); + btnPrevOrbit.setMargin(new Insets(0, 0, 0, 0)); + + rbPrograde.setActionCommand("prograde"); + rbPrograde.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); + rbNormal.setActionCommand("normal"); + rbNormal.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); + rbRadial.setActionCommand("radial"); + rbRadial.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); + rbTime.setActionCommand("time"); + rbTime.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); + bgManeuverType.add(rbPrograde); + bgManeuverType.add(rbNormal); + bgManeuverType.add(rbRadial); + bgManeuverType.add(rbTime); + bgManeuverType.setSelected(rbPrograde.getModel(), true); + + listCurrentManeuvers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + listCurrentManeuvers.addListSelectionListener(e -> selectedManeuverIndex = e.getFirstIndex()); + + sldScale.setSnapToTicks(true); + sldScale.setPaintTicks(true); + sldScale.setMajorTickSpacing(1); + sldScale.setMinorTickSpacing(1); + sldScale.setPaintLabels(false); + sldScale.addChangeListener(e -> handleChangeButtonText(sldScale.getValue())); + sldScale.addMouseWheelListener( + e -> { + int rotation = e.getWheelRotation(); + if (rotation < 0) { + sldScale.setValue(sldScale.getValue() + sldScale.getMinorTickSpacing()); + } else { + sldScale.setValue(sldScale.getValue() - sldScale.getMinorTickSpacing()); + } }); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + JPanel pnlPositionManeuver = new JPanel(); + pnlPositionManeuver.setLayout(new BoxLayout(pnlPositionManeuver, BoxLayout.X_AXIS)); + pnlPositionManeuver.setBorder(new TitledBorder("Posicionar manobra no:")); + pnlPositionManeuver.add(Box.createHorizontalGlue()); + pnlPositionManeuver.add(btnAp); + pnlPositionManeuver.add(btnPe); + pnlPositionManeuver.add(btnAN); + pnlPositionManeuver.add(btnDN); + pnlPositionManeuver.add(Box.createHorizontalGlue()); + + JPanel pnlRadioButtons = new JPanel(new GridLayout(4, 1)); + pnlRadioButtons.setMaximumSize(new Dimension(50, 100)); + pnlRadioButtons.add(rbPrograde); + pnlRadioButtons.add(rbNormal); + pnlRadioButtons.add(rbRadial); + pnlRadioButtons.add(rbTime); + + JPanel pnlSlider = new JPanel(); + pnlSlider.setLayout(new BoxLayout(pnlSlider, BoxLayout.Y_AXIS)); + pnlSlider.setAlignmentX(Component.LEFT_ALIGNMENT); + pnlSlider.setBorder(new TitledBorder("Escala:")); + pnlSlider.add(sldScale); + pnlSlider.add(Box.createHorizontalStrut(40)); + + JPanel pnlOrbitControl = new JPanel(); + pnlOrbitControl.setLayout(new BoxLayout(pnlOrbitControl, BoxLayout.X_AXIS)); + pnlOrbitControl.setAlignmentX(Component.LEFT_ALIGNMENT); + pnlOrbitControl.add(btnPrevOrbit); + pnlOrbitControl.add(btnNextOrbit); + + JPanel pnlManeuverButtons = new JPanel(); + pnlManeuverButtons.setLayout(new BoxLayout(pnlManeuverButtons, BoxLayout.Y_AXIS)); + pnlManeuverButtons.setAlignmentX(Component.LEFT_ALIGNMENT); + pnlManeuverButtons.add(btnIncrease); + pnlManeuverButtons.add(pnlOrbitControl); + pnlManeuverButtons.add(btnDecrease); + + JPanel pnlManeuverController = new JPanel(); + pnlManeuverController.setLayout(new BoxLayout(pnlManeuverController, BoxLayout.X_AXIS)); + pnlManeuverController.setBorder(new TitledBorder("Controlador de Manobra:")); + pnlManeuverController.setMaximumSize(new Dimension(180, 300)); + pnlManeuverController.add(pnlRadioButtons); + pnlManeuverController.add(pnlSlider); + pnlManeuverController.add(pnlManeuverButtons); + + JPanel pnlManeuverInfo = new JPanel(); + pnlManeuverInfo.setLayout(new BoxLayout(pnlManeuverInfo, BoxLayout.Y_AXIS)); + pnlManeuverInfo.setBorder(new TitledBorder("Info. Manobra:")); + pnlManeuverInfo.setMaximumSize(new Dimension(300, 300)); + + JPanel pnlMCpnlAP = new JPanel(); + pnlMCpnlAP.setLayout(new BoxLayout(pnlMCpnlAP, BoxLayout.X_AXIS)); + pnlMCpnlAP.add(pnlManeuverController); + pnlMCpnlAP.add(pnlManeuverInfo); + + JPanel pnlControls = new JPanel(); + pnlControls.setLayout(new BoxLayout(pnlControls, BoxLayout.Y_AXIS)); + pnlControls.add(pnlPositionManeuver); + pnlControls.add(pnlMCpnlAP); + + JPanel pnlManeuverList = new JPanel(); + pnlManeuverList.setLayout(new BoxLayout(pnlManeuverList, BoxLayout.Y_AXIS)); + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setViewportView(listCurrentManeuvers); + pnlManeuverList.add(scrollPane); + pnlManeuverList.add(btnCreateManeuver); + pnlManeuverList.add(btnDeleteManeuver); + + JPanel pnlOptions = new JPanel(); + pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); + pnlOptions.setBorder(new TitledBorder("Lista de Manobras:")); + pnlOptions.add(pnlManeuverList); + pnlOptions.setPreferredSize(new Dimension(110, 300)); + pnlOptions.setMaximumSize(new Dimension(110, 300)); + + JPanel pnlOptionsAndList = new JPanel(); + pnlOptionsAndList.setLayout(new BoxLayout(pnlOptionsAndList, BoxLayout.X_AXIS)); + pnlControls.setAlignmentY(TOP_ALIGNMENT); + pnlOptions.setAlignmentY(TOP_ALIGNMENT); + pnlOptionsAndList.add(pnlOptions); + pnlOptionsAndList.add(Box.createHorizontalStrut(5)); + pnlOptionsAndList.add(pnlControls); + + JPanel pnlBackButton = new JPanel(); + pnlBackButton.setLayout(new BoxLayout(pnlBackButton, BoxLayout.X_AXIS)); + pnlBackButton.add(lblManeuverInfo); + pnlBackButton.add(Box.createHorizontalGlue()); + pnlBackButton.add(btnBack); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlMain.add(pnlOptionsAndList); + + add(pnlMain, BorderLayout.CENTER); + add(pnlBackButton, BorderLayout.SOUTH); + } + + public void updatePanel(ListModel list) { + try { + boolean hasManeuverNodes = list.getSize() > 0; + boolean hasTargetVessel = vesselManager.getSpaceCenter().getTargetVessel() != null; + listCurrentManeuvers.setModel(list); + listCurrentManeuvers.setSelectedIndex(selectedManeuverIndex); + btnDeleteManeuver.setEnabled(hasManeuverNodes); + btnAp.setEnabled(hasManeuverNodes); + btnPe.setEnabled(hasManeuverNodes); + btnAN.setEnabled(hasManeuverNodes && hasTargetVessel); + btnDN.setEnabled(hasManeuverNodes && hasTargetVessel); + btnIncrease.setEnabled(hasManeuverNodes); + btnDecrease.setEnabled(hasManeuverNodes); + btnNextOrbit.setEnabled(hasManeuverNodes); + btnPrevOrbit.setEnabled(hasManeuverNodes); + lblManeuverInfo.setText(listCurrentManeuvers.getSelectedValue()); + } catch (RPCException ignored) { } - - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - JPanel pnlPositionManeuver = new JPanel(); - pnlPositionManeuver.setLayout(new BoxLayout(pnlPositionManeuver, BoxLayout.X_AXIS)); - pnlPositionManeuver.setBorder(new TitledBorder("Posicionar manobra no:")); - pnlPositionManeuver.add(Box.createHorizontalGlue()); - pnlPositionManeuver.add(btnAp); - pnlPositionManeuver.add(btnPe); - pnlPositionManeuver.add(btnAN); - pnlPositionManeuver.add(btnDN); - pnlPositionManeuver.add(Box.createHorizontalGlue()); - - JPanel pnlRadioButtons = new JPanel(new GridLayout(4, 1)); - pnlRadioButtons.setMaximumSize(new Dimension(50, 100)); - pnlRadioButtons.add(rbPrograde); - pnlRadioButtons.add(rbNormal); - pnlRadioButtons.add(rbRadial); - pnlRadioButtons.add(rbTime); - - JPanel pnlSlider = new JPanel(); - pnlSlider.setLayout(new BoxLayout(pnlSlider, BoxLayout.Y_AXIS)); - pnlSlider.setAlignmentX(Component.LEFT_ALIGNMENT); - pnlSlider.setBorder(new TitledBorder("Escala:")); - pnlSlider.add(sldScale); - pnlSlider.add(Box.createHorizontalStrut(40)); - - JPanel pnlOrbitControl = new JPanel(); - pnlOrbitControl.setLayout(new BoxLayout(pnlOrbitControl, BoxLayout.X_AXIS)); - pnlOrbitControl.setAlignmentX(Component.LEFT_ALIGNMENT); - pnlOrbitControl.add(btnPrevOrbit); - pnlOrbitControl.add(btnNextOrbit); - - JPanel pnlManeuverButtons = new JPanel(); - pnlManeuverButtons.setLayout(new BoxLayout(pnlManeuverButtons, BoxLayout.Y_AXIS)); - pnlManeuverButtons.setAlignmentX(Component.LEFT_ALIGNMENT); - pnlManeuverButtons.add(btnIncrease); - pnlManeuverButtons.add(pnlOrbitControl); - pnlManeuverButtons.add(btnDecrease); - - JPanel pnlManeuverController = new JPanel(); - pnlManeuverController.setLayout(new BoxLayout(pnlManeuverController, BoxLayout.X_AXIS)); - pnlManeuverController.setBorder(new TitledBorder("Controlador de Manobra:")); - pnlManeuverController.setMaximumSize(new Dimension(180, 300)); - pnlManeuverController.add(pnlRadioButtons); - pnlManeuverController.add(pnlSlider); - pnlManeuverController.add(pnlManeuverButtons); - - JPanel pnlManeuverInfo = new JPanel(); - pnlManeuverInfo.setLayout(new BoxLayout(pnlManeuverInfo, BoxLayout.Y_AXIS)); - pnlManeuverInfo.setBorder(new TitledBorder("Info. Manobra:")); - pnlManeuverInfo.setMaximumSize(new Dimension(300, 300)); - - JPanel pnlMCpnlAP = new JPanel(); - pnlMCpnlAP.setLayout(new BoxLayout(pnlMCpnlAP, BoxLayout.X_AXIS)); - pnlMCpnlAP.add(pnlManeuverController); - pnlMCpnlAP.add(pnlManeuverInfo); - - JPanel pnlControls = new JPanel(); - pnlControls.setLayout(new BoxLayout(pnlControls, BoxLayout.Y_AXIS)); - pnlControls.add(pnlPositionManeuver); - pnlControls.add(pnlMCpnlAP); - - JPanel pnlManeuverList = new JPanel(); - pnlManeuverList.setLayout(new BoxLayout(pnlManeuverList, BoxLayout.Y_AXIS)); - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setViewportView(listCurrentManeuvers); - pnlManeuverList.add(scrollPane); - pnlManeuverList.add(btnCreateManeuver); - pnlManeuverList.add(btnDeleteManeuver); - - JPanel pnlOptions = new JPanel(); - pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); - pnlOptions.setBorder(new TitledBorder("Lista de Manobras:")); - pnlOptions.add(pnlManeuverList); - pnlOptions.setPreferredSize(new Dimension(110, 300)); - pnlOptions.setMaximumSize(new Dimension(110, 300)); - - JPanel pnlOptionsAndList = new JPanel(); - pnlOptionsAndList.setLayout(new BoxLayout(pnlOptionsAndList, BoxLayout.X_AXIS)); - pnlControls.setAlignmentY(TOP_ALIGNMENT); - pnlOptions.setAlignmentY(TOP_ALIGNMENT); - pnlOptionsAndList.add(pnlOptions); - pnlOptionsAndList.add(Box.createHorizontalStrut(5)); - pnlOptionsAndList.add(pnlControls); - - JPanel pnlBackButton = new JPanel(); - pnlBackButton.setLayout(new BoxLayout(pnlBackButton, BoxLayout.X_AXIS)); - pnlBackButton.add(lblManeuverInfo); - pnlBackButton.add(Box.createHorizontalGlue()); - pnlBackButton.add(btnBack); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlMain.add(pnlOptionsAndList); - - add(pnlMain, BorderLayout.CENTER); - add(pnlBackButton, BorderLayout.SOUTH); + } + + private static void handleChangeButtonText(int value) { + String decimalPlaces = value > 1 ? "%.0f" : "%.2f"; + String formattedValue = String.format(Locale.ENGLISH, decimalPlaces, sliderValues.get(value)); + String suffix = bgManeuverType.getSelection() == rbTime.getModel() ? " s" : "m/s"; + btnIncrease.setText("+ " + formattedValue + suffix); + btnDecrease.setText("- " + formattedValue + suffix); + } + + public void createManeuver() { + try { + createManeuver(vesselManager.getSpaceCenter().getUT() + 60); + } catch (RPCException e) { } - - public static void updatePanel(ListModel list) { - try { - boolean hasManeuverNodes = list.getSize() > 0; - boolean hasTargetVessel = MechPeste.getSpaceCenter().getTargetVessel() != null; - listCurrentManeuvers.setModel(list); - listCurrentManeuvers.setSelectedIndex(selectedManeuverIndex); - btnDeleteManeuver.setEnabled(hasManeuverNodes); - btnAp.setEnabled(hasManeuverNodes); - btnPe.setEnabled(hasManeuverNodes); - btnAN.setEnabled(hasManeuverNodes && hasTargetVessel); - btnDN.setEnabled(hasManeuverNodes && hasTargetVessel); - btnIncrease.setEnabled(hasManeuverNodes); - btnDecrease.setEnabled(hasManeuverNodes); - btnNextOrbit.setEnabled(hasManeuverNodes); - btnPrevOrbit.setEnabled(hasManeuverNodes); - lblManeuverInfo.setText(listCurrentManeuvers.getSelectedValue()); - } catch (RPCException ignored) { - } + } + + public void createManeuver(double atFutureTime) { + try { + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + + if (vessel.getSituation() != VesselSituation.ORBITING) { + statusDisplay.setStatusMessage("Não é possível criar a manobra fora de órbita."); + return; + } + vessel.getControl().addNode(atFutureTime, 0, 0, 0); + } catch (Exception e) { } - - private static void handleChangeButtonText(int value) { - String decimalPlaces = value > 1 ? "%.0f" : "%.2f"; - String formattedValue = String.format(Locale.ENGLISH, decimalPlaces, sliderValues.get(value)); - String suffix = bgManeuverType.getSelection() == rbTime.getModel() ? " s" : "m/s"; - btnIncrease.setText("+ " + formattedValue + suffix); - btnDecrease.setText("- " + formattedValue + suffix); + } + + private void deleteManeuver() { + try { + MechPeste.newInstance(); + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); + currentManeuver.remove(); + } catch (Exception e) { } - - private void createManeuver() { - try { - createManeuver(MechPeste.getSpaceCenter().getUT() + 60); - } catch (RPCException e) { - } + } + + public void positionManeuverAt(String node) { + try { + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + Orbit orbit = vessel.getOrbit(); + Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); + double timeToNode = 0; + switch (node) { + case "apoapsis": + timeToNode = vesselManager.getSpaceCenter().getUT() + orbit.getTimeToApoapsis(); + break; + case "periapsis": + timeToNode = vesselManager.getSpaceCenter().getUT() + orbit.getTimeToPeriapsis(); + break; + case "ascending": + double ascendingAnomaly = + orbit.trueAnomalyAtAN(vesselManager.getSpaceCenter().getTargetVessel().getOrbit()); + timeToNode = orbit.uTAtTrueAnomaly(ascendingAnomaly); + break; + case "descending": + double descendingAnomaly = + orbit.trueAnomalyAtDN(vesselManager.getSpaceCenter().getTargetVessel().getOrbit()); + timeToNode = orbit.uTAtTrueAnomaly(descendingAnomaly); + break; + } + currentManeuver.setUT(timeToNode); + // Print the maneuver node information + System.out.println("Maneuver Node updated:"); + System.out.println(" Time to node: " + currentManeuver.getTimeTo() + " s"); + } catch (Exception e) { } - - private void createManeuver(double atFutureTime) { - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - - if (vessel.getSituation() != VesselSituation.ORBITING) { - StatusJPanel.setStatusMessage("Não é possível criar a manobra fora de órbita."); - return; - } - vessel.getControl().addNode(atFutureTime, 0, 0, 0); - } catch (Exception e) { - } + } + + private void changeManeuverDeltaV(String command) { + try { + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); + String maneuverType = bgManeuverType.getSelection().getActionCommand(); + float currentSliderValue = sliderValues.get(sldScale.getValue()); + currentSliderValue = command == "decrease" ? -currentSliderValue : currentSliderValue; + + switch (maneuverType) { + case "prograde": + currentManeuver.setPrograde(currentManeuver.getPrograde() + currentSliderValue); + break; + case "normal": + currentManeuver.setNormal(currentManeuver.getNormal() + currentSliderValue); + break; + case "radial": + currentManeuver.setRadial(currentManeuver.getRadial() + currentSliderValue); + break; + case "time": + currentManeuver.setUT(currentManeuver.getUT() + currentSliderValue); + break; + } + } catch (RPCException e) { + System.err.println("Erro RPC ao mudar o delta-V da manobra: " + e.getMessage()); + } catch (Exception e) { + System.err.println("Erro ao mudar o delta-V da manobra: " + e.getMessage()); } - - private void deleteManeuver() { - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); - currentManeuver.remove(); - } catch (Exception e) { - } + } + + private void changeOrbit(String command) { + try { + Vessel vessel; + vessel = vesselManager.getSpaceCenter().getActiveVessel(); + Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); + double currentOrbitPeriod = vessel.getOrbit().getPeriod(); + if (command == "next_orbit") { + currentManeuver.setUT(currentManeuver.getUT() + currentOrbitPeriod); + } else { + double newUT = currentManeuver.getUT() - currentOrbitPeriod; + newUT = + newUT < vesselManager.getSpaceCenter().getUT() + ? vesselManager.getSpaceCenter().getUT() + 60 + : newUT; + currentManeuver.setUT(newUT); + } + } catch (RPCException ignored) { } + } - private void positionManeuverAt(String node) { - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - Orbit orbit = vessel.getOrbit(); - Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); - double timeToNode = 0; - switch (node) { - case "apoapsis" : - timeToNode = MechPeste.getSpaceCenter().getUT() + orbit.getTimeToApoapsis(); - break; - case "periapsis" : - timeToNode = MechPeste.getSpaceCenter().getUT() + orbit.getTimeToPeriapsis(); - break; - case "ascending" : - double ascendingAnomaly = orbit - .trueAnomalyAtAN(MechPeste.getSpaceCenter().getTargetVessel().getOrbit()); - timeToNode = orbit.uTAtTrueAnomaly(ascendingAnomaly); - break; - case "descending" : - double descendingAnomaly = orbit - .trueAnomalyAtDN(MechPeste.getSpaceCenter().getTargetVessel().getOrbit()); - timeToNode = orbit.uTAtTrueAnomaly(descendingAnomaly); - break; - } - currentManeuver.setUT(timeToNode); - // Print the maneuver node information - System.out.println("Maneuver Node updated:"); - System.out.println(" Time to node: " + currentManeuver.getTimeTo() + " s"); - } catch (Exception e) { - } + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == btnCreateManeuver) { + createManeuver(); } - - private void changeManeuverDeltaV(String command) { - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); - String maneuverType = bgManeuverType.getSelection().getActionCommand(); - float currentSliderValue = sliderValues.get(sldScale.getValue()); - currentSliderValue = command == "decrease" ? -currentSliderValue : currentSliderValue; - - switch (maneuverType) { - case "prograde" : - currentManeuver.setPrograde(currentManeuver.getPrograde() + currentSliderValue); - break; - case "normal" : - currentManeuver.setNormal(currentManeuver.getNormal() + currentSliderValue); - break; - case "radial" : - currentManeuver.setRadial(currentManeuver.getRadial() + currentSliderValue); - break; - case "time" : - currentManeuver.setUT(currentManeuver.getUT() + currentSliderValue); - break; - } - } catch (RPCException e) { - System.err.println("Erro RPC ao mudar o delta-V da manobra: " + e.getMessage()); - } catch (Exception e) { - System.err.println("Erro ao mudar o delta-V da manobra: " + e.getMessage()); - } - + if (e.getSource() == btnDeleteManeuver) { + deleteManeuver(); } - - private void changeOrbit(String command) { - try { - MechPeste.newInstance(); - Vessel vessel; - vessel = MechPeste.getSpaceCenter().getActiveVessel(); - Node currentManeuver = vessel.getControl().getNodes().get(selectedManeuverIndex); - double currentOrbitPeriod = vessel.getOrbit().getPeriod(); - if (command == "next_orbit") { - currentManeuver.setUT(currentManeuver.getUT() + currentOrbitPeriod); - } else { - double newUT = currentManeuver.getUT() - currentOrbitPeriod; - newUT = newUT < MechPeste.getSpaceCenter().getUT() ? MechPeste.getSpaceCenter().getUT() + 60 : newUT; - currentManeuver.setUT(newUT); - } - } catch (RPCException ignored) { - } + if (e.getSource() == btnIncrease || e.getSource() == btnDecrease) { + changeManeuverDeltaV(e.getActionCommand()); } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == btnCreateManeuver) { - createManeuver(); - } - if (e.getSource() == btnDeleteManeuver) { - deleteManeuver(); - } - if (e.getSource() == btnIncrease || e.getSource() == btnDecrease) { - changeManeuverDeltaV(e.getActionCommand()); - } - if (e.getSource() == btnNextOrbit || e.getSource() == btnPrevOrbit) { - changeOrbit(e.getActionCommand()); - } - if (e.getSource() == btnAp || e.getSource() == btnPe || e.getSource() == btnAN || e.getSource() == btnDN) { - positionManeuverAt(e.getActionCommand()); - } - if (e.getSource() == btnBack) { - MainGui.backToTelemetry(e); - } + if (e.getSource() == btnNextOrbit || e.getSource() == btnPrevOrbit) { + changeOrbit(e.getActionCommand()); + } + if (e.getSource() == btnAp + || e.getSource() == btnPe + || e.getSource() == btnAN + || e.getSource() == btnDN) { + positionManeuverAt(e.getActionCommand()); + } + if (e.getSource() == btnBack) { + MainGui.backToTelemetry(e); } + } } diff --git a/src/main/java/com/pesterenan/views/DockingJPanel.java b/src/main/java/com/pesterenan/views/DockingJPanel.java index 151c929..683a2b8 100644 --- a/src/main/java/com/pesterenan/views/DockingJPanel.java +++ b/src/main/java/com/pesterenan/views/DockingJPanel.java @@ -1,156 +1,171 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import java.awt.*; +import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import static com.pesterenan.views.MainGui.PNL_DIMENSION; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.border.TitledBorder; public class DockingJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 0L; - - private JLabel lblMaxSpeed, lblSafeDistance, lblCurrentDockingStepText; - private static JLabel lblDockingStep; - - private JTextField txfMaxSpeed, txfSafeDistance; - private JButton btnBack, btnStartDocking; - - public DockingJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblMaxSpeed = new JLabel(Bundle.getString("pnl_docking_max_speed")); - lblSafeDistance = new JLabel(Bundle.getString("pnl_docking_safe_distance")); - lblDockingStep = new JLabel(Bundle.getString("pnl_docking_step_ready")); - lblCurrentDockingStepText = new JLabel(Bundle.getString("pnl_docking_current_step")); - // Textfields: - txfMaxSpeed = new JTextField("3"); - txfSafeDistance = new JTextField("50"); - - // Buttons: - btnBack = new JButton(Bundle.getString("pnl_rover_btn_back")); - btnStartDocking = new JButton(Bundle.getString("pnl_rover_btn_docking")); - } - - @Override - public void setupComponents() { - // Main Panel setup: - setBorder(new TitledBorder(null, Bundle.getString("btn_func_docking"))); - - // Setting-up components: - txfMaxSpeed.setHorizontalAlignment(SwingConstants.RIGHT); - txfMaxSpeed.setMaximumSize(BTN_DIMENSION); - txfMaxSpeed.setPreferredSize(BTN_DIMENSION); - txfSafeDistance.setHorizontalAlignment(SwingConstants.RIGHT); - txfSafeDistance.setMaximumSize(BTN_DIMENSION); - txfSafeDistance.setPreferredSize(BTN_DIMENSION); - - btnBack.addActionListener(MainGui::backToTelemetry); - btnBack.setMaximumSize(BTN_DIMENSION); - btnBack.setPreferredSize(BTN_DIMENSION); - btnStartDocking.addActionListener(this::handleStartDocking); - btnStartDocking.setMaximumSize(BTN_DIMENSION); - btnStartDocking.setPreferredSize(BTN_DIMENSION); - } - - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - // Layout components: - JPanel pnlMaxSpeed = new JPanel(); - pnlMaxSpeed.setLayout(new BoxLayout(pnlMaxSpeed, BoxLayout.X_AXIS)); - pnlMaxSpeed.add(lblMaxSpeed); - pnlMaxSpeed.add(Box.createHorizontalGlue()); - pnlMaxSpeed.add(txfMaxSpeed); - - JPanel pnlSafeDistance = new JPanel(); - pnlSafeDistance.setLayout(new BoxLayout(pnlSafeDistance, BoxLayout.X_AXIS)); - pnlSafeDistance.add(lblSafeDistance); - pnlSafeDistance.add(Box.createHorizontalGlue()); - pnlSafeDistance.add(txfSafeDistance); - - JPanel pnlDockingStep = new JPanel(); - pnlDockingStep.setLayout(new BoxLayout(pnlDockingStep, BoxLayout.X_AXIS)); - pnlDockingStep.add(lblCurrentDockingStepText); - pnlDockingStep.add(Box.createHorizontalGlue()); - pnlDockingStep.add(lblDockingStep); - - JPanel pnlDockingControls = new JPanel(); - pnlDockingControls.setLayout(new BoxLayout(pnlDockingControls, BoxLayout.Y_AXIS)); - pnlDockingControls.setBorder(MARGIN_BORDER_10_PX_LR); - pnlDockingControls.add(MainGui.createMarginComponent(0, 6)); - pnlDockingControls.add(pnlMaxSpeed); - pnlDockingControls.add(pnlSafeDistance); - pnlDockingControls.add(Box.createVerticalGlue()); - pnlDockingControls.add(pnlDockingStep); - pnlDockingControls.add(Box.createVerticalGlue()); - - JPanel pnlButtons = new JPanel(); - pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); - pnlButtons.add(btnStartDocking); - pnlButtons.add(Box.createHorizontalGlue()); - pnlButtons.add(btnBack); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlDockingControls.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlDockingControls); - - setLayout(new BorderLayout()); - add(pnlMain, BorderLayout.CENTER); - add(pnlButtons, BorderLayout.SOUTH); - } - - public static void setDockingStep(String step) { - lblDockingStep.setText(step); + private static final long serialVersionUID = 0L; + + private JLabel lblMaxSpeed, lblSafeDistance, lblCurrentDockingStepText; + private static JLabel lblDockingStep; + + private JTextField txfMaxSpeed, txfSafeDistance; + private JButton btnBack, btnStartDocking; + + private VesselManager vesselManager; + + private StatusDisplay statusDisplay; + + public DockingJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + } + + @Override + public void initComponents() { + // Labels: + lblMaxSpeed = new JLabel(Bundle.getString("pnl_docking_max_speed")); + lblSafeDistance = new JLabel(Bundle.getString("pnl_docking_safe_distance")); + lblDockingStep = new JLabel(Bundle.getString("pnl_docking_step_ready")); + lblCurrentDockingStepText = new JLabel(Bundle.getString("pnl_docking_current_step")); + // Textfields: + txfMaxSpeed = new JTextField("3"); + txfSafeDistance = new JTextField("50"); + + // Buttons: + btnBack = new JButton(Bundle.getString("pnl_rover_btn_back")); + btnStartDocking = new JButton(Bundle.getString("pnl_rover_btn_docking")); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setBorder(new TitledBorder(null, Bundle.getString("btn_func_docking"))); + + // Setting-up components: + txfMaxSpeed.setHorizontalAlignment(SwingConstants.RIGHT); + txfMaxSpeed.setMaximumSize(BTN_DIMENSION); + txfMaxSpeed.setPreferredSize(BTN_DIMENSION); + txfSafeDistance.setHorizontalAlignment(SwingConstants.RIGHT); + txfSafeDistance.setMaximumSize(BTN_DIMENSION); + txfSafeDistance.setPreferredSize(BTN_DIMENSION); + + btnBack.addActionListener(MainGui::backToTelemetry); + btnBack.setMaximumSize(BTN_DIMENSION); + btnBack.setPreferredSize(BTN_DIMENSION); + btnStartDocking.addActionListener(this::handleStartDocking); + btnStartDocking.setMaximumSize(BTN_DIMENSION); + btnStartDocking.setPreferredSize(BTN_DIMENSION); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + // Layout components: + JPanel pnlMaxSpeed = new JPanel(); + pnlMaxSpeed.setLayout(new BoxLayout(pnlMaxSpeed, BoxLayout.X_AXIS)); + pnlMaxSpeed.add(lblMaxSpeed); + pnlMaxSpeed.add(Box.createHorizontalGlue()); + pnlMaxSpeed.add(txfMaxSpeed); + + JPanel pnlSafeDistance = new JPanel(); + pnlSafeDistance.setLayout(new BoxLayout(pnlSafeDistance, BoxLayout.X_AXIS)); + pnlSafeDistance.add(lblSafeDistance); + pnlSafeDistance.add(Box.createHorizontalGlue()); + pnlSafeDistance.add(txfSafeDistance); + + JPanel pnlDockingStep = new JPanel(); + pnlDockingStep.setLayout(new BoxLayout(pnlDockingStep, BoxLayout.X_AXIS)); + pnlDockingStep.add(lblCurrentDockingStepText); + pnlDockingStep.add(Box.createHorizontalGlue()); + pnlDockingStep.add(lblDockingStep); + + JPanel pnlDockingControls = new JPanel(); + pnlDockingControls.setLayout(new BoxLayout(pnlDockingControls, BoxLayout.Y_AXIS)); + pnlDockingControls.setBorder(MARGIN_BORDER_10_PX_LR); + pnlDockingControls.add(MainGui.createMarginComponent(0, 6)); + pnlDockingControls.add(pnlMaxSpeed); + pnlDockingControls.add(pnlSafeDistance); + pnlDockingControls.add(Box.createVerticalGlue()); + pnlDockingControls.add(pnlDockingStep); + pnlDockingControls.add(Box.createVerticalGlue()); + + JPanel pnlButtons = new JPanel(); + pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); + pnlButtons.add(btnStartDocking); + pnlButtons.add(Box.createHorizontalGlue()); + pnlButtons.add(btnBack); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlDockingControls.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlDockingControls); + + setLayout(new BorderLayout()); + add(pnlMain, BorderLayout.CENTER); + add(pnlButtons, BorderLayout.SOUTH); + } + + public static void setDockingStep(String step) { + lblDockingStep.setText(step); + } + + private void handleStartDocking(ActionEvent e) { + if (validateTextFields()) { + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), Module.DOCKING.get()); + commands.put(Module.SAFE_DISTANCE.get(), txfSafeDistance.getText()); + commands.put(Module.MAX_SPEED.get(), txfMaxSpeed.getText()); + vesselManager.startModule(commands); } - - private void handleStartDocking(ActionEvent e) { - if (validateTextFields()) { - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), Module.DOCKING.get()); - commands.put(Module.SAFE_DISTANCE.get(), txfSafeDistance.getText()); - commands.put(Module.MAX_SPEED.get(), txfMaxSpeed.getText()); - MechPeste.newInstance().startModule(commands); - } - } - - private boolean validateTextFields() { - try { - if (Float.parseFloat(txfMaxSpeed.getText()) > 10) { - StatusJPanel.setStatusMessage("Velocidade de acoplagem muito alta. Tem que ser menor que 10m/s."); - return false; - } - if (Float.parseFloat(txfSafeDistance.getText()) > 200) { - StatusJPanel.setStatusMessage("Distância segura muito alta. Tem que ser menor que 200m."); - return false; - } - } catch (NumberFormatException e) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_lift_stat_only_numbers")); - return false; - } catch (IllegalArgumentException e) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_rover_waypoint_name_not_empty")); - return false; - } - return true; + } + + private boolean validateTextFields() { + try { + if (Float.parseFloat(txfMaxSpeed.getText()) > 10) { + statusDisplay.setStatusMessage( + "Velocidade de acoplagem muito alta. Tem que ser menor que 10m/s."); + return false; + } + if (Float.parseFloat(txfSafeDistance.getText()) > 200) { + statusDisplay.setStatusMessage("Distância segura muito alta. Tem que ser menor que 200m."); + return false; + } + } catch (NumberFormatException e) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_lift_stat_only_numbers")); + return false; + } catch (IllegalArgumentException e) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_rover_waypoint_name_not_empty")); + return false; } + return true; + } } diff --git a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java index 816b06a..b603bfc 100644 --- a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java +++ b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java @@ -1,197 +1,235 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; import com.pesterenan.utils.Telemetry; import com.pesterenan.utils.Utilities; - -import javax.swing.*; -import javax.swing.border.TitledBorder; - import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.Map; - -import static com.pesterenan.views.MainGui.PNL_DIMENSION; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; public class FunctionsAndTelemetryJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 0L; - - private final Dimension btnFuncDimension = new Dimension(140, 25); - private JButton btnLiftoff, btnLanding, btnManeuvers, btnDocking, btnRover, btnCancel; - private static JLabel lblAltitude, lblSurfaceAlt, lblApoapsis, lblPeriapsis, lblVertSpeed, lblHorzSpeed; - private static JLabel lblAltitudeValue, lblSurfaceAltValue, lblApoapsisValue; - private static JLabel lblPeriapsisValue, lblVertSpeedValue, lblHorzSpeedValue; - - public FunctionsAndTelemetryJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblAltitude = new JLabel(Bundle.getString("pnl_tel_lbl_alt")); - lblSurfaceAlt = new JLabel(Bundle.getString("pnl_tel_lbl_alt_sur")); - lblApoapsis = new JLabel(Bundle.getString("pnl_tel_lbl_apoapsis")); - lblPeriapsis = new JLabel(Bundle.getString("pnl_tel_lbl_periapsis")); - lblVertSpeed = new JLabel(Bundle.getString("pnl_tel_lbl_vert_spd")); - lblHorzSpeed = new JLabel(Bundle.getString("pnl_tel_lbl_horz_spd")); - lblAltitudeValue = new JLabel("---"); - lblSurfaceAltValue = new JLabel("---"); - lblApoapsisValue = new JLabel("---"); - lblPeriapsisValue = new JLabel("---"); - lblVertSpeedValue = new JLabel("---"); - lblHorzSpeedValue = new JLabel("---"); - - // Buttons: - btnLiftoff = new JButton(Bundle.getString("btn_func_liftoff")); - btnLanding = new JButton(Bundle.getString("btn_func_landing")); - btnManeuvers = new JButton(Bundle.getString("btn_func_maneuver")); - btnRover = new JButton(Bundle.getString("btn_func_rover")); - btnDocking = new JButton(Bundle.getString("btn_func_docking")); - btnCancel = new JButton(Bundle.getString("pnl_tel_btn_cancel")); - } - - @Override - public void setupComponents() { - setPreferredSize(PNL_DIMENSION); - setBorder(new TitledBorder(null, Bundle.getString("pnl_func_title"), TitledBorder.LEADING, TitledBorder.TOP, - null, null)); - setLayout(new BorderLayout()); - - // Setting up components: - btnCancel.addActionListener(MechPeste::cancelControl); - btnCancel.setMaximumSize(btnFuncDimension); - btnCancel.setPreferredSize(btnFuncDimension); - btnLanding.addActionListener(this::changeFunctionPanel); - btnLanding.setActionCommand(Module.LANDING.get()); - btnLanding.setMaximumSize(btnFuncDimension); - btnLanding.setPreferredSize(btnFuncDimension); - btnLiftoff.addActionListener(this::changeFunctionPanel); - btnLiftoff.setActionCommand(Module.LIFTOFF.get()); - btnLiftoff.setMaximumSize(btnFuncDimension); - btnLiftoff.setPreferredSize(btnFuncDimension); - btnManeuvers.addActionListener(this::changeFunctionPanel); - btnManeuvers.setActionCommand(Module.MANEUVER.get()); - btnManeuvers.setMaximumSize(btnFuncDimension); - btnManeuvers.setPreferredSize(btnFuncDimension); - btnRover.addActionListener(this::changeFunctionPanel); - btnRover.setActionCommand(Module.ROVER.get()); - btnRover.setMaximumSize(btnFuncDimension); - btnRover.setPreferredSize(btnFuncDimension); - btnDocking.addActionListener(this::changeFunctionPanel); - btnDocking.setActionCommand(Module.DOCKING.get()); - btnDocking.setMaximumSize(btnFuncDimension); - btnDocking.setPreferredSize(btnFuncDimension); - } - - @Override - public void layoutComponents() { - - JPanel pnlFunctionControls = new JPanel(); - pnlFunctionControls.setLayout(new BoxLayout(pnlFunctionControls, BoxLayout.Y_AXIS)); - pnlFunctionControls.add(MainGui.createMarginComponent(0, 4)); - pnlFunctionControls.add(btnLiftoff); - pnlFunctionControls.add(btnLanding); - pnlFunctionControls.add(btnRover); - pnlFunctionControls.add(btnDocking); - pnlFunctionControls.add(btnManeuvers); - pnlFunctionControls.add(Box.createVerticalGlue()); - - JPanel pnlLeftPanel = new JPanel(); - pnlLeftPanel.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); - pnlLeftPanel.setLayout(new BoxLayout(pnlLeftPanel, BoxLayout.Y_AXIS)); - pnlLeftPanel.add(lblAltitude); - pnlLeftPanel.add(Box.createVerticalStrut(5)); - pnlLeftPanel.add(lblSurfaceAlt); - pnlLeftPanel.add(Box.createVerticalStrut(5)); - pnlLeftPanel.add(lblApoapsis); - pnlLeftPanel.add(Box.createVerticalStrut(5)); - pnlLeftPanel.add(lblPeriapsis); - pnlLeftPanel.add(Box.createVerticalStrut(5)); - pnlLeftPanel.add(lblHorzSpeed); - pnlLeftPanel.add(Box.createVerticalStrut(5)); - pnlLeftPanel.add(lblVertSpeed); - pnlLeftPanel.add(Box.createGlue()); - - JPanel pnlRightPanel = new JPanel(); - pnlRightPanel.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); - pnlRightPanel.setLayout(new BoxLayout(pnlRightPanel, BoxLayout.Y_AXIS)); - lblAltitudeValue.setAlignmentX(RIGHT_ALIGNMENT); - lblSurfaceAltValue.setAlignmentX(RIGHT_ALIGNMENT); - lblApoapsisValue.setAlignmentX(RIGHT_ALIGNMENT); - lblPeriapsisValue.setAlignmentX(RIGHT_ALIGNMENT); - lblHorzSpeedValue.setAlignmentX(RIGHT_ALIGNMENT); - lblVertSpeedValue.setAlignmentX(RIGHT_ALIGNMENT); - pnlRightPanel.add(lblAltitudeValue); - pnlRightPanel.add(Box.createVerticalStrut(5)); - pnlRightPanel.add(lblSurfaceAltValue); - pnlRightPanel.add(Box.createVerticalStrut(5)); - pnlRightPanel.add(lblApoapsisValue); - pnlRightPanel.add(Box.createVerticalStrut(5)); - pnlRightPanel.add(lblPeriapsisValue); - pnlRightPanel.add(Box.createVerticalStrut(5)); - pnlRightPanel.add(lblHorzSpeedValue); - pnlRightPanel.add(Box.createVerticalStrut(5)); - pnlRightPanel.add(lblVertSpeedValue); - pnlRightPanel.add(Box.createGlue()); - - JPanel pnlLeftRightContainer = new JPanel(); - pnlLeftRightContainer.setLayout(new BoxLayout(pnlLeftRightContainer, BoxLayout.X_AXIS)); - pnlLeftRightContainer.add(pnlLeftPanel); - pnlLeftRightContainer.add(pnlRightPanel); - - JPanel pnlTelemetry = new JPanel(); - pnlTelemetry.setLayout(new BoxLayout(pnlTelemetry, BoxLayout.Y_AXIS)); - pnlTelemetry.setBorder(new TitledBorder(null, Bundle.getString("pnl_tel_border"))); - pnlTelemetry.add(pnlLeftRightContainer); - pnlTelemetry.add(Box.createGlue()); - btnCancel.setAlignmentX(CENTER_ALIGNMENT); - pnlTelemetry.add(btnCancel); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlFunctionControls.setAlignmentY(TOP_ALIGNMENT); - pnlTelemetry.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlFunctionControls); - pnlMain.add(pnlTelemetry); - - add(pnlMain, BorderLayout.CENTER); - } - - private void changeFunctionPanel(ActionEvent e) { - MainGui.changeToPage(e); - } - - public static void updateTelemetry(Map telemetryData) { - synchronized (telemetryData) { - for (Telemetry key : telemetryData.keySet()) { - switch (key) { - case ALTITUDE : - lblAltitudeValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); - break; - case ALT_SURF : - lblSurfaceAltValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); - break; - case APOAPSIS : - lblApoapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); - break; - case PERIAPSIS : - lblPeriapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); - break; - case VERT_SPEED : - lblVertSpeedValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); - break; - case HORZ_SPEED : - lblHorzSpeedValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); - break; - } - } + private static final long serialVersionUID = 0L; + + private final Dimension btnFuncDimension = new Dimension(140, 25); + private JButton btnLiftoff, btnLanding, btnManeuvers, btnDocking, btnRover, btnCancel; + private JLabel lblAltitude, lblSurfaceAlt, lblApoapsis, lblPeriapsis, lblVertSpeed, lblHorzSpeed; + private JLabel lblAltitudeValue, lblSurfaceAltValue, lblApoapsisValue; + private JLabel lblPeriapsisValue, lblVertSpeedValue, lblHorzSpeedValue; + + private VesselManager vesselManager; + + private StatusDisplay statusDisplay; + + public FunctionsAndTelemetryJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + btnCancel.setEnabled(true); + } + + @Override + public void initComponents() { + // Labels: + lblAltitude = new JLabel(Bundle.getString("pnl_tel_lbl_alt")); + lblSurfaceAlt = new JLabel(Bundle.getString("pnl_tel_lbl_alt_sur")); + lblApoapsis = new JLabel(Bundle.getString("pnl_tel_lbl_apoapsis")); + lblPeriapsis = new JLabel(Bundle.getString("pnl_tel_lbl_periapsis")); + lblVertSpeed = new JLabel(Bundle.getString("pnl_tel_lbl_vert_spd")); + lblHorzSpeed = new JLabel(Bundle.getString("pnl_tel_lbl_horz_spd")); + lblAltitudeValue = new JLabel("---"); + lblSurfaceAltValue = new JLabel("---"); + lblApoapsisValue = new JLabel("---"); + lblPeriapsisValue = new JLabel("---"); + lblVertSpeedValue = new JLabel("---"); + lblHorzSpeedValue = new JLabel("---"); + + // Buttons: + btnLiftoff = new JButton(Bundle.getString("btn_func_liftoff")); + btnLanding = new JButton(Bundle.getString("btn_func_landing")); + btnManeuvers = new JButton(Bundle.getString("btn_func_maneuver")); + btnRover = new JButton(Bundle.getString("btn_func_rover")); + btnDocking = new JButton(Bundle.getString("btn_func_docking")); + btnCancel = new JButton(Bundle.getString("pnl_tel_btn_cancel")); + } + + @Override + public void setupComponents() { + setPreferredSize(PNL_DIMENSION); + setBorder( + new TitledBorder( + null, + Bundle.getString("pnl_func_title"), + TitledBorder.LEADING, + TitledBorder.TOP, + null, + null)); + setLayout(new BorderLayout()); + + // Setting up components: + btnCancel.addActionListener(e -> cancelCurrentAction(e)); + btnCancel.setMaximumSize(btnFuncDimension); + btnCancel.setPreferredSize(btnFuncDimension); + btnCancel.setEnabled(false); + btnLanding.addActionListener(this::changeFunctionPanel); + btnLanding.setActionCommand(Module.LANDING.get()); + btnLanding.setMaximumSize(btnFuncDimension); + btnLanding.setPreferredSize(btnFuncDimension); + btnLiftoff.addActionListener(this::changeFunctionPanel); + btnLiftoff.setActionCommand(Module.LIFTOFF.get()); + btnLiftoff.setMaximumSize(btnFuncDimension); + btnLiftoff.setPreferredSize(btnFuncDimension); + btnManeuvers.addActionListener(this::changeFunctionPanel); + btnManeuvers.setActionCommand(Module.MANEUVER.get()); + btnManeuvers.setMaximumSize(btnFuncDimension); + btnManeuvers.setPreferredSize(btnFuncDimension); + btnRover.addActionListener(this::changeFunctionPanel); + btnRover.setActionCommand(Module.ROVER.get()); + btnRover.setMaximumSize(btnFuncDimension); + btnRover.setPreferredSize(btnFuncDimension); + btnDocking.addActionListener(this::changeFunctionPanel); + btnDocking.setActionCommand(Module.DOCKING.get()); + btnDocking.setMaximumSize(btnFuncDimension); + btnDocking.setPreferredSize(btnFuncDimension); + btnDocking.setEnabled(false); // FIX: Not yet ready + } + + private void cancelCurrentAction(ActionEvent e) { + statusDisplay.setStatusMessage("Canceling current action..."); + vesselManager.cancelControl(e); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + executor.schedule( + () -> { + statusDisplay.setStatusMessage(Bundle.getString("lbl_stat_ready")); + }, + 2, + TimeUnit.SECONDS); + executor.shutdown(); + } + + @Override + public void layoutComponents() { + + JPanel pnlFunctionControls = new JPanel(); + pnlFunctionControls.setLayout(new BoxLayout(pnlFunctionControls, BoxLayout.Y_AXIS)); + pnlFunctionControls.add(MainGui.createMarginComponent(0, 4)); + pnlFunctionControls.add(btnLiftoff); + pnlFunctionControls.add(btnLanding); + pnlFunctionControls.add(btnRover); + pnlFunctionControls.add(btnDocking); + pnlFunctionControls.add(btnManeuvers); + pnlFunctionControls.add(Box.createVerticalGlue()); + + JPanel pnlLeftPanel = new JPanel(); + pnlLeftPanel.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); + pnlLeftPanel.setLayout(new BoxLayout(pnlLeftPanel, BoxLayout.Y_AXIS)); + pnlLeftPanel.add(lblAltitude); + pnlLeftPanel.add(Box.createVerticalStrut(5)); + pnlLeftPanel.add(lblSurfaceAlt); + pnlLeftPanel.add(Box.createVerticalStrut(5)); + pnlLeftPanel.add(lblApoapsis); + pnlLeftPanel.add(Box.createVerticalStrut(5)); + pnlLeftPanel.add(lblPeriapsis); + pnlLeftPanel.add(Box.createVerticalStrut(5)); + pnlLeftPanel.add(lblHorzSpeed); + pnlLeftPanel.add(Box.createVerticalStrut(5)); + pnlLeftPanel.add(lblVertSpeed); + pnlLeftPanel.add(Box.createGlue()); + + JPanel pnlRightPanel = new JPanel(); + pnlRightPanel.setBorder(MainGui.MARGIN_BORDER_10_PX_LR); + pnlRightPanel.setLayout(new BoxLayout(pnlRightPanel, BoxLayout.Y_AXIS)); + lblAltitudeValue.setAlignmentX(RIGHT_ALIGNMENT); + lblSurfaceAltValue.setAlignmentX(RIGHT_ALIGNMENT); + lblApoapsisValue.setAlignmentX(RIGHT_ALIGNMENT); + lblPeriapsisValue.setAlignmentX(RIGHT_ALIGNMENT); + lblHorzSpeedValue.setAlignmentX(RIGHT_ALIGNMENT); + lblVertSpeedValue.setAlignmentX(RIGHT_ALIGNMENT); + pnlRightPanel.add(lblAltitudeValue); + pnlRightPanel.add(Box.createVerticalStrut(5)); + pnlRightPanel.add(lblSurfaceAltValue); + pnlRightPanel.add(Box.createVerticalStrut(5)); + pnlRightPanel.add(lblApoapsisValue); + pnlRightPanel.add(Box.createVerticalStrut(5)); + pnlRightPanel.add(lblPeriapsisValue); + pnlRightPanel.add(Box.createVerticalStrut(5)); + pnlRightPanel.add(lblHorzSpeedValue); + pnlRightPanel.add(Box.createVerticalStrut(5)); + pnlRightPanel.add(lblVertSpeedValue); + pnlRightPanel.add(Box.createGlue()); + + JPanel pnlLeftRightContainer = new JPanel(); + pnlLeftRightContainer.setLayout(new BoxLayout(pnlLeftRightContainer, BoxLayout.X_AXIS)); + pnlLeftRightContainer.add(pnlLeftPanel); + pnlLeftRightContainer.add(pnlRightPanel); + + JPanel pnlTelemetry = new JPanel(); + pnlTelemetry.setLayout(new BoxLayout(pnlTelemetry, BoxLayout.Y_AXIS)); + pnlTelemetry.setBorder(new TitledBorder(null, Bundle.getString("pnl_tel_border"))); + pnlTelemetry.add(pnlLeftRightContainer); + pnlTelemetry.add(Box.createGlue()); + btnCancel.setAlignmentX(CENTER_ALIGNMENT); + pnlTelemetry.add(btnCancel); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlFunctionControls.setAlignmentY(TOP_ALIGNMENT); + pnlTelemetry.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlFunctionControls); + pnlMain.add(pnlTelemetry); + + add(pnlMain, BorderLayout.CENTER); + } + + private void changeFunctionPanel(ActionEvent e) { + MainGui.changeToPage(e); + } + + public void updateTelemetry(Map telemetryData) { + synchronized (telemetryData) { + for (Telemetry key : telemetryData.keySet()) { + switch (key) { + case ALTITUDE: + lblAltitudeValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); + break; + case ALT_SURF: + lblSurfaceAltValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); + break; + case APOAPSIS: + lblApoapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); + break; + case PERIAPSIS: + lblPeriapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); + break; + case VERT_SPEED: + lblVertSpeedValue.setText( + Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); + break; + case HORZ_SPEED: + lblHorzSpeedValue.setText( + Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); + break; } + } } + } } diff --git a/src/main/java/com/pesterenan/views/InstallKrpcDialog.java b/src/main/java/com/pesterenan/views/InstallKrpcDialog.java index 370cdfc..051bb75 100644 --- a/src/main/java/com/pesterenan/views/InstallKrpcDialog.java +++ b/src/main/java/com/pesterenan/views/InstallKrpcDialog.java @@ -2,7 +2,6 @@ import com.pesterenan.resources.Bundle; import com.pesterenan.updater.KrpcInstaller; - import javax.swing.*; import javax.swing.GroupLayout.Alignment; import javax.swing.LayoutStyle.ComponentPlacement; @@ -10,136 +9,209 @@ import javax.swing.border.TitledBorder; public class InstallKrpcDialog extends JDialog { - private static final long serialVersionUID = 1L; - private JLabel lblInstallerInfo; - private final JSeparator separator = new JSeparator(); - private final JPanel pnlKspFolderPath = new JPanel(); - private final JTextField txfPath = new JTextField(); - private JButton btnBrowsePath; - private JButton btnDownloadInstall; - private JButton btnCancel; - private JPanel pnlStatus; - private static JLabel lblStatus; - - public InstallKrpcDialog() { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - initComponents(); - } catch (Throwable e) { - e.printStackTrace(); - } + private static final long serialVersionUID = 1L; + private JLabel lblInstallerInfo; + private final JSeparator separator = new JSeparator(); + private final JPanel pnlKspFolderPath = new JPanel(); + private final JTextField txfPath = new JTextField(); + private JButton btnBrowsePath; + private JButton btnDownloadInstall; + private JButton btnCancel; + private JPanel pnlStatus; + private static JLabel lblStatus; + + public InstallKrpcDialog() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + initComponents(); + } catch (Throwable e) { + e.printStackTrace(); } - - private void initComponents() { - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - setResizable(false); - setBounds(MainGui.centerDialogOnScreen()); - setAlwaysOnTop(true); - setModalityType(ModalityType.APPLICATION_MODAL); - setTitle(Bundle.getString("installer_dialog_title")); //$NON-NLS-1$ - - lblInstallerInfo = new JLabel(Bundle.getString("installer_dialog_txt_info")); //$NON-NLS-1$ - - pnlKspFolderPath - .setBorder(new TitledBorder(null, Bundle.getString("installer_dialog_pnl_path"), TitledBorder.LEADING, // $NON - // -NLS-1$ - TitledBorder.TOP, null, null)); - - btnDownloadInstall = new JButton(Bundle.getString("installer_dialog_btn_download")); // $NON - // -NLS-1$ - btnDownloadInstall.addActionListener((e) -> KrpcInstaller.downloadAndInstallKrpc()); - btnDownloadInstall.setEnabled(false); - - btnCancel = new JButton(Bundle.getString("installer_dialog_btn_cancel")); //$NON-NLS-1$ - btnCancel.addActionListener((e) -> this.dispose()); - - pnlStatus = new JPanel(); - pnlStatus.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null)); - - GroupLayout groupLayout = new GroupLayout(getContentPane()); - groupLayout.setHorizontalGroup(groupLayout.createParallelGroup(Alignment.TRAILING).addGroup(groupLayout - .createSequentialGroup().addContainerGap() - .addGroup(groupLayout.createParallelGroup(Alignment.LEADING) - .addComponent(pnlKspFolderPath, GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE) - .addComponent(lblInstallerInfo, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 414, + } + + private void initComponents() { + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setResizable(false); + setBounds(MainGui.centerDialogOnScreen()); + setAlwaysOnTop(true); + setModalityType(ModalityType.APPLICATION_MODAL); + setTitle(Bundle.getString("installer_dialog_title")); // $NON-NLS-1$ + + lblInstallerInfo = new JLabel(Bundle.getString("installer_dialog_txt_info")); // $NON-NLS-1$ + + pnlKspFolderPath.setBorder( + new TitledBorder( + null, + Bundle.getString("installer_dialog_pnl_path"), + TitledBorder.LEADING, // $NON + // -NLS-1$ + TitledBorder.TOP, + null, + null)); + + btnDownloadInstall = new JButton(Bundle.getString("installer_dialog_btn_download")); // $NON + // -NLS-1$ + btnDownloadInstall.addActionListener((e) -> KrpcInstaller.downloadAndInstallKrpc()); + btnDownloadInstall.setEnabled(false); + + btnCancel = new JButton(Bundle.getString("installer_dialog_btn_cancel")); // $NON-NLS-1$ + btnCancel.addActionListener((e) -> this.dispose()); + + pnlStatus = new JPanel(); + pnlStatus.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null)); + + GroupLayout groupLayout = new GroupLayout(getContentPane()); + groupLayout.setHorizontalGroup( + groupLayout + .createParallelGroup(Alignment.TRAILING) + .addGroup( + groupLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + groupLayout + .createParallelGroup(Alignment.LEADING) + .addComponent( + pnlKspFolderPath, GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE) + .addComponent( + lblInstallerInfo, + Alignment.TRAILING, + GroupLayout.DEFAULT_SIZE, + 414, Short.MAX_VALUE) - .addComponent(separator, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE) - .addGroup(groupLayout.createSequentialGroup().addComponent(btnDownloadInstall) - .addPreferredGap(ComponentPlacement.RELATED, 184, Short.MAX_VALUE) - .addComponent(btnCancel))) - .addGap(10)) - .addComponent(pnlStatus, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)); - groupLayout.setVerticalGroup(groupLayout.createParallelGroup(Alignment.LEADING) - .addGroup(groupLayout.createSequentialGroup().addContainerGap() - .addComponent(lblInstallerInfo, GroupLayout.PREFERRED_SIZE, 60, GroupLayout.PREFERRED_SIZE) - .addGap(2).addComponent(separator, GroupLayout.PREFERRED_SIZE, 2, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(ComponentPlacement.UNRELATED) - .addComponent(pnlKspFolderPath, GroupLayout.PREFERRED_SIZE, 51, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(ComponentPlacement.UNRELATED) - .addGroup(groupLayout.createParallelGroup(Alignment.BASELINE).addComponent(btnDownloadInstall) - .addComponent(btnCancel)) - .addPreferredGap(ComponentPlacement.RELATED, 60, Short.MAX_VALUE) - .addComponent(pnlStatus, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))); - - lblStatus = new JLabel(); - GroupLayout glPnlStatus = new GroupLayout(pnlStatus); - glPnlStatus.setHorizontalGroup( - glPnlStatus.createParallelGroup(Alignment.LEADING).addGroup(glPnlStatus.createSequentialGroup() - .addContainerGap().addComponent(lblStatus).addContainerGap(389, Short.MAX_VALUE))); - glPnlStatus.setVerticalGroup(glPnlStatus.createParallelGroup(Alignment.TRAILING) - .addGroup(glPnlStatus.createSequentialGroup().addGap(2) - .addComponent(lblStatus, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(0))); - pnlStatus.setLayout(glPnlStatus); - - txfPath.setEditable(false); - txfPath.setColumns(10); - - btnBrowsePath = new JButton(Bundle.getString("installer_dialog_btn_browse")); //$NON-NLS-1$ - btnBrowsePath.addActionListener(e -> { - chooseKSPFolder(); - txfPath.setText(KrpcInstaller.getKspFolder()); + .addComponent( + separator, + Alignment.TRAILING, + GroupLayout.DEFAULT_SIZE, + 414, + Short.MAX_VALUE) + .addGroup( + groupLayout + .createSequentialGroup() + .addComponent(btnDownloadInstall) + .addPreferredGap( + ComponentPlacement.RELATED, 184, Short.MAX_VALUE) + .addComponent(btnCancel))) + .addGap(10)) + .addComponent( + pnlStatus, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)); + groupLayout.setVerticalGroup( + groupLayout + .createParallelGroup(Alignment.LEADING) + .addGroup( + groupLayout + .createSequentialGroup() + .addContainerGap() + .addComponent( + lblInstallerInfo, + GroupLayout.PREFERRED_SIZE, + 60, + GroupLayout.PREFERRED_SIZE) + .addGap(2) + .addComponent( + separator, GroupLayout.PREFERRED_SIZE, 2, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.UNRELATED) + .addComponent( + pnlKspFolderPath, + GroupLayout.PREFERRED_SIZE, + 51, + GroupLayout.PREFERRED_SIZE) + .addPreferredGap(ComponentPlacement.UNRELATED) + .addGroup( + groupLayout + .createParallelGroup(Alignment.BASELINE) + .addComponent(btnDownloadInstall) + .addComponent(btnCancel)) + .addPreferredGap(ComponentPlacement.RELATED, 60, Short.MAX_VALUE) + .addComponent( + pnlStatus, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))); + + lblStatus = new JLabel(); + GroupLayout glPnlStatus = new GroupLayout(pnlStatus); + glPnlStatus.setHorizontalGroup( + glPnlStatus + .createParallelGroup(Alignment.LEADING) + .addGroup( + glPnlStatus + .createSequentialGroup() + .addContainerGap() + .addComponent(lblStatus) + .addContainerGap(389, Short.MAX_VALUE))); + glPnlStatus.setVerticalGroup( + glPnlStatus + .createParallelGroup(Alignment.TRAILING) + .addGroup( + glPnlStatus + .createSequentialGroup() + .addGap(2) + .addComponent( + lblStatus, + GroupLayout.DEFAULT_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGap(0))); + pnlStatus.setLayout(glPnlStatus); + + txfPath.setEditable(false); + txfPath.setColumns(10); + + btnBrowsePath = new JButton(Bundle.getString("installer_dialog_btn_browse")); // $NON-NLS-1$ + btnBrowsePath.addActionListener( + e -> { + chooseKSPFolder(); + txfPath.setText(KrpcInstaller.getKspFolder()); }); - GroupLayout glPnlKspFolderPath = new GroupLayout(pnlKspFolderPath); - glPnlKspFolderPath.setHorizontalGroup( - glPnlKspFolderPath.createParallelGroup(Alignment.LEADING).addGroup(Alignment.TRAILING, - glPnlKspFolderPath.createSequentialGroup().addContainerGap() - .addComponent(txfPath, GroupLayout.DEFAULT_SIZE, 273, Short.MAX_VALUE) - .addPreferredGap(ComponentPlacement.RELATED).addComponent(btnBrowsePath, - GroupLayout.PREFERRED_SIZE, 103, GroupLayout.PREFERRED_SIZE) - .addContainerGap())); + GroupLayout glPnlKspFolderPath = new GroupLayout(pnlKspFolderPath); + glPnlKspFolderPath.setHorizontalGroup( glPnlKspFolderPath - .setVerticalGroup(glPnlKspFolderPath.createParallelGroup(Alignment.LEADING) - .addGroup(glPnlKspFolderPath.createSequentialGroup() - .addGroup(glPnlKspFolderPath.createParallelGroup(Alignment.BASELINE) - .addComponent(txfPath, GroupLayout.PREFERRED_SIZE, 23, - GroupLayout.PREFERRED_SIZE) - .addComponent(btnBrowsePath)) - .addContainerGap(24, Short.MAX_VALUE))); - pnlKspFolderPath.setLayout(glPnlKspFolderPath); - getContentPane().setLayout(groupLayout); - - setVisible(true); - } - - public void chooseKSPFolder() { - JFileChooser kspDir = new JFileChooser(); - kspDir.setDialogTitle("Escolha a pasta do KSP na Steam"); - kspDir.setMultiSelectionEnabled(false); - kspDir.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - int response = kspDir.showOpenDialog(this); - if (response == JFileChooser.APPROVE_OPTION) { - KrpcInstaller.setKspFolder(kspDir.getSelectedFile().getPath()); - btnDownloadInstall.setEnabled(true); - setStatus("Pasta escolhida, pronto para instalar."); - } else { - KrpcInstaller.setKspFolder(null); - btnDownloadInstall.setEnabled(false); - setStatus(""); - } + .createParallelGroup(Alignment.LEADING) + .addGroup( + Alignment.TRAILING, + glPnlKspFolderPath + .createSequentialGroup() + .addContainerGap() + .addComponent(txfPath, GroupLayout.DEFAULT_SIZE, 273, Short.MAX_VALUE) + .addPreferredGap(ComponentPlacement.RELATED) + .addComponent( + btnBrowsePath, GroupLayout.PREFERRED_SIZE, 103, GroupLayout.PREFERRED_SIZE) + .addContainerGap())); + glPnlKspFolderPath.setVerticalGroup( + glPnlKspFolderPath + .createParallelGroup(Alignment.LEADING) + .addGroup( + glPnlKspFolderPath + .createSequentialGroup() + .addGroup( + glPnlKspFolderPath + .createParallelGroup(Alignment.BASELINE) + .addComponent( + txfPath, GroupLayout.PREFERRED_SIZE, 23, GroupLayout.PREFERRED_SIZE) + .addComponent(btnBrowsePath)) + .addContainerGap(24, Short.MAX_VALUE))); + pnlKspFolderPath.setLayout(glPnlKspFolderPath); + getContentPane().setLayout(groupLayout); + + setVisible(true); + } + + public void chooseKSPFolder() { + JFileChooser kspDir = new JFileChooser(); + kspDir.setDialogTitle("Escolha a pasta do KSP na Steam"); + kspDir.setMultiSelectionEnabled(false); + kspDir.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int response = kspDir.showOpenDialog(this); + if (response == JFileChooser.APPROVE_OPTION) { + KrpcInstaller.setKspFolder(kspDir.getSelectedFile().getPath()); + btnDownloadInstall.setEnabled(true); + setStatus("Pasta escolhida, pronto para instalar."); + } else { + KrpcInstaller.setKspFolder(null); + btnDownloadInstall.setEnabled(false); + setStatus(""); } + } - public static void setStatus(String status) { - lblStatus.setText(status); - } + public static void setStatus(String status) { + lblStatus.setText(status); + } } diff --git a/src/main/java/com/pesterenan/views/LandingJPanel.java b/src/main/java/com/pesterenan/views/LandingJPanel.java index a547286..554e831 100644 --- a/src/main/java/com/pesterenan/views/LandingJPanel.java +++ b/src/main/java/com/pesterenan/views/LandingJPanel.java @@ -1,168 +1,201 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.EtchedBorder; -import javax.swing.border.TitledBorder; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import static com.pesterenan.views.MainGui.PNL_DIMENSION; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; public class LandingJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 1L; - private JLabel lblHoverAltitude, lblTWRLimit; - private JTextField txfHover, txfMaxTWR; - private JButton btnHover, btnAutoLanding, btnBack; - private JCheckBox chkHoverAfterLanding; - - public LandingJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); + private static final long serialVersionUID = 1L; + private JLabel lblHoverAltitude, lblTWRLimit; + private JTextField txfHover, txfMaxTWR; + private JButton btnHover, btnAutoLanding, btnBack; + private JCheckBox chkHoverAfterLanding; + private VesselManager vesselManager; + + private StatusDisplay statusDisplay; + + public LandingJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + } + + @Override + public void initComponents() { + // Labels: + lblHoverAltitude = new JLabel(Bundle.getString("pnl_land_lbl_alt")); + lblTWRLimit = new JLabel(Bundle.getString("pnl_common_lbl_limit_twr")); + + // Textfields: + txfHover = new JTextField("50"); + txfMaxTWR = new JTextField("5"); + + // Buttons: + btnHover = new JButton(Bundle.getString("pnl_land_btn_hover")); + btnAutoLanding = new JButton(Bundle.getString("pnl_land_btn_land")); + btnBack = new JButton(Bundle.getString("pnl_land_btn_back")); + + // Checkboxes: + chkHoverAfterLanding = new JCheckBox(Bundle.getString("pnl_land_hover_checkbox")); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setBorder( + new TitledBorder( + null, + Bundle.getString("pnl_land_border"), + TitledBorder.LEADING, + TitledBorder.TOP, + null, + null)); + + // Setting-up components: + txfHover.setPreferredSize(BTN_DIMENSION); + txfHover.setMaximumSize(BTN_DIMENSION); + txfHover.setHorizontalAlignment(JTextField.RIGHT); + txfMaxTWR.setPreferredSize(BTN_DIMENSION); + txfMaxTWR.setMaximumSize(BTN_DIMENSION); + txfMaxTWR.setHorizontalAlignment(JTextField.RIGHT); + + btnAutoLanding.addActionListener(this::handleLandingAction); + btnAutoLanding.setActionCommand(Module.LANDING.get()); + btnAutoLanding.setPreferredSize(BTN_DIMENSION); + btnAutoLanding.setMaximumSize(BTN_DIMENSION); + btnHover.addActionListener(this::handleLandingAction); + btnHover.setActionCommand(Module.HOVERING.get()); + btnHover.setPreferredSize(BTN_DIMENSION); + btnHover.setMaximumSize(BTN_DIMENSION); + btnBack.addActionListener(MainGui::backToTelemetry); + btnBack.setPreferredSize(BTN_DIMENSION); + btnBack.setMaximumSize(BTN_DIMENSION); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + // Laying out components: + JPanel pnlHoverControls = new JPanel(); + pnlHoverControls.setBorder(MARGIN_BORDER_10_PX_LR); + pnlHoverControls.setLayout(new BoxLayout(pnlHoverControls, BoxLayout.X_AXIS)); + pnlHoverControls.add(lblHoverAltitude); + pnlHoverControls.add(Box.createHorizontalGlue()); + pnlHoverControls.add(txfHover); + + JPanel pnlTWRLimitControls = new JPanel(); + pnlTWRLimitControls.setBorder(MARGIN_BORDER_10_PX_LR); + pnlTWRLimitControls.setLayout(new BoxLayout(pnlTWRLimitControls, BoxLayout.X_AXIS)); + pnlTWRLimitControls.add(lblTWRLimit); + pnlTWRLimitControls.add(Box.createHorizontalGlue()); + pnlTWRLimitControls.add(txfMaxTWR); + + JPanel pnlLandingControls = new JPanel(); + pnlLandingControls.setLayout(new BoxLayout(pnlLandingControls, BoxLayout.X_AXIS)); + Border titledEtched = + new TitledBorder( + new EtchedBorder( + EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)), + Bundle.getString("pnl_land_lbl_land"), + TitledBorder.LEADING, + TitledBorder.TOP, + null, + new Color(0, 0, 0)); + Border combined = BorderFactory.createCompoundBorder(titledEtched, MARGIN_BORDER_10_PX_LR); + pnlLandingControls.setBorder(combined); + pnlLandingControls.add(btnAutoLanding); + pnlLandingControls.add(Box.createHorizontalGlue()); + pnlLandingControls.add(btnHover); + + JPanel pnlControls = new JPanel(); + pnlControls.setLayout(new BoxLayout(pnlControls, BoxLayout.Y_AXIS)); + pnlControls.add(MainGui.createMarginComponent(0, 6)); + pnlControls.add(pnlHoverControls); + pnlControls.add(pnlTWRLimitControls); + pnlControls.add(pnlLandingControls); + + JPanel pnlOptions = new JPanel(); + pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); + pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); + pnlOptions.add(chkHoverAfterLanding); + pnlOptions.add(Box.createHorizontalGlue()); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlControls.setAlignmentY(TOP_ALIGNMENT); + pnlOptions.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlControls); + pnlMain.add(pnlOptions); + + JPanel pnlBackbtn = new JPanel(); + pnlBackbtn.setLayout(new BoxLayout(pnlBackbtn, BoxLayout.X_AXIS)); + pnlBackbtn.add(Box.createHorizontalGlue()); + pnlBackbtn.add(btnBack); + + add(pnlMain, BorderLayout.CENTER); + add(pnlBackbtn, BorderLayout.SOUTH); + } + + private void handleLandingAction(ActionEvent e) { + if (vesselManager == null) { + statusDisplay.setStatusMessage("Conexão não estabelecida ao pousar."); + return; } - - @Override - public void initComponents() { - // Labels: - lblHoverAltitude = new JLabel(Bundle.getString("pnl_land_lbl_alt")); - lblTWRLimit = new JLabel(Bundle.getString("pnl_common_lbl_limit_twr")); - - // Textfields: - txfHover = new JTextField("50"); - txfMaxTWR = new JTextField("5"); - - // Buttons: - btnHover = new JButton(Bundle.getString("pnl_land_btn_hover")); - btnAutoLanding = new JButton(Bundle.getString("pnl_land_btn_land")); - btnBack = new JButton(Bundle.getString("pnl_land_btn_back")); - - // Checkboxes: - chkHoverAfterLanding = new JCheckBox(Bundle.getString("pnl_land_hover_checkbox")); + try { + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), e.getActionCommand()); + validateTextFields(); + commands.put(Module.HOVER_ALTITUDE.get(), txfHover.getText()); + commands.put(Module.MAX_TWR.get(), txfMaxTWR.getText()); + commands.put( + Module.HOVER_AFTER_LANDING.get(), String.valueOf(chkHoverAfterLanding.isSelected())); + vesselManager.startModule(commands); + MainGui.backToTelemetry(e); + chkHoverAfterLanding.setSelected(false); + } catch (NumberFormatException nfe) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_land_hover_alt_err")); + } catch (NullPointerException npe) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_land_hover_alt")); } - - @Override - public void setupComponents() { - // Main Panel setup: - setBorder(new TitledBorder(null, Bundle.getString("pnl_land_border"), TitledBorder.LEADING, TitledBorder.TOP, - null, null)); - - // Setting-up components: - txfHover.setPreferredSize(BTN_DIMENSION); - txfHover.setMaximumSize(BTN_DIMENSION); - txfHover.setHorizontalAlignment(JTextField.RIGHT); - txfMaxTWR.setPreferredSize(BTN_DIMENSION); - txfMaxTWR.setMaximumSize(BTN_DIMENSION); - txfMaxTWR.setHorizontalAlignment(JTextField.RIGHT); - - btnAutoLanding.addActionListener(this::handleLandingAction); - btnAutoLanding.setActionCommand(Module.LANDING.get()); - btnAutoLanding.setPreferredSize(BTN_DIMENSION); - btnAutoLanding.setMaximumSize(BTN_DIMENSION); - btnHover.addActionListener(this::handleLandingAction); - btnHover.setActionCommand(Module.HOVERING.get()); - btnHover.setPreferredSize(BTN_DIMENSION); - btnHover.setMaximumSize(BTN_DIMENSION); - btnBack.addActionListener(MainGui::backToTelemetry); - btnBack.setPreferredSize(BTN_DIMENSION); - btnBack.setMaximumSize(BTN_DIMENSION); - } - - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - // Laying out components: - JPanel pnlHoverControls = new JPanel(); - pnlHoverControls.setBorder(MARGIN_BORDER_10_PX_LR); - pnlHoverControls.setLayout(new BoxLayout(pnlHoverControls, BoxLayout.X_AXIS)); - pnlHoverControls.add(lblHoverAltitude); - pnlHoverControls.add(Box.createHorizontalGlue()); - pnlHoverControls.add(txfHover); - - JPanel pnlTWRLimitControls = new JPanel(); - pnlTWRLimitControls.setBorder(MARGIN_BORDER_10_PX_LR); - pnlTWRLimitControls.setLayout(new BoxLayout(pnlTWRLimitControls, BoxLayout.X_AXIS)); - pnlTWRLimitControls.add(lblTWRLimit); - pnlTWRLimitControls.add(Box.createHorizontalGlue()); - pnlTWRLimitControls.add(txfMaxTWR); - - JPanel pnlLandingControls = new JPanel(); - pnlLandingControls.setLayout(new BoxLayout(pnlLandingControls, BoxLayout.X_AXIS)); - Border titledEtched = new TitledBorder( - new EtchedBorder(EtchedBorder.LOWERED, new Color(255, 255, 255), new Color(160, 160, 160)), - Bundle.getString("pnl_land_lbl_land"), TitledBorder.LEADING, TitledBorder.TOP, null, - new Color(0, 0, 0)); - Border combined = BorderFactory.createCompoundBorder(titledEtched, MARGIN_BORDER_10_PX_LR); - pnlLandingControls.setBorder(combined); - pnlLandingControls.add(btnAutoLanding); - pnlLandingControls.add(Box.createHorizontalGlue()); - pnlLandingControls.add(btnHover); - - JPanel pnlControls = new JPanel(); - pnlControls.setLayout(new BoxLayout(pnlControls, BoxLayout.Y_AXIS)); - pnlControls.add(MainGui.createMarginComponent(0, 6)); - pnlControls.add(pnlHoverControls); - pnlControls.add(pnlTWRLimitControls); - pnlControls.add(pnlLandingControls); - - JPanel pnlOptions = new JPanel(); - pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); - pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); - pnlOptions.add(chkHoverAfterLanding); - pnlOptions.add(Box.createHorizontalGlue()); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlControls.setAlignmentY(TOP_ALIGNMENT); - pnlOptions.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlControls); - pnlMain.add(pnlOptions); - - JPanel pnlBackbtn = new JPanel(); - pnlBackbtn.setLayout(new BoxLayout(pnlBackbtn, BoxLayout.X_AXIS)); - pnlBackbtn.add(Box.createHorizontalGlue()); - pnlBackbtn.add(btnBack); - - add(pnlMain, BorderLayout.CENTER); - add(pnlBackbtn, BorderLayout.SOUTH); - } - - private void handleLandingAction(ActionEvent e) { - try { - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), e.getActionCommand()); - validateTextFields(); - commands.put(Module.HOVER_ALTITUDE.get(), txfHover.getText()); - commands.put(Module.MAX_TWR.get(), txfMaxTWR.getText()); - commands.put(Module.HOVER_AFTER_LANDING.get(), String.valueOf(chkHoverAfterLanding.isSelected())); - MechPeste.newInstance().startModule(commands); - MainGui.backToTelemetry(e); - chkHoverAfterLanding.setSelected(false); - } catch (NumberFormatException nfe) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_land_hover_alt_err")); - } catch (NullPointerException npe) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_land_hover_alt")); - } - } - - private void validateTextFields() throws NumberFormatException, NullPointerException { - if (txfHover.getText().equals("") || txfHover.getText().equals("0") || txfMaxTWR.getText().equals("") - || txfMaxTWR.getText().equals("0")) { - throw new NullPointerException(); - } - Float.parseFloat(txfHover.getText()); - Float.parseFloat(txfMaxTWR.getText()); + } + + private void validateTextFields() throws NumberFormatException, NullPointerException { + if (txfHover.getText().equals("") + || txfHover.getText().equals("0") + || txfMaxTWR.getText().equals("") + || txfMaxTWR.getText().equals("0")) { + throw new NullPointerException(); } + Float.parseFloat(txfHover.getText()); + Float.parseFloat(txfMaxTWR.getText()); + } } diff --git a/src/main/java/com/pesterenan/views/LiftoffJPanel.java b/src/main/java/com/pesterenan/views/LiftoffJPanel.java index b7b0e10..220fdb9 100644 --- a/src/main/java/com/pesterenan/views/LiftoffJPanel.java +++ b/src/main/java/com/pesterenan/views/LiftoffJPanel.java @@ -1,214 +1,251 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import static com.pesterenan.views.MainGui.PNL_DIMENSION; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; public class LiftoffJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 1L; - - private JLabel lblFinalApoapsis, lblHeading, lblRoll, lblCurveModel, lblLimitTWR; - private JTextField txfFinalApoapsis, txfHeading, txfLimitTWR; - private JButton btnLiftoff, btnBack; - private JComboBox cbGravityCurveModel; - private JSlider sldRoll; - private JCheckBox chkOpenPanels, chkDecoupleStages; - - public LiftoffJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblFinalApoapsis = new JLabel(Bundle.getString("pnl_lift_lbl_final_apoapsis")); - lblHeading = new JLabel(Bundle.getString("pnl_lift_lbl_direction")); - lblRoll = new JLabel(Bundle.getString("pnl_lift_lbl_roll")); - lblRoll.setToolTipText(Bundle.getString("pnl_lift_lbl_roll_tooltip")); - lblCurveModel = new JLabel(Bundle.getString("pnl_lift_lbl_gravity_curve")); - lblLimitTWR = new JLabel(Bundle.getString("pnl_common_lbl_limit_twr")); - - // Textfields: - txfFinalApoapsis = new JTextField("80000"); - txfFinalApoapsis.setToolTipText(Bundle.getString("pnl_lift_txf_final_apo_tooltip")); - txfHeading = new JTextField("90"); - txfLimitTWR = new JTextField("2.2"); - - // Buttons: - btnLiftoff = new JButton(Bundle.getString("pnl_lift_btn_liftoff")); - btnBack = new JButton(Bundle.getString("pnl_lift_btn_back")); - - // Checkboxes: - chkOpenPanels = new JCheckBox(Bundle.getString("pnl_lift_chk_open_panels")); - chkOpenPanels.setToolTipText(Bundle.getString("pnl_lift_chk_open_panels_tooltip")); - chkDecoupleStages = new JCheckBox(Bundle.getString("pnl_lift_chk_staging")); - chkDecoupleStages.setToolTipText(Bundle.getString("pnl_lift_chk_staging_tooltip")); - - // Misc: - cbGravityCurveModel = new JComboBox<>(); - cbGravityCurveModel.setToolTipText(Bundle.getString("pnl_lift_cb_gravity_curve_tooltip")); - - sldRoll = new JSlider(); + private static final long serialVersionUID = 1L; + + private JLabel lblFinalApoapsis, lblHeading, lblRoll, lblCurveModel, lblLimitTWR; + private JTextField txfFinalApoapsis, txfHeading, txfLimitTWR; + private JButton btnLiftoff, btnBack; + private JComboBox cbGravityCurveModel; + private JSlider sldRoll; + private JCheckBox chkOpenPanels, chkDecoupleStages; + private VesselManager vesselManager; + + private StatusDisplay statusDisplay; + + public LiftoffJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + btnLiftoff.setEnabled(true); + } + + @Override + public void initComponents() { + // Labels: + lblFinalApoapsis = new JLabel(Bundle.getString("pnl_lift_lbl_final_apoapsis")); + lblHeading = new JLabel(Bundle.getString("pnl_lift_lbl_direction")); + lblRoll = new JLabel(Bundle.getString("pnl_lift_lbl_roll")); + lblRoll.setToolTipText(Bundle.getString("pnl_lift_lbl_roll_tooltip")); + lblCurveModel = new JLabel(Bundle.getString("pnl_lift_lbl_gravity_curve")); + lblLimitTWR = new JLabel(Bundle.getString("pnl_common_lbl_limit_twr")); + + // Textfields: + txfFinalApoapsis = new JTextField("80000"); + txfFinalApoapsis.setToolTipText(Bundle.getString("pnl_lift_txf_final_apo_tooltip")); + txfHeading = new JTextField("90"); + txfLimitTWR = new JTextField("2.2"); + + // Buttons: + btnLiftoff = new JButton(Bundle.getString("pnl_lift_btn_liftoff")); + btnLiftoff.setEnabled(false); + btnBack = new JButton(Bundle.getString("pnl_lift_btn_back")); + + // Checkboxes: + chkOpenPanels = new JCheckBox(Bundle.getString("pnl_lift_chk_open_panels")); + chkOpenPanels.setToolTipText(Bundle.getString("pnl_lift_chk_open_panels_tooltip")); + chkDecoupleStages = new JCheckBox(Bundle.getString("pnl_lift_chk_staging")); + chkDecoupleStages.setToolTipText(Bundle.getString("pnl_lift_chk_staging_tooltip")); + + // Misc: + cbGravityCurveModel = new JComboBox<>(); + cbGravityCurveModel.setToolTipText(Bundle.getString("pnl_lift_cb_gravity_curve_tooltip")); + + sldRoll = new JSlider(); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setBorder( + new TitledBorder( + null, + Bundle.getString("pnl_lift_pnl_title"), + TitledBorder.LEADING, + TitledBorder.TOP, + null, + null)); + + // Setting-up components: + lblFinalApoapsis.setLabelFor(txfFinalApoapsis); + txfFinalApoapsis.setMaximumSize(BTN_DIMENSION); + txfFinalApoapsis.setPreferredSize(BTN_DIMENSION); + txfFinalApoapsis.setHorizontalAlignment(JTextField.RIGHT); + lblHeading.setLabelFor(txfHeading); + txfHeading.setMaximumSize(BTN_DIMENSION); + txfHeading.setPreferredSize(BTN_DIMENSION); + txfHeading.setHorizontalAlignment(JTextField.RIGHT); + lblLimitTWR.setLabelFor(txfLimitTWR); + txfLimitTWR.setMaximumSize(BTN_DIMENSION); + txfLimitTWR.setPreferredSize(BTN_DIMENSION); + txfLimitTWR.setHorizontalAlignment(JTextField.RIGHT); + + cbGravityCurveModel.setModel( + new DefaultComboBoxModel<>( + new String[] { + Module.SINUSOIDAL.get(), + Module.QUADRATIC.get(), + Module.CUBIC.get(), + Module.CIRCULAR.get(), + Module.EXPONENCIAL.get() + })); + cbGravityCurveModel.setSelectedIndex(3); + cbGravityCurveModel.setPreferredSize(BTN_DIMENSION); + cbGravityCurveModel.setMaximumSize(BTN_DIMENSION); + + sldRoll.setPaintLabels(true); + sldRoll.setMajorTickSpacing(90); + sldRoll.setMaximum(270); + sldRoll.setSnapToTicks(true); + sldRoll.setValue(90); + sldRoll.setPreferredSize(new Dimension(110, 40)); + sldRoll.setMaximumSize(new Dimension(110, 40)); + + chkDecoupleStages.setSelected(true); + + btnLiftoff.addActionListener(this::handleLiftoff); + btnLiftoff.setPreferredSize(BTN_DIMENSION); + btnLiftoff.setMaximumSize(BTN_DIMENSION); + btnBack.addActionListener(MainGui::backToTelemetry); + btnBack.setPreferredSize(BTN_DIMENSION); + btnBack.setMaximumSize(BTN_DIMENSION); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + // Laying out components: + JPanel pnlFinalApoapsis = new JPanel(); + pnlFinalApoapsis.setLayout(new BoxLayout(pnlFinalApoapsis, BoxLayout.X_AXIS)); + pnlFinalApoapsis.setBorder(MARGIN_BORDER_10_PX_LR); + pnlFinalApoapsis.add(lblFinalApoapsis); + pnlFinalApoapsis.add(Box.createHorizontalGlue()); + pnlFinalApoapsis.add(txfFinalApoapsis); + + JPanel pnlHeading = new JPanel(); + pnlHeading.setLayout(new BoxLayout(pnlHeading, BoxLayout.X_AXIS)); + pnlHeading.setBorder(MARGIN_BORDER_10_PX_LR); + pnlHeading.add(lblHeading); + pnlHeading.add(Box.createHorizontalGlue()); + pnlHeading.add(txfHeading); + + JPanel pnlRoll = new JPanel(); + pnlRoll.setLayout(new BoxLayout(pnlRoll, BoxLayout.X_AXIS)); + pnlRoll.setBorder(MARGIN_BORDER_10_PX_LR); + pnlRoll.add(lblRoll); + pnlRoll.add(Box.createHorizontalGlue()); + pnlRoll.add(sldRoll); + + JPanel pnlLimitTWR = new JPanel(); + pnlLimitTWR.setLayout(new BoxLayout(pnlLimitTWR, BoxLayout.X_AXIS)); + pnlLimitTWR.setBorder(MARGIN_BORDER_10_PX_LR); + pnlLimitTWR.add(lblLimitTWR); + pnlLimitTWR.add(Box.createHorizontalGlue()); + pnlLimitTWR.add(txfLimitTWR); + + JPanel pnlCurveModel = new JPanel(); + pnlCurveModel.setLayout(new BoxLayout(pnlCurveModel, BoxLayout.X_AXIS)); + pnlCurveModel.setBorder(MARGIN_BORDER_10_PX_LR); + pnlCurveModel.add(lblCurveModel); + pnlCurveModel.add(Box.createHorizontalGlue()); + pnlCurveModel.add(cbGravityCurveModel); + + JPanel pnlButtons = new JPanel(); + pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); + pnlButtons.add(btnLiftoff); + pnlButtons.add(Box.createHorizontalGlue()); + pnlButtons.add(btnBack); + + JPanel pnlSetup = new JPanel(); + pnlSetup.setLayout(new BoxLayout(pnlSetup, BoxLayout.Y_AXIS)); + pnlSetup.add(MainGui.createMarginComponent(0, 6)); + pnlSetup.add(pnlFinalApoapsis); + pnlSetup.add(pnlHeading); + pnlSetup.add(pnlRoll); + pnlSetup.add(pnlLimitTWR); + pnlSetup.add(pnlCurveModel); + + JPanel pnlOptions = new JPanel(); + pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); + pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); + pnlOptions.add(chkDecoupleStages); + pnlOptions.add(chkOpenPanels); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlMain.add(pnlSetup); + pnlSetup.setAlignmentY(Component.TOP_ALIGNMENT); + pnlMain.add(pnlOptions); + pnlOptions.setAlignmentY(Component.TOP_ALIGNMENT); + + add(pnlMain, BorderLayout.CENTER); + add(pnlButtons, BorderLayout.SOUTH); + } + + private boolean validateTextFields() { + try { + Float.parseFloat(txfFinalApoapsis.getText()); + Float.parseFloat(txfHeading.getText()); + Float.parseFloat(txfLimitTWR.getText()); + } catch (NumberFormatException e) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_lift_stat_only_numbers")); + return false; } + return true; + } - @Override - public void setupComponents() { - // Main Panel setup: - setBorder(new TitledBorder(null, Bundle.getString("pnl_lift_pnl_title"), TitledBorder.LEADING, TitledBorder.TOP, - null, null)); - - // Setting-up components: - lblFinalApoapsis.setLabelFor(txfFinalApoapsis); - txfFinalApoapsis.setMaximumSize(BTN_DIMENSION); - txfFinalApoapsis.setPreferredSize(BTN_DIMENSION); - txfFinalApoapsis.setHorizontalAlignment(JTextField.RIGHT); - lblHeading.setLabelFor(txfHeading); - txfHeading.setMaximumSize(BTN_DIMENSION); - txfHeading.setPreferredSize(BTN_DIMENSION); - txfHeading.setHorizontalAlignment(JTextField.RIGHT); - lblLimitTWR.setLabelFor(txfLimitTWR); - txfLimitTWR.setMaximumSize(BTN_DIMENSION); - txfLimitTWR.setPreferredSize(BTN_DIMENSION); - txfLimitTWR.setHorizontalAlignment(JTextField.RIGHT); - - cbGravityCurveModel.setModel(new DefaultComboBoxModel<>(new String[]{Module.SINUSOIDAL.get(), - Module.QUADRATIC.get(), Module.CUBIC.get(), Module.CIRCULAR.get(), Module.EXPONENCIAL.get()})); - cbGravityCurveModel.setSelectedIndex(3); - cbGravityCurveModel.setPreferredSize(BTN_DIMENSION); - cbGravityCurveModel.setMaximumSize(BTN_DIMENSION); - - sldRoll.setPaintLabels(true); - sldRoll.setMajorTickSpacing(90); - sldRoll.setMaximum(270); - sldRoll.setSnapToTicks(true); - sldRoll.setValue(90); - sldRoll.setPreferredSize(new Dimension(110, 40)); - sldRoll.setMaximumSize(new Dimension(110, 40)); - - chkDecoupleStages.setSelected(true); - - btnLiftoff.addActionListener(this::handleLiftoff); - btnLiftoff.setPreferredSize(BTN_DIMENSION); - btnLiftoff.setMaximumSize(BTN_DIMENSION); - btnBack.addActionListener(MainGui::backToTelemetry); - btnBack.setPreferredSize(BTN_DIMENSION); - btnBack.setMaximumSize(BTN_DIMENSION); + private void handleLiftoff(ActionEvent e) { + if (vesselManager == null) { + statusDisplay.setStatusMessage("Conexão não estabelecida."); + return; } - - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - // Laying out components: - JPanel pnlFinalApoapsis = new JPanel(); - pnlFinalApoapsis.setLayout(new BoxLayout(pnlFinalApoapsis, BoxLayout.X_AXIS)); - pnlFinalApoapsis.setBorder(MARGIN_BORDER_10_PX_LR); - pnlFinalApoapsis.add(lblFinalApoapsis); - pnlFinalApoapsis.add(Box.createHorizontalGlue()); - pnlFinalApoapsis.add(txfFinalApoapsis); - - JPanel pnlHeading = new JPanel(); - pnlHeading.setLayout(new BoxLayout(pnlHeading, BoxLayout.X_AXIS)); - pnlHeading.setBorder(MARGIN_BORDER_10_PX_LR); - pnlHeading.add(lblHeading); - pnlHeading.add(Box.createHorizontalGlue()); - pnlHeading.add(txfHeading); - - JPanel pnlRoll = new JPanel(); - pnlRoll.setLayout(new BoxLayout(pnlRoll, BoxLayout.X_AXIS)); - pnlRoll.setBorder(MARGIN_BORDER_10_PX_LR); - pnlRoll.add(lblRoll); - pnlRoll.add(Box.createHorizontalGlue()); - pnlRoll.add(sldRoll); - - JPanel pnlLimitTWR = new JPanel(); - pnlLimitTWR.setLayout(new BoxLayout(pnlLimitTWR, BoxLayout.X_AXIS)); - pnlLimitTWR.setBorder(MARGIN_BORDER_10_PX_LR); - pnlLimitTWR.add(lblLimitTWR); - pnlLimitTWR.add(Box.createHorizontalGlue()); - pnlLimitTWR.add(txfLimitTWR); - - JPanel pnlCurveModel = new JPanel(); - pnlCurveModel.setLayout(new BoxLayout(pnlCurveModel, BoxLayout.X_AXIS)); - pnlCurveModel.setBorder(MARGIN_BORDER_10_PX_LR); - pnlCurveModel.add(lblCurveModel); - pnlCurveModel.add(Box.createHorizontalGlue()); - pnlCurveModel.add(cbGravityCurveModel); - - JPanel pnlButtons = new JPanel(); - pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); - pnlButtons.add(btnLiftoff); - pnlButtons.add(Box.createHorizontalGlue()); - pnlButtons.add(btnBack); - - JPanel pnlSetup = new JPanel(); - pnlSetup.setLayout(new BoxLayout(pnlSetup, BoxLayout.Y_AXIS)); - pnlSetup.add(MainGui.createMarginComponent(0, 6)); - pnlSetup.add(pnlFinalApoapsis); - pnlSetup.add(pnlHeading); - pnlSetup.add(pnlRoll); - pnlSetup.add(pnlLimitTWR); - pnlSetup.add(pnlCurveModel); - - JPanel pnlOptions = new JPanel(); - pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); - pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); - pnlOptions.add(chkDecoupleStages); - pnlOptions.add(chkOpenPanels); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlMain.add(pnlSetup); - pnlSetup.setAlignmentY(Component.TOP_ALIGNMENT); - pnlMain.add(pnlOptions); - pnlOptions.setAlignmentY(Component.TOP_ALIGNMENT); - - add(pnlMain, BorderLayout.CENTER); - add(pnlButtons, BorderLayout.SOUTH); - } - - private boolean validateTextFields() { - try { - Float.parseFloat(txfFinalApoapsis.getText()); - Float.parseFloat(txfHeading.getText()); - Float.parseFloat(txfLimitTWR.getText()); - } catch (NumberFormatException e) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_lift_stat_only_numbers")); - return false; - } - return true; - } - - private void handleLiftoff(ActionEvent e) { - if (validateTextFields()) { - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), Module.LIFTOFF.get()); - commands.put(Module.APOAPSIS.get(), txfFinalApoapsis.getText()); - commands.put(Module.DIRECTION.get(), txfHeading.getText()); - commands.put(Module.MAX_TWR.get(), txfLimitTWR.getText()); - commands.put(Module.ROLL.get(), String.valueOf(sldRoll.getValue())); - commands.put(Module.INCLINATION.get(), cbGravityCurveModel.getSelectedItem().toString()); - commands.put(Module.STAGE.get(), String.valueOf(chkDecoupleStages.isSelected())); - commands.put(Module.OPEN_PANELS.get(), String.valueOf(chkOpenPanels.isSelected())); - MechPeste.newInstance().startModule(commands); - MainGui.backToTelemetry(e); - } + if (validateTextFields()) { + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), Module.LIFTOFF.get()); + commands.put(Module.APOAPSIS.get(), txfFinalApoapsis.getText()); + commands.put(Module.DIRECTION.get(), txfHeading.getText()); + commands.put(Module.MAX_TWR.get(), txfLimitTWR.getText()); + commands.put(Module.ROLL.get(), String.valueOf(sldRoll.getValue())); + commands.put(Module.INCLINATION.get(), cbGravityCurveModel.getSelectedItem().toString()); + commands.put(Module.STAGE.get(), String.valueOf(chkDecoupleStages.isSelected())); + commands.put(Module.OPEN_PANELS.get(), String.valueOf(chkOpenPanels.isSelected())); + vesselManager.startModule(commands); + MainGui.backToTelemetry(e); } + } } diff --git a/src/main/java/com/pesterenan/views/MainGui.java b/src/main/java/com/pesterenan/views/MainGui.java index fa79424..e9ce014 100644 --- a/src/main/java/com/pesterenan/views/MainGui.java +++ b/src/main/java/com/pesterenan/views/MainGui.java @@ -1,195 +1,258 @@ package com.pesterenan.views; +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; - -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import javax.swing.Box; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JTabbedPane; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; public class MainGui extends JFrame implements ActionListener, UIMethods { - private static final long serialVersionUID = 1L; - - private final Dimension APP_DIMENSION = new Dimension(480, 300); - public static final Dimension PNL_DIMENSION = new Dimension(464, 216); - public static final Dimension BTN_DIMENSION = new Dimension(110, 25); - public static final EmptyBorder MARGIN_BORDER_10_PX_LR = new EmptyBorder(0, 10, 0, 10); - private static MainGui mainGui = null; - private static StatusJPanel pnlStatus; - private static FunctionsAndTelemetryJPanel pnlFunctionsAndTelemetry; - private final JPanel ctpMainGui = new JPanel(); - private final static JPanel cardJPanels = new JPanel(); - private JMenuBar menuBar; - private JMenu mnFile, mnOptions, mnHelp; - private JMenuItem mntmInstallKrpc, mntmExit, mntmChangeVessels, mntmAbout; - - private final static CardLayout cardLayout = new CardLayout(0, 0); - private LiftoffJPanel pnlLiftoff; - private LandingJPanel pnlLanding; - private CreateManeuverJPanel pnlCreateManeuvers; - private RunManeuverJPanel pnlRunManeuvers; - private RoverJPanel pnlRover; - private DockingJPanel pnlDocking; - - private MainGui() { - initComponents(); - setupComponents(); - layoutComponents(); - } + private static final long serialVersionUID = 1L; - public static MainGui newInstance() { - if (mainGui == null) { - mainGui = new MainGui(); - } - return mainGui; - } + public static final Dimension PNL_DIMENSION = new Dimension(464, 216); + public static final Dimension BTN_DIMENSION = new Dimension(110, 25); + public static final EmptyBorder MARGIN_BORDER_10_PX_LR = new EmptyBorder(0, 10, 0, 10); + private static MainGui instance = null; - @Override - public void initComponents() { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ignored) { - } - - // Menu bar - menuBar = new JMenuBar(); - - // Menus - mnFile = new JMenu(Bundle.getString("main_mn_file")); - mnOptions = new JMenu(Bundle.getString("main_mn_options")); - mnHelp = new JMenu(Bundle.getString("main_mn_help")); - - // Menu Items - mntmInstallKrpc = new JMenuItem(Bundle.getString("main_mntm_install_krpc")); - mntmChangeVessels = new JMenuItem(Bundle.getString("main_mntm_change_vessels")); - mntmExit = new JMenuItem(Bundle.getString("main_mntm_exit")); - mntmAbout = new JMenuItem(Bundle.getString("main_mntm_about")); - - // Panels - pnlFunctionsAndTelemetry = new FunctionsAndTelemetryJPanel(); - pnlLiftoff = new LiftoffJPanel(); - pnlLanding = new LandingJPanel(); - pnlCreateManeuvers = new CreateManeuverJPanel(); - pnlRunManeuvers = new RunManeuverJPanel(); - pnlRover = new RoverJPanel(); - pnlDocking = new DockingJPanel(); - pnlStatus = new StatusJPanel(); - } + private static final JPanel cardJPanels = new JPanel(); - @Override - public void setupComponents() { - // Main Panel setup: - setAlwaysOnTop(true); - setTitle("MechPeste - Pesterenan"); - setJMenuBar(menuBar); - setResizable(false); - setLocation(100, 100); - setContentPane(ctpMainGui); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - // Setting-up components: - mntmAbout.addActionListener(this); - mntmChangeVessels.addActionListener(this); - mntmExit.addActionListener(this); - mntmInstallKrpc.addActionListener(this); - - cardJPanels.setPreferredSize(PNL_DIMENSION); - cardJPanels.setSize(PNL_DIMENSION); - } + private static final CardLayout cardLayout = new CardLayout(0, 0); - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(APP_DIMENSION); - setSize(APP_DIMENSION); - setVisible(true); - - // Laying out components: - ctpMainGui.setLayout(new BorderLayout()); - ctpMainGui.add(cardJPanels, BorderLayout.CENTER); - ctpMainGui.add(pnlStatus, BorderLayout.SOUTH); - - mnFile.add(mntmInstallKrpc); - mnFile.add(new JSeparator()); - mnFile.add(mntmExit); - mnOptions.add(mntmChangeVessels); - mnHelp.add(mntmAbout); - menuBar.add(mnFile); - menuBar.add(mnOptions); - menuBar.add(mnHelp); - - JTabbedPane pnlManeuverJTabbedPane = new JTabbedPane(); - pnlManeuverJTabbedPane.addTab("Criar Manobras", pnlCreateManeuvers); - pnlManeuverJTabbedPane.addTab("Executar Manobras", pnlRunManeuvers); - - cardJPanels.setLayout(cardLayout); - cardJPanels.add(pnlFunctionsAndTelemetry, Module.TELEMETRY.get()); - cardJPanels.add(pnlLiftoff, Module.LIFTOFF.get()); - cardJPanels.add(pnlLanding, Module.LANDING.get()); - cardJPanels.add(pnlManeuverJTabbedPane, Module.MANEUVER.get()); - cardJPanels.add(pnlRover, Module.ROVER.get()); - cardJPanels.add(pnlDocking, Module.DOCKING.get()); - } + public static MainGui getInstance() { + return instance; + } - public void actionPerformed(ActionEvent e) { - if (e.getSource() == mntmAbout) { - handleMntmAboutActionPerformed(e); - } - if (e.getSource() == mntmInstallKrpc) { - handleMntmInstallKrpcActionPerformed(e); - } - if (e.getSource() == mntmExit) { - handleMntmExitActionPerformed(e); - } - if (e.getSource() == mntmChangeVessels) { - handleMntmMultiControlActionPerformed(e); - } + public static MainGui newInstance() { + if (instance == null) { + instance = new MainGui(); } - - private void handleMntmMultiControlActionPerformed(ActionEvent e) { - new ChangeVesselDialog(); + return instance; + } + + public static Rectangle centerDialogOnScreen() { + Dimension SCREEN_DIMENSIONS = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension DIALOG_DIMENSIONS = new Dimension(400, 240); + int w = DIALOG_DIMENSIONS.width; + int h = DIALOG_DIMENSIONS.height; + int x = (SCREEN_DIMENSIONS.width - w) / 2; + int y = (SCREEN_DIMENSIONS.height - h) / 2; + return new Rectangle(x, y, w, h); + } + + public static JPanel getCardJPanels() { + return cardJPanels; + } + + public static void changeToPage(ActionEvent e) { + cardLayout.show(cardJPanels, e.getActionCommand()); + } + + public static void backToTelemetry(ActionEvent e) { + cardLayout.show(cardJPanels, Module.TELEMETRY.get()); + } + + public static Component createMarginComponent(int width, int height) { + Component marginComp = Box.createRigidArea(new Dimension(width, height)); + return marginComp; + } + + private FunctionsAndTelemetryJPanel pnlFunctionsAndTelemetry; + + public FunctionsAndTelemetryJPanel getTelemetryPanel() { + return pnlFunctionsAndTelemetry; + } + + private StatusJPanel pnlStatus; + + private final Dimension APP_DIMENSION = new Dimension(480, 300); + + private final JPanel ctpMainGui = new JPanel(); + + private JMenuBar menuBar; + private JMenu mnFile, mnOptions, mnHelp; + private JMenuItem mntmInstallKrpc, mntmExit, mntmChangeVessels, mntmAbout; + private LiftoffJPanel pnlLiftoff; + private LandingJPanel pnlLanding; + + private CreateManeuverJPanel pnlCreateManeuvers; + + private RunManeuverJPanel pnlRunManeuvers; + + private RoverJPanel pnlRover; + + private DockingJPanel pnlDocking; + + private VesselManager vesselManager; + + private MainGui() { + initComponents(); + setupComponents(); + layoutComponents(); + } + + public FunctionsAndTelemetryJPanel getFunctionsAndTelemetryPanel() { + return pnlFunctionsAndTelemetry; + } + + public StatusJPanel getStatusPanel() { + return pnlStatus; + } + + public LiftoffJPanel getLiftoffPanel() { + return pnlLiftoff; + } + + public LandingJPanel getLandingPanel() { + return pnlLanding; + } + + public CreateManeuverJPanel getCreateManeuverPanel() { + return pnlCreateManeuvers; + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + pnlFunctionsAndTelemetry.setVesselManager(vesselManager); + pnlDocking.setVesselManager(vesselManager); + pnlRover.setVesselManager(vesselManager); + pnlLiftoff.setVesselManager(vesselManager); + pnlLanding.setVesselManager(vesselManager); + pnlCreateManeuvers.setVesselManager(vesselManager); + pnlRunManeuvers.setVesselManager(vesselManager); + } + + @Override + public void initComponents() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ignored) { } - protected void handleMntmInstallKrpcActionPerformed(ActionEvent e) { - new InstallKrpcDialog(); + // Menu bar + menuBar = new JMenuBar(); + + // Menus + mnFile = new JMenu(Bundle.getString("main_mn_file")); + mnOptions = new JMenu(Bundle.getString("main_mn_options")); + mnHelp = new JMenu(Bundle.getString("main_mn_help")); + + // Menu Items + mntmInstallKrpc = new JMenuItem(Bundle.getString("main_mntm_install_krpc")); + mntmChangeVessels = new JMenuItem(Bundle.getString("main_mntm_change_vessels")); + mntmExit = new JMenuItem(Bundle.getString("main_mntm_exit")); + mntmAbout = new JMenuItem(Bundle.getString("main_mntm_about")); + + // Panels + pnlStatus = new StatusJPanel(); + pnlFunctionsAndTelemetry = new FunctionsAndTelemetryJPanel(getStatusPanel()); + pnlLiftoff = new LiftoffJPanel(getStatusPanel()); + pnlLanding = new LandingJPanel(getStatusPanel()); + pnlCreateManeuvers = new CreateManeuverJPanel(getStatusPanel()); + pnlRunManeuvers = new RunManeuverJPanel(getStatusPanel()); + pnlRover = new RoverJPanel(getStatusPanel()); + pnlDocking = new DockingJPanel(getStatusPanel()); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setAlwaysOnTop(true); + setTitle("MechPeste - Pesterenan"); + setJMenuBar(menuBar); + setResizable(false); + setLocation(50, 50); + setContentPane(ctpMainGui); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + // Setting-up components: + mntmAbout.addActionListener(this); + mntmChangeVessels.addActionListener(this); + mntmExit.addActionListener(this); + mntmInstallKrpc.addActionListener(this); + + cardJPanels.setPreferredSize(PNL_DIMENSION); + cardJPanels.setSize(PNL_DIMENSION); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(APP_DIMENSION); + setSize(APP_DIMENSION); + setVisible(true); + + // Laying out components: + ctpMainGui.setLayout(new BorderLayout()); + ctpMainGui.add(cardJPanels, BorderLayout.CENTER); + ctpMainGui.add(pnlStatus, BorderLayout.SOUTH); + + mnFile.add(mntmInstallKrpc); + mnFile.add(new JSeparator()); + mnFile.add(mntmExit); + mnOptions.add(mntmChangeVessels); + mnHelp.add(mntmAbout); + menuBar.add(mnFile); + menuBar.add(mnOptions); + menuBar.add(mnHelp); + + JTabbedPane pnlManeuverJTabbedPane = new JTabbedPane(); + pnlManeuverJTabbedPane.addTab("Criar Manobras", pnlCreateManeuvers); + pnlManeuverJTabbedPane.addTab("Executar Manobras", pnlRunManeuvers); + + cardJPanels.setLayout(cardLayout); + cardJPanels.add(pnlFunctionsAndTelemetry, Module.TELEMETRY.get()); + cardJPanels.add(pnlLiftoff, Module.LIFTOFF.get()); + cardJPanels.add(pnlLanding, Module.LANDING.get()); + cardJPanels.add(pnlManeuverJTabbedPane, Module.MANEUVER.get()); + cardJPanels.add(pnlRover, Module.ROVER.get()); + cardJPanels.add(pnlDocking, Module.DOCKING.get()); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == mntmAbout) { + handleMntmAboutActionPerformed(e); } - - protected void handleMntmExitActionPerformed(ActionEvent e) { - System.exit(0); + if (e.getSource() == mntmInstallKrpc) { + handleMntmInstallKrpcActionPerformed(e); } - - public static Rectangle centerDialogOnScreen() { - Dimension SCREEN_DIMENSIONS = Toolkit.getDefaultToolkit().getScreenSize(); - Dimension DIALOG_DIMENSIONS = new Dimension(400, 240); - int w = DIALOG_DIMENSIONS.width; - int h = DIALOG_DIMENSIONS.height; - int x = (SCREEN_DIMENSIONS.width - w) / 2; - int y = (SCREEN_DIMENSIONS.height - h) / 2; - return new Rectangle(x, y, w, h); + if (e.getSource() == mntmExit) { + handleMntmExitActionPerformed(e); } - - public static JPanel getCardJPanels() { - return cardJPanels; + if (e.getSource() == mntmChangeVessels) { + handleMntmMultiControlActionPerformed(e); } + } - public static void changeToPage(ActionEvent e) { - cardLayout.show(cardJPanels, e.getActionCommand()); - } + protected void handleMntmInstallKrpcActionPerformed(ActionEvent e) { + new InstallKrpcDialog(); + } - public static void backToTelemetry(ActionEvent e) { - cardLayout.show(cardJPanels, Module.TELEMETRY.get()); - } + protected void handleMntmExitActionPerformed(ActionEvent e) { + System.exit(0); + } - protected void handleMntmAboutActionPerformed(ActionEvent e) { - new AboutJFrame(); - } + protected void handleMntmAboutActionPerformed(ActionEvent e) { + new AboutJFrame(); + } - public static Component createMarginComponent(int width, int height) { - Component marginComp = Box.createRigidArea(new Dimension(width, height)); - return marginComp; - } + private void handleMntmMultiControlActionPerformed(ActionEvent e) { + new ChangeVesselDialog(vesselManager); + } } diff --git a/src/main/java/com/pesterenan/views/RoverJPanel.java b/src/main/java/com/pesterenan/views/RoverJPanel.java index 008a16d..5224840 100644 --- a/src/main/java/com/pesterenan/views/RoverJPanel.java +++ b/src/main/java/com/pesterenan/views/RoverJPanel.java @@ -1,172 +1,189 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; - -import javax.swing.*; -import javax.swing.border.EtchedBorder; -import javax.swing.border.TitledBorder; -import java.awt.*; +import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import static com.pesterenan.views.MainGui.PNL_DIMENSION; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; public class RoverJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 0L; - - private JLabel lblWaypointName, lblMaxSpeed; - private JTextField txfWaypointName, txfMaxSpeed; - private JButton btnBack, btnDrive; - private ButtonGroup bgTargetChoice; - private JRadioButton rbTargetVessel, rbWaypointOnMap; - - public RoverJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); + private static final long serialVersionUID = 0L; + + private JLabel lblWaypointName, lblMaxSpeed; + private JTextField txfWaypointName, txfMaxSpeed; + private JButton btnBack, btnDrive; + private ButtonGroup bgTargetChoice; + private JRadioButton rbTargetVessel, rbWaypointOnMap; + private VesselManager vesselManager; + + private StatusDisplay statusDisplay; + + public RoverJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + } + + @Override + public void initComponents() { + // Labels: + lblMaxSpeed = new JLabel(Bundle.getString("pnl_rover_lbl_max_speed")); + lblWaypointName = new JLabel(Bundle.getString("pnl_rover_waypoint_name")); + + // Textfields: + txfMaxSpeed = new JTextField("10"); + txfWaypointName = new JTextField(Bundle.getString("pnl_rover_default_name")); + + // Buttons: + btnBack = new JButton(Bundle.getString("pnl_rover_btn_back")); + btnDrive = new JButton(Bundle.getString("pnl_rover_btn_drive")); + + // Misc: + rbTargetVessel = new JRadioButton(Bundle.getString("pnl_rover_target_vessel")); + rbWaypointOnMap = new JRadioButton(Bundle.getString("pnl_rover_waypoint_on_map")); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setBorder(new TitledBorder(null, Bundle.getString("pnl_rover_border"))); + + // Setting-up components: + txfWaypointName.setEnabled(false); + txfWaypointName.setHorizontalAlignment(SwingConstants.CENTER); + txfWaypointName.setMaximumSize(BTN_DIMENSION); + txfWaypointName.setPreferredSize(BTN_DIMENSION); + txfMaxSpeed.setHorizontalAlignment(SwingConstants.RIGHT); + txfMaxSpeed.setMaximumSize(BTN_DIMENSION); + txfMaxSpeed.setPreferredSize(BTN_DIMENSION); + + rbTargetVessel.setSelected(true); + rbTargetVessel.setActionCommand(Module.TARGET_VESSEL.get()); + rbTargetVessel.addActionListener(this::handleTargetSelection); + rbWaypointOnMap.setActionCommand(Module.MAP_MARKER.get()); + rbWaypointOnMap.addActionListener(this::handleTargetSelection); + + bgTargetChoice = new ButtonGroup(); + bgTargetChoice.add(rbTargetVessel); + bgTargetChoice.add(rbWaypointOnMap); + + btnBack.addActionListener(MainGui::backToTelemetry); + btnBack.setMaximumSize(BTN_DIMENSION); + btnBack.setPreferredSize(BTN_DIMENSION); + btnDrive.addActionListener(this::handleDriveTo); + btnDrive.setMaximumSize(BTN_DIMENSION); + btnDrive.setPreferredSize(BTN_DIMENSION); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + // Layout components: + JPanel pnlWaypointName = new JPanel(); + pnlWaypointName.setLayout(new BoxLayout(pnlWaypointName, BoxLayout.X_AXIS)); + pnlWaypointName.add(lblWaypointName); + pnlWaypointName.add(Box.createHorizontalGlue()); + pnlWaypointName.add(txfWaypointName); + + JPanel pnlMaxSpeed = new JPanel(); + pnlMaxSpeed.setLayout(new BoxLayout(pnlMaxSpeed, BoxLayout.X_AXIS)); + pnlMaxSpeed.add(lblMaxSpeed); + pnlMaxSpeed.add(Box.createHorizontalGlue()); + pnlMaxSpeed.add(txfMaxSpeed); + + JPanel pnlRoverControls = new JPanel(); + pnlRoverControls.setLayout(new BoxLayout(pnlRoverControls, BoxLayout.Y_AXIS)); + pnlRoverControls.setBorder(MARGIN_BORDER_10_PX_LR); + pnlRoverControls.add(MainGui.createMarginComponent(0, 6)); + pnlRoverControls.add(pnlWaypointName); + pnlRoverControls.add(pnlMaxSpeed); + pnlRoverControls.add(Box.createVerticalGlue()); + + JPanel pnlTargetChoice = new JPanel(); + pnlTargetChoice.setBorder( + new TitledBorder( + new EtchedBorder(EtchedBorder.LOWERED), Bundle.getString("pnl_rover_target_choice"))); + pnlTargetChoice.setLayout(new BoxLayout(pnlTargetChoice, BoxLayout.Y_AXIS)); + pnlTargetChoice.add(rbTargetVessel); + pnlTargetChoice.add(rbWaypointOnMap); + pnlTargetChoice.add(Box.createHorizontalGlue()); + + JPanel pnlButtons = new JPanel(); + pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); + pnlButtons.add(btnDrive); + pnlButtons.add(Box.createHorizontalGlue()); + pnlButtons.add(btnBack); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlRoverControls.setAlignmentY(TOP_ALIGNMENT); + pnlTargetChoice.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlRoverControls); + pnlMain.add(pnlTargetChoice); + + setLayout(new BorderLayout()); + add(pnlMain, BorderLayout.CENTER); + add(pnlButtons, BorderLayout.SOUTH); + } + + private void handleTargetSelection(ActionEvent e) { + txfWaypointName.setEnabled(e.getSource().equals(rbWaypointOnMap)); + } + + private void handleDriveTo(ActionEvent e) { + if (validateTextFields()) { + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), Module.ROVER.get()); + commands.put( + Module.ROVER_TARGET_TYPE.get(), bgTargetChoice.getSelection().getActionCommand()); + commands.put(Module.MARKER_NAME.get(), txfWaypointName.getText()); + commands.put(Module.MAX_SPEED.get(), txfMaxSpeed.getText()); + vesselManager.startModule(commands); } - - @Override - public void initComponents() { - // Labels: - lblMaxSpeed = new JLabel(Bundle.getString("pnl_rover_lbl_max_speed")); - lblWaypointName = new JLabel(Bundle.getString("pnl_rover_waypoint_name")); - - // Textfields: - txfMaxSpeed = new JTextField("10"); - txfWaypointName = new JTextField(Bundle.getString("pnl_rover_default_name")); - - // Buttons: - btnBack = new JButton(Bundle.getString("pnl_rover_btn_back")); - btnDrive = new JButton(Bundle.getString("pnl_rover_btn_drive")); - - // Misc: - rbTargetVessel = new JRadioButton(Bundle.getString("pnl_rover_target_vessel")); - rbWaypointOnMap = new JRadioButton(Bundle.getString("pnl_rover_waypoint_on_map")); - } - - @Override - public void setupComponents() { - // Main Panel setup: - setBorder(new TitledBorder(null, Bundle.getString("pnl_rover_border"))); - - // Setting-up components: - txfWaypointName.setEnabled(false); - txfWaypointName.setHorizontalAlignment(SwingConstants.CENTER); - txfWaypointName.setMaximumSize(BTN_DIMENSION); - txfWaypointName.setPreferredSize(BTN_DIMENSION); - txfMaxSpeed.setHorizontalAlignment(SwingConstants.RIGHT); - txfMaxSpeed.setMaximumSize(BTN_DIMENSION); - txfMaxSpeed.setPreferredSize(BTN_DIMENSION); - - rbTargetVessel.setSelected(true); - rbTargetVessel.setActionCommand(Module.TARGET_VESSEL.get()); - rbTargetVessel.addActionListener(this::handleTargetSelection); - rbWaypointOnMap.setActionCommand(Module.MAP_MARKER.get()); - rbWaypointOnMap.addActionListener(this::handleTargetSelection); - - bgTargetChoice = new ButtonGroup(); - bgTargetChoice.add(rbTargetVessel); - bgTargetChoice.add(rbWaypointOnMap); - - btnBack.addActionListener(MainGui::backToTelemetry); - btnBack.setMaximumSize(BTN_DIMENSION); - btnBack.setPreferredSize(BTN_DIMENSION); - btnDrive.addActionListener(this::handleDriveTo); - btnDrive.setMaximumSize(BTN_DIMENSION); - btnDrive.setPreferredSize(BTN_DIMENSION); - } - - @Override - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - // Layout components: - JPanel pnlWaypointName = new JPanel(); - pnlWaypointName.setLayout(new BoxLayout(pnlWaypointName, BoxLayout.X_AXIS)); - pnlWaypointName.add(lblWaypointName); - pnlWaypointName.add(Box.createHorizontalGlue()); - pnlWaypointName.add(txfWaypointName); - - JPanel pnlMaxSpeed = new JPanel(); - pnlMaxSpeed.setLayout(new BoxLayout(pnlMaxSpeed, BoxLayout.X_AXIS)); - pnlMaxSpeed.add(lblMaxSpeed); - pnlMaxSpeed.add(Box.createHorizontalGlue()); - pnlMaxSpeed.add(txfMaxSpeed); - - JPanel pnlRoverControls = new JPanel(); - pnlRoverControls.setLayout(new BoxLayout(pnlRoverControls, BoxLayout.Y_AXIS)); - pnlRoverControls.setBorder(MARGIN_BORDER_10_PX_LR); - pnlRoverControls.add(MainGui.createMarginComponent(0, 6)); - pnlRoverControls.add(pnlWaypointName); - pnlRoverControls.add(pnlMaxSpeed); - pnlRoverControls.add(Box.createVerticalGlue()); - - JPanel pnlTargetChoice = new JPanel(); - pnlTargetChoice.setBorder( - new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), Bundle.getString("pnl_rover_target_choice"))); - pnlTargetChoice.setLayout(new BoxLayout(pnlTargetChoice, BoxLayout.Y_AXIS)); - pnlTargetChoice.add(rbTargetVessel); - pnlTargetChoice.add(rbWaypointOnMap); - pnlTargetChoice.add(Box.createHorizontalGlue()); - - JPanel pnlButtons = new JPanel(); - pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); - pnlButtons.add(btnDrive); - pnlButtons.add(Box.createHorizontalGlue()); - pnlButtons.add(btnBack); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlRoverControls.setAlignmentY(TOP_ALIGNMENT); - pnlTargetChoice.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlRoverControls); - pnlMain.add(pnlTargetChoice); - - setLayout(new BorderLayout()); - add(pnlMain, BorderLayout.CENTER); - add(pnlButtons, BorderLayout.SOUTH); - } - - private void handleTargetSelection(ActionEvent e) { - txfWaypointName.setEnabled(e.getSource().equals(rbWaypointOnMap)); - } - - private void handleDriveTo(ActionEvent e) { - if (validateTextFields()) { - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), Module.ROVER.get()); - commands.put(Module.ROVER_TARGET_TYPE.get(), bgTargetChoice.getSelection().getActionCommand()); - commands.put(Module.MARKER_NAME.get(), txfWaypointName.getText()); - commands.put(Module.MAX_SPEED.get(), txfMaxSpeed.getText()); - MechPeste.newInstance().startModule(commands); - } - } - - private boolean validateTextFields() { - try { - if (Float.parseFloat(txfMaxSpeed.getText()) < 3) { - throw new NumberFormatException(); - } - if (txfWaypointName.getText().equals("")) { - throw new IllegalArgumentException(); - } - } catch (NumberFormatException e) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_rover_max_speed_above_3")); - return false; - } catch (IllegalArgumentException e) { - StatusJPanel.setStatusMessage(Bundle.getString("pnl_rover_waypoint_name_not_empty")); - return false; - } - return true; + } + + private boolean validateTextFields() { + try { + if (Float.parseFloat(txfMaxSpeed.getText()) < 3) { + throw new NumberFormatException(); + } + if (txfWaypointName.getText().equals("")) { + throw new IllegalArgumentException(); + } + } catch (NumberFormatException e) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_rover_max_speed_above_3")); + return false; + } catch (IllegalArgumentException e) { + statusDisplay.setStatusMessage(Bundle.getString("pnl_rover_waypoint_name_not_empty")); + return false; } + return true; + } } diff --git a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java index 90cd3ee..3e463e9 100644 --- a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java @@ -1,240 +1,262 @@ package com.pesterenan.views; +import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import static com.pesterenan.views.MainGui.PNL_DIMENSION; + import com.pesterenan.MechPeste; +import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; - +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.TitledBorder; import krpc.client.RPCException; import krpc.client.services.SpaceCenter.Node; import krpc.client.services.SpaceCenter.Orbit; import krpc.client.services.SpaceCenter.Vessel; import krpc.client.services.SpaceCenter.VesselSituation; -import javax.swing.*; -import javax.swing.border.CompoundBorder; -import javax.swing.border.TitledBorder; - -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.HashMap; -import java.util.Map; - -import static com.pesterenan.views.MainGui.BTN_DIMENSION; -import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import static com.pesterenan.views.MainGui.PNL_DIMENSION; - public class RunManeuverJPanel extends JPanel implements ActionListener, UIMethods { - private static final long serialVersionUID = 1L; - - private JLabel lblExecute; - private JButton btnLowerOrbit, btnApoapsis, btnPeriapsis, btnExecute, btnBack, btnAlignPlanes, btnRendezvous; - private JCheckBox chkFineAdjusment; - private final ControlePID ctrlManeuver = new ControlePID(); - - public RunManeuverJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); + private static final long serialVersionUID = 1L; + + private StatusDisplay statusDisplay; + + private JLabel lblExecute; + private JButton btnLowerOrbit, + btnApoapsis, + btnPeriapsis, + btnExecute, + btnBack, + btnAlignPlanes, + btnRendezvous; + private JCheckBox chkFineAdjusment; + private VesselManager vesselManager; + + public RunManeuverJPanel(StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + initComponents(); + setupComponents(); + layoutComponents(); + } + + public void setVesselManager(VesselManager vesselManager) { + this.vesselManager = vesselManager; + } + + public void initComponents() { + // Labels: + lblExecute = new JLabel(Bundle.getString("pnl_mnv_lbl_exec_mnv")); + + // Buttons: + btnApoapsis = new JButton(Bundle.getString("pnl_mnv_btn_apoapsis")); + btnBack = new JButton(Bundle.getString("pnl_mnv_btn_back")); + btnExecute = new JButton(Bundle.getString("pnl_mnv_btn_exec_mnv")); + btnLowerOrbit = new JButton(Bundle.getString("pnl_mnv_btn_lower_orbit")); + btnPeriapsis = new JButton(Bundle.getString("pnl_mnv_btn_periapsis")); + btnAlignPlanes = new JButton("Alinhar planos"); + btnRendezvous = new JButton("Rendezvous"); + + // Misc: + chkFineAdjusment = new JCheckBox(Bundle.getString("pnl_mnv_chk_adj_mnv_rcs")); + } + + public void createManeuver() { + System.out.println("Create maneuver"); + try { + createManeuver(vesselManager.getSpaceCenter().getUT() + 60); + } catch (RPCException e) { } - - public void initComponents() { - // Labels: - lblExecute = new JLabel(Bundle.getString("pnl_mnv_lbl_exec_mnv")); - - // Buttons: - btnApoapsis = new JButton(Bundle.getString("pnl_mnv_btn_apoapsis")); - btnBack = new JButton(Bundle.getString("pnl_mnv_btn_back")); - btnExecute = new JButton(Bundle.getString("pnl_mnv_btn_exec_mnv")); - btnLowerOrbit = new JButton(Bundle.getString("pnl_mnv_btn_lower_orbit")); - btnPeriapsis = new JButton(Bundle.getString("pnl_mnv_btn_periapsis")); - btnAlignPlanes = new JButton("Alinhar planos"); - btnRendezvous = new JButton("Rendezvous"); - - // Misc: - chkFineAdjusment = new JCheckBox(Bundle.getString("pnl_mnv_chk_adj_mnv_rcs")); + } + + public void createManeuver(double atFutureTime) { + System.out.println("Create maneuver overloaded"); + try { + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + System.out.println("vessel: " + vessel); + + if (vessel.getSituation() != VesselSituation.ORBITING) { + statusDisplay.setStatusMessage("Não é possível criar a manobra fora de órbita."); + return; + } + vessel.getControl().addNode(atFutureTime, 0, 0, 0); + } catch (Exception e) { } - - public static void createManeuver() { - System.out.println("Create maneuver"); - try { - createManeuver(MechPeste.getSpaceCenter().getUT() + 60); - } catch (RPCException e) { - } + } + + public void positionManeuverAt(String node) { + try { + MechPeste.newInstance(); + Vessel vessel = vesselManager.getSpaceCenter().getActiveVessel(); + Orbit orbit = vessel.getOrbit(); + Node currentManeuver = vessel.getControl().getNodes().get(0); + double timeToNode = 0; + switch (node) { + case "apoapsis": + timeToNode = vesselManager.getSpaceCenter().getUT() + orbit.getTimeToApoapsis(); + break; + case "periapsis": + timeToNode = vesselManager.getSpaceCenter().getUT() + orbit.getTimeToPeriapsis(); + break; + case "ascending": + double ascendingAnomaly = + orbit.trueAnomalyAtAN(vesselManager.getSpaceCenter().getTargetVessel().getOrbit()); + timeToNode = orbit.uTAtTrueAnomaly(ascendingAnomaly); + break; + case "descending": + double descendingAnomaly = + orbit.trueAnomalyAtDN(vesselManager.getSpaceCenter().getTargetVessel().getOrbit()); + timeToNode = orbit.uTAtTrueAnomaly(descendingAnomaly); + break; + } + currentManeuver.setUT(timeToNode); + // Print the maneuver node information + System.out.println("Maneuver Node updated:"); + System.out.println(" Time to node: " + currentManeuver.getTimeTo() + " s"); + } catch (Exception e) { } - - public static void createManeuver(double atFutureTime) { - System.out.println("Create maneuver overloaded"); - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - System.out.println("vessel: " + vessel); - - if (vessel.getSituation() != VesselSituation.ORBITING) { - StatusJPanel.setStatusMessage("Não é possível criar a manobra fora de órbita."); - return; - } - vessel.getControl().addNode(atFutureTime, 0, 0, 0); - } catch (Exception e) { - } + } + + public void setupComponents() { + // Setting-up components: + btnAlignPlanes.addActionListener(this); + btnAlignPlanes.setMaximumSize(BTN_DIMENSION); + btnAlignPlanes.setPreferredSize(BTN_DIMENSION); + btnAlignPlanes.setEnabled(false); // FIX: Not yet ready + + btnRendezvous.addActionListener(this); + btnRendezvous.setMaximumSize(BTN_DIMENSION); + btnRendezvous.setPreferredSize(BTN_DIMENSION); + btnRendezvous.setEnabled(false); // FIX: Not yet ready + + btnApoapsis.addActionListener(this); + btnApoapsis.setMaximumSize(BTN_DIMENSION); + btnApoapsis.setPreferredSize(BTN_DIMENSION); + + btnBack.addActionListener(this); + btnBack.setMaximumSize(BTN_DIMENSION); + btnBack.setPreferredSize(BTN_DIMENSION); + + btnExecute.addActionListener(this); + btnExecute.setMaximumSize(BTN_DIMENSION); + btnExecute.setPreferredSize(BTN_DIMENSION); + + btnLowerOrbit.addActionListener(this); + btnLowerOrbit.setMaximumSize(BTN_DIMENSION); + btnLowerOrbit.setPreferredSize(BTN_DIMENSION); + + btnPeriapsis.addActionListener(this); + btnPeriapsis.setMaximumSize(BTN_DIMENSION); + btnPeriapsis.setPreferredSize(BTN_DIMENSION); + } + + public void layoutComponents() { + // Main Panel layout: + setPreferredSize(PNL_DIMENSION); + setSize(PNL_DIMENSION); + setLayout(new BorderLayout()); + + JPanel pnlExecuteManeuver = new JPanel(); + pnlExecuteManeuver.setLayout(new BoxLayout(pnlExecuteManeuver, BoxLayout.X_AXIS)); + pnlExecuteManeuver.setBorder(MARGIN_BORDER_10_PX_LR); + pnlExecuteManeuver.add(lblExecute); + pnlExecuteManeuver.add(MainGui.createMarginComponent(10, 0)); + pnlExecuteManeuver.add(btnExecute); + + JPanel pnlAutoPosition = new JPanel(); + pnlAutoPosition.setLayout(new BoxLayout(pnlAutoPosition, BoxLayout.X_AXIS)); + pnlAutoPosition.setBorder(new TitledBorder("Auto posição:")); + pnlAutoPosition.add(btnAlignPlanes); + pnlAutoPosition.add(btnRendezvous); + + JPanel pnlCircularize = new JPanel(); + pnlCircularize.setLayout(new BoxLayout(pnlCircularize, BoxLayout.X_AXIS)); + TitledBorder titled = + new TitledBorder( + null, + Bundle.getString("pnl_mnv_circularize"), + TitledBorder.LEADING, + TitledBorder.TOP, + null, + null); + CompoundBorder combined = new CompoundBorder(titled, MARGIN_BORDER_10_PX_LR); + pnlCircularize.setBorder(combined); + pnlCircularize.add(btnLowerOrbit); + pnlCircularize.add(Box.createHorizontalGlue()); + pnlCircularize.add(btnApoapsis); + pnlCircularize.add(btnPeriapsis); + + JPanel pnlSetup = new JPanel(); + pnlSetup.setLayout(new BoxLayout(pnlSetup, BoxLayout.Y_AXIS)); + pnlSetup.add(pnlExecuteManeuver); + pnlSetup.add(pnlAutoPosition); + + JPanel pnlOptions = new JPanel(); + pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); + pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); + pnlOptions.add(chkFineAdjusment); + + JPanel pnlFunctions = new JPanel(); + pnlFunctions.setLayout(new BoxLayout(pnlFunctions, BoxLayout.X_AXIS)); + pnlFunctions.add(pnlSetup); + pnlFunctions.add(pnlOptions); + + JPanel pnlButtons = new JPanel(); + pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); + pnlButtons.add(Box.createHorizontalGlue()); + pnlButtons.add(btnBack); + + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); + pnlFunctions.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlFunctions); + pnlCircularize.setAlignmentY(TOP_ALIGNMENT); + pnlMain.add(pnlCircularize); + + add(pnlMain, BorderLayout.CENTER); + add(pnlButtons, BorderLayout.SOUTH); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == btnExecute) { + handleManeuverFunction(Module.EXECUTE.get()); } - - public static void positionManeuverAt(String node) { - try { - MechPeste.newInstance(); - Vessel vessel = MechPeste.getSpaceCenter().getActiveVessel(); - Orbit orbit = vessel.getOrbit(); - Node currentManeuver = vessel.getControl().getNodes().get(0); - double timeToNode = 0; - switch (node) { - case "apoapsis" : - timeToNode = MechPeste.getSpaceCenter().getUT() + orbit.getTimeToApoapsis(); - break; - case "periapsis" : - timeToNode = MechPeste.getSpaceCenter().getUT() + orbit.getTimeToPeriapsis(); - break; - case "ascending" : - double ascendingAnomaly = orbit - .trueAnomalyAtAN(MechPeste.getSpaceCenter().getTargetVessel().getOrbit()); - timeToNode = orbit.uTAtTrueAnomaly(ascendingAnomaly); - break; - case "descending" : - double descendingAnomaly = orbit - .trueAnomalyAtDN(MechPeste.getSpaceCenter().getTargetVessel().getOrbit()); - timeToNode = orbit.uTAtTrueAnomaly(descendingAnomaly); - break; - } - currentManeuver.setUT(timeToNode); - // Print the maneuver node information - System.out.println("Maneuver Node updated:"); - System.out.println(" Time to node: " + currentManeuver.getTimeTo() + " s"); - } catch (Exception e) { - } + if (e.getSource() == btnLowerOrbit) { + handleManeuverFunction(Module.LOW_ORBIT.get()); } - - public void setupComponents() { - // Setting-up components: - btnAlignPlanes.addActionListener(this); - btnAlignPlanes.setMaximumSize(BTN_DIMENSION); - btnAlignPlanes.setPreferredSize(BTN_DIMENSION); - - btnRendezvous.addActionListener(this); - btnRendezvous.setMaximumSize(BTN_DIMENSION); - btnRendezvous.setPreferredSize(BTN_DIMENSION); - - btnApoapsis.addActionListener(this); - btnApoapsis.setMaximumSize(BTN_DIMENSION); - btnApoapsis.setPreferredSize(BTN_DIMENSION); - - btnBack.addActionListener(this); - btnBack.setMaximumSize(BTN_DIMENSION); - btnBack.setPreferredSize(BTN_DIMENSION); - - btnExecute.addActionListener(this); - btnExecute.setMaximumSize(BTN_DIMENSION); - btnExecute.setPreferredSize(BTN_DIMENSION); - - btnLowerOrbit.addActionListener(this); - btnLowerOrbit.setMaximumSize(BTN_DIMENSION); - btnLowerOrbit.setPreferredSize(BTN_DIMENSION); - - btnPeriapsis.addActionListener(this); - btnPeriapsis.setMaximumSize(BTN_DIMENSION); - btnPeriapsis.setPreferredSize(BTN_DIMENSION); + if (e.getSource() == btnApoapsis) { + handleManeuverFunction(Module.APOAPSIS.get()); } - - public void layoutComponents() { - // Main Panel layout: - setPreferredSize(PNL_DIMENSION); - setSize(PNL_DIMENSION); - setLayout(new BorderLayout()); - - JPanel pnlExecuteManeuver = new JPanel(); - pnlExecuteManeuver.setLayout(new BoxLayout(pnlExecuteManeuver, BoxLayout.X_AXIS)); - pnlExecuteManeuver.setBorder(MARGIN_BORDER_10_PX_LR); - pnlExecuteManeuver.add(lblExecute); - pnlExecuteManeuver.add(MainGui.createMarginComponent(10, 0)); - pnlExecuteManeuver.add(btnExecute); - - JPanel pnlAutoPosition = new JPanel(); - pnlAutoPosition.setLayout(new BoxLayout(pnlAutoPosition, BoxLayout.X_AXIS)); - pnlAutoPosition.setBorder(new TitledBorder("Auto posição:")); - pnlAutoPosition.add(btnAlignPlanes); - pnlAutoPosition.add(btnRendezvous); - - JPanel pnlCircularize = new JPanel(); - pnlCircularize.setLayout(new BoxLayout(pnlCircularize, BoxLayout.X_AXIS)); - TitledBorder titled = new TitledBorder(null, Bundle.getString("pnl_mnv_circularize"), TitledBorder.LEADING, - TitledBorder.TOP, null, null); - CompoundBorder combined = new CompoundBorder(titled, MARGIN_BORDER_10_PX_LR); - pnlCircularize.setBorder(combined); - pnlCircularize.add(btnLowerOrbit); - pnlCircularize.add(Box.createHorizontalGlue()); - pnlCircularize.add(btnApoapsis); - pnlCircularize.add(btnPeriapsis); - - JPanel pnlSetup = new JPanel(); - pnlSetup.setLayout(new BoxLayout(pnlSetup, BoxLayout.Y_AXIS)); - pnlSetup.add(pnlExecuteManeuver); - pnlSetup.add(pnlAutoPosition); - - JPanel pnlOptions = new JPanel(); - pnlOptions.setLayout(new BoxLayout(pnlOptions, BoxLayout.Y_AXIS)); - pnlOptions.setBorder(new TitledBorder(Bundle.getString("pnl_lift_chk_options"))); - pnlOptions.add(chkFineAdjusment); - - JPanel pnlFunctions = new JPanel(); - pnlFunctions.setLayout(new BoxLayout(pnlFunctions, BoxLayout.X_AXIS)); - pnlFunctions.add(pnlSetup); - pnlFunctions.add(pnlOptions); - - JPanel pnlButtons = new JPanel(); - pnlButtons.setLayout(new BoxLayout(pnlButtons, BoxLayout.X_AXIS)); - pnlButtons.add(Box.createHorizontalGlue()); - pnlButtons.add(btnBack); - - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.Y_AXIS)); - pnlFunctions.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlFunctions); - pnlCircularize.setAlignmentY(TOP_ALIGNMENT); - pnlMain.add(pnlCircularize); - - add(pnlMain, BorderLayout.CENTER); - add(pnlButtons, BorderLayout.SOUTH); + if (e.getSource() == btnPeriapsis) { + handleManeuverFunction(Module.PERIAPSIS.get()); } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == btnExecute) { - handleManeuverFunction(Module.EXECUTE.get()); - } - if (e.getSource() == btnLowerOrbit) { - handleManeuverFunction(Module.LOW_ORBIT.get()); - } - if (e.getSource() == btnApoapsis) { - handleManeuverFunction(Module.APOAPSIS.get()); - } - if (e.getSource() == btnPeriapsis) { - handleManeuverFunction(Module.PERIAPSIS.get()); - } - if (e.getSource() == btnAlignPlanes) { - handleManeuverFunction(Module.ADJUST.get()); - } - if (e.getSource() == btnRendezvous) { - handleManeuverFunction(Module.RENDEZVOUS.get()); - } - if (e.getSource() == btnBack) { - MainGui.backToTelemetry(e); - } + if (e.getSource() == btnAlignPlanes) { + handleManeuverFunction(Module.ADJUST.get()); } - - protected void handleManeuverFunction(String maneuverFunction) { - Map commands = new HashMap<>(); - commands.put(Module.MODULO.get(), Module.MANEUVER.get()); - commands.put(Module.FUNCTION.get(), maneuverFunction.toString()); - commands.put(Module.FINE_ADJUST.get(), String.valueOf(chkFineAdjusment.isSelected())); - MechPeste.newInstance().startModule(commands); + if (e.getSource() == btnRendezvous) { + handleManeuverFunction(Module.RENDEZVOUS.get()); + } + if (e.getSource() == btnBack) { + MainGui.backToTelemetry(e); } + } + + protected void handleManeuverFunction(String maneuverFunction) { + Map commands = new HashMap<>(); + commands.put(Module.MODULO.get(), Module.MANEUVER.get()); + commands.put(Module.FUNCTION.get(), maneuverFunction.toString()); + commands.put(Module.FINE_ADJUST.get(), String.valueOf(chkFineAdjusment.isSelected())); + MechPeste.newInstance().getVesselManager().startModule(commands); + } } diff --git a/src/main/java/com/pesterenan/views/StatusDisplay.java b/src/main/java/com/pesterenan/views/StatusDisplay.java new file mode 100644 index 0000000..89954c7 --- /dev/null +++ b/src/main/java/com/pesterenan/views/StatusDisplay.java @@ -0,0 +1,7 @@ +package com.pesterenan.views; + +public interface StatusDisplay { + void setStatusMessage(String message); + + void setBtnConnectVisible(boolean visible); +} diff --git a/src/main/java/com/pesterenan/views/StatusJPanel.java b/src/main/java/com/pesterenan/views/StatusJPanel.java index e3f6e34..da82833 100644 --- a/src/main/java/com/pesterenan/views/StatusJPanel.java +++ b/src/main/java/com/pesterenan/views/StatusJPanel.java @@ -1,76 +1,80 @@ package com.pesterenan.views; -import com.pesterenan.MechPeste; -import com.pesterenan.resources.Bundle; - -import javax.swing.*; - import static com.pesterenan.views.MainGui.BTN_DIMENSION; import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; -import java.awt.*; +import com.pesterenan.MechPeste; +import com.pesterenan.resources.Bundle; +import java.awt.BorderLayout; +import java.awt.Dimension; import java.awt.event.ActionEvent; - -public class StatusJPanel extends JPanel implements UIMethods { - private static final long serialVersionUID = 1L; - - private static JLabel lblStatus; - private static JButton btnConnect; - private Dimension pnlDimension = new Dimension(464, 30); - - public StatusJPanel() { - initComponents(); - setupComponents(); - layoutComponents(); - } - - @Override - public void initComponents() { - // Labels: - lblStatus = new JLabel(Bundle.getString("lbl_stat_ready")); - - // Buttons: - btnConnect = new JButton(Bundle.getString("btn_stat_connect")); - } - - @Override - public void setupComponents() { - // Main Panel setup: - setBorder(MARGIN_BORDER_10_PX_LR); - setPreferredSize(pnlDimension); - - // Setting-up components: - btnConnect.addActionListener(this::handleConnect); - btnConnect.setPreferredSize(BTN_DIMENSION); - btnConnect.setMaximumSize(BTN_DIMENSION); - btnConnect.setVisible(false); - } - - @Override - public void layoutComponents() { - // Main Panel layout: - setLayout(new BorderLayout()); - - // Laying out components: - JPanel pnlMain = new JPanel(); - pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); - pnlMain.add(lblStatus); - pnlMain.add(Box.createHorizontalGlue()); - pnlMain.add(btnConnect); - - add(pnlMain, BorderLayout.CENTER); - } - - public static void setStatusMessage(String newStatus) { - lblStatus.setText(newStatus); - } - - public static void isBtnConnectVisible(boolean visible) { - btnConnect.setVisible(visible); - } - - private void handleConnect(ActionEvent e) { - setStatusMessage(Bundle.getString("status_connecting")); - MechPeste.newInstance().connectToKSP(); - } +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class StatusJPanel extends JPanel implements UIMethods, StatusDisplay { + private static final long serialVersionUID = 1L; + + private static JLabel lblStatus; + private static JButton btnConnect; + private Dimension pnlDimension = new Dimension(464, 30); + + public StatusJPanel() { + initComponents(); + setupComponents(); + layoutComponents(); + } + + @Override + public void initComponents() { + // Labels: + lblStatus = new JLabel(Bundle.getString("lbl_stat_ready")); + + // Buttons: + btnConnect = new JButton(Bundle.getString("btn_stat_connect")); + } + + @Override + public void setupComponents() { + // Main Panel setup: + setBorder(MARGIN_BORDER_10_PX_LR); + setPreferredSize(pnlDimension); + + // Setting-up components: + btnConnect.addActionListener(this::handleConnect); + btnConnect.setPreferredSize(BTN_DIMENSION); + btnConnect.setMaximumSize(BTN_DIMENSION); + btnConnect.setVisible(false); + } + + @Override + public void layoutComponents() { + // Main Panel layout: + setLayout(new BorderLayout()); + + // Laying out components: + JPanel pnlMain = new JPanel(); + pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.X_AXIS)); + pnlMain.add(lblStatus); + pnlMain.add(Box.createHorizontalGlue()); + pnlMain.add(btnConnect); + + add(pnlMain, BorderLayout.CENTER); + } + + @Override + public void setStatusMessage(String message) { + lblStatus.setText(message); + } + + @Override + public void setBtnConnectVisible(boolean visible) { + btnConnect.setVisible(visible); + } + + private void handleConnect(ActionEvent e) { + MechPeste.newInstance().getConnectionManager().connect(); + } } diff --git a/src/main/java/com/pesterenan/views/UIMethods.java b/src/main/java/com/pesterenan/views/UIMethods.java index eff0f48..919d512 100644 --- a/src/main/java/com/pesterenan/views/UIMethods.java +++ b/src/main/java/com/pesterenan/views/UIMethods.java @@ -2,9 +2,9 @@ public interface UIMethods { - public void initComponents(); + public void initComponents(); - public void setupComponents(); + public void setupComponents(); - public void layoutComponents(); + public void layoutComponents(); } diff --git a/src/main/resources/MechPesteBundle.properties b/src/main/resources/MechPesteBundle.properties index 91f4d43..a5a635a 100644 --- a/src/main/resources/MechPesteBundle.properties +++ b/src/main/resources/MechPesteBundle.properties @@ -123,3 +123,6 @@ status_separating_stage = Decoupling stage... status_starting_landing = Starting Automatic Landing\! status_starting_landing_at = Starting automatic landing at\: status_waiting_for_landing = Waiting for auto-landing... +status_waiting_flight = Waiting for flight scene... +status_stabilizing_roll=Stabilizing roll... +status_orienting_to_maneuver=Orienting to maneuver... \ No newline at end of file diff --git a/src/main/resources/MechPesteBundle_pt.properties b/src/main/resources/MechPesteBundle_pt.properties index b2636c5..efe0c51 100644 --- a/src/main/resources/MechPesteBundle_pt.properties +++ b/src/main/resources/MechPesteBundle_pt.properties @@ -122,3 +122,6 @@ status_separating_stage = Separando est\u00E1gio... status_starting_landing = Iniciando Pouso Autom\u00E1tico\! status_starting_landing_at = Iniciando pouso autom\u00E1tico em\: status_waiting_for_landing = Esperando pelo pouso autom\u00E1tico... +status_waiting_flight = Aguardando cena de voo... +status_stabilizing_roll=Estabilizando rolagem... │ +status_orienting_to_maneuver=Orientando para a manobra... │