From 70ecabe5cdda5cf43d714b71ef03ab200868a573 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sat, 6 Sep 2025 21:00:52 -0300 Subject: [PATCH 01/25] [MP-8]: Update protobuf on pom.xml file --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71cf327..46f310f 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ com.google.protobuf protobuf-java - 3.22.1 + 4.32.0 From 1e96de8be72e012fc728bbbaa51846e429dcb872 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sat, 6 Sep 2025 21:03:26 -0300 Subject: [PATCH 02/25] [MP-8]: Use stream events to update telemetry data --- src/main/java/com/pesterenan/MechPeste.java | 1 + .../pesterenan/controllers/Controller.java | 11 +---- .../com/pesterenan/model/ActiveVessel.java | 42 ++++++++++--------- .../pesterenan/model/ConnectionManager.java | 4 ++ .../com/pesterenan/model/VesselManager.java | 30 +++++-------- .../com/pesterenan/utils/ControlePID.java | 32 +++++++++++--- .../views/FunctionsAndTelemetryJPanel.java | 21 +++++----- 7 files changed, 76 insertions(+), 65 deletions(-) diff --git a/src/main/java/com/pesterenan/MechPeste.java b/src/main/java/com/pesterenan/MechPeste.java index 250cc73..c40f9c9 100644 --- a/src/main/java/com/pesterenan/MechPeste.java +++ b/src/main/java/com/pesterenan/MechPeste.java @@ -44,6 +44,7 @@ public static void main(String[] args) { StatusDisplay statusDisplay = MainGui.newInstance().getStatusPanel(); app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); app.vesselManager = new VesselManager(app.connectionManager, statusDisplay); + app.vesselManager.setTelemetryPanel(MainGui.getInstance().getFunctionsAndTelemetryPanel()); app.vesselManager.startUpdateLoop(); } diff --git a/src/main/java/com/pesterenan/controllers/Controller.java b/src/main/java/com/pesterenan/controllers/Controller.java index 8552d27..d6e81fd 100644 --- a/src/main/java/com/pesterenan/controllers/Controller.java +++ b/src/main/java/com/pesterenan/controllers/Controller.java @@ -11,15 +11,6 @@ public Controller(ConnectionManager connectionManager, VesselManager vesselManag } public void run() { - try { - while (!Thread.interrupted()) { - long currentTime = System.currentTimeMillis(); - if (currentTime > timer + 100) { - recordTelemetryData(); - timer = currentTime; - } - } - } catch (Exception ignored) { - } + // This method should be overridden by subclasses. } } diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 164fa29..3a9bcd1 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -1,7 +1,7 @@ package com.pesterenan.model; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import com.pesterenan.controllers.Controller; import com.pesterenan.controllers.DockingController; @@ -31,7 +31,7 @@ public class ActiveVessel { protected Vessel activeVessel; protected SpaceCenter spaceCenter; protected Connection connection; - private final Map telemetryData = new HashMap<>(); + private final Map telemetryData = new ConcurrentHashMap<>(); public AutoPilot ap; public Flight flightParameters; public ReferenceFrame orbitalReferenceFrame; @@ -172,23 +172,6 @@ public void startModule(Map commands) { 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 Map getTelemetryData() { return telemetryData; } @@ -239,12 +222,33 @@ private void initializeParameters() { surfaceReferenceFrame = getActiveVessel().getSurfaceReferenceFrame(); flightParameters = getActiveVessel().flight(orbitalReferenceFrame); totalMass = connection.addStream(getActiveVessel(), "getMass"); + totalMass.start(); + + // Add callbacks to update telemetry data automatically altitude = connection.addStream(flightParameters, "getMeanAltitude"); + altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); + altitude.start(); + surfaceAltitude = connection.addStream(flightParameters, "getSurfaceAltitude"); + surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); + surfaceAltitude.start(); + apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); + apoapsis.addCallback(val -> telemetryData.put(Telemetry.APOAPSIS, val < 0 ? 0 : val)); + apoapsis.start(); + periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); + periapsis.addCallback(val -> telemetryData.put(Telemetry.PERIAPSIS, val < 0 ? 0 : val)); + periapsis.start(); + verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); + verticalVelocity.addCallback(val -> telemetryData.put(Telemetry.VERT_SPEED, val)); + verticalVelocity.start(); + horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); + horizontalVelocity.addCallback(val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); + horizontalVelocity.start(); + } catch (RPCException | StreamException e) { System.err.println("Error while initializing parameters for active vessel: " + e.getMessage()); } diff --git a/src/main/java/com/pesterenan/model/ConnectionManager.java b/src/main/java/com/pesterenan/model/ConnectionManager.java index 89f1337..391413e 100644 --- a/src/main/java/com/pesterenan/model/ConnectionManager.java +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -36,6 +36,10 @@ public SpaceCenter getSpaceCenter() { return spaceCenter; } + public KRPC getKRPC() { + return krpc; + } + public void connectAndMonitor(final String connectionName) { if (connectionScheduler != null && !connectionScheduler.isShutdown()) { return; diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index f581ab3..24ecd97 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -29,6 +29,11 @@ public class VesselManager { private ActiveVessel currentVessel; private int currentVesselId = -1; private StatusDisplay statusDisplay; + private FunctionsAndTelemetryJPanel telemetryPanel; + + public void setTelemetryPanel(FunctionsAndTelemetryJPanel panel) { + this.telemetryPanel = panel; + } public VesselManager(ConnectionManager connectionManager, StatusDisplay statusDisplay) { this.connectionManager = connectionManager; @@ -68,12 +73,11 @@ public void startUpdateLoop() { return; } checkAndUpdateActiveVessel(); - updateTelemetryData(); updateUI(); } catch (RPCException e) { System.err.println("RPC Error: " + e.getMessage()); } - }, 0, 250, TimeUnit.MILLISECONDS); + }, 0, 100, TimeUnit.MILLISECONDS); } public ListModel getCurrentManeuvers() { @@ -89,7 +93,7 @@ public ListModel getCurrentManeuvers() { } catch (RPCException ignored) { } }); - } catch (RPCException | NullPointerException ignored) { + } catch (RPCException | NullPointerException | UnsupportedOperationException ignored) { } return list; } @@ -156,22 +160,10 @@ private void checkAndUpdateActiveVessel() throws RPCException { } } - private void updateTelemetryData() { - if (currentVesselId == -1 || currentVessel == null) - return; - try { - currentVessel.recordTelemetryData(); - } catch (RPCException e) { - System.err.println("Error while recording telemetry: " + e.getMessage()); - currentVessel = null; - currentVesselId = -1; - } - } - private void updateUI() { SwingUtilities.invokeLater(() -> { - if (currentVessel != null) { - FunctionsAndTelemetryJPanel.updateTelemetry(currentVessel.getTelemetryData()); + if (currentVessel != null && telemetryPanel != null) { + telemetryPanel.updateTelemetry(currentVessel.getTelemetryData()); MainGui.getInstance().getCreateManeuverPanel().updatePanel(getCurrentManeuvers()); if (currentVessel.hasModuleRunning()) { statusDisplay.setStatusMessage(currentVessel.getCurrentStatus()); @@ -192,12 +184,12 @@ private boolean filterVessels(Vessel vessel, String search) { final Vector vesselPos = new Vector(vessel.position(active.getSurfaceReferenceFrame())); final double distance = Vector.distance(activePos, vesselPos); switch (search) { - case "closest": + case "closest" : if (distance < TEN_KILOMETERS) { return true; } break; - case "samebody": + case "samebody" : return true; } } diff --git a/src/main/java/com/pesterenan/utils/ControlePID.java b/src/main/java/com/pesterenan/utils/ControlePID.java index 1fce344..4c9c07b 100644 --- a/src/main/java/com/pesterenan/utils/ControlePID.java +++ b/src/main/java/com/pesterenan/utils/ControlePID.java @@ -14,6 +14,7 @@ public class ControlePID { private double previousError, previousMeasurement, lastTime = 0.0; private double timeSample = 0.025; // 25 millisegundos private double proportionalTerm; + private double derivativeTerm; public ControlePID() { @@ -32,6 +33,10 @@ public ControlePID(double kp, double ki, double kd, double outputMin, double out this.outputMax = outputMax; } + public double getTimeSample() { + return timeSample; + } + public void reset() { this.previousError = 0; this.previousMeasurement = 0; @@ -63,10 +68,6 @@ public double calculate(double measurement, double setPoint) { return limitOutput(proportionalTerm + integralTerm + derivativeTerm); } - private double limitOutput(double value) { - return Utilities.clamp(value, outputMin, outputMax); - } - public void setOutput(double min, double max) { if (min > max) { return; @@ -92,13 +93,32 @@ public void setTimeSample(double milliseconds) { timeSample = milliseconds > 0 ? milliseconds / 1000 : timeSample; } - private double getCurrentTime() { + /** + * 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) { - 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/views/FunctionsAndTelemetryJPanel.java b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java index b6593bd..353bfe1 100644 --- a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java +++ b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java @@ -14,7 +14,6 @@ import javax.swing.JPanel; import javax.swing.border.TitledBorder; -import com.pesterenan.MechPeste; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; @@ -27,9 +26,9 @@ public class FunctionsAndTelemetryJPanel extends JPanel implements UIMethods { 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; + private JLabel lblAltitude, lblSurfaceAlt, lblApoapsis, lblPeriapsis, lblVertSpeed, lblHorzSpeed; + private JLabel lblAltitudeValue, lblSurfaceAltValue, lblApoapsisValue; + private JLabel lblPeriapsisValue, lblVertSpeedValue, lblHorzSpeedValue; private VesselManager vesselManager; @@ -189,26 +188,26 @@ private void changeFunctionPanel(ActionEvent e) { MainGui.changeToPage(e); } - public static void updateTelemetry(Map telemetryData) { + public void updateTelemetry(Map telemetryData) { synchronized (telemetryData) { for (Telemetry key : telemetryData.keySet()) { switch (key) { - case ALTITUDE: + case ALTITUDE : lblAltitudeValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); break; - case ALT_SURF: + case ALT_SURF : lblSurfaceAltValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); break; - case APOAPSIS: + case APOAPSIS : lblApoapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); break; - case PERIAPSIS: + case PERIAPSIS : lblPeriapsisValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key))); break; - case VERT_SPEED: + case VERT_SPEED : lblVertSpeedValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); break; - case HORZ_SPEED: + case HORZ_SPEED : lblHorzSpeedValue.setText(Utilities.convertToMetersMagnitudes(telemetryData.get(key)) + "/s"); break; } From 875c5d302c71d1f724ac0384af58e947ea4323fd Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sun, 7 Sep 2025 22:43:12 -0300 Subject: [PATCH 03/25] [MP-8]: Refactored LiftoffController to use callbacks on streams --- .../controllers/LiftoffController.java | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index 4dfb85e..5b6a691 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -13,6 +13,7 @@ import com.pesterenan.utils.Utilities; import krpc.client.RPCException; +import krpc.client.Stream; import krpc.client.StreamException; import krpc.client.services.SpaceCenter.Engine; import krpc.client.services.SpaceCenter.Fairing; @@ -21,12 +22,9 @@ 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 float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; + private volatile boolean targetApoapsisReached, dynamicPressureLowEnough = false; + private int apoapsisCallbackTag, pressureCallbackTag; private boolean willDecoupleStages, willOpenPanelsAndAntenna; private String gravityCurveModel = Module.CIRCULAR.get(); private Navigation navigation; @@ -55,6 +53,25 @@ private void initializeParameters() { @Override public void run() { try { + // Apoapsis Check using Stream Callback + apoapsisCallbackTag = apoapsis.addCallback((apo) -> { + if (apo >= getFinalApoapsis()) { + targetApoapsisReached = true; + apoapsis.removeCallback(apoapsisCallbackTag); + } + }); + apoapsis.start(); + + // Dynamic Pressure Check using Stream Callback + Stream pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); + pressureCallbackTag = pressureStream.addCallback((pressure) -> { + if (pressure <= 10) { + dynamicPressureLowEnough = true; + pressureStream.removeCallback(pressureCallbackTag); + } + }); + pressureStream.start(); + tuneAutoPilot(); liftoff(); gravityCurve(); @@ -73,29 +90,22 @@ private void gravityCurve() throws RPCException, StreamException, InterruptedExc throttle(getMaxThrottleForTWR(maxTWR)); double startCurveAlt = altitude.get(); - while (currentPitch > 1) { - if (apoapsis.get() > getFinalApoapsis()) { - throttle(0); - break; - } + while (currentPitch > 1 && !targetApoapsisReached) { 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))); + double throttleValue = Math.min(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000), + getMaxThrottleForTWR(currentMaxTWR)); + throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); if (willDecoupleStages && isCurrentStageWithoutFuel()) { decoupleStage(); } setCurrentStatus(String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); - - Thread.sleep(25); - if (Thread.interrupted()) { - throw new InterruptedException(); - } } + throttle(0); } private double calculateTWRBasedOnPressure(float currentPitch) throws RPCException { @@ -110,13 +120,9 @@ private void finalizeCurve() throws RPCException, StreamException, InterruptedEx setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); getActiveVessel().getControl().setRCS(true); - while (flightParameters.getDynamicPressure() > 10) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } + while (!dynamicPressureLowEnough) { navigation.aimAtPrograde(); throttle(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000)); - Thread.sleep(25); } throttle(0.0f); if (willDecoupleStages) { @@ -142,8 +148,6 @@ private void jettisonFairings() throws RPCException, InterruptedException { 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); From 9dc4ee226d0bdaee0ba2fe36f4c60c93b3102ccd Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sun, 7 Sep 2025 23:19:36 -0300 Subject: [PATCH 04/25] [MP-8]: Refactored ManeuverController to use callbacks on streams --- .../controllers/ManeuverController.java | 289 ++++++++++-------- 1 file changed, 170 insertions(+), 119 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 83af766..78463c5 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -29,12 +29,18 @@ public class ManeuverController extends Controller { public final static float CONST_GRAV = 9.81f; - private ControlePID ctrlRCS; - private ControlePID ctrlManeuver; + private ControlePID ctrlManeuver, ctrlRCS; private Navigation navigation; private boolean fineAdjustment; private double lowOrbitAltitude; + private volatile boolean isBurnComplete, isOriented, isTimeToBurn = false; + private int burnCompleteCallbackTag, headingErrorCallbackTag, pitchErrorCallbackTag, timeToBurnCallbackTag; + private Stream headingErrorStream; + private Stream pitchErrorStream; + private Stream timeToNodeStream; + private Stream> remainingBurn; + public ManeuverController(ConnectionManager connectionManager, VesselManager vesselManager, Map commands) { super(connectionManager, vesselManager); @@ -58,11 +64,15 @@ private void initializeParameters() { @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(); + 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 (InterruptedException e) { + cleanup(); } } @@ -146,30 +156,26 @@ public void calculateManeuver() { } } - 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(); - } - 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); + public void matchOrbitApoapsis() throws RPCException, StreamException, InterruptedException { + 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(); + } + 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; } - } catch (Exception e) { - setCurrentStatus("Não foi possivel ajustar a inclinação"); + double dvPrograde = maneuver.getPrograde(); + double ctrlOutput = ctrlManeuver.calculate(currentDeltaApo, 0); + + maneuver.setPrograde(dvPrograde - (ctrlOutput)); + Thread.sleep(25); } } @@ -191,7 +197,7 @@ private double[] getTimeToIncNodes(Orbit targetOrbit) throws RPCException { return new double[]{vesselOrbit.uTAtTrueAnomaly(ascendingNode), vesselOrbit.uTAtTrueAnomaly(descendingNode)}; } - private void alignPlanesWithTargetVessel() { + private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { try { Vessel vessel = getActiveVessel(); Orbit vesselOrbit = getActiveVessel().getOrbit(); @@ -211,6 +217,9 @@ private void alignPlanesWithTargetVessel() { .toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); ctrlManeuver.setTimeSample(25); while (currentInclination > 0.05) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } currentInclination = Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); currentManeuver.setNormal(currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); @@ -221,7 +230,7 @@ private void alignPlanesWithTargetVessel() { } } - private void rendezvousWithTargetVessel() { + private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { try { boolean hasManeuverNodes = getActiveVessel().getControl().getNodes().size() > 0; List currentManeuvers = getActiveVessel().getControl().getNodes(); @@ -244,6 +253,9 @@ private void rendezvousWithTargetVessel() { double angularDiff = 10; while (angularDiff >= 0.005) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } double maneuverUT = lastManeuverNode.getUT(); double targetOrbitPosition = new Vector(targetVesselOrbit.positionAt(maneuverUT, currentBodyRefFrame)) .magnitude(); @@ -254,6 +266,9 @@ private void rendezvousWithTargetVessel() { if (targetOrbitPosition < maneuverPE) { while (Math.floor(targetOrbitPosition) != Math.floor(maneuverPE)) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() + ctrlManeuver.calculate(maneuverPE / targetOrbitPosition * 1000, 1000)); maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); @@ -263,6 +278,9 @@ private void rendezvousWithTargetVessel() { if (targetOrbitPosition > maneuverAP) { while (Math.floor(targetOrbitPosition) != Math.floor(maneuverAP)) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() + ctrlManeuver.calculate(maneuverAP / targetOrbitPosition * 1000, 1000)); maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); @@ -278,55 +296,10 @@ private void rendezvousWithTargetVessel() { System.out.println(angularDiff); Thread.sleep(25); } - // 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) { + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; + } } } @@ -387,96 +360,147 @@ private Node createManeuver(double laterTime, double[] deltaV) { return maneuverNode; } - public void executeNextManeuver() { + public void executeNextManeuver() throws InterruptedException { try { Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); double burnTime = calculateBurnTime(maneuverNode); orientToManeuverNode(maneuverNode); executeBurn(maneuverNode, burnTime); } catch (UnsupportedOperationException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); } catch (IndexOutOfBoundsException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); - } catch (RPCException e) { + } catch (RPCException | StreamException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_data_unavailable")); } catch (InterruptedException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_couldnt_orient")); + throw e; } } - public void orientToManeuverNode(Node maneuverNode) throws InterruptedException, RPCException { + public void orientToManeuverNode(Node maneuverNode) throws RPCException, StreamException, InterruptedException { setCurrentStatus(Bundle.getString("status_orienting_ship")); ap.engage(); - while (ap.getHeadingError() > 3 || ap.getPitchError() > 3) { + + // Create streams for heading and pitch errors + headingErrorStream = connection.addStream(ap, "getHeadingError"); + pitchErrorStream = connection.addStream(ap, "getPitchError"); + + // A shared callback for both streams + Runnable checkOrientation = () -> { + try { + if (headingErrorStream.get() <= 3 && pitchErrorStream.get() <= 3) { + isOriented = true; + // Clean up the callbacks once orientation is complete + headingErrorStream.removeCallback(headingErrorCallbackTag); + pitchErrorStream.removeCallback(pitchErrorCallbackTag); + } + } catch (RPCException | StreamException e) { + // Handle exceptions + } + }; + + // Add the same callback to both streams + headingErrorCallbackTag = headingErrorStream.addCallback(v -> checkOrientation.run()); + pitchErrorCallbackTag = pitchErrorStream.addCallback(v -> checkOrientation.run()); + + // Start the streams + headingErrorStream.start(); + pitchErrorStream.start(); + + while (!isOriented) { if (Thread.interrupted()) { throw new InterruptedException(); } navigation.aimAtManeuver(maneuverNode); - Thread.sleep(100); } - } - 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); + public double calculateBurnTime(Node maneuverNode) { + try { + List engines = getActiveVessel().getParts().getEngines(); + for (Engine engine : engines) { + if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() + && !engine.getActive()) { + engine.setActive(true); + } } + } catch (RPCException e) { + System.err.println("Não foi possível ativar os motores." + e.getMessage()); } - 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"); + double burnDuration = 0; + try { + 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; + burnDuration = (totalMass - dryMass) / burnRatio; + } catch (RPCException e) { + System.err.println("Não foi possível calcular o tempo de queima." + e.getMessage()); + } + setCurrentStatus("Tempo de Queima da Manobra: " + burnDuration + " segundos."); return burnDuration; } - public void executeBurn(Node maneuverNode, double burnDuration) { + public void executeBurn(Node maneuverNode, double burnDuration) throws InterruptedException { try { + // Countdown Stream + timeToNodeStream = connection.addStream(maneuverNode, "getTimeTo"); + timeToBurnCallbackTag = timeToNodeStream.addCallback((time) -> { + if (time <= (burnDuration / 2.0)) { + isTimeToBurn = true; + timeToNodeStream.removeCallback(timeToBurnCallbackTag); + } + }); + timeToNodeStream.start(); + double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - (fineAdjustment ? 5 : 0); setCurrentStatus(Bundle.getString("status_maneuver_warp")); if (burnStartTime > 30) { getConnectionManager().getSpaceCenter() .warpTo((getConnectionManager().getSpaceCenter().getUT() + burnStartTime - 10), 100000, 4); } - // Mostrar tempo de ignição: + setCurrentStatus(String.format(Bundle.getString("status_maneuver_duration"), burnDuration)); - while (burnStartTime > 0) { + while (!isTimeToBurn) { 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); + setCurrentStatus(String.format(Bundle.getString("status_maneuver_ignition_in"), + maneuverNode.getTimeTo() - (burnDuration / 2.0))); } - // Executar a manobra: - Stream> remainingBurn = getConnectionManager().getConnection().addStream( + + // Main Burn Stream + remainingBurn = getConnectionManager().getConnection().addStream( maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); + burnCompleteCallbackTag = remainingBurn.addCallback((burn) -> { + if (burn.getValue1() < (fineAdjustment ? 2 : 0.5)) { + isBurnComplete = true; + remainingBurn.removeCallback(burnCompleteCallbackTag); + } + }); + remainingBurn.start(); + setCurrentStatus(Bundle.getString("status_maneuver_executing")); - while (maneuverNode != null) { - double burnDvLeft = remainingBurn.get().getValue1(); + while (!isBurnComplete && maneuverNode != null) { if (Thread.interrupted()) { throw new InterruptedException(); } - if (burnDvLeft < (fineAdjustment ? 2 : 0.5)) { - throttle(0.0f); - break; - } navigation.aimAtManeuver(maneuverNode); + double burnDvLeft = remainingBurn.get().getValue1(); 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); @@ -489,9 +513,37 @@ public void executeBurn(Node maneuverNode, double burnDuration) { maneuverNode.remove(); setCurrentStatus(Bundle.getString("status_ready")); } catch (StreamException | RPCException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_data_unavailable")); } catch (InterruptedException e) { + System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); + throw e; + } + } + + private void cleanup() { + try { + if (headingErrorStream != null) { + headingErrorStream.removeCallback(headingErrorCallbackTag); + headingErrorStream.remove(); + } + if (pitchErrorStream != null) { + pitchErrorStream.removeCallback(pitchErrorCallbackTag); + pitchErrorStream.remove(); + } + if (timeToNodeStream != null) { + timeToNodeStream.removeCallback(timeToBurnCallbackTag); + timeToNodeStream.remove(); + } + if (remainingBurn != null) { + remainingBurn.removeCallback(burnCompleteCallbackTag); + remainingBurn.remove(); + } + ap.disengage(); + throttle(0); + } catch (RPCException e) { + // ignore } } @@ -504,7 +556,6 @@ private void adjustManeuverWithRCS(Stream> remaini } getActiveVessel().getControl() .setForward((float) ctrlRCS.calculate(-remainingDeltaV.get().getValue1() * 10, 0)); - Thread.sleep(25); } getActiveVessel().getControl().setForward(0); } @@ -528,7 +579,7 @@ private boolean canFineAdjust(String string) { } private double calculatePhaseAngle(Triplet startPos, Triplet endPos) - throws RPCException, InterruptedException { + throws RPCException { double targetPhaseAngle = 10; double angularDifference = 15; Vector startPosition = new Vector(startPos); From 1b450868747614f597e01819e366982b9fbf63fd Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 9 Sep 2025 10:21:51 -0300 Subject: [PATCH 05/25] [MP-8]: Enforcing new formatting style --- .settings/eclipse-java-google-style.xml | 337 ++++++++++++++++++ .settings/org.eclipse.core.resources.prefs | 2 - .settings/org.eclipse.jdt.apt.core.prefs | 2 - .settings/org.eclipse.jdt.core.prefs | 19 - .settings/org.eclipse.jdt.ui.prefs | 3 - ....eclipse.wst.common.project.facet.core.xml | 4 - pom.xml | 7 +- 7 files changed, 340 insertions(+), 34 deletions(-) create mode 100644 .settings/eclipse-java-google-style.xml delete mode 100644 .settings/org.eclipse.core.resources.prefs delete mode 100644 .settings/org.eclipse.jdt.apt.core.prefs delete mode 100644 .settings/org.eclipse.jdt.core.prefs delete mode 100644 .settings/org.eclipse.jdt.ui.prefs delete mode 100644 .settings/org.eclipse.wst.common.project.facet.core.xml 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.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 2b76340..0000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index d4313d4..0000000 --- a/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 3d58a74..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,19 +0,0 @@ -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 -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -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 -org.eclipse.jdt.core.formatter.indentation.size=4 -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=true -org.eclipse.jdt.core.formatter.join_wrapped_lines=false -org.eclipse.jdt.core.formatter.lineSplit=120 -org.eclipse.jdt.core.formatter.tabulation.char=space -org.eclipse.jdt.core.formatter.tabulation.size=4 diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index 91a46d2..0000000 --- a/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -formatter_profile=_custom -formatter_settings_version=12 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 46f310f..4ed1c25 100644 --- a/pom.xml +++ b/pom.xml @@ -61,10 +61,9 @@ 2.37.0 - - 4.19 - ${project.basedir}/.settings/org.eclipse.jdt.core.prefs - + + 1.22.0 + From 38d525bd2e1a9fd5f98110bca37b8b957fd7965d Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 9 Sep 2025 10:41:07 -0300 Subject: [PATCH 06/25] [MP-8]: Applying new code style format --- .settings/org.eclipse.core.resources.prefs | 2 + .settings/org.eclipse.jdt.apt.core.prefs | 2 + .settings/org.eclipse.jdt.core.prefs | 9 + .settings/org.eclipse.m2e.core.prefs | 4 + src/main/java/com/pesterenan/Main.java | 427 ++++--- src/main/java/com/pesterenan/MechPeste.java | 66 +- .../pesterenan/controllers/Controller.java | 21 +- .../controllers/DockingController.java | 583 +++++---- .../controllers/LandingController.java | 554 ++++---- .../controllers/LiftoffController.java | 431 ++++--- .../controllers/ManeuverController.java | 1133 +++++++++-------- .../controllers/RoverController.java | 633 +++++---- .../com/pesterenan/model/ActiveVessel.java | 430 ++++--- .../pesterenan/model/ConnectionManager.java | 154 +-- .../com/pesterenan/model/VesselManager.java | 364 +++--- .../java/com/pesterenan/resources/Bundle.java | 19 +- .../com/pesterenan/updater/KrpcInstaller.java | 130 +- .../java/com/pesterenan/utils/Attributes.java | 47 +- .../com/pesterenan/utils/ControlePID.java | 214 ++-- .../java/com/pesterenan/utils/Module.java | 94 +- .../java/com/pesterenan/utils/Navigation.java | 299 +++-- .../com/pesterenan/utils/PathFinding.java | 432 ++++--- .../java/com/pesterenan/utils/Telemetry.java | 7 +- .../java/com/pesterenan/utils/Utilities.java | 135 +- .../java/com/pesterenan/utils/Vector.java | 438 +++---- .../com/pesterenan/utils/VersionUtil.java | 39 +- .../com/pesterenan/views/AboutJFrame.java | 123 +- .../pesterenan/views/ChangeVesselDialog.java | 283 ++-- .../views/CreateManeuverJPanel.java | 819 ++++++------ .../com/pesterenan/views/DockingJPanel.java | 295 +++-- .../views/FunctionsAndTelemetryJPanel.java | 400 +++--- .../pesterenan/views/InstallKrpcDialog.java | 326 +++-- .../com/pesterenan/views/LandingJPanel.java | 334 ++--- .../com/pesterenan/views/LiftoffJPanel.java | 429 ++++--- .../java/com/pesterenan/views/MainGui.java | 439 ++++--- .../com/pesterenan/views/RoverJPanel.java | 324 ++--- .../pesterenan/views/RunManeuverJPanel.java | 441 +++---- .../com/pesterenan/views/StatusDisplay.java | 4 +- .../com/pesterenan/views/StatusJPanel.java | 134 +- .../java/com/pesterenan/views/UIMethods.java | 6 +- 40 files changed, 5757 insertions(+), 5267 deletions(-) create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.apt.core.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..2b76340 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 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 new file mode 100644 index 0000000..1b6e1ef --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +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 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/src/main/java/com/pesterenan/Main.java b/src/main/java/com/pesterenan/Main.java index ac0b287..1a6d8bb 100644 --- a/src/main/java/com/pesterenan/Main.java +++ b/src/main/java/com/pesterenan/Main.java @@ -1,10 +1,8 @@ package com.pesterenan; -import java.io.IOException; - import com.pesterenan.utils.Utilities; import com.pesterenan.utils.Vector; - +import java.io.IOException; import krpc.client.Connection; import krpc.client.RPCException; import krpc.client.services.Drawing; @@ -17,207 +15,232 @@ 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(); - } + 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(); - } + } + + 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 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) { - } + } + + 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 c40f9c9..4ee48f9 100644 --- a/src/main/java/com/pesterenan/MechPeste.java +++ b/src/main/java/com/pesterenan/MechPeste.java @@ -1,51 +1,47 @@ package com.pesterenan; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.SwingUtilities; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.views.MainGui; import com.pesterenan.views.StatusDisplay; +import java.lang.reflect.InvocationTargetException; +import javax.swing.SwingUtilities; public class MechPeste { - private static MechPeste instance; - private ConnectionManager connectionManager = null; - private VesselManager vesselManager; + private static MechPeste instance; + private ConnectionManager connectionManager = null; + private VesselManager vesselManager; - public ConnectionManager getConnectionManager() { - return connectionManager; - } + public ConnectionManager getConnectionManager() { + return connectionManager; + } - public VesselManager getVesselManager() { - return vesselManager; - } + public VesselManager getVesselManager() { + return vesselManager; + } - private MechPeste() { - } + private MechPeste() {} - public static MechPeste newInstance() { - if (instance == null) { - instance = new MechPeste(); - } - return instance; + public static MechPeste newInstance() { + if (instance == null) { + instance = new MechPeste(); } - - 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()); - e.printStackTrace(); - } - - StatusDisplay statusDisplay = MainGui.newInstance().getStatusPanel(); - app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); - app.vesselManager = new VesselManager(app.connectionManager, statusDisplay); - app.vesselManager.setTelemetryPanel(MainGui.getInstance().getFunctionsAndTelemetryPanel()); - app.vesselManager.startUpdateLoop(); + return instance; + } + + 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()); + e.printStackTrace(); } + StatusDisplay statusDisplay = MainGui.newInstance().getStatusPanel(); + app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); + app.vesselManager = new VesselManager(app.connectionManager, statusDisplay); + app.vesselManager.setTelemetryPanel(MainGui.getInstance().getFunctionsAndTelemetryPanel()); + app.vesselManager.startUpdateLoop(); + } } diff --git a/src/main/java/com/pesterenan/controllers/Controller.java b/src/main/java/com/pesterenan/controllers/Controller.java index d6e81fd..57fd2bc 100644 --- a/src/main/java/com/pesterenan/controllers/Controller.java +++ b/src/main/java/com/pesterenan/controllers/Controller.java @@ -5,12 +5,21 @@ import com.pesterenan.model.VesselManager; public class Controller extends ActiveVessel implements Runnable { + private String currentStatus = ""; - public Controller(ConnectionManager connectionManager, VesselManager vesselManager) { - super(connectionManager, vesselManager); - } + public Controller(ConnectionManager connectionManager, VesselManager vesselManager) { + super(connectionManager, vesselManager); + } - public void run() { - // This method should be overridden by subclasses. - } + public void run() { + // This method should be overridden by subclasses. + } + + 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 916250d..aac2165 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -1,7 +1,5 @@ package com.pesterenan.controllers; -import java.util.Map; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; @@ -9,7 +7,7 @@ import com.pesterenan.utils.Utilities; import com.pesterenan.utils.Vector; import com.pesterenan.views.DockingJPanel; - +import java.util.Map; import krpc.client.RPCException; import krpc.client.services.Drawing; import krpc.client.services.Drawing.Line; @@ -21,281 +19,330 @@ 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; - private ConnectionManager connectionManager; - - public DockingController(ConnectionManager connectionManager, VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.connectionManager = connectionManager; - this.commands = commands; - initializeParameters(); + 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; + private ConnectionManager connectionManager; + + public DockingController( + ConnectionManager connectionManager, + VesselManager vesselManager, + Map commands) { + super(connectionManager, vesselManager); + this.connectionManager = connectionManager; + 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(connectionManager.getConnection()); + targetVessel = connectionManager.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) { } + } - 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(connectionManager.getConnection()); - targetVessel = connectionManager.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(); } - - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { - startDocking(); - } + } + + 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()); } - - 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); + 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); } - - 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() { + 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); + control.setRCS(true); + double safeDistance = 10; + while (true) { + if (Thread.interrupted()) { + throw new InterruptedException(); } - - } - - 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); - 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) { - setCurrentStatus("Docking interrupted."); + 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) { + cleanup(); + setCurrentStatus("Docking interrupted."); } - - /* - * 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 - } - - 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) { - } + } + + /* + * 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 + } + + 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 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 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 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 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 cleanup() { + try { + distanceLine.remove(); + distLineXAxis.remove(); + distLineYAxis.remove(); + distLineZAxis.remove(); + ap.disengage(); + throttle(0); + } catch (RPCException e) { + // ignore } - + } } diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index 0841f38..c467a7b 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -1,7 +1,5 @@ package com.pesterenan.controllers; -import java.util.Map; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; @@ -9,276 +7,350 @@ import com.pesterenan.utils.Module; import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Utilities; - +import java.io.IOException; +import java.util.Map; import krpc.client.RPCException; +import krpc.client.Stream; import krpc.client.StreamException; import krpc.client.services.SpaceCenter.VesselSituation; public class LandingController extends Controller { - private enum MODE { - DEORBITING, APPROACHING, GOING_UP, HOVERING, GOING_DOWN, LANDING, WAITING + private enum MODE { + DEORBITING, + APPROACHING, + GOING_UP, + HOVERING, + GOING_DOWN, + LANDING, + WAITING + } + + 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 altitudeErrorPercentage, hoverAltitude; + private boolean hoveringMode, hoverAfterApproximation, landingMode; + private MODE currentMode; + private float maxTWR; + + private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling = false; + private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag; + private Stream apErrorStream; + + public LandingController( + ConnectionManager connectionManager, + VesselManager vesselManager, + Map commands) { + super(connectionManager, vesselManager); + this.commands = commands; + this.navigation = new Navigation(connectionManager, getActiveVessel()); + this.initializeParameters(); + } + + @Override + public void run() { + if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { + hoverArea(); } - 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(ConnectionManager connectionManager, VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); - this.initializeParameters(); + if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { + autoLanding(); } + } - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { - hoverArea(); + private void initializeParameters() { + altitudeCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); + velocityCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); + altitudeCtrl.setOutput(0, 1); + velocityCtrl.setOutput(0, 1); + } + + 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(); } - if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { - autoLanding(); + altitudeErrorPercentage = surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; + 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 (InterruptedException | RPCException | StreamException | IOException e) { + cleanup(); + setCurrentStatus(Bundle.getString("status_ready")); } + } - private void initializeParameters() { - altitudeCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); - velocityCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); - altitudeCtrl.setOutput(0, 1); - velocityCtrl.setOutput(0, 1); - } - - 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 autoLanding() { + try { + setupCallbacks(); + 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(); + } + } catch (RPCException | StreamException | InterruptedException | IOException e) { + cleanup(); + System.err.println("landing: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_ready")); } + } + + private void setupCallbacks() throws IOException, RPCException, StreamException { + // Callback for ship orientation + apErrorStream = connection.addStream(ap, "getError"); + isOrientedCallbackTag = + apErrorStream.addCallback( + (error) -> { + if (error <= 5.0) { + isOrientedForDeorbit = true; + apErrorStream.removeCallback(isOrientedCallbackTag); + } + }); - 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(); + // Callback for de-orbit burn completion + isDeorbitBurnDoneCallbackTag = + periapsis.addCallback( + p -> { + try { + if (p <= -apoapsis.get()) { + isDeorbitBurnDone = true; + periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); } - getActiveVessel().getControl().setBrakes(true); - changeControlMode(); - Thread.sleep(sleepTime); - } - } catch (RPCException | StreamException | InterruptedException e) { - setCurrentStatus(Bundle.getString("status_ready")); + } catch (RPCException | StreamException e) { + System.err.println("isDeorbitBurnDoneCallbackTag error: " + e.getMessage()); + } + }); + + // Callback for when the ship starts falling back to the ground + isFallingCallbackTag = + verticalVelocity.addCallback( + (vv) -> { + if (vv <= 0) { + isFalling = true; + verticalVelocity.removeCallback(isFallingCallbackTag); + } + }); + + // Start all streams + apErrorStream.start(); + apoapsis.start(); + periapsis.start(); + verticalVelocity.start(); + } + + private void changeControlMode() + throws RPCException, StreamException, InterruptedException, IOException { + adjustPIDbyTWR(); + // Change vessel behavior depending on which mode is active + switch (currentMode) { + case DEORBITING: + deOrbitShip(); + currentMode = MODE.WAITING; + break; + case WAITING: + if (isFalling) { + currentMode = MODE.APPROACHING; + } else { + setCurrentStatus(Bundle.getString("status_waiting_for_landing")); + throttle(0.0f); } - } + break; + case APPROACHING: + altitudeCtrl.setOutput(0, 1); + velocityCtrl.setOutput(0, 1); - 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; - } - 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; + double[] currentDistanceToGround = calculateCurrentDistanceToGround(); + double[] currentVelocities = calculateZeroVelocityMagnitude(); + double aMax = getMaxAcel(maxTWR); + double landingDistanceThreshold = Math.max(hoverAltitude, aMax * 3); + + double stoppingDistance = (currentVelocities[0] * currentVelocities[0]) / (2 * aMax); + + double threshold = Utilities.clamp(stoppingDistance / landingDistanceThreshold, 0, 1); + + double altPID = + altitudeCtrl.calculate( + (currentDistanceToGround[0]), (stoppingDistance + landingDistanceThreshold)); + double velPID = + velocityCtrl.calculate( + verticalVelocity.get(), (-Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20))); + + double interpolation = Utilities.linearInterpolation(velPID, altPID, threshold); + + throttle(interpolation); + navigation.aimForLanding(); + + if (threshold < 0.15 && surfaceAltitude.get() < landingDistanceThreshold * 2) { + hoverAltitude = landingDistanceThreshold; + getActiveVessel().getControl().setGear(true); + if (hoverAfterApproximation) { + landingMode = false; + hoverArea(); + break; + } + currentMode = MODE.LANDING; } + setCurrentStatus("Se aproximando do momento do pouso..."); + break; + case GOING_UP: + altitudeCtrl.setOutput(-0.5, 0.5); + velocityCtrl.setOutput(-0.5, 0.5); + throttle( + altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) + + velocityCtrl.calculate(verticalVelocity.get(), MAX_VELOCITY)); + navigation.aimAtRadialOut(); + setCurrentStatus("Subindo altitude..."); + break; + case GOING_DOWN: + controlThrottleByMatchingVerticalVelocity(-MAX_VELOCITY); + navigation.aimAtRadialOut(); + setCurrentStatus("Baixando altitude..."); + break; + case LANDING: + if (hasTheVesselLanded()) break; + controlThrottleByMatchingVerticalVelocity( + horizontalVelocity.get() > 4 + ? 0 + : -Utilities.clamp(surfaceAltitude.get() * 0.1, 1, 10)); + navigation.aimForLanding(); + setCurrentStatus("Pousando..."); + break; + case HOVERING: + altitudeCtrl.setOutput(-0.5, 0.5); + velocityCtrl.setOutput(-0.5, 0.5); + throttle( + altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) + + velocityCtrl.calculate(verticalVelocity.get(), 0)); + navigation.aimAtRadialOut(); + setCurrentStatus("Sobrevoando area..."); + break; } + } - private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) - throws RPCException, StreamException { - velocityCtrl.setOutput(0, 1); - throttle(velocityCtrl.calculate(verticalVelocity.get(), velocityToMatch + horizontalVelocity.get())); - } + private void adjustPIDbyTWR() throws RPCException, StreamException { + double currentTWR = getMaxAcel(maxTWR); + double pGain = Math.min(getTWR(), maxTWR); + altitudeCtrl.setPIDValues(currentTWR * velP * velP, 0.1, velD); + velocityCtrl.setPIDValues(pGain * velP, 0.1, velD); + } + + private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) + throws RPCException, StreamException { + velocityCtrl.setOutput(0, 1); + throttle( + velocityCtrl.calculate(verticalVelocity.get(), velocityToMatch + horizontalVelocity.get())); + } + + private void deOrbitShip() throws RPCException, InterruptedException, StreamException { + 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); - 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); + while (!isOrientedForDeorbit) { + if (Thread.interrupted()) { + throw new InterruptedException(); } - } + navigation.aimForLanding(); + setCurrentStatus(Bundle.getString("status_orienting_ship")); + Thread.sleep(100); // Prevent tight loop while waiting for event + } - /** - * 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 targetPeriapsis = currentBody.getAtmosphereDepth(); + targetPeriapsis = + targetPeriapsis > 0 ? targetPeriapsis / 2 : -currentBody.getEquatorialRadius() / 2; - private boolean hasTheVesselLanded() throws RPCException { - if (getActiveVessel().getSituation().equals(VesselSituation.LANDED) - || getActiveVessel().getSituation().equals(VesselSituation.SPLASHED)) { - setCurrentStatus(Bundle.getString("status_landed")); - hoveringMode = false; - landingMode = false; - throttle(0.0f); - getActiveVessel().getControl().setSAS(true); - getActiveVessel().getControl().setRCS(true); - getActiveVessel().getControl().setBrakes(false); - ap.disengage(); - return true; + while (!isDeorbitBurnDone) { + if (Thread.interrupted()) { + throw new InterruptedException(); } - return false; + navigation.aimForLanding(); + throttle(altitudeCtrl.calculate(targetPeriapsis, periapsis.get())); + setCurrentStatus(Bundle.getString("status_lowering_periapsis")); + } + getActiveVessel().getControl().setRCS(false); + throttle(0.0f); } + } - private double calculateCurrentVelocityMagnitude() throws RPCException, StreamException { - double timeToGround = surfaceAltitude.get() / verticalVelocity.get(); - double horizontalDistance = horizontalVelocity.get() * timeToGround; - return calculateEllipticTrajectory(horizontalDistance, surfaceAltitude.get()); + private boolean hasTheVesselLanded() throws RPCException { + if (getActiveVessel().getSituation().equals(VesselSituation.LANDED) + || getActiveVessel().getSituation().equals(VesselSituation.SPLASHED)) { + setCurrentStatus(Bundle.getString("status_landed")); + hoveringMode = false; + landingMode = false; + throttle(0.0f); + getActiveVessel().getControl().setSAS(true); + getActiveVessel().getControl().setRCS(true); + getActiveVessel().getControl().setBrakes(false); + ap.disengage(); + return true; } + return false; + } - private double calculateZeroVelocityMagnitude() throws RPCException, StreamException { - double zeroVelocityDistance = calculateEllipticTrajectory(horizontalVelocity.get(), verticalVelocity.get()); - double zeroVelocityBurnTime = zeroVelocityDistance / getMaxAcel(maxTWR); - return zeroVelocityDistance * zeroVelocityBurnTime; - } + private double[] calculateCurrentDistanceToGround() throws RPCException, StreamException { + double vertVel = verticalVelocity.get(); + double horzVel = horizontalVelocity.get(); + double vertAlt = surfaceAltitude.get(); + double horzDist = horzVel * (vertAlt / Math.abs(vertVel)); + double euclidianDistance = Math.sqrt(vertAlt * vertAlt + horzDist * horzDist); + return new double[] {euclidianDistance, 0}; + } + + private double[] calculateZeroVelocityMagnitude() throws RPCException, StreamException { + double vertVel = verticalVelocity.get(); + double horzVel = horizontalVelocity.get(); + return new double[] {Math.sqrt(horzVel * horzVel + vertVel * vertVel), 0}; + } - 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 void cleanup() { + try { + apErrorStream.removeCallback(isOrientedCallbackTag); + periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); + verticalVelocity.removeCallback(isFallingCallbackTag); + apErrorStream.remove(); + ap.disengage(); + throttle(0); + } catch (RPCException e) { + // ignore } + } } diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index 5b6a691..26f6d1b 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -1,9 +1,5 @@ package com.pesterenan.controllers; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; @@ -11,7 +7,9 @@ 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 krpc.client.RPCException; import krpc.client.Stream; import krpc.client.StreamException; @@ -20,205 +18,236 @@ public class LiftoffController extends Controller { - private static final float PITCH_UP = 90; - private ControlePID thrControl; - private float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; - private volatile boolean targetApoapsisReached, dynamicPressureLowEnough = false; - private int apoapsisCallbackTag, pressureCallbackTag; - private boolean willDecoupleStages, willOpenPanelsAndAntenna; - private String gravityCurveModel = Module.CIRCULAR.get(); - private Navigation navigation; - - public LiftoffController(ConnectionManager connectionManager, VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); - initializeParameters(); - } - - 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(getConnectionManager().getSpaceCenter(), 25); - thrControl.setOutput(0.0, 1.0); - } - - @Override - public void run() { - try { - // Apoapsis Check using Stream Callback - apoapsisCallbackTag = apoapsis.addCallback((apo) -> { + private static final float PITCH_UP = 90; + private ControlePID thrControl; + private float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; + private volatile boolean targetApoapsisReached, dynamicPressureLowEnough = false; + private int apoapsisCallbackTag, pressureCallbackTag; + private Stream pressureStream; + private boolean willDecoupleStages, willOpenPanelsAndAntenna; + private String gravityCurveModel = Module.CIRCULAR.get(); + private Navigation navigation; + + public LiftoffController( + ConnectionManager connectionManager, + VesselManager vesselManager, + Map commands) { + super(connectionManager, vesselManager); + this.commands = commands; + this.navigation = new Navigation(connectionManager, getActiveVessel()); + initializeParameters(); + } + + 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(getConnectionManager().getSpaceCenter(), 25); + thrControl.setOutput(0.0, 1.0); + } + + @Override + public void run() { + try { + // Apoapsis Check using Stream Callback + apoapsisCallbackTag = + apoapsis.addCallback( + (apo) -> { if (apo >= getFinalApoapsis()) { - targetApoapsisReached = true; - apoapsis.removeCallback(apoapsisCallbackTag); + targetApoapsisReached = true; + apoapsis.removeCallback(apoapsisCallbackTag); } - }); - apoapsis.start(); - - // Dynamic Pressure Check using Stream Callback - Stream pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); - pressureCallbackTag = pressureStream.addCallback((pressure) -> { + }); + apoapsis.start(); + + // Dynamic Pressure Check using Stream Callback + pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); + pressureCallbackTag = + pressureStream.addCallback( + (pressure) -> { if (pressure <= 10) { - dynamicPressureLowEnough = true; - pressureStream.removeCallback(pressureCallbackTag); + dynamicPressureLowEnough = true; + pressureStream.removeCallback(pressureCallbackTag); } - }); - pressureStream.start(); - - tuneAutoPilot(); - liftoff(); - gravityCurve(); - finalizeCurve(); - circularizeOrbitOnApoapsis(); - } catch (RPCException | InterruptedException | StreamException ignored) { - setCurrentStatus(Bundle.getString("status_ready")); + }); + pressureStream.start(); + + tuneAutoPilot(); + liftoff(); + gravityCurve(); + finalizeCurve(); + circularizeOrbitOnApoapsis(); + } catch (RPCException | InterruptedException | StreamException e) { + cleanup(); + setCurrentStatus(Bundle.getString("status_ready")); + } + } + + private void cleanup() { + try { + apoapsis.removeCallback(apoapsisCallbackTag); + pressureStream.removeCallback(pressureCallbackTag); + pressureStream.remove(); + ap.disengage(); + throttle(0); + } catch (RPCException e) { + // ignore + } + } + + 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 && !targetApoapsisReached) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + double altitudeProgress = + Utilities.remap(startCurveAlt, getFinalApoapsis(), 1, 0.01, altitude.get(), false); + currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); + double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); + ap.setTargetPitch(currentPitch); + double throttleValue = + Math.min( + thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000), + getMaxThrottleForTWR(currentMaxTWR)); + throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); + + if (willDecoupleStages && isCurrentStageWithoutFuel()) { + decoupleStage(); + } + setCurrentStatus( + String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); + } + throttle(0); + } + + 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); + } + + private void finalizeCurve() throws RPCException, StreamException, InterruptedException { + setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); + getActiveVessel().getControl().setRCS(true); + + while (!dynamicPressureLowEnough) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + navigation.aimAtPrograde(); + throttle(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000)); + } + throttle(0.0f); + if (willDecoupleStages) { + jettisonFairings(); + } + if (willOpenPanelsAndAntenna) { + openPanelsAndAntenna(); + } + } + + 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)); + getVesselManager().startModule(commands); + } + + 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()) { + String eventName = f.getPart().getModules().get(0).getEvents().get(0); + f.getPart().getModules().get(0).triggerEvent(eventName); + Thread.sleep(10000); } - } - - 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 && !targetApoapsisReached) { - double altitudeProgress = Utilities.remap(startCurveAlt, getFinalApoapsis(), 1, 0.01, altitude.get(), - false); - currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); - double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); - ap.setTargetPitch(currentPitch); - double throttleValue = Math.min(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000), - getMaxThrottleForTWR(currentMaxTWR)); - throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); - - if (willDecoupleStages && isCurrentStageWithoutFuel()) { - decoupleStage(); - } - setCurrentStatus(String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); - } - throttle(0); - } - - 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); - } - - private void finalizeCurve() throws RPCException, StreamException, InterruptedException { - setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); - getActiveVessel().getControl().setRCS(true); - - while (!dynamicPressureLowEnough) { - navigation.aimAtPrograde(); - throttle(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000)); - } - throttle(0.0f); - if (willDecoupleStages) { - jettisonFairings(); - } - if (willOpenPanelsAndAntenna) { - openPanelsAndAntenna(); - } - } - - 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)); - getVesselManager().startModule(commands); - } - - 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()) { - String eventName = f.getPart().getModules().get(0).getEvents().get(0); - f.getPart().getModules().get(0).triggerEvent(eventName); - Thread.sleep(10000); - } - } - } - } - - private void openPanelsAndAntenna() throws RPCException, InterruptedException { - getActiveVessel().getControl().setSolarPanels(true); - getActiveVessel().getControl().setRadiators(true); - 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; - } - - private boolean isCurrentStageWithoutFuel() throws RPCException { - for (Engine engine : getActiveVessel().getParts().getEngines()) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() - && !engine.getHasFuel()) { - return true; - } - } - return false; - } - - public float getHeading() { - return heading; - } - - 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); - } - - public float getFinalApoapsis() { - return finalApoapsisAlt; - } - - public float getRoll() { - return this.roll; - } - - 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 setGravityCurveModel(String model) { - this.gravityCurveModel = model; - } - - 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 void openPanelsAndAntenna() throws RPCException, InterruptedException { + getActiveVessel().getControl().setSolarPanels(true); + getActiveVessel().getControl().setRadiators(true); + 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; + } + + private boolean isCurrentStageWithoutFuel() throws RPCException { + for (Engine engine : getActiveVessel().getParts().getEngines()) { + if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() + && !engine.getHasFuel()) { + return true; + } + } + return false; + } + + public float getHeading() { + return heading; + } + + 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); + } + + public float getFinalApoapsis() { + return finalApoapsisAlt; + } + + public float getRoll() { + return this.roll; + } + + 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 setGravityCurveModel(String model) { + this.gravityCurveModel = model; + } + + 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); + } } diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 78463c5..332122b 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -1,10 +1,5 @@ package com.pesterenan.controllers; -import java.util.List; -import java.util.Map; - -import org.javatuples.Triplet; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; @@ -14,7 +9,8 @@ import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Vector; import com.pesterenan.views.MainGui; - +import java.util.List; +import java.util.Map; import krpc.client.RPCException; import krpc.client.Stream; import krpc.client.StreamException; @@ -25,582 +21,639 @@ 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 ctrlManeuver, ctrlRCS; - private Navigation navigation; - private boolean fineAdjustment; - private double lowOrbitAltitude; - - private volatile boolean isBurnComplete, isOriented, isTimeToBurn = false; - private int burnCompleteCallbackTag, headingErrorCallbackTag, pitchErrorCallbackTag, timeToBurnCallbackTag; - private Stream headingErrorStream; - private Stream pitchErrorStream; - private Stream timeToNodeStream; - private Stream> remainingBurn; - - public ManeuverController(ConnectionManager connectionManager, VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); - initializeParameters(); + public static final float CONST_GRAV = 9.81f; + private ControlePID ctrlManeuver, ctrlRCS; + private Navigation navigation; + private boolean fineAdjustment; + private double lowOrbitAltitude; + + private volatile boolean isBurnComplete, isOriented, isTimeToBurn = false; + private int burnCompleteCallbackTag, + headingErrorCallbackTag, + pitchErrorCallbackTag, + timeToBurnCallbackTag; + private Stream headingErrorStream; + private Stream pitchErrorStream; + private Stream timeToNodeStream; + private Stream> remainingBurn; + + public ManeuverController( + ConnectionManager connectionManager, + VesselManager vesselManager, + Map commands) { + super(connectionManager, vesselManager); + this.commands = commands; + this.navigation = new Navigation(connectionManager, getActiveVessel()); + initializeParameters(); + } + + private void initializeParameters() { + ctrlRCS = new ControlePID(getConnectionManager().getSpaceCenter(), 25); + ctrlManeuver = new ControlePID(getConnectionManager().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) { } - - private void initializeParameters() { - ctrlRCS = new ControlePID(getConnectionManager().getSpaceCenter(), 25); - ctrlManeuver = new ControlePID(getConnectionManager().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) { - } + } + + @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 (InterruptedException e) { + cleanup(); } - - @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 (InterruptedException e) { - cleanup(); - } + } + + 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); + + // Calculate the final radius for the target orbit + double targetRadius = currentBody.getEquatorialRadius() + targetAltitude; + + // 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); + + // Total delta-v for the bi-elliptic transfer + totalDv[0] = deltaV1 + deltaV2 + deltaV3 + deltaV4; + } catch (RPCException e) { + // Handle the exception } - - 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); - - // Calculate the final radius for the target orbit - double targetRadius = currentBody.getEquatorialRadius() + targetAltitude; - - // 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); - - // Total delta-v for the bi-elliptic transfer - totalDv[0] = deltaV1 + deltaV2 + deltaV3 + deltaV4; - } catch (RPCException e) { - // Handle the exception - } - return createManeuver(timeToStart, totalDv); + return createManeuver(timeToStart, totalDv); + } + + 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(); + } + + 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")); } - - 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(); - } - - 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")); + } + + public void matchOrbitApoapsis() throws RPCException, StreamException, InterruptedException { + 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(); + } + 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); + } + } + + 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]) - getConnectionManager().getSpaceCenter().getUT(); + } catch (Exception ignored) { + } + return createManeuver(uTatClosestNode, dv); + } + + 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) + }; + } + + private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { + try { + Vessel vessel = getActiveVessel(); + Orbit vesselOrbit = getActiveVessel().getOrbit(); + Orbit targetVesselOrbit = + getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + boolean hasManeuverNodes = vessel.getControl().getNodes().size() > 0; + System.out.println("hasManeuverNodes: " + hasManeuverNodes); + if (!hasManeuverNodes) { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + } + 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]; + MainGui.newInstance() + .getCreateManeuverPanel() + .positionManeuverAt(closestIsAN ? "ascending" : "descending"); + double currentInclination = + Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); + ctrlManeuver.setTimeSample(25); + while (currentInclination > 0.05) { + if (Thread.interrupted()) { + throw new InterruptedException(); } + currentInclination = + Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); + double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); + currentManeuver.setNormal( + currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); + Thread.sleep(25); + } + } catch (Exception err) { + System.err.println(err); } - - public void matchOrbitApoapsis() throws RPCException, StreamException, InterruptedException { - Orbit targetOrbit = getTargetOrbit(); - System.out.println(targetOrbit.getApoapsis() + "-- APO"); - Node maneuver = biEllipticTransferToOrbit(targetOrbit.getApoapsis(), - getActiveVessel().getOrbit().getTimeToPeriapsis()); - while (true) { + } + + private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { + 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(); + MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); + } else { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + } + currentManeuvers = getActiveVessel().getControl().getNodes(); + lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); + + Orbit activeVesselOrbit = getActiveVessel().getOrbit(); + Orbit targetVesselOrbit = + getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + ReferenceFrame currentBodyRefFrame = + activeVesselOrbit.getBody().getNonRotatingReferenceFrame(); + + double angularDiff = 10; + while (angularDiff >= 0.005) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + 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)) { if (Thread.interrupted()) { - throw new InterruptedException(); + throw new InterruptedException(); } - 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)); + lastManeuverNode.setPrograde( + lastManeuverNode.getPrograde() + + ctrlManeuver.calculate(maneuverPE / targetOrbitPosition * 1000, 1000)); + maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); Thread.sleep(25); + } } - } - 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]) - getConnectionManager().getSpaceCenter().getUT(); - } catch (Exception ignored) { - } - return createManeuver(uTatClosestNode, dv); - } - - 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)}; - } - - private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { - try { - Vessel vessel = getActiveVessel(); - Orbit vesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - boolean hasManeuverNodes = vessel.getControl().getNodes().size() > 0; - System.out.println("hasManeuverNodes: " + hasManeuverNodes); - if (!hasManeuverNodes) { - MainGui.newInstance().getCreateManeuverPanel().createManeuver(); - } - 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]; - MainGui.newInstance().getCreateManeuverPanel().positionManeuverAt(closestIsAN ? "ascending" : "descending"); - double currentInclination = Math - .toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - ctrlManeuver.setTimeSample(25); - while (currentInclination > 0.05) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - currentInclination = Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); - currentManeuver.setNormal(currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); - Thread.sleep(25); + if (targetOrbitPosition > maneuverAP) { + while (Math.floor(targetOrbitPosition) != Math.floor(maneuverAP)) { + if (Thread.interrupted()) { + throw new InterruptedException(); } - } catch (Exception err) { - System.err.println(err); + lastManeuverNode.setPrograde( + lastManeuverNode.getPrograde() + + ctrlManeuver.calculate(maneuverAP / targetOrbitPosition * 1000, 1000)); + maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); + Thread.sleep(25); + } } + angularDiff = + calculatePhaseAngle( + lastManeuverNode.getOrbit().positionAt(maneuverUT, currentBodyRefFrame), + getConnectionManager() + .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); + } + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; + } } - - private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { - 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(); - MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); - } else { - MainGui.newInstance().getCreateManeuverPanel().createManeuver(); - } - currentManeuvers = getActiveVessel().getControl().getNodes(); - lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); - - Orbit activeVesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - ReferenceFrame currentBodyRefFrame = activeVesselOrbit.getBody().getNonRotatingReferenceFrame(); - - double angularDiff = 10; - while (angularDiff >= 0.005) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - 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)) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverPE / targetOrbitPosition * 1000, 1000)); - maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); - Thread.sleep(25); - } - } - - if (targetOrbitPosition > maneuverAP) { - while (Math.floor(targetOrbitPosition) != Math.floor(maneuverAP)) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - lastManeuverNode.setPrograde(lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverAP / targetOrbitPosition * 1000, 1000)); - maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); - Thread.sleep(25); - } - } - angularDiff = calculatePhaseAngle( - lastManeuverNode.getOrbit().positionAt(maneuverUT, currentBodyRefFrame), - getConnectionManager().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); - } - } catch (Exception err) { - if (err instanceof InterruptedException) { - throw (InterruptedException) err; - } - } + } + + 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 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 Orbit getTargetOrbit() throws RPCException { + if (getConnectionManager().getSpaceCenter().getTargetBody() != null) { + return getConnectionManager().getSpaceCenter().getTargetBody().getOrbit(); } - - private Orbit getTargetOrbit() throws RPCException { - if (getConnectionManager().getSpaceCenter().getTargetBody() != null) { - return getConnectionManager().getSpaceCenter().getTargetBody().getOrbit(); - } - if (getConnectionManager().getSpaceCenter().getTargetVessel() != null) { - return getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - } - return null; + if (getConnectionManager().getSpaceCenter().getTargetVessel() != null) { + return getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); } - - private Node createManeuver(double laterTime, double[] deltaV) { - Node maneuverNode = null; - try { - getActiveVessel().getControl().addNode(getConnectionManager().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 null; + } + + private Node createManeuver(double laterTime, double[] deltaV) { + Node maneuverNode = null; + try { + getActiveVessel() + .getControl() + .addNode( + getConnectionManager().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")); } - - public void executeNextManeuver() throws InterruptedException { - try { - Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); - double burnTime = calculateBurnTime(maneuverNode); - orientToManeuverNode(maneuverNode); - executeBurn(maneuverNode, burnTime); - } catch (UnsupportedOperationException e) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); - } catch (IndexOutOfBoundsException e) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); - } catch (RPCException | StreamException e) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_data_unavailable")); - } catch (InterruptedException e) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_couldnt_orient")); - throw e; - } + return maneuverNode; + } + + public void executeNextManeuver() throws InterruptedException { + try { + Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); + double burnTime = calculateBurnTime(maneuverNode); + orientToManeuverNode(maneuverNode); + executeBurn(maneuverNode, burnTime); + } catch (UnsupportedOperationException e) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); + } catch (IndexOutOfBoundsException e) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); + } catch (RPCException | StreamException e) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_data_unavailable")); + } catch (InterruptedException e) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_couldnt_orient")); + throw e; } - - public void orientToManeuverNode(Node maneuverNode) throws RPCException, StreamException, InterruptedException { - setCurrentStatus(Bundle.getString("status_orienting_ship")); - ap.engage(); - - // Create streams for heading and pitch errors - headingErrorStream = connection.addStream(ap, "getHeadingError"); - pitchErrorStream = connection.addStream(ap, "getPitchError"); - - // A shared callback for both streams - Runnable checkOrientation = () -> { - try { - if (headingErrorStream.get() <= 3 && pitchErrorStream.get() <= 3) { - isOriented = true; - // Clean up the callbacks once orientation is complete - headingErrorStream.removeCallback(headingErrorCallbackTag); - pitchErrorStream.removeCallback(pitchErrorCallbackTag); - } - } catch (RPCException | StreamException e) { - // Handle exceptions + } + + public void orientToManeuverNode(Node maneuverNode) + throws RPCException, StreamException, InterruptedException { + setCurrentStatus(Bundle.getString("status_orienting_ship")); + ap.engage(); + + // Create streams for heading and pitch errors + headingErrorStream = connection.addStream(ap, "getHeadingError"); + pitchErrorStream = connection.addStream(ap, "getPitchError"); + + // A shared callback for both streams + Runnable checkOrientation = + () -> { + try { + if (headingErrorStream.get() <= 3 && pitchErrorStream.get() <= 3) { + isOriented = true; + // Clean up the callbacks once orientation is complete + headingErrorStream.removeCallback(headingErrorCallbackTag); + pitchErrorStream.removeCallback(pitchErrorCallbackTag); } + } catch (RPCException | StreamException e) { + // Handle exceptions + } }; - // Add the same callback to both streams - headingErrorCallbackTag = headingErrorStream.addCallback(v -> checkOrientation.run()); - pitchErrorCallbackTag = pitchErrorStream.addCallback(v -> checkOrientation.run()); + // Add the same callback to both streams + headingErrorCallbackTag = headingErrorStream.addCallback(v -> checkOrientation.run()); + pitchErrorCallbackTag = pitchErrorStream.addCallback(v -> checkOrientation.run()); - // Start the streams - headingErrorStream.start(); - pitchErrorStream.start(); + // Start the streams + headingErrorStream.start(); + pitchErrorStream.start(); - while (!isOriented) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - } + while (!isOriented) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + navigation.aimAtManeuver(maneuverNode); } - - public double calculateBurnTime(Node maneuverNode) { - try { - List engines = getActiveVessel().getParts().getEngines(); - for (Engine engine : engines) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() - && !engine.getActive()) { - engine.setActive(true); - } - } - } catch (RPCException e) { - System.err.println("Não foi possível ativar os motores." + e.getMessage()); + } + + public double calculateBurnTime(Node maneuverNode) { + try { + List engines = getActiveVessel().getParts().getEngines(); + for (Engine engine : engines) { + if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() + && !engine.getActive()) { + engine.setActive(true); } - double burnDuration = 0; - try { - 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; - burnDuration = (totalMass - dryMass) / burnRatio; - } catch (RPCException e) { - System.err.println("Não foi possível calcular o tempo de queima." + e.getMessage()); - } - setCurrentStatus("Tempo de Queima da Manobra: " + burnDuration + " segundos."); - return burnDuration; + } + } catch (RPCException e) { + System.err.println("Não foi possível ativar os motores." + e.getMessage()); } - - public void executeBurn(Node maneuverNode, double burnDuration) throws InterruptedException { - try { - // Countdown Stream - timeToNodeStream = connection.addStream(maneuverNode, "getTimeTo"); - timeToBurnCallbackTag = timeToNodeStream.addCallback((time) -> { + double burnDuration = 0; + try { + 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; + burnDuration = (totalMass - dryMass) / burnRatio; + } catch (RPCException e) { + System.err.println("Não foi possível calcular o tempo de queima." + e.getMessage()); + } + setCurrentStatus("Tempo de Queima da Manobra: " + burnDuration + " segundos."); + return burnDuration; + } + + public void executeBurn(Node maneuverNode, double burnDuration) throws InterruptedException { + try { + // Countdown Stream + timeToNodeStream = connection.addStream(maneuverNode, "getTimeTo"); + timeToBurnCallbackTag = + timeToNodeStream.addCallback( + (time) -> { if (time <= (burnDuration / 2.0)) { - isTimeToBurn = true; - timeToNodeStream.removeCallback(timeToBurnCallbackTag); + isTimeToBurn = true; + timeToNodeStream.removeCallback(timeToBurnCallbackTag); } - }); - timeToNodeStream.start(); - - double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - (fineAdjustment ? 5 : 0); - setCurrentStatus(Bundle.getString("status_maneuver_warp")); - if (burnStartTime > 30) { - getConnectionManager().getSpaceCenter() - .warpTo((getConnectionManager().getSpaceCenter().getUT() + burnStartTime - 10), 100000, 4); - } - - setCurrentStatus(String.format(Bundle.getString("status_maneuver_duration"), burnDuration)); - while (!isTimeToBurn) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - setCurrentStatus(String.format(Bundle.getString("status_maneuver_ignition_in"), - maneuverNode.getTimeTo() - (burnDuration / 2.0))); - } - - // Main Burn Stream - remainingBurn = getConnectionManager().getConnection().addStream( - maneuverNode, - "remainingBurnVector", maneuverNode.getReferenceFrame()); - burnCompleteCallbackTag = remainingBurn.addCallback((burn) -> { + }); + timeToNodeStream.start(); + + double burnStartTime = + maneuverNode.getTimeTo() - (burnDuration / 2.0) - (fineAdjustment ? 5 : 0); + setCurrentStatus(Bundle.getString("status_maneuver_warp")); + if (burnStartTime > 30) { + getConnectionManager() + .getSpaceCenter() + .warpTo( + (getConnectionManager().getSpaceCenter().getUT() + burnStartTime - 10), 100000, 4); + } + + setCurrentStatus(String.format(Bundle.getString("status_maneuver_duration"), burnDuration)); + while (!isTimeToBurn) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + navigation.aimAtManeuver(maneuverNode); + setCurrentStatus( + String.format( + Bundle.getString("status_maneuver_ignition_in"), + maneuverNode.getTimeTo() - (burnDuration / 2.0))); + } + + // Main Burn Stream + remainingBurn = + getConnectionManager() + .getConnection() + .addStream(maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); + burnCompleteCallbackTag = + remainingBurn.addCallback( + (burn) -> { if (burn.getValue1() < (fineAdjustment ? 2 : 0.5)) { - isBurnComplete = true; - remainingBurn.removeCallback(burnCompleteCallbackTag); + isBurnComplete = true; + remainingBurn.removeCallback(burnCompleteCallbackTag); } - }); - remainingBurn.start(); + }); + remainingBurn.start(); - setCurrentStatus(Bundle.getString("status_maneuver_executing")); - while (!isBurnComplete && maneuverNode != null) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - double burnDvLeft = remainingBurn.get().getValue1(); - float limitValue = burnDvLeft > 100 ? 1000 : 100; - throttle(ctrlManeuver.calculate( - (maneuverNode.getDeltaV() - Math.floor(burnDvLeft)) / maneuverNode.getDeltaV() * limitValue, - limitValue)); - } - - 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) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_data_unavailable")); - } catch (InterruptedException e) { - System.err.println("Erro: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); - throw e; + setCurrentStatus(Bundle.getString("status_maneuver_executing")); + while (!isBurnComplete && maneuverNode != null) { + if (Thread.interrupted()) { + throw new InterruptedException(); } + navigation.aimAtManeuver(maneuverNode); + double burnDvLeft = remainingBurn.get().getValue1(); + float limitValue = burnDvLeft > 100 ? 1000 : 100; + throttle( + ctrlManeuver.calculate( + (maneuverNode.getDeltaV() - Math.floor(burnDvLeft)) + / maneuverNode.getDeltaV() + * limitValue, + limitValue)); + } + + 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) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_data_unavailable")); + } catch (InterruptedException e) { + System.err.println("Erro: " + e.getMessage()); + setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); + throw e; } - - private void cleanup() { - try { - if (headingErrorStream != null) { - headingErrorStream.removeCallback(headingErrorCallbackTag); - headingErrorStream.remove(); - } - if (pitchErrorStream != null) { - pitchErrorStream.removeCallback(pitchErrorCallbackTag); - pitchErrorStream.remove(); - } - if (timeToNodeStream != null) { - timeToNodeStream.removeCallback(timeToBurnCallbackTag); - timeToNodeStream.remove(); - } - if (remainingBurn != null) { - remainingBurn.removeCallback(burnCompleteCallbackTag); - remainingBurn.remove(); - } - ap.disengage(); - throttle(0); - } catch (RPCException e) { - // ignore - } + } + + private void cleanup() { + try { + if (headingErrorStream != null) { + headingErrorStream.removeCallback(headingErrorCallbackTag); + headingErrorStream.remove(); + } + if (pitchErrorStream != null) { + pitchErrorStream.removeCallback(pitchErrorCallbackTag); + pitchErrorStream.remove(); + } + if (timeToNodeStream != null) { + timeToNodeStream.removeCallback(timeToBurnCallbackTag); + timeToNodeStream.remove(); + } + if (remainingBurn != null) { + remainingBurn.removeCallback(burnCompleteCallbackTag); + remainingBurn.remove(); + } + ap.disengage(); + throttle(0); + } catch (RPCException e) { + // ignore } - - 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)); - } - getActiveVessel().getControl().setForward(0); + } + + 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)); } - - 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; - } - } - } - return false; - } catch (RPCException ignored) { + getActiveVessel().getControl().setForward(0); + } + + 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; } + } } return false; + } catch (RPCException ignored) { + } } - - 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 = 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 = 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 + } } diff --git a/src/main/java/com/pesterenan/controllers/RoverController.java b/src/main/java/com/pesterenan/controllers/RoverController.java index b9adb12..f07ac55 100644 --- a/src/main/java/com/pesterenan/controllers/RoverController.java +++ b/src/main/java/com/pesterenan/controllers/RoverController.java @@ -1,13 +1,5 @@ package com.pesterenan.controllers; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.javatuples.Pair; -import org.javatuples.Triplet; - import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import com.pesterenan.resources.Bundle; @@ -16,299 +8,386 @@ 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.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; import krpc.client.services.SpaceCenter.SolarPanelState; +import org.javatuples.Pair; +import org.javatuples.Triplet; 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; - private ConnectionManager connectionManager; - - public RoverController(ConnectionManager connectionManager, VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.connectionManager = connectionManager; - 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 boolean isAutoRoverRunning; + private PathFinding pathFinding; + private Vector targetPoint = new Vector(); + private Vector roverDirection = new Vector(); + private MODE currentMode; + private ConnectionManager connectionManager; + + private volatile double currentDistanceToTarget = 0; + private volatile float currentChargePercentage = 100; + private int distanceCallbackTag, chargeCallbackTag; + private Stream> positionStream; + private Stream chargeAmountStream; + + public RoverController( + ConnectionManager connectionManager, + VesselManager vesselManager, + Map commands) { + super(connectionManager, vesselManager); + this.connectionManager = connectionManager; + this.commands = commands; + initializeParameters(); + } + + 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(getConnectionManager(), getVesselManager()); + acelCtrl.setOutput(0, 1); + sterringCtrl.setOutput(-1, 1); + isAutoRoverRunning = true; + } 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(getConnectionManager(), getVesselManager()); - 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(); } - - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.ROVER.get())) { - setTarget(); - driveRoverToTarget(); - } + } + + private void setupCallbacks() throws RPCException, IOException, StreamException { + positionStream = connection.addStream(getActiveVessel(), "position", orbitalReferenceFrame); + distanceCallbackTag = + positionStream.addCallback( + (pos) -> { + currentDistanceToTarget = Vector.distance(new Vector(pos), targetPoint); + }); + positionStream.start(); + + chargeAmountStream = + connection.addStream(getActiveVessel().getResources(), "amount", "ElectricCharge"); + chargeCallbackTag = + chargeAmountStream.addCallback( + (amount) -> { + try { + float totalCharge = 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( + connectionManager + .getSpaceCenter() + .getTargetVessel() + .position(orbitalReferenceFrame)); + setCurrentStatus("Calculando rota até o alvo..."); + pathFinding.buildPathToTarget(targetVesselPosition); + } + } catch (RPCException | IOException | InterruptedException ignored) { } - - 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( - connectionManager.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; } - - private void changeControlMode() throws RPCException, IOException, StreamException, InterruptedException { - switch (currentMode) { - case DRIVE : - driveRover(); - break; - case CHARGING : - rechargeRover(); - break; - case NEXT_POINT : - setNextPointInPath(); - break; + } + + private void driveRoverToTarget() { + currentMode = MODE.NEXT_POINT; + try { + setupCallbacks(); + while (isAutoRoverRunning) { + if (Thread.interrupted()) { + throw new InterruptedException(); } - } - - private void driveRoverToTarget() { - 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) { - } + changeControlMode(); + if (isFarFromTarget()) { + currentMode = needToChargeBatteries() ? MODE.CHARGING : MODE.DRIVE; + } else { // Rover arrived at destiny + currentMode = MODE.NEXT_POINT; } + Thread.sleep(100); + } + } catch (InterruptedException e) { + cleanup(); + } catch (RPCException | IOException | StreamException 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 boolean isFarFromTarget() throws RPCException { - double distance = Vector.distance(new Vector(getActiveVessel().position(orbitalReferenceFrame)), targetPoint); - return distance > distanceFromTargetLimit; - } - - 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 { + getActiveVessel().getControl().setBrakes(true); + pathFinding.removeDrawnPath(); + isAutoRoverRunning = false; + 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; - } - connectionManager.getSpaceCenter().warpTo((connectionManager.getSpaceCenter().getUT() + chargeTime), 10000, - 4); - getActiveVessel().getControl().setLights(true); + } + + 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()); + } - 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); - } - setCurrentStatus("Driving... " + deltaAngle); + } else { + targetPoint = pathFinding.getPathsFirstPoint(); } - - 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 boolean isFarFromTarget() { + return currentDistanceToTarget > distanceFromTargetLimit; + } + + private boolean needToChargeBatteries() { + float minChargeLevel = 10.0f; + return (currentChargePercentage < minChargeLevel); + } + + 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; + } + connectionManager + .getSpaceCenter() + .warpTo((connectionManager.getSpaceCenter().getUT() + chargeTime), 10000, 4); + getActiveVessel().getControl().setLights(true); } - - 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 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); } - - private Vector transformDirection(Vector vector) throws RPCException { - return new Vector( - connectionManager.getSpaceCenter().transformDirection(vector.toTriplet(), roverReferenceFrame, - surfaceReferenceFrame)); + 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), surfaceReferenceFrame, distance); + return direction.multiply(raycast); + } + + private Vector transformDirection(Vector vector) throws RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformDirection(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); + } + + private Vector posSurfToRover(Vector vector) throws RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformPosition(vector.toTriplet(), surfaceReferenceFrame, roverReferenceFrame)); + } + + private Vector posRoverToSurf(Vector vector) throws RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformPosition(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); + } + + private Vector posOrbToSurf(Vector vector) throws RPCException { + return new Vector( + connectionManager + .getSpaceCenter() + .transformPosition(vector.toTriplet(), orbitalReferenceFrame, surfaceReferenceFrame)); + } + + 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); } + } - private Vector posSurfToRover(Vector vector) throws RPCException { - return new Vector( - connectionManager.getSpaceCenter().transformPosition(vector.toTriplet(), surfaceReferenceFrame, - roverReferenceFrame)); - } - - private Vector posRoverToSurf(Vector vector) throws RPCException { - return new Vector( - connectionManager.getSpaceCenter().transformPosition(vector.toTriplet(), roverReferenceFrame, - surfaceReferenceFrame)); - } + private void setRoverSteering(double steering) throws RPCException { + getActiveVessel().getControl().setWheelSteering((float) steering); + } - private Vector posOrbToSurf(Vector vector) throws RPCException { - return new Vector( - connectionManager.getSpaceCenter().transformPosition(vector.toTriplet(), orbitalReferenceFrame, - surfaceReferenceFrame)); - } - - 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); - } - } - - private void setRoverSteering(double steering) throws RPCException { - 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 3a9bcd1..5b540b2 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -1,8 +1,5 @@ package com.pesterenan.model; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import com.pesterenan.controllers.Controller; import com.pesterenan.controllers.DockingController; import com.pesterenan.controllers.LandingController; @@ -13,7 +10,8 @@ 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; @@ -28,229 +26,237 @@ public class ActiveVessel { - protected Vessel activeVessel; - protected SpaceCenter spaceCenter; - protected Connection connection; - private final Map telemetryData = new ConcurrentHashMap<>(); - 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; - private ConnectionManager connectionManager; - private VesselManager vesselManager; - - public ActiveVessel(ConnectionManager connectionManager, VesselManager vesselManager) { - this.connectionManager = connectionManager; - this.vesselManager = vesselManager; - this.connection = connectionManager.getConnection(); - this.spaceCenter = connectionManager.getSpaceCenter(); - initializeParameters(); + protected Vessel activeVessel; + protected SpaceCenter spaceCenter; + protected Connection connection; + private final Map telemetryData = new ConcurrentHashMap<>(); + 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; + private ConnectionManager connectionManager; + private VesselManager vesselManager; + + public ActiveVessel(ConnectionManager connectionManager, VesselManager vesselManager) { + this.connectionManager = connectionManager; + this.vesselManager = vesselManager; + this.connection = connectionManager.getConnection(); + this.spaceCenter = connectionManager.getSpaceCenter(); + initializeParameters(); + } + + public ConnectionManager getConnectionManager() { + return connectionManager; + } + + public VesselManager getVesselManager() { + return vesselManager; + } + + public String getCurrentStatus() { + if (controller != null) { + return controller.getCurrentStatus(); } - - public ConnectionManager getConnectionManager() { - return connectionManager; - } - - public VesselManager getVesselManager() { - return vesselManager; + return currentStatus; + } + + public void setCurrentStatus(String status) { + if (controller != null) { + controller.setCurrentStatus(status); + } else { + this.currentStatus = status; } - - public String getCurrentStatus() { - if (controller != null) { - return controller.getCurrentStatus(); + } + + public int getCurrentVesselId() { + return currentVesselId; + } + + public Vessel getActiveVessel() { + return activeVessel; + } + + 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(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 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); } - return currentStatus; + spaceCenter.setActiveVessel(activeVessel); + getActiveVessel().getControl().activateNextStage(); + } + setCurrentStatus(Bundle.getString("status_liftoff")); + } catch (RPCException | InterruptedException | StreamException ignored) { + setCurrentStatus(Bundle.getString("status_liftoff_abort")); } + } - public void setCurrentStatus(String status) { - currentStatus = status; - } + public double getTWR() throws RPCException, StreamException { + return getActiveVessel().getAvailableThrust() / (totalMass.get() * gravityAcel); + } - public int getCurrentVesselId() { - return currentVesselId; - } + public double getMaxThrottleForTWR(double targetTWR) throws RPCException, StreamException { + return targetTWR / getTWR(); + } - public Vessel getActiveVessel() { - return activeVessel; - } + public double getMaxAcel(float maxTWR) throws RPCException, StreamException { + return Math.min(maxTWR, getTWR()) * gravityAcel - gravityAcel; + } - public void setActiveVessel(Vessel currentVessel) { - activeVessel = currentVessel; + public void startModule(Map commands) { + String currentFunction = commands.get(Module.MODULO.get()); + if (controllerThread != null) { + controllerThread.interrupt(); + runningModule = false; } - - public void throttle(float acel) throws RPCException { - getActiveVessel().getControl().setThrottle(Math.min(acel, 1.0f)); + if (currentFunction.equals(Module.LIFTOFF.get())) { + controller = new LiftoffController(this.connectionManager, this.vesselManager, commands); + runningModule = true; } - - public void throttle(double acel) throws RPCException { - throttle((float) acel); + if (currentFunction.equals(Module.HOVERING.get()) + || currentFunction.equals(Module.LANDING.get())) { + controller = new LandingController(this.connectionManager, this.vesselManager, commands); + runningModule = true; } - - public void tuneAutoPilot() throws RPCException { - ap.setTimeToPeak(new Vector(2, 2, 2).toTriplet()); - ap.setDecelerationTime(new Vector(5, 5, 5).toTriplet()); + if (currentFunction.equals(Module.MANEUVER.get())) { + controller = new ManeuverController(this.connectionManager, this.vesselManager, commands); + runningModule = true; } - - 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); - } - spaceCenter.setActiveVessel(activeVessel); - getActiveVessel().getControl().activateNextStage(); - } - setCurrentStatus(Bundle.getString("status_liftoff")); - } catch (RPCException | InterruptedException | StreamException ignored) { - setCurrentStatus(Bundle.getString("status_liftoff_abort")); - } + if (currentFunction.equals(Module.ROVER.get())) { + controller = new RoverController(this.connectionManager, this.vesselManager, commands); + runningModule = true; } - - public double getTWR() throws RPCException, StreamException { - return getActiveVessel().getAvailableThrust() / (totalMass.get() * gravityAcel); + if (currentFunction.equals(Module.DOCKING.get())) { + controller = new DockingController(this.connectionManager, this.vesselManager, commands); + runningModule = true; } - - public double getMaxThrottleForTWR(double targetTWR) throws RPCException, StreamException { - return targetTWR / getTWR(); + controllerThread = new Thread(controller, currentVesselId + " - " + currentFunction); + controllerThread.start(); + } + + public Map getTelemetryData() { + return telemetryData; + } + + public void cancelControl() { + try { + ap.disengage(); + throttle(0); + if (controllerThread != null) { + controllerThread.interrupt(); + runningModule = false; + } + } catch (RPCException ignored) { } - - public double getMaxAcel(float maxTWR) throws RPCException, StreamException { - return Math.min(maxTWR, getTWR()) * gravityAcel - gravityAcel; + } + + public boolean hasModuleRunning() { + return runningModule; + } + + protected 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); + } + + 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 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(this.connectionManager, this.vesselManager, commands); - runningModule = true; - } - if (currentFunction.equals(Module.HOVERING.get()) || currentFunction.equals(Module.LANDING.get())) { - controller = new LandingController(this.connectionManager, this.vesselManager, commands); - runningModule = true; - } - if (currentFunction.equals(Module.MANEUVER.get())) { - controller = new ManeuverController(this.connectionManager, this.vesselManager, commands); - runningModule = true; - } - if (currentFunction.equals(Module.ROVER.get())) { - controller = new RoverController(this.connectionManager, this.vesselManager, commands); - runningModule = true; - } - if (currentFunction.equals(Module.DOCKING.get())) { - controller = new DockingController(this.connectionManager, this.vesselManager, commands); - runningModule = true; - } - controllerThread = new Thread(controller, currentVesselId + " - " + currentFunction); - controllerThread.start(); - } - - public Map getTelemetryData() { - return telemetryData; - } - - public void cancelControl() { - try { - ap.disengage(); - throttle(0); - if (controllerThread != null) { - controllerThread.interrupt(); - runningModule = false; - } - } catch (RPCException ignored) { - } - } - - public boolean hasModuleRunning() { - return runningModule; - } - - protected 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); - } - - 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); - } - } - - private void initializeParameters() { - 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); - totalMass = connection.addStream(getActiveVessel(), "getMass"); - totalMass.start(); - - // Add callbacks to update telemetry data automatically - altitude = connection.addStream(flightParameters, "getMeanAltitude"); - altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); - altitude.start(); - - surfaceAltitude = connection.addStream(flightParameters, "getSurfaceAltitude"); - surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); - surfaceAltitude.start(); - - apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); - apoapsis.addCallback(val -> telemetryData.put(Telemetry.APOAPSIS, val < 0 ? 0 : val)); - apoapsis.start(); - - periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); - periapsis.addCallback(val -> telemetryData.put(Telemetry.PERIAPSIS, val < 0 ? 0 : val)); - periapsis.start(); - - verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); - verticalVelocity.addCallback(val -> telemetryData.put(Telemetry.VERT_SPEED, val)); - verticalVelocity.start(); - - horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); - horizontalVelocity.addCallback(val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); - horizontalVelocity.start(); - - } catch (RPCException | StreamException e) { - System.err.println("Error while initializing parameters for active vessel: " + e.getMessage()); - } + } + + private void initializeParameters() { + 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); + totalMass = connection.addStream(getActiveVessel(), "getMass"); + totalMass.start(); + + // Add callbacks to update telemetry data automatically + altitude = connection.addStream(flightParameters, "getMeanAltitude"); + altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); + altitude.start(); + + surfaceAltitude = connection.addStream(flightParameters, "getSurfaceAltitude"); + surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); + surfaceAltitude.start(); + + apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); + apoapsis.addCallback(val -> telemetryData.put(Telemetry.APOAPSIS, val < 0 ? 0 : val)); + apoapsis.start(); + + periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); + periapsis.addCallback(val -> telemetryData.put(Telemetry.PERIAPSIS, val < 0 ? 0 : val)); + periapsis.start(); + + verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); + verticalVelocity.addCallback(val -> telemetryData.put(Telemetry.VERT_SPEED, val)); + verticalVelocity.start(); + + horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); + horizontalVelocity.addCallback( + val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); + horizontalVelocity.start(); + + } catch (RPCException | StreamException e) { + System.err.println( + "Error while initializing parameters for active vessel: " + e.getMessage()); } + } } diff --git a/src/main/java/com/pesterenan/model/ConnectionManager.java b/src/main/java/com/pesterenan/model/ConnectionManager.java index 391413e..d8ef470 100644 --- a/src/main/java/com/pesterenan/model/ConnectionManager.java +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -1,101 +1,103 @@ package com.pesterenan.model; +import com.pesterenan.resources.Bundle; +import com.pesterenan.views.StatusDisplay; import java.io.IOException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; - import javax.swing.SwingUtilities; - -import com.pesterenan.resources.Bundle; -import com.pesterenan.views.StatusDisplay; - 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 final StatusDisplay statusDisplay; - private volatile boolean isConnecting = false; - private ScheduledExecutorService connectionScheduler; - - public ConnectionManager(final String connectionName, final StatusDisplay statusDisplay) { - this.statusDisplay = statusDisplay; - connectAndMonitor(connectionName); - } - - public Connection getConnection() { - return connection; + private KRPC krpc; + private Connection connection; + private SpaceCenter spaceCenter; + private final StatusDisplay statusDisplay; + private volatile boolean isConnecting = false; + private ScheduledExecutorService connectionScheduler; + + public ConnectionManager(final String connectionName, final StatusDisplay statusDisplay) { + this.statusDisplay = statusDisplay; + connectAndMonitor(connectionName); + } + + public Connection getConnection() { + return connection; + } + + public SpaceCenter getSpaceCenter() { + return spaceCenter; + } + + public KRPC getKRPC() { + return krpc; + } + + public void connectAndMonitor(final String connectionName) { + if (connectionScheduler != null && !connectionScheduler.isShutdown()) { + return; } - - public SpaceCenter getSpaceCenter() { - return spaceCenter; - } - - public KRPC getKRPC() { - return krpc; - } - - public void connectAndMonitor(final String connectionName) { - if (connectionScheduler != null && !connectionScheduler.isShutdown()) { + connectionScheduler = Executors.newSingleThreadScheduledExecutor(); + connectionScheduler.scheduleAtFixedRate( + () -> { + if (isConnectionAlive() || isConnecting) { return; - } - connectionScheduler = Executors.newSingleThreadScheduledExecutor(); - connectionScheduler.scheduleAtFixedRate(() -> { - if (isConnectionAlive() || isConnecting) { - return; - } + } - isConnecting = true; - SwingUtilities.invokeLater(() -> { + isConnecting = true; + SwingUtilities.invokeLater( + () -> { statusDisplay.setStatusMessage(Bundle.getString("status_connecting")); statusDisplay.setBtnConnectVisible(false); - }); - - try { - connection = Connection.newInstance(connectionName); - krpc = KRPC.newInstance(getConnection()); - spaceCenter = SpaceCenter.newInstance(getConnection()); - SwingUtilities.invokeLater(() -> { - statusDisplay.setStatusMessage(Bundle.getString("status_connected")); - statusDisplay.setBtnConnectVisible(false); + }); + + try { + connection = Connection.newInstance(connectionName); + krpc = KRPC.newInstance(getConnection()); + spaceCenter = SpaceCenter.newInstance(getConnection()); + SwingUtilities.invokeLater( + () -> { + statusDisplay.setStatusMessage(Bundle.getString("status_connected")); + statusDisplay.setBtnConnectVisible(false); }); - } catch (final IOException e) { - SwingUtilities.invokeLater(() -> { - statusDisplay.setStatusMessage(Bundle.getString("status_error_connection")); - statusDisplay.setBtnConnectVisible(true); + } catch (final IOException e) { + SwingUtilities.invokeLater( + () -> { + statusDisplay.setStatusMessage(Bundle.getString("status_error_connection")); + statusDisplay.setBtnConnectVisible(true); }); - } finally { - isConnecting = false; - } - }, 0, 5, TimeUnit.SECONDS); + } finally { + isConnecting = false; + } + }, + 0, + 5, + TimeUnit.SECONDS); + } + + public boolean isConnectionAlive() { + try { + if (krpc == null) return false; + krpc.getStatus(); + return true; + } catch (final RPCException e) { + return false; } + } - public boolean isConnectionAlive() { - try { - if (krpc == null) - return false; - krpc.getStatus(); - return true; - } catch (final RPCException e) { - return false; - } - } + public KRPC.GameScene getCurrentGameScene() throws RPCException { + return krpc.getCurrentGameScene(); + } - public KRPC.GameScene getCurrentGameScene() throws RPCException { - return krpc.getCurrentGameScene(); + public boolean isOnFlightScene() { + try { + return this.getCurrentGameScene().equals(KRPC.GameScene.FLIGHT); + } catch (final RPCException e) { + return false; } - - public boolean isOnFlightScene() { - try { - return this.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 index 24ecd97..8ead94e 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -1,5 +1,10 @@ package com.pesterenan.model; +import com.pesterenan.resources.Bundle; +import com.pesterenan.utils.Vector; +import com.pesterenan.views.FunctionsAndTelemetryJPanel; +import com.pesterenan.views.MainGui; +import com.pesterenan.views.StatusDisplay; import java.awt.event.ActionEvent; import java.util.List; import java.util.Map; @@ -7,17 +12,9 @@ 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 com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Vector; -import com.pesterenan.views.FunctionsAndTelemetryJPanel; -import com.pesterenan.views.MainGui; -import com.pesterenan.views.StatusDisplay; - import krpc.client.Connection; import krpc.client.RPCException; import krpc.client.services.SpaceCenter; @@ -25,176 +22,199 @@ import krpc.client.services.SpaceCenter.Vessel; public class VesselManager { - private ConnectionManager connectionManager; - private ActiveVessel currentVessel; - private int currentVesselId = -1; - private StatusDisplay statusDisplay; - private FunctionsAndTelemetryJPanel telemetryPanel; - - public void setTelemetryPanel(FunctionsAndTelemetryJPanel panel) { - this.telemetryPanel = panel; - } - - public VesselManager(ConnectionManager connectionManager, StatusDisplay statusDisplay) { - this.connectionManager = connectionManager; - this.statusDisplay = statusDisplay; - } - - public Connection getConnection() { - return connectionManager.getConnection(); - } - - public SpaceCenter getSpaceCenter() { - return connectionManager.getSpaceCenter(); - } - - public ActiveVessel getCurrentVessel() { - return currentVessel; - } - - public int getCurrentVesselId() { - return currentVesselId; - } - - public void startUpdateLoop() { - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.scheduleAtFixedRate(() -> { - if (getConnection() == null || getSpaceCenter() == null) { - System.out.println("Connection not established yet, skipping update cycle."); - return; + private ConnectionManager connectionManager; + private ActiveVessel currentVessel; + private int currentVesselId = -1; + private StatusDisplay statusDisplay; + private FunctionsAndTelemetryJPanel telemetryPanel; + + public void setTelemetryPanel(FunctionsAndTelemetryJPanel panel) { + this.telemetryPanel = panel; + } + + public VesselManager(ConnectionManager connectionManager, StatusDisplay statusDisplay) { + this.connectionManager = connectionManager; + this.statusDisplay = statusDisplay; + } + + public Connection getConnection() { + return connectionManager.getConnection(); + } + + public SpaceCenter getSpaceCenter() { + return connectionManager.getSpaceCenter(); + } + + public ActiveVessel getCurrentVessel() { + return currentVessel; + } + + public int getCurrentVesselId() { + return currentVesselId; + } + + public void startUpdateLoop() { + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduler.scheduleAtFixedRate( + () -> { + if (getConnection() == null || getSpaceCenter() == null) { + System.out.println("Connection not established yet, skipping update cycle."); + return; + } + try { + if (!connectionManager.isOnFlightScene()) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return; } + checkAndUpdateActiveVessel(); + updateUI(); + } catch (RPCException e) { + System.err.println("RPC Error: " + e.getMessage()); + } + }, + 0, + 100, + TimeUnit.MILLISECONDS); + } + + public ListModel getCurrentManeuvers() { + DefaultListModel list = new DefaultListModel<>(); + try { + List maneuvers = this.getSpaceCenter().getActiveVessel().getControl().getNodes(); + maneuvers.forEach( + m -> { try { - if (!connectionManager.isOnFlightScene()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - return; - } - checkAndUpdateActiveVessel(); - updateUI(); - } catch (RPCException e) { - System.err.println("RPC Error: " + e.getMessage()); + 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) { } - }, 0, 100, TimeUnit.MILLISECONDS); - } - - public ListModel getCurrentManeuvers() { - DefaultListModel list = new DefaultListModel<>(); - try { - List maneuvers = this.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 ignored) { - } - 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 vesselNameError) { - System.err.println("Couldn't add vessel name to list. Error: " + vesselNameError.getMessage()); - } - }); - } catch (RPCException rpcOrNpeException) { - System.err.println("Couldn't get vessel list, Error: " + rpcOrNpeException.getMessage()); - } - return list; - } - - public String getVesselInfo(int selectedIndex) { - try { - Vessel activeVessel = this.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 ignored) { - } - 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) { - currentVessel.cancelControl(); - } - - private void checkAndUpdateActiveVessel() throws RPCException { - Vessel activeVessel = getSpaceCenter().getActiveVessel(); - int activeVesselId = activeVessel.hashCode(); - if (currentVesselId != activeVesselId) { - currentVessel = new ActiveVessel(connectionManager, this); - currentVesselId = currentVessel.getCurrentVesselId(); - MainGui.getInstance().setVesselManager(this); - } - } - - private void updateUI() { - SwingUtilities.invokeLater(() -> { - if (currentVessel != null && telemetryPanel != null) { - telemetryPanel.updateTelemetry(currentVessel.getTelemetryData()); - MainGui.getInstance().getCreateManeuverPanel().updatePanel(getCurrentManeuvers()); - if (currentVessel.hasModuleRunning()) { - statusDisplay.setStatusMessage(currentVessel.getCurrentStatus()); - } + }); + } catch (RPCException | NullPointerException | UnsupportedOperationException ignored) { + } + 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 vesselNameError) { + System.err.println( + "Couldn't add vessel name to list. Error: " + vesselNameError.getMessage()); } + }); + } catch (RPCException rpcOrNpeException) { + System.err.println("Couldn't get vessel list, Error: " + rpcOrNpeException.getMessage()); + } + return list; + } + + public String getVesselInfo(int selectedIndex) { + try { + Vessel activeVessel = + this.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 ignored) { + } + 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) { + currentVessel.cancelControl(); + } + + private void checkAndUpdateActiveVessel() throws RPCException { + Vessel activeVessel = getSpaceCenter().getActiveVessel(); + int activeVesselId = activeVessel.hashCode(); + if (currentVesselId != activeVesselId) { + currentVessel = new ActiveVessel(connectionManager, this); + currentVesselId = currentVessel.getCurrentVesselId(); + MainGui.getInstance().setVesselManager(this); + } + } + + private void updateUI() { + SwingUtilities.invokeLater( + () -> { + if (currentVessel != null && telemetryPanel != null) { + telemetryPanel.updateTelemetry(currentVessel.getTelemetryData()); + MainGui.getInstance().getCreateManeuverPanel().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 = this.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; - } + } + + private boolean filterVessels(Vessel vessel, String search) { + if ("all".equals(search)) { + return true; + } + double TEN_KILOMETERS = 10000.0; + try { + Vessel active = this.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; } - } catch (RPCException ignored) { + break; + case "samebody": + return true; } - return false; + } + } 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 4c9c07b..b5b56fc 100644 --- a/src/main/java/com/pesterenan/utils/ControlePID.java +++ b/src/main/java/com/pesterenan/utils/ControlePID.java @@ -4,121 +4,119 @@ 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, previousMeasurement, lastTime = 0.0; + private double timeSample = 0.025; // 25 millisegundos + private double proportionalTerm; + + private double derivativeTerm; + + public ControlePID() {} + + public ControlePID(SpaceCenter spaceCenter, double timeSample) { + this.spaceCenter = spaceCenter; + setTimeSample(timeSample); + } + + 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.previousMeasurement = 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.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); + } - 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 double getTimeSample() { - return timeSample; - } - - public void reset() { - this.previousError = 0; - this.previousMeasurement = 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.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); + outputMin = min; + outputMax = max; + integralTerm = limitOutput(integralTerm); + } + + public void setPIDValues(double Kp, double Ki, double Kd) { + if (Kp > 0) { + this.kp = Kp; } - - public void setOutput(double min, double max) { - if (min > max) { - return; - } - outputMin = min; - outputMax = max; - integralTerm = limitOutput(integralTerm); + if (Ki >= 0) { + this.ki = Ki; } - - 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; - } + if (Kd >= 0) { + this.kd = Kd; } - - public void setTimeSample(double milliseconds) { - timeSample = milliseconds > 0 ? milliseconds / 1000 : timeSample; + } + + 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(); } - - /** - * 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(); - } + } + + /** 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(); + } } + } - /** 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); - } + 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 fcd7ebe..566af1e 100644 --- a/src/main/java/com/pesterenan/utils/Module.java +++ b/src/main/java/com/pesterenan/utils/Module.java @@ -1,57 +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 5294880..1f76328 100644 --- a/src/main/java/com/pesterenan/utils/Navigation.java +++ b/src/main/java/com/pesterenan/utils/Navigation.java @@ -1,161 +1,178 @@ 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 com.pesterenan.model.ConnectionManager; - -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; - private ConnectionManager connectionManager; + 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; - public Navigation(ConnectionManager connectionManager, Vessel currentVessel) { - this.connectionManager = connectionManager; - this.currentVessel = currentVessel; - initializeParameters(); - } + public Navigation(ConnectionManager connectionManager, Vessel currentVessel) { + this.connectionManager = connectionManager; + this.currentVessel = currentVessel; + initializeParameters(); + } - private void initializeParameters() { - try { - // drawing = Drawing.newInstance(getConexao()); - orbitalReference = currentVessel.getOrbit().getBody().getReferenceFrame(); - flightParameters = currentVessel.flight(orbitalReference); - horizontalSpeed = connectionManager.getConnection().addStream(flightParameters, "getHorizontalSpeed"); - } catch (RPCException | StreamException ignored) { - } + private void initializeParameters() { + try { + // drawing = Drawing.newInstance(getConexao()); + orbitalReference = currentVessel.getOrbit().getBody().getReferenceFrame(); + flightParameters = currentVessel.flight(orbitalReference); + horizontalSpeed = + connectionManager.getConnection().addStream(flightParameters, "getHorizontalSpeed"); + } catch (RPCException | StreamException ignored) { } + } - public void aimAtManeuver(Node maneuver) throws RPCException { - aimAtDirection(connectionManager.getSpaceCenter().transformDirection(PROGRADE, maneuver.getReferenceFrame(), - orbitalReference)); - } + public void aimAtManeuver(Node maneuver) throws RPCException { + aimAtDirection( + connectionManager + .getSpaceCenter() + .transformDirection(PROGRADE, maneuver.getReferenceFrame(), orbitalReference)); + } - public void aimForLanding() throws RPCException, StreamException { - Vector currentPosition = new Vector(currentVessel.position(orbitalReference)); - Vector retrograde = new Vector(connectionManager.getSpaceCenter().transformPosition(RETROGRADE, - currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)).subtract(currentPosition); - Vector radial = new Vector( - connectionManager.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 currentPosition = new Vector(currentVessel.position(orbitalReference)); + Vector retrograde = + new Vector( + connectionManager + .getSpaceCenter() + .transformPosition( + RETROGRADE, + currentVessel.getSurfaceVelocityReferenceFrame(), + orbitalReference)) + .subtract(currentPosition); + Vector radial = + new Vector( + connectionManager + .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 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 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(connectionManager.getSpaceCenter().transformDirection(PROGRADE, - currentVessel.getSurfaceVelocityReferenceFrame(), - orbitalReference)); - } + public void aimAtPrograde() throws RPCException { + aimAtDirection( + connectionManager + .getSpaceCenter() + .transformDirection( + PROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)); + } - public void aimAtRadialOut() throws RPCException { - aimAtDirection( - connectionManager.getSpaceCenter().transformDirection(RADIAL, currentVessel.getSurfaceReferenceFrame(), - orbitalReference)); - } + public void aimAtRadialOut() throws RPCException { + aimAtDirection( + connectionManager + .getSpaceCenter() + .transformDirection( + RADIAL, currentVessel.getSurfaceReferenceFrame(), orbitalReference)); + } - public void aimAtRetrograde() throws RPCException { - aimAtDirection(connectionManager.getSpaceCenter().transformDirection(RETROGRADE, - currentVessel.getSurfaceVelocityReferenceFrame(), - orbitalReference)); - } - - public void aimAtDirection(Triplet currentDirection) throws RPCException { - currentVessel.getAutoPilot().setReferenceFrame(orbitalReference); - currentVessel.getAutoPilot().setTargetDirection(currentDirection); - } + public void aimAtRetrograde() throws RPCException { + aimAtDirection( + connectionManager + .getSpaceCenter() + .transformDirection( + RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)); + } + public void aimAtDirection(Triplet currentDirection) throws RPCException { + currentVessel.getAutoPilot().setReferenceFrame(orbitalReference); + currentVessel.getAutoPilot().setTargetDirection(currentDirection); + } } diff --git a/src/main/java/com/pesterenan/utils/PathFinding.java b/src/main/java/com/pesterenan/utils/PathFinding.java index 8dd1a3f..bed5c53 100644 --- a/src/main/java/com/pesterenan/utils/PathFinding.java +++ b/src/main/java/com/pesterenan/utils/PathFinding.java @@ -3,217 +3,241 @@ import com.pesterenan.controllers.Controller; import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; - -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 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; public class PathFinding extends Controller { - 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) { - super(connectionManager, vesselManager); - initializeParameters(); - } - - private void initializeParameters() { - try { - waypointManager = getConnectionManager().getSpaceCenter().getWaypointManager(); - waypointsToReach = new ArrayList<>(); - pathToTarget = new ArrayList<>(); - drawing = Drawing.newInstance(getConnectionManager().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()); - } - - 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(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( - currentBody.surfacePosition(waypoint.getLatitude(), waypoint.getLongitude(), orbitalReferenceFrame)); - } - - 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 Vector getPathsFirstPoint() { - return pathToTarget.get(0); - } - - public void removePathsCurrentPoint() { - if (isPathToTargetEmpty()) { - return; - } - pathToTarget.remove(0); - } - - public void removeWaypointFromList() throws RPCException { - if (waypointsToReach.isEmpty()) { - return; - } - waypointsToReach.remove(0); - } - - /** - * 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()), 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) { - } - } - - 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( - getConnectionManager().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 transformSurfToOrb(Vector vector) throws IOException, RPCException { - return new Vector( - getConnectionManager().getSpaceCenter().transformPosition(vector.toTriplet(), surfaceReferenceFrame, - orbitalReferenceFrame)); - } - - private Vector transformOrbToSurf(Vector vector) throws IOException, RPCException { - return new Vector( - getConnectionManager().getSpaceCenter().transformPosition(vector.toTriplet(), orbitalReferenceFrame, - surfaceReferenceFrame)); - } + 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) { + super(connectionManager, vesselManager); + initializeParameters(); + } + + private void initializeParameters() { + try { + waypointManager = getConnectionManager().getSpaceCenter().getWaypointManager(); + waypointsToReach = new ArrayList<>(); + pathToTarget = new ArrayList<>(); + drawing = Drawing.newInstance(getConnectionManager().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()); + } + + 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(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( + currentBody.surfacePosition( + waypoint.getLatitude(), waypoint.getLongitude(), orbitalReferenceFrame)); + } + + 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 Vector getPathsFirstPoint() { + return pathToTarget.get(0); + } + + public void removePathsCurrentPoint() { + if (isPathToTargetEmpty()) { + return; + } + pathToTarget.remove(0); + } + + public void removeWaypointFromList() throws RPCException { + if (waypointsToReach.isEmpty()) { + return; + } + waypointsToReach.remove(0); + } + + /** + * 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()), 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) { + } + } + + 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( + getConnectionManager() + .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 transformSurfToOrb(Vector vector) throws IOException, RPCException { + return new Vector( + getConnectionManager() + .getSpaceCenter() + .transformPosition(vector.toTriplet(), surfaceReferenceFrame, orbitalReferenceFrame)); + } + + private Vector transformOrbToSurf(Vector vector) throws IOException, RPCException { + return new Vector( + getConnectionManager() + .getSpaceCenter() + .transformPosition(vector.toTriplet(), orbitalReferenceFrame, 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..1466bf2 100644 --- a/src/main/java/com/pesterenan/views/AboutJFrame.java +++ b/src/main/java/com/pesterenan/views/AboutJFrame.java @@ -1,7 +1,11 @@ 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.Font; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; @@ -9,75 +13,72 @@ 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; - 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, lblAboutInfo; + 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()); - 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"); + @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("MechPeste - por Pesterenan"); - 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); - lblAboutInfo.setAlignmentX(CENTER_ALIGNMENT); + // Setting-up components: + lblMechpeste.setFont(new Font("Trajan Pro", Font.BOLD, 18)); + lblMechpeste.setAlignmentX(CENTER_ALIGNMENT); + lblAboutInfo.setAlignmentX(CENTER_ALIGNMENT); - 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(lblAboutInfo); + pnlMain.add(Box.createVerticalGlue()); + pnlMain.add(btnOk); + pnlMain.add(createMarginComponent(10, 10)); - getContentPane().add(pnlMain); - setVisible(true); - } + getContentPane().add(pnlMain); + setVisible(true); + } } diff --git a/src/main/java/com/pesterenan/views/ChangeVesselDialog.java b/src/main/java/com/pesterenan/views/ChangeVesselDialog.java index 328ae06..507986e 100644 --- a/src/main/java/com/pesterenan/views/ChangeVesselDialog.java +++ b/src/main/java/com/pesterenan/views/ChangeVesselDialog.java @@ -2,8 +2,8 @@ import static com.pesterenan.views.MainGui.BTN_DIMENSION; +import com.pesterenan.model.VesselManager; import java.awt.event.ActionEvent; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; @@ -18,149 +18,148 @@ import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; -import com.pesterenan.model.VesselManager; - 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; - 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)); + 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 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]); - 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); + 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 8fc1eab..28a1bf8 100644 --- a/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/CreateManeuverJPanel.java @@ -3,6 +3,10 @@ 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 java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; @@ -13,7 +17,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; @@ -27,12 +30,6 @@ import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.border.TitledBorder; - -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; @@ -41,420 +38,426 @@ 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(); - 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()); - } + 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 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) { - } + } + + 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) { } - - public void createManeuver() { - try { - createManeuver(vesselManager.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) { } - - 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 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 = vesselManager.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) { } + } - 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) { - } + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == btnCreateManeuver) { + createManeuver(); } - - 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()); - } - + if (e.getSource() == btnDeleteManeuver) { + deleteManeuver(); } - - 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) { - } + 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 7224f73..683a2b8 100644 --- a/src/main/java/com/pesterenan/views/DockingJPanel.java +++ b/src/main/java/com/pesterenan/views/DockingJPanel.java @@ -4,11 +4,13 @@ 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 java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; @@ -18,155 +20,152 @@ import javax.swing.SwingConstants; import javax.swing.border.TitledBorder; -import com.pesterenan.model.VesselManager; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Module; - 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; - - private VesselManager vesselManager; - - private StatusDisplay statusDisplay; - - public DockingJPanel(StatusDisplay statusDisplay) { - this.statusDisplay = statusDisplay; - initComponents(); - setupComponents(); - layoutComponents(); + 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); } - - 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 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; + } + + 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 353bfe1..01f4fa4 100644 --- a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java +++ b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java @@ -2,11 +2,15 @@ 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 java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.Map; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; @@ -14,204 +18,206 @@ import javax.swing.JPanel; import javax.swing.border.TitledBorder; -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; - 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 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); - } - - private void cancelCurrentAction(ActionEvent e) { - statusDisplay.setStatusMessage("Canceling current action..."); - vesselManager.cancelControl(e); - } - - @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; - } - } + 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); + } + + private void cancelCurrentAction(ActionEvent e) { + statusDisplay.setStatusMessage("Canceling current action..."); + vesselManager.cancelControl(e); + } + + @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 3ae167f..554e831 100644 --- a/src/main/java/com/pesterenan/views/LandingJPanel.java +++ b/src/main/java/com/pesterenan/views/LandingJPanel.java @@ -4,12 +4,14 @@ 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 java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -22,168 +24,178 @@ import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; -import com.pesterenan.model.VesselManager; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Module; - 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; - private VesselManager vesselManager; - - private StatusDisplay statusDisplay; - - public LandingJPanel(StatusDisplay statusDisplay) { - this.statusDisplay = statusDisplay; - 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; } - - public void setVesselManager(VesselManager vesselManager) { - this.vesselManager = vesselManager; + 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 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; - } - 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")); - } - } - - 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 672d929..220fdb9 100644 --- a/src/main/java/com/pesterenan/views/LiftoffJPanel.java +++ b/src/main/java/com/pesterenan/views/LiftoffJPanel.java @@ -4,13 +4,15 @@ 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 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 javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultComboBoxModel; @@ -23,218 +25,227 @@ import javax.swing.JTextField; import javax.swing.border.TitledBorder; -import com.pesterenan.model.VesselManager; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Module; - 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; - 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(); + 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) { - statusDisplay.setStatusMessage(Bundle.getString("pnl_lift_stat_only_numbers")); - return false; - } - return true; - } - - private void handleLiftoff(ActionEvent e) { - if (vesselManager == null) { - statusDisplay.setStatusMessage("Conexão não estabelecida."); - return; - } - 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); - } + 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 6baf6b4..cd07d48 100644 --- a/src/main/java/com/pesterenan/views/MainGui.java +++ b/src/main/java/com/pesterenan/views/MainGui.java @@ -1,5 +1,8 @@ package com.pesterenan.views; +import com.pesterenan.model.VesselManager; +import com.pesterenan.resources.Bundle; +import com.pesterenan.utils.Module; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; @@ -8,7 +11,6 @@ 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; @@ -20,236 +22,233 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; -import com.pesterenan.model.VesselManager; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Module; - public class MainGui extends JFrame implements ActionListener, UIMethods { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - 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; + 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; - private final static JPanel cardJPanels = new JPanel(); + private static final JPanel cardJPanels = new JPanel(); - private final static CardLayout cardLayout = new CardLayout(0, 0); + private static final CardLayout cardLayout = new CardLayout(0, 0); - public static MainGui getInstance() { - return instance; - } + public static MainGui getInstance() { + return instance; + } - public static MainGui newInstance() { - if (instance == null) { - instance = new MainGui(); - } - return instance; + public static MainGui newInstance() { + if (instance == null) { + instance = new MainGui(); } + 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 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 JPanel getCardJPanels() { + return cardJPanels; + } - public static void changeToPage(ActionEvent e) { - cardLayout.show(cardJPanels, e.getActionCommand()); - } + 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 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; - - 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) { - } - - // 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(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); - } - - @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); - } - if (e.getSource() == mntmInstallKrpc) { - handleMntmInstallKrpcActionPerformed(e); - } - if (e.getSource() == mntmExit) { - handleMntmExitActionPerformed(e); - } - if (e.getSource() == mntmChangeVessels) { - handleMntmMultiControlActionPerformed(e); - } - } - - protected void handleMntmInstallKrpcActionPerformed(ActionEvent e) { - new InstallKrpcDialog(); - } - - protected void handleMntmExitActionPerformed(ActionEvent e) { - System.exit(0); - } - - protected void handleMntmAboutActionPerformed(ActionEvent e) { - new AboutJFrame(); - } - - private void handleMntmMultiControlActionPerformed(ActionEvent e) { - new ChangeVesselDialog(vesselManager); - } + public static Component createMarginComponent(int width, int height) { + Component marginComp = Box.createRigidArea(new Dimension(width, height)); + return marginComp; + } + + private FunctionsAndTelemetryJPanel 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) { + } + + // 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(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); + } + + @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); + } + if (e.getSource() == mntmInstallKrpc) { + handleMntmInstallKrpcActionPerformed(e); + } + if (e.getSource() == mntmExit) { + handleMntmExitActionPerformed(e); + } + if (e.getSource() == mntmChangeVessels) { + handleMntmMultiControlActionPerformed(e); + } + } + + protected void handleMntmInstallKrpcActionPerformed(ActionEvent e) { + new InstallKrpcDialog(); + } + + protected void handleMntmExitActionPerformed(ActionEvent e) { + System.exit(0); + } + + protected void handleMntmAboutActionPerformed(ActionEvent e) { + new AboutJFrame(); + } + + 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 20692c6..5224840 100644 --- a/src/main/java/com/pesterenan/views/RoverJPanel.java +++ b/src/main/java/com/pesterenan/views/RoverJPanel.java @@ -4,11 +4,13 @@ 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 java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.util.HashMap; import java.util.Map; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; @@ -21,169 +23,167 @@ import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; -import com.pesterenan.model.VesselManager; -import com.pesterenan.resources.Bundle; -import com.pesterenan.utils.Module; - 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; - private VesselManager vesselManager; - - private StatusDisplay statusDisplay; - - public RoverJPanel(StatusDisplay statusDisplay) { - this.statusDisplay = statusDisplay; - 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); } - - 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); - } - } - - 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; + } + + 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 ce592b6..2fb884d 100644 --- a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java @@ -4,12 +4,16 @@ 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; @@ -18,13 +22,6 @@ import javax.swing.JPanel; import javax.swing.border.CompoundBorder; import javax.swing.border.TitledBorder; - -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 krpc.client.RPCException; import krpc.client.services.SpaceCenter.Node; import krpc.client.services.SpaceCenter.Orbit; @@ -32,222 +29,234 @@ import krpc.client.services.SpaceCenter.VesselSituation; public class RunManeuverJPanel extends JPanel implements ActionListener, UIMethods { - 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 final ControlePID ctrlManeuver = new ControlePID(); - private VesselManager vesselManager; - - public RunManeuverJPanel(StatusDisplay statusDisplay) { - this.statusDisplay = statusDisplay; - 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 final ControlePID ctrlManeuver = new ControlePID(); + 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 setVesselManager(VesselManager vesselManager) { - this.vesselManager = vesselManager; + } + + 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 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 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 void createManeuver() { - System.out.println("Create maneuver"); - try { - createManeuver(vesselManager.getSpaceCenter().getUT() + 60); - } catch (RPCException e) { - } + } + + 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); + } + + 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 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) { - } + if (e.getSource() == btnLowerOrbit) { + handleManeuverFunction(Module.LOW_ORBIT.get()); } - - 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) { - } + if (e.getSource() == btnApoapsis) { + handleManeuverFunction(Module.APOAPSIS.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() == btnPeriapsis) { + handleManeuverFunction(Module.PERIAPSIS.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() == btnAlignPlanes) { + handleManeuverFunction(Module.ADJUST.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() == btnRendezvous) { + handleManeuverFunction(Module.RENDEZVOUS.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().getVesselManager().startModule(commands); + 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 index ba0b2ec..89954c7 100644 --- a/src/main/java/com/pesterenan/views/StatusDisplay.java +++ b/src/main/java/com/pesterenan/views/StatusDisplay.java @@ -1,7 +1,7 @@ package com.pesterenan.views; public interface StatusDisplay { - void setStatusMessage(String message); + void setStatusMessage(String message); - void setBtnConnectVisible(boolean visible); + 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 6f31baf..5b4b43a 100644 --- a/src/main/java/com/pesterenan/views/StatusJPanel.java +++ b/src/main/java/com/pesterenan/views/StatusJPanel.java @@ -3,82 +3,80 @@ import static com.pesterenan.views.MainGui.BTN_DIMENSION; import static com.pesterenan.views.MainGui.MARGIN_BORDER_10_PX_LR; +import com.pesterenan.MechPeste; +import com.pesterenan.resources.Bundle; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; - import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; -import com.pesterenan.MechPeste; -import com.pesterenan.resources.Bundle; - 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) { - setStatusMessage(Bundle.getString("status_connecting")); - setBtnConnectVisible(false); - MechPeste.newInstance().getConnectionManager().connectAndMonitor("MechPeste - Pesterenan"); - } + 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) { + setStatusMessage(Bundle.getString("status_connecting")); + setBtnConnectVisible(false); + MechPeste.newInstance().getConnectionManager().connectAndMonitor("MechPeste - Pesterenan"); + } } 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(); } From bdd16a9f56f9686ae46dbed747c1fd5c535532d3 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 11 Sep 2025 13:29:36 -0300 Subject: [PATCH 07/25] [MP-8]: Refactoring LandingController to use callbacks on streams --- .../controllers/LandingController.java | 186 +++++++++++------- 1 file changed, 112 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index c467a7b..161152e 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -7,7 +7,6 @@ import com.pesterenan.utils.Module; import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Utilities; -import java.io.IOException; import java.util.Map; import krpc.client.RPCException; import krpc.client.Stream; @@ -17,11 +16,11 @@ public class LandingController extends Controller { private enum MODE { - DEORBITING, APPROACHING, + DEORBITING, + GOING_DOWN, GOING_UP, HOVERING, - GOING_DOWN, LANDING, WAITING } @@ -41,8 +40,12 @@ private enum MODE { private float maxTWR; private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling = false; - private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag; + private int isOrientedCallbackTag, + isDeorbitBurnDoneCallbackTag, + isFallingCallbackTag, + utCallbackTag; private Stream apErrorStream; + private Stream utStream; public LandingController( ConnectionManager connectionManager, @@ -56,11 +59,23 @@ public LandingController( @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(); + try { + if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { + hoverArea(); + } + if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { + autoLanding(); + } + while (landingMode || hoveringMode) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Thread.sleep(1000); + } + } catch (InterruptedException | RPCException | StreamException e) { + System.err.println("Erro no run do landingController:" + e.getMessage()); + cleanup(); + setCurrentStatus(Bundle.getString("status_ready")); } } @@ -91,45 +106,32 @@ private void hoverArea() { } changeControlMode(); } - } catch (InterruptedException | RPCException | StreamException | IOException e) { + } catch (InterruptedException | RPCException | StreamException e) { cleanup(); setCurrentStatus(Bundle.getString("status_ready")); } } - private void autoLanding() { - try { - setupCallbacks(); - 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(); - } - } catch (RPCException | StreamException | InterruptedException | IOException e) { - cleanup(); - System.err.println("landing: " + e.getMessage()); - setCurrentStatus(Bundle.getString("status_ready")); + private void autoLanding() throws RPCException, StreamException, InterruptedException { + 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(); + tuneAutoPilot(); + getActiveVessel().getControl().setBrakes(true); + setCurrentStatus(Bundle.getString("status_starting_landing")); + setupCallbacks(); + altitudeCtrl.reset(); + velocityCtrl.reset(); } - private void setupCallbacks() throws IOException, RPCException, StreamException { + private void setupCallbacks() throws RPCException, StreamException { // Callback for ship orientation apErrorStream = connection.addStream(ap, "getError"); isOrientedCallbackTag = @@ -165,16 +167,32 @@ private void setupCallbacks() throws IOException, RPCException, StreamException } }); + utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utCallbackTag = + utStream.addCallback( + (ut) -> { + try { + if (!landingMode) { + utStream.removeCallback(utCallbackTag); + return; + } + changeControlMode(); + } catch (Exception e) { + System.err.println("UT Callback error: " + e.getMessage()); + } + }); + // Start all streams apErrorStream.start(); apoapsis.start(); periapsis.start(); verticalVelocity.start(); + utStream.start(); } - private void changeControlMode() - throws RPCException, StreamException, InterruptedException, IOException { + 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: @@ -192,29 +210,29 @@ private void changeControlMode() case APPROACHING: altitudeCtrl.setOutput(0, 1); velocityCtrl.setOutput(0, 1); - - double[] currentDistanceToGround = calculateCurrentDistanceToGround(); - double[] currentVelocities = calculateZeroVelocityMagnitude(); - double aMax = getMaxAcel(maxTWR); - double landingDistanceThreshold = Math.max(hoverAltitude, aMax * 3); - - double stoppingDistance = (currentVelocities[0] * currentVelocities[0]) / (2 * aMax); - - double threshold = Utilities.clamp(stoppingDistance / landingDistanceThreshold, 0, 1); - - double altPID = - altitudeCtrl.calculate( - (currentDistanceToGround[0]), (stoppingDistance + landingDistanceThreshold)); - double velPID = + double currentVelocity = calculateCurrentVelocityMagnitude(); + double zeroVelocity = calculateZeroVelocityMagnitude(); + double landingDistanceThreshold = Math.max(hoverAltitude, getMaxAcel(maxTWR) * 3); + double threshold = + Utilities.clamp( + ((currentVelocity + zeroVelocity) - landingDistanceThreshold) + / landingDistanceThreshold / sleepTime, + 0, + 1); + System.out.println( + "Current: " + + currentVelocity / sleepTime + + " Zero: " + + zeroVelocity / sleepTime + + " Threshold: " + + threshold); + altPID = altitudeCtrl.calculate(currentVelocity / sleepTime, zeroVelocity / sleepTime); + velPID = velocityCtrl.calculate( verticalVelocity.get(), (-Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20))); - - double interpolation = Utilities.linearInterpolation(velPID, altPID, threshold); - - throttle(interpolation); + throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); navigation.aimForLanding(); - - if (threshold < 0.15 && surfaceAltitude.get() < landingDistanceThreshold * 2) { + if (threshold < 0.15 || surfaceAltitude.get() < landingDistanceThreshold) { hoverAltitude = landingDistanceThreshold; getActiveVessel().getControl().setGear(true); if (hoverAfterApproximation) { @@ -227,6 +245,8 @@ private void changeControlMode() 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); throttle( @@ -236,6 +256,8 @@ private void changeControlMode() setCurrentStatus("Subindo altitude..."); break; case GOING_DOWN: + altitudeCtrl.reset(); + velocityCtrl.reset(); controlThrottleByMatchingVerticalVelocity(-MAX_VELOCITY); navigation.aimAtRadialOut(); setCurrentStatus("Baixando altitude..."); @@ -250,6 +272,8 @@ private void changeControlMode() setCurrentStatus("Pousando..."); break; case HOVERING: + altitudeCtrl.reset(); + velocityCtrl.reset(); altitudeCtrl.setOutput(-0.5, 0.5); velocityCtrl.setOutput(-0.5, 0.5); throttle( @@ -262,6 +286,12 @@ private void changeControlMode() } 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 currentTWR = getMaxAcel(maxTWR); double pGain = Math.min(getTWR(), maxTWR); altitudeCtrl.setPIDValues(currentTWR * velP * velP, 0.1, velD); @@ -326,27 +356,35 @@ private boolean hasTheVesselLanded() throws RPCException { return false; } - private double[] calculateCurrentDistanceToGround() throws RPCException, StreamException { - double vertVel = verticalVelocity.get(); - double horzVel = horizontalVelocity.get(); - double vertAlt = surfaceAltitude.get(); - double horzDist = horzVel * (vertAlt / Math.abs(vertVel)); - double euclidianDistance = Math.sqrt(vertAlt * vertAlt + horzDist * horzDist); - return new double[] {euclidianDistance, 0}; + private double calculateCurrentVelocityMagnitude() throws RPCException, StreamException { + double timeToGround = surfaceAltitude.get() / verticalVelocity.get(); + double horizontalDistance = horizontalVelocity.get() * timeToGround; + return calculateEllipticTrajectory(horizontalDistance, surfaceAltitude.get()); } - private double[] calculateZeroVelocityMagnitude() throws RPCException, StreamException { - double vertVel = verticalVelocity.get(); - double horzVel = horizontalVelocity.get(); - return new double[] {Math.sqrt(horzVel * horzVel + vertVel * vertVel), 0}; + private double calculateZeroVelocityMagnitude() throws RPCException, StreamException { + double zeroVelocityDistance = + calculateEllipticTrajectory(horizontalVelocity.get(), verticalVelocity.get()); + double zeroVelocityBurnTime = zeroVelocityDistance / getMaxAcel(maxTWR); + return zeroVelocityDistance * zeroVelocityBurnTime; + } + + 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 void cleanup() { try { + landingMode = false; + hoveringMode = false; + utStream.removeCallback(utCallbackTag); + utStream.remove(); apErrorStream.removeCallback(isOrientedCallbackTag); + apErrorStream.remove(); periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); verticalVelocity.removeCallback(isFallingCallbackTag); - apErrorStream.remove(); ap.disengage(); throttle(0); } catch (RPCException e) { From e2f20c2ee6b977d65f1cc32c9732e0526b3c8c0c Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sun, 14 Sep 2025 01:34:51 -0300 Subject: [PATCH 08/25] [MP-8]: Refactored ConnectionManager --- src/main/java/com/pesterenan/MechPeste.java | 8 +- .../com/pesterenan/model/ActiveVessel.java | 10 +- .../pesterenan/model/ConnectionManager.java | 94 ++++++++++--------- .../com/pesterenan/model/VesselManager.java | 19 ++-- .../java/com/pesterenan/views/MainGui.java | 4 + .../com/pesterenan/views/StatusJPanel.java | 4 +- 6 files changed, 75 insertions(+), 64 deletions(-) diff --git a/src/main/java/com/pesterenan/MechPeste.java b/src/main/java/com/pesterenan/MechPeste.java index 4ee48f9..5287cd6 100644 --- a/src/main/java/com/pesterenan/MechPeste.java +++ b/src/main/java/com/pesterenan/MechPeste.java @@ -2,15 +2,16 @@ import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; +import com.pesterenan.views.FunctionsAndTelemetryJPanel; import com.pesterenan.views.MainGui; import com.pesterenan.views.StatusDisplay; import java.lang.reflect.InvocationTargetException; import javax.swing.SwingUtilities; public class MechPeste { - private static MechPeste instance; private ConnectionManager connectionManager = null; private VesselManager vesselManager; + private static MechPeste instance; public ConnectionManager getConnectionManager() { return connectionManager; @@ -35,13 +36,12 @@ public static void main(String[] args) { SwingUtilities.invokeAndWait(() -> MainGui.newInstance()); } catch (InvocationTargetException | InterruptedException e) { System.err.println("Error while invoking GUI: " + e.getMessage()); - e.printStackTrace(); } StatusDisplay statusDisplay = MainGui.newInstance().getStatusPanel(); + FunctionsAndTelemetryJPanel telemetryPanel = MainGui.newInstance().getTelemetryPanel(); app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); - app.vesselManager = new VesselManager(app.connectionManager, statusDisplay); - app.vesselManager.setTelemetryPanel(MainGui.getInstance().getFunctionsAndTelemetryPanel()); + app.vesselManager = new VesselManager(app.connectionManager, statusDisplay, telemetryPanel); app.vesselManager.startUpdateLoop(); } } diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 5b540b2..16fc59b 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -33,7 +33,7 @@ public class ActiveVessel { public AutoPilot ap; public Flight flightParameters; public ReferenceFrame orbitalReferenceFrame; - protected Stream totalMass; + public Stream totalMass; public ReferenceFrame surfaceReferenceFrame; public float totalCharge; public double gravityAcel; @@ -183,12 +183,12 @@ public void cancelControl() { try { ap.disengage(); throttle(0); - if (controllerThread != null) { - controllerThread.interrupt(); - runningModule = false; - } } catch (RPCException ignored) { } + if (controllerThread != null) { + controllerThread.interrupt(); + runningModule = false; + } } public boolean hasModuleRunning() { diff --git a/src/main/java/com/pesterenan/model/ConnectionManager.java b/src/main/java/com/pesterenan/model/ConnectionManager.java index d8ef470..4c4140f 100644 --- a/src/main/java/com/pesterenan/model/ConnectionManager.java +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -16,13 +16,15 @@ public class ConnectionManager { private KRPC krpc; private Connection connection; private SpaceCenter spaceCenter; - private final StatusDisplay statusDisplay; + private StatusDisplay statusDisplay; private volatile boolean isConnecting = false; - private ScheduledExecutorService connectionScheduler; + private ScheduledExecutorService connectionMonitor; + private String connectionName; public ConnectionManager(final String connectionName, final StatusDisplay statusDisplay) { this.statusDisplay = statusDisplay; - connectAndMonitor(connectionName); + this.connectionName = connectionName; + startMonitoring(); } public Connection getConnection() { @@ -37,41 +39,18 @@ public KRPC getKRPC() { return krpc; } - public void connectAndMonitor(final String connectionName) { - if (connectionScheduler != null && !connectionScheduler.isShutdown()) { - return; - } - connectionScheduler = Executors.newSingleThreadScheduledExecutor(); - connectionScheduler.scheduleAtFixedRate( - () -> { - if (isConnectionAlive() || isConnecting) { - return; - } - - isConnecting = true; - SwingUtilities.invokeLater( - () -> { - statusDisplay.setStatusMessage(Bundle.getString("status_connecting")); - statusDisplay.setBtnConnectVisible(false); - }); + public void connect() { + attemptConnection(); + } - try { - connection = Connection.newInstance(connectionName); - krpc = KRPC.newInstance(getConnection()); - spaceCenter = SpaceCenter.newInstance(getConnection()); - SwingUtilities.invokeLater( - () -> { - statusDisplay.setStatusMessage(Bundle.getString("status_connected")); - statusDisplay.setBtnConnectVisible(false); - }); - } catch (final IOException e) { - SwingUtilities.invokeLater( - () -> { - statusDisplay.setStatusMessage(Bundle.getString("status_error_connection")); - statusDisplay.setBtnConnectVisible(true); - }); - } finally { - isConnecting = false; + 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, @@ -79,23 +58,50 @@ public void connectAndMonitor(final String connectionName) { 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); + + 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) return false; + if (krpc == null || connection == null) return false; krpc.getStatus(); return true; - } catch (final RPCException e) { + } catch (RPCException e) { return false; } } - public KRPC.GameScene getCurrentGameScene() throws RPCException { - return krpc.getCurrentGameScene(); - } - public boolean isOnFlightScene() { try { - return this.getCurrentGameScene().equals(KRPC.GameScene.FLIGHT); + 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 index 8ead94e..654cd0f 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -22,19 +22,19 @@ import krpc.client.services.SpaceCenter.Vessel; public class VesselManager { - private ConnectionManager connectionManager; private ActiveVessel currentVessel; - private int currentVesselId = -1; - private StatusDisplay statusDisplay; + private ConnectionManager connectionManager; private FunctionsAndTelemetryJPanel telemetryPanel; + private StatusDisplay statusDisplay; + private int currentVesselId = -1; - public void setTelemetryPanel(FunctionsAndTelemetryJPanel panel) { - this.telemetryPanel = panel; - } - - public VesselManager(ConnectionManager connectionManager, StatusDisplay statusDisplay) { + public VesselManager( + final ConnectionManager connectionManager, + final StatusDisplay statusDisplay, + final FunctionsAndTelemetryJPanel telemetryPanel) { this.connectionManager = connectionManager; this.statusDisplay = statusDisplay; + this.telemetryPanel = telemetryPanel; } public Connection getConnection() { @@ -173,6 +173,9 @@ private void checkAndUpdateActiveVessel() throws RPCException { Vessel activeVessel = getSpaceCenter().getActiveVessel(); int activeVesselId = activeVessel.hashCode(); if (currentVesselId != activeVesselId) { + if (currentVessel != null && currentVessel.hasModuleRunning()) { + currentVessel.cancelControl(); + } currentVessel = new ActiveVessel(connectionManager, this); currentVesselId = currentVessel.getCurrentVesselId(); MainGui.getInstance().setVesselManager(this); diff --git a/src/main/java/com/pesterenan/views/MainGui.java b/src/main/java/com/pesterenan/views/MainGui.java index cd07d48..e065e3d 100644 --- a/src/main/java/com/pesterenan/views/MainGui.java +++ b/src/main/java/com/pesterenan/views/MainGui.java @@ -75,6 +75,10 @@ public static Component createMarginComponent(int width, int height) { private FunctionsAndTelemetryJPanel pnlFunctionsAndTelemetry; + public FunctionsAndTelemetryJPanel getTelemetryPanel() { + return pnlFunctionsAndTelemetry; + } + private StatusJPanel pnlStatus; private final Dimension APP_DIMENSION = new Dimension(480, 300); diff --git a/src/main/java/com/pesterenan/views/StatusJPanel.java b/src/main/java/com/pesterenan/views/StatusJPanel.java index 5b4b43a..da82833 100644 --- a/src/main/java/com/pesterenan/views/StatusJPanel.java +++ b/src/main/java/com/pesterenan/views/StatusJPanel.java @@ -75,8 +75,6 @@ public void setBtnConnectVisible(boolean visible) { } private void handleConnect(ActionEvent e) { - setStatusMessage(Bundle.getString("status_connecting")); - setBtnConnectVisible(false); - MechPeste.newInstance().getConnectionManager().connectAndMonitor("MechPeste - Pesterenan"); + MechPeste.newInstance().getConnectionManager().connect(); } } From 4735f9dc811dfb26b1771eb2cbcf775c85980ed2 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 10:17:34 -0300 Subject: [PATCH 09/25] [MP-8]: Refactored VesselManager --- src/main/java/com/pesterenan/MechPeste.java | 2 +- .../com/pesterenan/model/VesselManager.java | 80 ++++++++++--------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/pesterenan/MechPeste.java b/src/main/java/com/pesterenan/MechPeste.java index 5287cd6..89f1685 100644 --- a/src/main/java/com/pesterenan/MechPeste.java +++ b/src/main/java/com/pesterenan/MechPeste.java @@ -42,6 +42,6 @@ public static void main(String[] args) { FunctionsAndTelemetryJPanel telemetryPanel = MainGui.newInstance().getTelemetryPanel(); app.connectionManager = new ConnectionManager("MechPeste - Pesterenan", statusDisplay); app.vesselManager = new VesselManager(app.connectionManager, statusDisplay, telemetryPanel); - app.vesselManager.startUpdateLoop(); + app.vesselManager.startTelemetryLoop(); } } diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index 654cd0f..383beb5 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -53,27 +53,16 @@ public int getCurrentVesselId() { return currentVesselId; } - public void startUpdateLoop() { - ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + public void startTelemetryLoop() { + ScheduledExecutorService scheduler = + Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "MP_TELEMETRY_UPDATER")); scheduler.scheduleAtFixedRate( () -> { - if (getConnection() == null || getSpaceCenter() == null) { - System.out.println("Connection not established yet, skipping update cycle."); - return; - } - try { - if (!connectionManager.isOnFlightScene()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - return; - } + if (connectionManager.isConnectionAlive()) { checkAndUpdateActiveVessel(); + } + if (currentVessel != null && connectionManager.isOnFlightScene()) { updateUI(); - } catch (RPCException e) { - System.err.println("RPC Error: " + e.getMessage()); } }, 0, @@ -84,7 +73,7 @@ public void startUpdateLoop() { public ListModel getCurrentManeuvers() { DefaultListModel list = new DefaultListModel<>(); try { - List maneuvers = this.getSpaceCenter().getActiveVessel().getControl().getNodes(); + List maneuvers = getSpaceCenter().getActiveVessel().getControl().getNodes(); maneuvers.forEach( m -> { try { @@ -102,7 +91,8 @@ public ListModel getCurrentManeuvers() { } catch (RPCException ignored) { } }); - } catch (RPCException | NullPointerException | UnsupportedOperationException ignored) { + } catch (RPCException | NullPointerException | UnsupportedOperationException e) { + System.err.println("ERRO: Não foi possivel atualizar as manobras atuais." + e.getMessage()); } return list; } @@ -121,13 +111,13 @@ public ListModel getActiveVessels(String search) { try { String vesselName = v.hashCode() + " - \t" + v.getName(); list.addElement(vesselName); - } catch (RPCException vesselNameError) { + } catch (RPCException e) { System.err.println( - "Couldn't add vessel name to list. Error: " + vesselNameError.getMessage()); + "ERRO: Não foi possível adicionar nave na lista. " + e.getMessage()); } }); - } catch (RPCException rpcOrNpeException) { - System.err.println("Couldn't get vessel list, Error: " + rpcOrNpeException.getMessage()); + } catch (RPCException | NullPointerException e) { + System.err.println("ERRO: Não foi possível buscar lista de naves. " + e.getMessage()); } return list; } @@ -135,7 +125,7 @@ public ListModel getActiveVessels(String search) { public String getVesselInfo(int selectedIndex) { try { Vessel activeVessel = - this.getSpaceCenter().getVessels().stream() + getSpaceCenter().getVessels().stream() .filter(v -> v.hashCode() == selectedIndex) .findFirst() .get(); @@ -147,7 +137,8 @@ public String getVesselInfo(int selectedIndex) { String.format( "Nome: %s\t\t\t | Corpo: %s", name, activeVessel.getOrbit().getBody().getName()); return vesselInfo; - } catch (RPCException | NullPointerException ignored) { + } catch (RPCException | NullPointerException e) { + System.err.println("ERRO: Não foi possível buscar informações da nave. " + e.getMessage()); } return ""; } @@ -166,19 +157,36 @@ public void changeToVessel(int selectedIndex) { } public void cancelControl(ActionEvent e) { - currentVessel.cancelControl(); + if (currentVessel != null) { + currentVessel.cancelControl(); + } } - private void checkAndUpdateActiveVessel() throws RPCException { - Vessel activeVessel = getSpaceCenter().getActiveVessel(); - int activeVesselId = activeVessel.hashCode(); - if (currentVesselId != activeVesselId) { - if (currentVessel != null && currentVessel.hasModuleRunning()) { - currentVessel.cancelControl(); + public void checkAndUpdateActiveVessel() { + try { + int activeVesselId = getSpaceCenter().getActiveVessel().hashCode(); + if (currentVesselId != activeVesselId) { + System.out.println("DEBUG: Vessel ID mismatch. Re-initializing..."); + if (currentVessel != null) { + Thread oldThread = currentVessel.getControllerThread(); + currentVessel.destroy(); // This will set flags and interrupt + if (oldThread != null) { + try { + oldThread.join(1000); // Wait for the old thread to die, with a timeout + } catch (InterruptedException e) { + System.err.println("Interrupted while waiting for old controller thread to die."); + } + } + } + currentVessel = new ActiveVessel(connectionManager, this); + currentVesselId = currentVessel.getCurrentVesselId(); + MainGui.getInstance().setVesselManager(this); } - currentVessel = new ActiveVessel(connectionManager, this); - currentVesselId = currentVessel.getCurrentVesselId(); - MainGui.getInstance().setVesselManager(this); + } catch (RPCException e) { + System.err.println( + "ERRO: Atualização de nave interrompida. Não foi possível buscar o ID da nave atual." + + e.getMessage()); + e.printStackTrace(); } } @@ -201,7 +209,7 @@ private boolean filterVessels(Vessel vessel, String search) { } double TEN_KILOMETERS = 10000.0; try { - Vessel active = this.getSpaceCenter().getActiveVessel(); + 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())); From d306b506c1e8c12f78f99cd16dce4cb6c852acd8 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 10:40:13 -0300 Subject: [PATCH 10/25] [MP-8]: Refactored ActiveVessel --- .../com/pesterenan/model/ActiveVessel.java | 73 +++++++++++-------- .../com/pesterenan/model/VesselManager.java | 2 +- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 16fc59b..8f92649 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -171,26 +171,43 @@ public void startModule(Map commands) { controller = new DockingController(this.connectionManager, this.vesselManager, commands); runningModule = true; } - controllerThread = new Thread(controller, currentVesselId + " - " + currentFunction); + String controllerThreadName = "MP_CTRL_" + currentFunction + "_" + currentVesselId; + controllerThread = new Thread(controller, controllerThreadName); controllerThread.start(); } - public Map getTelemetryData() { - return telemetryData; + public Thread getControllerThread() { + return controllerThread; } public void cancelControl() { - try { - ap.disengage(); - throttle(0); - } catch (RPCException ignored) { - } if (controllerThread != null) { controllerThread.interrupt(); runningModule = false; } } + public void removeStreams() { + try { + if (totalMass != null) totalMass.remove(); + if (altitude != null) altitude.remove(); + if (surfaceAltitude != null) surfaceAltitude.remove(); + if (apoapsis != null) apoapsis.remove(); + if (periapsis != null) periapsis.remove(); + if (verticalVelocity != null) verticalVelocity.remove(); + if (horizontalVelocity != null) horizontalVelocity.remove(); + if (controllerThread != null && controllerThread.isAlive()) { + controllerThread.interrupt(); + } + } catch (RPCException e) { + System.err.println("ERRO: Ao remover streams da nave atual. " + e.getMessage()); + } + } + + public Map getTelemetryData() { + return telemetryData; + } + public boolean hasModuleRunning() { return runningModule; } @@ -216,6 +233,7 @@ protected void throttleUp(double throttleAmount, double seconds) } private void initializeParameters() { + System.out.println("DEBUG: Initializing ActiveVessel parameters..."); try { setActiveVessel(spaceCenter.getActiveVessel()); currentVesselId = getActiveVessel().hashCode(); @@ -225,38 +243,35 @@ private void initializeParameters() { orbitalReferenceFrame = currentBody.getReferenceFrame(); surfaceReferenceFrame = getActiveVessel().getSurfaceReferenceFrame(); flightParameters = getActiveVessel().flight(orbitalReferenceFrame); - totalMass = connection.addStream(getActiveVessel(), "getMass"); - totalMass.start(); - // Add callbacks to update telemetry data automatically + System.out.println("DEBUG: Basic parameters set. Creating streams..."); altitude = connection.addStream(flightParameters, "getMeanAltitude"); - altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); - altitude.start(); - + apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); + horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); + periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); surfaceAltitude = connection.addStream(flightParameters, "getSurfaceAltitude"); - surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); - surfaceAltitude.start(); + totalMass = connection.addStream(getActiveVessel(), "getMass"); + verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); - apoapsis = connection.addStream(getActiveVessel().getOrbit(), "getApoapsisAltitude"); + altitude.addCallback(val -> telemetryData.put(Telemetry.ALTITUDE, val < 0 ? 0 : val)); apoapsis.addCallback(val -> telemetryData.put(Telemetry.APOAPSIS, val < 0 ? 0 : val)); - apoapsis.start(); - - periapsis = connection.addStream(getActiveVessel().getOrbit(), "getPeriapsisAltitude"); + horizontalVelocity.addCallback( val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); periapsis.addCallback(val -> telemetryData.put(Telemetry.PERIAPSIS, val < 0 ? 0 : val)); - periapsis.start(); - - verticalVelocity = connection.addStream(flightParameters, "getVerticalSpeed"); + surfaceAltitude.addCallback(val -> telemetryData.put(Telemetry.ALT_SURF, val < 0 ? 0 : val)); verticalVelocity.addCallback(val -> telemetryData.put(Telemetry.VERT_SPEED, val)); - verticalVelocity.start(); - horizontalVelocity = connection.addStream(flightParameters, "getHorizontalSpeed"); - horizontalVelocity.addCallback( - val -> telemetryData.put(Telemetry.HORZ_SPEED, val < 0 ? 0 : val)); + altitude.start(); + apoapsis.start(); horizontalVelocity.start(); - + periapsis.start(); + surfaceAltitude.start(); + totalMass.start(); + verticalVelocity.start(); + System.out.println("DEBUG: All streams created successfully."); } catch (RPCException | StreamException e) { System.err.println( - "Error while initializing parameters for active vessel: " + e.getMessage()); + "DEBUG: CRITICAL ERROR while initializing parameters for active vessel: " + + e.getMessage()); } } } diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index 383beb5..6f2ecf4 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -169,7 +169,7 @@ public void checkAndUpdateActiveVessel() { System.out.println("DEBUG: Vessel ID mismatch. Re-initializing..."); if (currentVessel != null) { Thread oldThread = currentVessel.getControllerThread(); - currentVessel.destroy(); // This will set flags and interrupt + currentVessel.removeStreams(); // This will set flags and interrupt if (oldThread != null) { try { oldThread.join(1000); // Wait for the old thread to die, with a timeout From f5a4e9a336be509e746df40d747f7a55cbd29012 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 23:04:25 -0300 Subject: [PATCH 11/25] [MP-8]: Refactored ManeuverController --- .../controllers/ManeuverController.java | 888 ++++++++++-------- .../com/pesterenan/utils/ControlePID.java | 4 +- .../java/com/pesterenan/utils/Navigation.java | 207 ++-- .../java/com/pesterenan/views/MainGui.java | 2 +- src/main/resources/MechPesteBundle.properties | 3 + .../resources/MechPesteBundle_pt.properties | 3 + 6 files changed, 578 insertions(+), 529 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 332122b..237ee56 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -9,8 +9,11 @@ import com.pesterenan.utils.Navigation; import com.pesterenan.utils.Vector; 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; @@ -25,21 +28,48 @@ public class ManeuverController extends Controller { + 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; + } + } + public static final float CONST_GRAV = 9.81f; private ControlePID ctrlManeuver, ctrlRCS; + private Navigation navigation; private boolean fineAdjustment; private double lowOrbitAltitude; - - private volatile boolean isBurnComplete, isOriented, isTimeToBurn = false; - private int burnCompleteCallbackTag, - headingErrorCallbackTag, - pitchErrorCallbackTag, - timeToBurnCallbackTag; private Stream headingErrorStream; private Stream pitchErrorStream; private Stream timeToNodeStream; - private Stream> remainingBurn; + + private Stream rollErrorStream; + + private Stream> remainingBurnStream; + + private double lastBurnDv = Double.MAX_VALUE; public ManeuverController( ConnectionManager connectionManager, @@ -51,19 +81,6 @@ public ManeuverController( initializeParameters(); } - private void initializeParameters() { - ctrlRCS = new ControlePID(getConnectionManager().getSpaceCenter(), 25); - ctrlManeuver = new ControlePID(getConnectionManager().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) { - } - } - @Override public void run() { try { @@ -73,52 +90,15 @@ public void run() { || commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get()))) { executeNextManeuver(); } - } catch (InterruptedException e) { + } catch (ClassCastException | InterruptedException e) { + System.out.println("INFO: Interrompendo Controle de Manobras. Erro: " + e.getMessage()); cleanup(); } } - 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); - - // Calculate the final radius for the target orbit - double targetRadius = currentBody.getEquatorialRadius() + targetAltitude; - - // 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); - - // Total delta-v for the bi-elliptic transfer - totalDv[0] = deltaV1 + deltaV2 + deltaV3 + deltaV4; - } catch (RPCException e) { - // Handle the exception - } - return createManeuver(timeToStart, totalDv); - } - public void calculateManeuver() { try { tuneAutoPilot(); - System.out.println(commands + " calculate maneuvers"); if (commands.get(Module.FUNCTION.get()).equals(Module.EXECUTE.get())) { return; } @@ -163,251 +143,22 @@ public void calculateManeuver() { } } - public void matchOrbitApoapsis() throws RPCException, StreamException, InterruptedException { - 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(); - } - 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); - } - } - - 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]) - getConnectionManager().getSpaceCenter().getUT(); - } catch (Exception ignored) { - } - return createManeuver(uTatClosestNode, dv); - } - - 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) - }; - } - - private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { - try { - Vessel vessel = getActiveVessel(); - Orbit vesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = - getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - boolean hasManeuverNodes = vessel.getControl().getNodes().size() > 0; - System.out.println("hasManeuverNodes: " + hasManeuverNodes); - if (!hasManeuverNodes) { - MainGui.newInstance().getCreateManeuverPanel().createManeuver(); - } - 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]; - MainGui.newInstance() - .getCreateManeuverPanel() - .positionManeuverAt(closestIsAN ? "ascending" : "descending"); - double currentInclination = - Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - ctrlManeuver.setTimeSample(25); - while (currentInclination > 0.05) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - currentInclination = - Math.toDegrees(currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); - double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); - currentManeuver.setNormal( - currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); - Thread.sleep(25); - } - } catch (Exception err) { - System.err.println(err); - } - } - - private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { - 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(); - MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); - } else { - MainGui.newInstance().getCreateManeuverPanel().createManeuver(); - } - currentManeuvers = getActiveVessel().getControl().getNodes(); - lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); - - Orbit activeVesselOrbit = getActiveVessel().getOrbit(); - Orbit targetVesselOrbit = - getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - ReferenceFrame currentBodyRefFrame = - activeVesselOrbit.getBody().getNonRotatingReferenceFrame(); - - double angularDiff = 10; - while (angularDiff >= 0.005) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - 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)) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - lastManeuverNode.setPrograde( - lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverPE / targetOrbitPosition * 1000, 1000)); - maneuverPE = lastManeuverNode.getOrbit().getPeriapsis(); - Thread.sleep(25); - } - } - - if (targetOrbitPosition > maneuverAP) { - while (Math.floor(targetOrbitPosition) != Math.floor(maneuverAP)) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - lastManeuverNode.setPrograde( - lastManeuverNode.getPrograde() - + ctrlManeuver.calculate(maneuverAP / targetOrbitPosition * 1000, 1000)); - maneuverAP = lastManeuverNode.getOrbit().getApoapsis(); - Thread.sleep(25); - } - } - angularDiff = - calculatePhaseAngle( - lastManeuverNode.getOrbit().positionAt(maneuverUT, currentBodyRefFrame), - getConnectionManager() - .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); - } - } catch (Exception err) { - if (err instanceof InterruptedException) { - throw (InterruptedException) err; - } - } - } - - 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 Orbit getTargetOrbit() throws RPCException { - if (getConnectionManager().getSpaceCenter().getTargetBody() != null) { - return getConnectionManager().getSpaceCenter().getTargetBody().getOrbit(); - } - if (getConnectionManager().getSpaceCenter().getTargetVessel() != null) { - return getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - } - return null; - } - - private Node createManeuver(double laterTime, double[] deltaV) { - Node maneuverNode = null; - try { - getActiveVessel() - .getControl() - .addNode( - getConnectionManager().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; - } - public void executeNextManeuver() throws InterruptedException { try { Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); double burnTime = calculateBurnTime(maneuverNode); - orientToManeuverNode(maneuverNode); executeBurn(maneuverNode, burnTime); } catch (UnsupportedOperationException e) { - System.err.println("Erro: " + e.getMessage()); + System.err.println("Manobras ainda não desbloqueadas: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_not_unlocked")); } catch (IndexOutOfBoundsException e) { - System.err.println("Erro: " + e.getMessage()); + System.err.println("Erro de index: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_unavailable")); - } catch (RPCException | StreamException e) { - System.err.println("Erro: " + e.getMessage()); + } 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("Erro: " + e.getMessage()); + System.err.println("Cancelando executar manobra"); setCurrentStatus(Bundle.getString("status_couldnt_orient")); throw e; } @@ -417,39 +168,81 @@ public void orientToManeuverNode(Node maneuverNode) throws RPCException, StreamException, InterruptedException { setCurrentStatus(Bundle.getString("status_orienting_ship")); ap.engage(); + ap.setReferenceFrame(maneuverNode.getReferenceFrame()); - // Create streams for heading and pitch errors - headingErrorStream = connection.addStream(ap, "getHeadingError"); - pitchErrorStream = connection.addStream(ap, "getPitchError"); - - // A shared callback for both streams - Runnable checkOrientation = - () -> { - try { - if (headingErrorStream.get() <= 3 && pitchErrorStream.get() <= 3) { - isOriented = true; - // Clean up the callbacks once orientation is complete - headingErrorStream.removeCallback(headingErrorCallbackTag); - pitchErrorStream.removeCallback(pitchErrorCallbackTag); + // --- STAGE 1: ORIENT TO MANEUVER (PITCH/HEADING) --- + try { + setCurrentStatus(Bundle.getString("status_orienting_to_maneuver")); + ap.setTargetDirection(new Triplet<>(0.0, 1.0, 0.0)); // Prograde in node's frame + ap.setTargetRoll(Float.NaN); // Disable roll control + + final CountDownLatch directionLatch = new CountDownLatch(1); + this.headingErrorStream = connection.addStream(ap, "getHeadingError"); + this.pitchErrorStream = connection.addStream(ap, "getPitchError"); + + final Stream finalHeadingErrorStream = this.headingErrorStream; + final Stream finalPitchErrorStream = this.pitchErrorStream; + + 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(); } - } catch (RPCException | StreamException e) { - // Handle exceptions - } - }; + }; - // Add the same callback to both streams - headingErrorCallbackTag = headingErrorStream.addCallback(v -> checkOrientation.run()); - pitchErrorCallbackTag = pitchErrorStream.addCallback(v -> checkOrientation.run()); + this.headingErrorStream.addCallback(v -> checkOrientation.run()); + this.pitchErrorStream.addCallback(v -> checkOrientation.run()); + this.headingErrorStream.start(); + this.pitchErrorStream.start(); - // Start the streams - headingErrorStream.start(); - 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(); + } + } - while (!isOriented) { - if (Thread.interrupted()) { - throw new InterruptedException(); + // --- STAGE 2: STABILIZE ROLL --- + try { + setCurrentStatus(Bundle.getString("status_stabilizing_roll")); + ap.setTargetRoll(0.0f); + final CountDownLatch rollLatch = new CountDownLatch(1); + this.rollErrorStream = connection.addStream(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(); + } + }); + 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(); } - navigation.aimAtManeuver(maneuverNode); } } @@ -482,133 +275,413 @@ public double calculateBurnTime(Node maneuverNode) { public void executeBurn(Node maneuverNode, double burnDuration) throws InterruptedException { try { - // Countdown Stream + // 1. PRE-WARP ORIENTATION + orientToManeuverNode(maneuverNode); + + // 2. WARP AND COUNTDOWN + // Wait until it's time to burn + final CountDownLatch timeToBurnLatch = new CountDownLatch(1); timeToNodeStream = connection.addStream(maneuverNode, "getTimeTo"); - timeToBurnCallbackTag = - timeToNodeStream.addCallback( - (time) -> { - if (time <= (burnDuration / 2.0)) { - isTimeToBurn = true; - timeToNodeStream.removeCallback(timeToBurnCallbackTag); - } - }); + 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; + } + // 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) { + } + } + }); timeToNodeStream.start(); - double burnStartTime = - maneuverNode.getTimeTo() - (burnDuration / 2.0) - (fineAdjustment ? 5 : 0); - setCurrentStatus(Bundle.getString("status_maneuver_warp")); - if (burnStartTime > 30) { + double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - 30; + if (burnStartTime > 0) { + setCurrentStatus(Bundle.getString("status_maneuver_warp")); getConnectionManager() .getSpaceCenter() - .warpTo( - (getConnectionManager().getSpaceCenter().getUT() + burnStartTime - 10), 100000, 4); + .warpTo((getConnectionManager().getSpaceCenter().getUT() + burnStartTime), 100000, 4); } - setCurrentStatus(String.format(Bundle.getString("status_maneuver_duration"), burnDuration)); - while (!isTimeToBurn) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - setCurrentStatus( - String.format( - Bundle.getString("status_maneuver_ignition_in"), - maneuverNode.getTimeTo() - (burnDuration / 2.0))); + // 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; } - // Main Burn Stream - remainingBurn = - getConnectionManager() - .getConnection() - .addStream(maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); - burnCompleteCallbackTag = - remainingBurn.addCallback( - (burn) -> { - if (burn.getValue1() < (fineAdjustment ? 2 : 0.5)) { - isBurnComplete = true; - remainingBurn.removeCallback(burnCompleteCallbackTag); - } - }); - remainingBurn.start(); + // 4. EXECUTE THE BURN + final CountDownLatch burnCompleteLatch = new CountDownLatch(1); + remainingBurnStream = + connection.addStream( + maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); + remainingBurnStream.addCallback( + (burn) -> { + try { + double burnDvLeft = burn.getValue1(); + + // 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."); + throttle(0); + burnCompleteLatch.countDown(); + return; + } + lastBurnDv = burnDvLeft; // Update last known dV + + if (burnDvLeft < (fineAdjustment ? 2 : 0.75)) { + burnCompleteLatch.countDown(); + return; + } + navigation.aimAtManeuverNode(maneuverNode); + throttle(ctrlManeuver.calculate(0, burnDvLeft / 100.0)); + } catch (Exception e) { + e.printStackTrace(); + burnCompleteLatch.countDown(); + } + }); + remainingBurnStream.start(); setCurrentStatus(Bundle.getString("status_maneuver_executing")); - while (!isBurnComplete && maneuverNode != null) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtManeuver(maneuverNode); - double burnDvLeft = remainingBurn.get().getValue1(); - float limitValue = burnDvLeft > 100 ? 1000 : 100; - throttle( - ctrlManeuver.calculate( - (maneuverNode.getDeltaV() - Math.floor(burnDvLeft)) - / maneuverNode.getDeltaV() - * limitValue, - limitValue)); - } - + burnCompleteLatch.await(); throttle(0.0f); + remainingBurnStream.remove(); + if (fineAdjustment) { - adjustManeuverWithRCS(remainingBurn); + adjustManeuverWithRCS(maneuverNode); } + 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) { - System.err.println("Erro: " + e.getMessage()); + } catch (RPCException | StreamException e) { setCurrentStatus(Bundle.getString("status_data_unavailable")); } catch (InterruptedException e) { - System.err.println("Erro: " + e.getMessage()); setCurrentStatus(Bundle.getString("status_maneuver_cancelled")); throw e; } } - private void cleanup() { + private void initializeParameters() { + ctrlRCS = new ControlePID(getConnectionManager().getSpaceCenter(), 25); + ctrlManeuver = new ControlePID(getConnectionManager().getSpaceCenter(), 25); + 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(currentBody.getName()); + } catch (RPCException e) { + } + } + + private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart) { + double[] totalDv = {0, 0, 0}; try { - if (headingErrorStream != null) { - headingErrorStream.removeCallback(headingErrorCallbackTag); - headingErrorStream.remove(); + Orbit currentOrbit = getActiveVessel().getOrbit(); + double startingRadius = currentOrbit.getApoapsis(); + double gravParameter = currentBody.getGravitationalParameter(); + double deltaV1 = + Math.sqrt(2 * gravParameter / startingRadius) - Math.sqrt(gravParameter / startingRadius); + double intermediateRadius = currentBody.getEquatorialRadius() + targetAltitude; + double deltaV2 = + Math.sqrt(gravParameter / intermediateRadius) + - Math.sqrt(2 * gravParameter / intermediateRadius); + double targetRadius = 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()); + } + return createManeuver(timeToStart, totalDv); + } + + private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { + Stream utStream = null; + try { + Vessel vessel = getActiveVessel(); + Orbit vesselOrbit = getActiveVessel().getOrbit(); + Orbit targetVesselOrbit = + getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + if (vessel.getControl().getNodes().isEmpty()) { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + } + 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]; + MainGui.newInstance() + .getCreateManeuverPanel() + .positionManeuverAt(closestIsAN ? "ascending" : "descending"); + + ctrlManeuver.setTimeSample(25); + + final CountDownLatch latch = new CountDownLatch(1); + utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + final Stream finalUtStream = utStream; + final int[] utCallbackTag = new int[1]; + utCallbackTag[0] = + finalUtStream.addCallback( + (ut) -> { + try { + double currentInclination = + Math.toDegrees( + currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); + if (currentInclination <= 0.05) { + latch.countDown(); + finalUtStream.removeCallback(utCallbackTag[0]); + return; + } + double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); + currentManeuver.setNormal( + currentManeuver.getNormal() + (closestIsAN ? ctrlOutput : -ctrlOutput)); + } catch (Exception e) { + e.printStackTrace(); + latch.countDown(); + } + }); + utStream.start(); + latch.await(); // Wait until the alignment is done + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; } - if (pitchErrorStream != null) { - pitchErrorStream.removeCallback(pitchErrorCallbackTag); - pitchErrorStream.remove(); + System.err.println("Error aligning planes: " + err); + } finally { + if (utStream != null) { + utStream.remove(); } - if (timeToNodeStream != null) { - timeToNodeStream.removeCallback(timeToBurnCallbackTag); - timeToNodeStream.remove(); + } + } + + private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { + Stream utStream = null; + try { + List currentManeuvers = getActiveVessel().getControl().getNodes(); + Node lastManeuverNode; + if (currentManeuvers.isEmpty()) { + MainGui.newInstance().getCreateManeuverPanel().createManeuver(); + currentManeuvers = getActiveVessel().getControl().getNodes(); + } else { + double lastManeuverNodeUT = 60 + currentManeuvers.get(currentManeuvers.size() - 1).getUT(); + MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); + currentManeuvers = getActiveVessel().getControl().getNodes(); } - if (remainingBurn != null) { - remainingBurn.removeCallback(burnCompleteCallbackTag); - remainingBurn.remove(); + lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); + + final CountDownLatch latch = new CountDownLatch(1); + final RendezvousState state = new RendezvousState(lastManeuverNode); + + utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + final Stream finalUtStream = utStream; + final int[] utCallbackTag = new int[1]; + utCallbackTag[0] = + finalUtStream.addCallback( + ut -> { + try { + updateRendezvousState(state, latch, finalUtStream, utCallbackTag[0]); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Error in rendezvous update: " + e); + latch.countDown(); + } + }); + utStream.start(); + latch.await(); + } catch (Exception err) { + if (err instanceof InterruptedException) { + throw (InterruptedException) err; } - ap.disengage(); + System.err.println("Error during rendezvous: " + err); + } finally { + if (utStream != null) { + utStream.remove(); + } + } + } + + private void updateRendezvousState( + RendezvousState state, CountDownLatch latch, Stream stream, int callbackTag) + throws RPCException, IOException { + Orbit targetVesselOrbit = getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + ReferenceFrame currentBodyRefFrame = + 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; + } + 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; + } + break; + + case ADJUST_PERIAPSIS: + if (Math.floor(state.targetOrbitPosition) == Math.floor(state.maneuverPE)) { + state.phase = RendezvousPhase.ADJUST_UT; + break; + } + 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; + } + 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(); + stream.removeCallback(callbackTag); + break; + } + } + + private Node createManeuver(double laterTime, double[] deltaV) { + Node maneuverNode = null; + try { + getActiveVessel() + .getControl() + .addNode( + getConnectionManager().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; + } + + 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 (ap != null) ap.disengage(); throttle(0); - } catch (RPCException e) { + } catch (RPCException | NullPointerException e) { // ignore } } - private void adjustManeuverWithRCS(Stream> remainingDeltaV) + private void adjustManeuverWithRCS(Node maneuverNode) throws RPCException, StreamException, InterruptedException { + setCurrentStatus("Fine tuning with RCS..."); getActiveVessel().getControl().setRCS(true); - while (Math.floor(remainingDeltaV.get().getValue1()) > 0.2) { - if (Thread.interrupted()) { - throw new InterruptedException(); + final CountDownLatch rcsLatch = new CountDownLatch(1); + + Stream> rcsStream = null; + try { + rcsStream = + 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 + getActiveVessel() + .getControl() + .setForward((float) ctrlRCS.calculate(-progradeDv, 0)); + } catch (Exception e) { + e.printStackTrace(); + rcsLatch.countDown(); + } + }); + 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 { + getActiveVessel().getControl().setForward(0); + if (rcsStream != null) { + rcsStream.remove(); } - getActiveVessel() - .getControl() - .setForward((float) ctrlRCS.calculate(-remainingDeltaV.get().getValue1() * 10, 0)); } - getActiveVessel().getControl().setForward(0); } private boolean canFineAdjust(String string) { - if (string.equals("true")) { + if ("true".equals(string)) { try { List rcsEngines = getActiveVessel().getParts().getRCS(); if (rcsEngines.size() > 0) { @@ -619,7 +692,8 @@ private boolean canFineAdjust(String string) { } } return false; - } catch (RPCException ignored) { + } catch (RPCException e) { + System.err.println("ERRO: Ajuste fino de manobra." + e.getMessage()); } } return false; @@ -650,10 +724,4 @@ private double calculatePhaseAngle( return Math.abs(angularDifference); } - - enum Compare { - INC, - AP, - PE - } } diff --git a/src/main/java/com/pesterenan/utils/ControlePID.java b/src/main/java/com/pesterenan/utils/ControlePID.java index b5b56fc..5aeb86d 100644 --- a/src/main/java/com/pesterenan/utils/ControlePID.java +++ b/src/main/java/com/pesterenan/utils/ControlePID.java @@ -55,9 +55,7 @@ public double calculate(double measurement, double setPoint) { proportionalTerm = kp * error; // Integral - // integralTerm += 0.5f * ki * timeSample * (error + previousError); - // integralTerm += ki * (error + previousError); - integralTerm = ki * (integralTerm + (error * timeSample)); + integralTerm += 0.5 * ki * timeSample * (error + previousError); integralTerm = limitOutput(integralTerm); derivativeTerm = kd * ((error - previousError) / timeSample); diff --git a/src/main/java/com/pesterenan/utils/Navigation.java b/src/main/java/com/pesterenan/utils/Navigation.java index 1f76328..5eac8b8 100644 --- a/src/main/java/com/pesterenan/utils/Navigation.java +++ b/src/main/java/com/pesterenan/utils/Navigation.java @@ -26,153 +26,130 @@ public class Navigation { public Navigation(ConnectionManager connectionManager, Vessel currentVessel) { this.connectionManager = connectionManager; this.currentVessel = currentVessel; - initializeParameters(); - } - - private void initializeParameters() { try { // drawing = Drawing.newInstance(getConexao()); orbitalReference = currentVessel.getOrbit().getBody().getReferenceFrame(); flightParameters = currentVessel.flight(orbitalReference); horizontalSpeed = connectionManager.getConnection().addStream(flightParameters, "getHorizontalSpeed"); - } catch (RPCException | StreamException ignored) { + } 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( - connectionManager - .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( - connectionManager - .getSpaceCenter() - .transformPosition( - RETROGRADE, - currentVessel.getSurfaceVelocityReferenceFrame(), - orbitalReference)) - .subtract(currentPosition); + transformDirection(RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame())); Vector radial = - new Vector( - connectionManager - .getSpaceCenter() - .transformDirection( - RADIAL, currentVessel.getSurfaceReferenceFrame(), orbitalReference)); + new Vector(transformDirection(RADIAL, currentVessel.getSurfaceReferenceFrame())); + double angleLimit = Utilities.remap(0, 10, 0, 0.9, horizontalSpeed.get(), true); Vector landingVector = Utilities.linearInterpolation(radial, retrograde, angleLimit); aimAtDirection(landingVector.toTriplet()); } - // 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 aimForDeorbit() throws RPCException { + aimAtDirection(flightParameters.getRetrograde()); + } public void aimAtPrograde() throws RPCException { - aimAtDirection( - connectionManager - .getSpaceCenter() - .transformDirection( - PROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)); + transformAndAim(PROGRADE, currentVessel.getSurfaceVelocityReferenceFrame()); } public void aimAtRadialOut() throws RPCException { - aimAtDirection( - connectionManager - .getSpaceCenter() - .transformDirection( - RADIAL, currentVessel.getSurfaceReferenceFrame(), orbitalReference)); + transformAndAim(RADIAL, currentVessel.getSurfaceReferenceFrame()); } public void aimAtRetrograde() throws RPCException { - aimAtDirection( - connectionManager - .getSpaceCenter() - .transformDirection( - RETROGRADE, currentVessel.getSurfaceVelocityReferenceFrame(), orbitalReference)); + 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/views/MainGui.java b/src/main/java/com/pesterenan/views/MainGui.java index e065e3d..e9ce014 100644 --- a/src/main/java/com/pesterenan/views/MainGui.java +++ b/src/main/java/com/pesterenan/views/MainGui.java @@ -177,7 +177,7 @@ public void setupComponents() { setTitle("MechPeste - Pesterenan"); setJMenuBar(menuBar); setResizable(false); - setLocation(100, 100); + setLocation(50, 50); setContentPane(ctpMainGui); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 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... │ From 2f16f070747ad3f7d41b7ca8caa987f4d015c3f9 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 23:22:36 -0300 Subject: [PATCH 12/25] [MP-8]: Refactored LiftoffController --- .../controllers/LiftoffController.java | 280 ++++++++++-------- 1 file changed, 163 insertions(+), 117 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index 26f6d1b..a2d181b 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -15,18 +15,30 @@ 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 enum LIFTOFF_MODE { + GRAVITY_TURN, + FINALIZE_ORBIT, + CIRCULARIZE + } + private static final float PITCH_UP = 90; private ControlePID thrControl; private float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; - private volatile boolean targetApoapsisReached, dynamicPressureLowEnough = false; - private int apoapsisCallbackTag, pressureCallbackTag; + private volatile boolean targetApoapsisReached, + dynamicPressureLowEnough, + isLiftoffRunning = false; + private int apoapsisCallbackTag, pressureCallbackTag, utCallbackTag; private Stream pressureStream; + private Stream utStream; private boolean willDecoupleStages, willOpenPanelsAndAntenna; private String gravityCurveModel = Module.CIRCULAR.get(); private Navigation navigation; + private LIFTOFF_MODE liftoffMode; + private double startCurveAlt; public LiftoffController( ConnectionManager connectionManager, @@ -38,6 +50,65 @@ public LiftoffController( initializeParameters(); } + @Override + public void run() { + try { + isLiftoffRunning = true; + + // Part 1: Blocking Countdown and Launch + if (getActiveVessel().getSituation().equals(VesselSituation.PRE_LAUNCH)) { + throttleUp(getMaxThrottleForTWR(1.4), 1); + 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); + } + setCurrentStatus(Bundle.getString("status_liftoff")); + getActiveVessel().getControl().activateNextStage(); + } else { + throttle(1.0f); + } + + // Part 2: Async Gravity Turn + liftoffMode = LIFTOFF_MODE.GRAVITY_TURN; // Set initial state for async phase + setupCallbacks(); // This starts the UT stream + tuneAutoPilot(); + startCurveAlt = altitude.get(); + ap.setReferenceFrame(surfaceReferenceFrame); + ap.targetPitchAndHeading(currentPitch, heading); + ap.setTargetRoll(this.roll); + ap.engage(); + + // Loop to keep thread alive while async part runs + while (isLiftoffRunning) { + if (Thread.interrupted()) throw new InterruptedException(); + Thread.sleep(250); + } + } catch (RPCException | InterruptedException | StreamException e) { + cleanup(); + setCurrentStatus(Bundle.getString("status_ready")); + } + } + + 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); + } + + 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); + } + + 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 void initializeParameters() { currentPitch = PITCH_UP; setFinalApoapsisAlt(Float.parseFloat(commands.get(Module.APOAPSIS.get()))); @@ -45,82 +116,75 @@ private void initializeParameters() { 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())); + 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(getConnectionManager().getSpaceCenter(), 25); thrControl.setOutput(0.0, 1.0); } - @Override - public void run() { - try { - // Apoapsis Check using Stream Callback - apoapsisCallbackTag = - apoapsis.addCallback( - (apo) -> { - if (apo >= getFinalApoapsis()) { - targetApoapsisReached = true; - apoapsis.removeCallback(apoapsisCallbackTag); - } - }); - apoapsis.start(); - - // Dynamic Pressure Check using Stream Callback - pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); - pressureCallbackTag = - pressureStream.addCallback( - (pressure) -> { - if (pressure <= 10) { - dynamicPressureLowEnough = true; - pressureStream.removeCallback(pressureCallbackTag); - } - }); - pressureStream.start(); + private void setupCallbacks() throws RPCException, StreamException { + apoapsisCallbackTag = + apoapsis.addCallback( + (apo) -> { + if (apo >= finalApoapsisAlt) { + targetApoapsisReached = true; + } + }); + apoapsis.start(); - tuneAutoPilot(); - liftoff(); - gravityCurve(); - finalizeCurve(); - circularizeOrbitOnApoapsis(); - } catch (RPCException | InterruptedException | StreamException e) { - cleanup(); - setCurrentStatus(Bundle.getString("status_ready")); - } + pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); + pressureCallbackTag = + pressureStream.addCallback( + (pressure) -> { + if (pressure <= 10) { + dynamicPressureLowEnough = true; + } + }); + pressureStream.start(); + + utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utCallbackTag = + utStream.addCallback( + (ut) -> { + try { + if (!isLiftoffRunning) { + utStream.removeCallback(utCallbackTag); + return; + } + handleLiftoff(); + } catch (Exception e) { + System.err.println("Liftoff UT Callback error: " + e.getMessage()); + } + }); + utStream.start(); } - private void cleanup() { - try { - apoapsis.removeCallback(apoapsisCallbackTag); - pressureStream.removeCallback(pressureCallbackTag); - pressureStream.remove(); - ap.disengage(); - throttle(0); - } catch (RPCException e) { - // ignore + private void handleLiftoff() throws RPCException, StreamException, InterruptedException { + switch (liftoffMode) { + case GRAVITY_TURN: + gravityTurn(); + break; + case FINALIZE_ORBIT: + finalizeOrbit(); + break; + case CIRCULARIZE: + circularizeOrbitOnApoapsis(); + isLiftoffRunning = false; + break; } } - 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 && !targetApoapsisReached) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } + private void gravityTurn() throws RPCException, StreamException, InterruptedException { + if (currentPitch > 1 && !targetApoapsisReached) { double altitudeProgress = - Utilities.remap(startCurveAlt, getFinalApoapsis(), 1, 0.01, altitude.get(), false); + Utilities.remap(startCurveAlt, finalApoapsisAlt, 1, 0.01, altitude.get(), false); currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); ap.setTargetPitch(currentPitch); double throttleValue = Math.min( - thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000), + thrControl.calculate(apoapsis.get() / finalApoapsisAlt * 1000, 1000), getMaxThrottleForTWR(currentMaxTWR)); throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); @@ -129,8 +193,45 @@ private void gravityCurve() throws RPCException, StreamException, InterruptedExc } setCurrentStatus( String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); + } else { + throttle(0); + liftoffMode = LIFTOFF_MODE.FINALIZE_ORBIT; + } + } + + private void finalizeOrbit() throws RPCException, StreamException, InterruptedException { + if (!dynamicPressureLowEnough) { + setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); + getActiveVessel().getControl().setRCS(true); + navigation.aimAtPrograde(); + throttle(thrControl.calculate(apoapsis.get() / finalApoapsisAlt * 1000, 1000)); + } else { + throttle(0.0f); + if (willDecoupleStages) { + jettisonFairings(); + } + if (willOpenPanelsAndAntenna) { + openPanelsAndAntenna(); + } + apoapsis.removeCallback(apoapsisCallbackTag); + pressureStream.removeCallback(pressureCallbackTag); + liftoffMode = LIFTOFF_MODE.CIRCULARIZE; + } + } + + private void cleanup() { + try { + isLiftoffRunning = false; + apoapsis.removeCallback(apoapsisCallbackTag); + pressureStream.removeCallback(pressureCallbackTag); + utStream.removeCallback(utCallbackTag); + pressureStream.remove(); + utStream.remove(); + ap.disengage(); + throttle(0); + } catch (RPCException | NullPointerException e) { + // ignore } - throttle(0); } private double calculateTWRBasedOnPressure(float currentPitch) throws RPCException { @@ -141,26 +242,6 @@ private double calculateTWRBasedOnPressure(float currentPitch) throws RPCExcepti return Utilities.remap(22000.0, 10.0, maxTWR, 5.0, currentPressure, true); } - private void finalizeCurve() throws RPCException, StreamException, InterruptedException { - setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); - getActiveVessel().getControl().setRCS(true); - - while (!dynamicPressureLowEnough) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimAtPrograde(); - throttle(thrControl.calculate(apoapsis.get() / getFinalApoapsis() * 1000, 1000)); - } - throttle(0.0f); - if (willDecoupleStages) { - jettisonFairings(); - } - if (willOpenPanelsAndAntenna) { - openPanelsAndAntenna(); - } - } - private void circularizeOrbitOnApoapsis() { setCurrentStatus(Bundle.getString("status_planning_orbit")); Map commands = new HashMap<>(); @@ -178,7 +259,7 @@ private void jettisonFairings() throws RPCException, InterruptedException { if (f.getJettisoned()) { String eventName = f.getPart().getModules().get(0).getEvents().get(0); f.getPart().getModules().get(0).triggerEvent(eventName); - Thread.sleep(10000); + Thread.sleep(5000); } } } @@ -215,39 +296,4 @@ private boolean isCurrentStageWithoutFuel() throws RPCException { } return false; } - - public float getHeading() { - return heading; - } - - 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); - } - - public float getFinalApoapsis() { - return finalApoapsisAlt; - } - - public float getRoll() { - return this.roll; - } - - 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 setGravityCurveModel(String model) { - this.gravityCurveModel = model; - } - - 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); - } } From a6ed049d20125d645879baacbba7f063da4c26f8 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 23:37:35 -0300 Subject: [PATCH 13/25] [MP-8]: Refactored LandingController --- .../controllers/LandingController.java | 292 ++++++++++-------- 1 file changed, 161 insertions(+), 131 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index 161152e..faab03c 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -18,6 +18,8 @@ public class LandingController extends Controller { private enum MODE { APPROACHING, DEORBITING, + ORIENTING_DEORBIT, + EXECUTING_DEORBIT_BURN, GOING_DOWN, GOING_UP, HOVERING, @@ -34,12 +36,12 @@ private enum MODE { private ControlePID velocityCtrl; private Navigation navigation; private final int HUNDRED_PERCENT = 100; - private double altitudeErrorPercentage, hoverAltitude; + private double altitudeErrorPercentage, hoverAltitude, targetPeriapsis; private boolean hoveringMode, hoverAfterApproximation, landingMode; private MODE currentMode; private float maxTWR; - private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling = false; + private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling, wasAirborne = false; private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag, @@ -70,56 +72,43 @@ public void run() { if (Thread.interrupted()) { throw new InterruptedException(); } - Thread.sleep(1000); + Thread.sleep(100); } - } catch (InterruptedException | RPCException | StreamException e) { + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore interrupted status + System.err.println("Controle de Pouso finalizado via interrupção."); + } catch (RPCException | StreamException e) { System.err.println("Erro no run do landingController:" + e.getMessage()); + setCurrentStatus(Bundle.getString("status_data_unavailable")); + } finally { cleanup(); - setCurrentStatus(Bundle.getString("status_ready")); } } private void initializeParameters() { - altitudeCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); - velocityCtrl = new ControlePID(getConnectionManager().getSpaceCenter(), sleepTime); + maxTWR = Float.parseFloat(commands.get(Module.MAX_TWR.get())); + hoverAltitude = Double.parseDouble(commands.get(Module.HOVER_ALTITUDE.get())); + altitudeCtrl = new ControlePID(spaceCenter, sleepTime); + velocityCtrl = new ControlePID(spaceCenter, sleepTime); altitudeCtrl.setOutput(0, 1); velocityCtrl.setOutput(0, 1); } - 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(); - } - altitudeErrorPercentage = surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; - 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 (InterruptedException | RPCException | StreamException e) { - cleanup(); - setCurrentStatus(Bundle.getString("status_ready")); - } + private void hoverArea() throws RPCException, StreamException, InterruptedException { + hoveringMode = true; + ap.engage(); + tuneAutoPilot(); + setupCallbacks(); } private void autoLanding() throws RPCException, StreamException, InterruptedException { 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; } + altitudeCtrl.reset(); + velocityCtrl.reset(); setCurrentStatus(Bundle.getString("status_starting_landing_at") + " " + currentBody.getName()); currentMode = MODE.DEORBITING; ap.engage(); @@ -127,8 +116,6 @@ private void autoLanding() throws RPCException, StreamException, InterruptedExce getActiveVessel().getControl().setBrakes(true); setCurrentStatus(Bundle.getString("status_starting_landing")); setupCallbacks(); - altitudeCtrl.reset(); - velocityCtrl.reset(); } private void setupCallbacks() throws RPCException, StreamException { @@ -148,11 +135,19 @@ private void setupCallbacks() throws RPCException, StreamException { periapsis.addCallback( p -> { try { - if (p <= -apoapsis.get()) { - isDeorbitBurnDone = true; - periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); + 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; + throttle(0.0f); + periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); + } else { + throttle(altitudeCtrl.calculate(targetPeriapsis, p)); + } } - } catch (RPCException | StreamException e) { + } catch (RPCException e) { System.err.println("isDeorbitBurnDoneCallbackTag error: " + e.getMessage()); } }); @@ -161,7 +156,7 @@ private void setupCallbacks() throws RPCException, StreamException { isFallingCallbackTag = verticalVelocity.addCallback( (vv) -> { - if (vv <= 0) { + if (vv <= -1) { isFalling = true; verticalVelocity.removeCallback(isFallingCallbackTag); } @@ -172,11 +167,21 @@ private void setupCallbacks() throws RPCException, StreamException { utStream.addCallback( (ut) -> { try { - if (!landingMode) { + if (landingMode) { + executeLandingStep(); + } else if (hoveringMode) { + altitudeErrorPercentage = surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; + if (altitudeErrorPercentage > HUNDRED_PERCENT * 1.05) { + currentMode = MODE.GOING_DOWN; + } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.95) { + currentMode = MODE.GOING_UP; + } else { + currentMode = MODE.HOVERING; + } + executeHoverStep(); + } else { utStream.removeCallback(utCallbackTag); - return; } - changeControlMode(); } catch (Exception e) { System.err.println("UT Callback error: " + e.getMessage()); } @@ -190,14 +195,38 @@ private void setupCallbacks() throws RPCException, StreamException { utStream.start(); } - private void changeControlMode() throws RPCException, StreamException, InterruptedException { - adjustPIDbyTWR(); - double velPID, altPID = 0; + private void executeLandingStep() throws RPCException, StreamException, InterruptedException { + adjustLandingPID(); // Change vessel behavior depending on which mode is active switch (currentMode) { case DEORBITING: - deOrbitShip(); - currentMode = MODE.WAITING; + if (getActiveVessel().getSituation().equals(VesselSituation.ORBITING) + || getActiveVessel().getSituation().equals(VesselSituation.SUB_ORBITAL)) { + setCurrentStatus(Bundle.getString("status_going_suborbital")); + getActiveVessel().getControl().setRCS(true); + throttle(0.0f); + targetPeriapsis = currentBody.getAtmosphereDepth(); + targetPeriapsis = + targetPeriapsis > 0 ? targetPeriapsis / 2 : -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) { + getActiveVessel().getControl().setRCS(false); + throttle(0.0f); + currentMode = MODE.WAITING; + } break; case WAITING: if (isFalling) { @@ -212,22 +241,15 @@ private void changeControlMode() throws RPCException, StreamException, Interrupt velocityCtrl.setOutput(0, 1); double currentVelocity = calculateCurrentVelocityMagnitude(); double zeroVelocity = calculateZeroVelocityMagnitude(); - double landingDistanceThreshold = Math.max(hoverAltitude, getMaxAcel(maxTWR) * 3); + double landingDistanceThreshold = Math.max(hoverAltitude, getMaxAcel(maxTWR) * 5); double threshold = Utilities.clamp( ((currentVelocity + zeroVelocity) - landingDistanceThreshold) - / landingDistanceThreshold / sleepTime, + / landingDistanceThreshold, 0, 1); - System.out.println( - "Current: " - + currentVelocity / sleepTime - + " Zero: " - + zeroVelocity / sleepTime - + " Threshold: " - + threshold); - altPID = altitudeCtrl.calculate(currentVelocity / sleepTime, zeroVelocity / sleepTime); - velPID = + double altPID = altitudeCtrl.calculate(currentVelocity, zeroVelocity); + double velPID = velocityCtrl.calculate( verticalVelocity.get(), (-Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20))); throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); @@ -244,9 +266,28 @@ private void changeControlMode() throws RPCException, StreamException, Interrupt } setCurrentStatus("Se aproximando do momento do pouso..."); break; + case LANDING: + if (hasTheVesselLanded()) break; + controlThrottleByMatchingVerticalVelocity( + horizontalVelocity.get() > 4 + ? 0 + : -Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20)); + navigation.aimForLanding(); + setCurrentStatus("Pousando..."); + break; + default: + break; + } + } + + private void executeHoverStep() throws RPCException, StreamException, InterruptedException { + if (hasTheVesselLanded()) { + hoveringMode = false; + return; + } + adjustHoverPID(); + switch (currentMode) { case GOING_UP: - altitudeCtrl.reset(); - velocityCtrl.reset(); altitudeCtrl.setOutput(-0.5, 0.5); velocityCtrl.setOutput(-0.5, 0.5); throttle( @@ -256,24 +297,11 @@ private void changeControlMode() throws RPCException, StreamException, Interrupt setCurrentStatus("Subindo altitude..."); break; case GOING_DOWN: - altitudeCtrl.reset(); - velocityCtrl.reset(); controlThrottleByMatchingVerticalVelocity(-MAX_VELOCITY); navigation.aimAtRadialOut(); setCurrentStatus("Baixando altitude..."); break; - case LANDING: - if (hasTheVesselLanded()) break; - controlThrottleByMatchingVerticalVelocity( - horizontalVelocity.get() > 4 - ? 0 - : -Utilities.clamp(surfaceAltitude.get() * 0.1, 1, 10)); - navigation.aimForLanding(); - setCurrentStatus("Pousando..."); - break; case HOVERING: - altitudeCtrl.reset(); - velocityCtrl.reset(); altitudeCtrl.setOutput(-0.5, 0.5); velocityCtrl.setOutput(-0.5, 0.5); throttle( @@ -282,20 +310,46 @@ private void changeControlMode() throws RPCException, StreamException, Interrupt navigation.aimAtRadialOut(); setCurrentStatus("Sobrevoando area..."); break; + default: + break; } } - 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 currentTWR = getMaxAcel(maxTWR); - double pGain = Math.min(getTWR(), maxTWR); - altitudeCtrl.setPIDValues(currentTWR * velP * velP, 0.1, velD); - velocityCtrl.setPIDValues(pGain * velP, 0.1, velD); + private void adjustLandingPID() throws RPCException, StreamException { + double maxAccel = getMaxAcel(maxTWR); + double currentTWR = Math.min(getTWR(), maxTWR); + + if (currentMode == MODE.APPROACHING) { + // 1. Calcula a distância de trajetória restante (usando o método que já temos) + double trajectoryLength = calculateCurrentVelocityMagnitude(); + // 2. Calcula a velocidade total (vetorial) + double totalVelocity = + Math.sqrt(Math.pow(verticalVelocity.get(), 2) + Math.pow(horizontalVelocity.get(), 2)); + + // 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.0; + altitudeCtrl.setPIDValues(Kp, Ki, Kd); + } else { + // Para outros modos, usa um PID mais simples e estável + altitudeCtrl.setPIDValues(maxAccel * velP, velI, velD); + } + + // 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(getTWR(), maxTWR); + altitudeCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); + velocityCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); } private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) @@ -305,44 +359,11 @@ private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) velocityCtrl.calculate(verticalVelocity.get(), velocityToMatch + horizontalVelocity.get())); } - private void deOrbitShip() throws RPCException, InterruptedException, StreamException { - 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 (!isOrientedForDeorbit) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimForLanding(); - setCurrentStatus(Bundle.getString("status_orienting_ship")); - Thread.sleep(100); // Prevent tight loop while waiting for event - } - - double targetPeriapsis = currentBody.getAtmosphereDepth(); - targetPeriapsis = - targetPeriapsis > 0 ? targetPeriapsis / 2 : -currentBody.getEquatorialRadius() / 2; - - while (!isDeorbitBurnDone) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - navigation.aimForLanding(); - throttle(altitudeCtrl.calculate(targetPeriapsis, periapsis.get())); - setCurrentStatus(Bundle.getString("status_lowering_periapsis")); - } - getActiveVessel().getControl().setRCS(false); - throttle(0.0f); - } - } - private boolean hasTheVesselLanded() throws RPCException { - if (getActiveVessel().getSituation().equals(VesselSituation.LANDED) - || getActiveVessel().getSituation().equals(VesselSituation.SPLASHED)) { + VesselSituation situation = getActiveVessel().getSituation(); + if (wasAirborne + && (situation.equals(VesselSituation.LANDED) + || situation.equals(VesselSituation.SPLASHED))) { setCurrentStatus(Bundle.getString("status_landed")); hoveringMode = false; landingMode = false; @@ -353,6 +374,12 @@ private boolean hasTheVesselLanded() throws RPCException { 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; } @@ -379,16 +406,19 @@ private void cleanup() { try { landingMode = false; hoveringMode = false; - utStream.removeCallback(utCallbackTag); - utStream.remove(); - apErrorStream.removeCallback(isOrientedCallbackTag); - apErrorStream.remove(); - periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); - verticalVelocity.removeCallback(isFallingCallbackTag); - ap.disengage(); + if (ap != null) { + ap.disengage(); + } + if (apErrorStream != null) { + apErrorStream.remove(); + } + if (utStream != null) { + utStream.remove(); + } throttle(0); - } catch (RPCException e) { - // ignore + setCurrentStatus(Bundle.getString("status_ready")); + } catch (RPCException | NullPointerException e) { + System.err.println("Erro durante a limpeza do LandingController: " + e.getMessage()); } } } From dd2b74feddbad51cdd30ecc660b5879e042d2dc1 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Thu, 2 Oct 2025 23:49:46 -0300 Subject: [PATCH 14/25] [MP-8]: Refactoring DockingController to use callbacks on streams --- .gitignore | 1 + .../controllers/DockingController.java | 248 +++++++++++------- 2 files changed, 157 insertions(+), 92 deletions(-) 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/src/main/java/com/pesterenan/controllers/DockingController.java b/src/main/java/com/pesterenan/controllers/DockingController.java index aac2165..9004282 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -9,20 +9,41 @@ import com.pesterenan.views.DockingJPanel; import java.util.Map; 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 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 Control control; + private KRPC krpc; private ReferenceFrame orbitalRefVessel; private ReferenceFrame vesselRefFrame; private Line distanceLine; @@ -31,9 +52,9 @@ public class DockingController extends Controller { 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; @@ -43,8 +64,17 @@ public class DockingController extends Controller { private double lastYTargetPos = 0.0; private double lastZTargetPos = 0.0; private long sleepTime = 25; + private DOCKING_STEPS dockingStep; private ConnectionManager connectionManager; + private boolean isDocking, isOriented = false; + + private Stream utStream; + private Stream errorStream; + + private int utCallbackTag, errorCallbackTag; + + private DockingPhase currentPhase; public DockingController( ConnectionManager connectionManager, @@ -56,6 +86,47 @@ public DockingController( initializeParameters(); } + @Override + public void run() { + if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { + try { + startDocking(); + while (isDocking) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Thread.sleep(250); + } + } catch (RPCException | InterruptedException | StreamException | IllegalArgumentException e) { + cleanup(); + setCurrentStatus("Docking interrupted: " + e.getMessage()); + } + } + } + + public void startDocking() throws RPCException, StreamException { + isDocking = true; + krpc = KRPC.newInstance(connectionManager.getConnection()); + currentPhase = DockingPhase.SETUP; + + utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utCallbackTag = + utStream.addCallback( + (ut) -> { + try { + if (isDocking) { + updateDockingState(); + } else { + utStream.removeCallback(utCallbackTag); + } + } catch (Exception e) { + setCurrentStatus("Docking failed: " + e.getMessage()); + cleanup(); + } + }); + utStream.start(); + } + private void initializeParameters() { try { DOCKING_MAX_SPEED = Double.parseDouble(commands.get(Module.MAX_SPEED.get())); @@ -76,96 +147,68 @@ private void initializeParameters() { } } - @Override - public void run() { - if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { - startDocking(); - } - } - - 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() { - 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); - + private void updateDockingState() throws RPCException, StreamException, InterruptedException { + 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(getActiveVessel().position(orbitalRefVessel)) + .subtract(new Vector(targetVessel.position(orbitalRefVessel))) + .multiply(-1); + pointToTarget(targetDirection); + control.setRCS(true); + 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); - - 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); - control.setRCS(true); - double safeDistance = 10; - while (true) { - if (Thread.interrupted()) { - throw new InterruptedException(); + 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))); - if (targetPosition.magnitude() < safeDistance) { - safeDistance = 1; - } + double safeDistance = targetPosition.magnitude() < 10 ? 1 : 10; controlShipRCS(targetPosition, safeDistance); - Thread.sleep(sleepTime); - } - } catch (RPCException | InterruptedException | IllegalArgumentException e) { - cleanup(); - setCurrentStatus("Docking interrupted."); + if (myDockingPort.getState() == DockingPortState.DOCKED) { + setCurrentStatus("Docking successful!"); + currentPhase = DockingPhase.FINISHED; + } + break; + case FINISHED: + cleanup(); + break; } } @@ -177,11 +220,25 @@ public void startDocking() { * nave primeiro, Y negativo */ - private enum DOCKING_STEPS { - APPROACH, - STOP_RELATIVE_SPEED, - LINE_UP_WITH_TARGET, - GO_IN_FRONT_OF_TARGET + private void pointToTarget(Vector targetDirection) + throws RPCException, InterruptedException, StreamException { + getActiveVessel().getAutoPilot().setReferenceFrame(orbitalRefVessel); + getActiveVessel().getAutoPilot().setTargetDirection(targetDirection.toTriplet()); + getActiveVessel().getAutoPilot().setTargetRoll(90); + getActiveVessel().getAutoPilot().engage(); + errorStream = connection.addStream(ap, "getError"); + errorCallbackTag = + errorStream.addCallback( + (error) -> { + if (error < 3.0) { + isOriented = true; + errorStream.removeCallback(errorCallbackTag); + } + }); + errorStream.start(); + getActiveVessel().getAutoPilot().disengage(); + control.setSAS(true); + control.setSASMode(SASMode.STABILITY_ASSIST); } private void controlShipRCS(Vector targetPosition, double forwardsDistanceLimit) { @@ -335,13 +392,20 @@ private void updateLines(Vector start, Vector end) { private void cleanup() { try { + isDocking = false; + if (utStream != null) { + utStream.remove(); + } + if (errorStream != null) { + errorStream.remove(); + } distanceLine.remove(); distLineXAxis.remove(); distLineYAxis.remove(); distLineZAxis.remove(); ap.disengage(); throttle(0); - } catch (RPCException e) { + } catch (RPCException | NullPointerException e) { // ignore } } From ef4bc160fc81cb2e607a86393d283a8d07d57474 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Fri, 10 Oct 2025 09:23:44 -0300 Subject: [PATCH 15/25] [MP-8]: Finalize work on streams --- .../pesterenan/controllers/Controller.java | 9 +- .../controllers/DockingController.java | 47 +++--- .../controllers/LandingController.java | 141 ++++++++-------- .../controllers/LiftoffController.java | 87 +++++----- .../controllers/ManeuverController.java | 150 +++++++++--------- .../controllers/RoverController.java | 108 +++++++------ .../com/pesterenan/model/ActiveVessel.java | 88 +++++++--- .../com/pesterenan/model/VesselManager.java | 25 ++- .../com/pesterenan/utils/PathFinding.java | 108 +++++++------ 9 files changed, 414 insertions(+), 349 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/Controller.java b/src/main/java/com/pesterenan/controllers/Controller.java index 57fd2bc..6f0f98c 100644 --- a/src/main/java/com/pesterenan/controllers/Controller.java +++ b/src/main/java/com/pesterenan/controllers/Controller.java @@ -1,14 +1,13 @@ package com.pesterenan.controllers; import com.pesterenan.model.ActiveVessel; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; -public class Controller extends ActiveVessel implements Runnable { +public class Controller implements Runnable { + protected final ActiveVessel vessel; private String currentStatus = ""; - public Controller(ConnectionManager connectionManager, VesselManager vesselManager) { - super(connectionManager, vesselManager); + public Controller(ActiveVessel vessel) { + this.vessel = vessel; } public void run() { diff --git a/src/main/java/com/pesterenan/controllers/DockingController.java b/src/main/java/com/pesterenan/controllers/DockingController.java index 9004282..894eccf 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -1,7 +1,6 @@ package com.pesterenan.controllers; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Module; import com.pesterenan.utils.Utilities; @@ -66,8 +65,8 @@ private enum DOCKING_STEPS { private long sleepTime = 25; private DOCKING_STEPS dockingStep; - private ConnectionManager connectionManager; private boolean isDocking, isOriented = false; + private final Map commands; private Stream utStream; private Stream errorStream; @@ -76,12 +75,8 @@ private enum DOCKING_STEPS { private DockingPhase currentPhase; - public DockingController( - ConnectionManager connectionManager, - VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.connectionManager = connectionManager; + public DockingController(ActiveVessel vessel, Map commands) { + super(vessel); this.commands = commands; initializeParameters(); } @@ -106,10 +101,10 @@ public void run() { public void startDocking() throws RPCException, StreamException { isDocking = true; - krpc = KRPC.newInstance(connectionManager.getConnection()); + krpc = KRPC.newInstance(vessel.getConnectionManager().getConnection()); currentPhase = DockingPhase.SETUP; - utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = utStream.addCallback( (ut) -> { @@ -131,13 +126,13 @@ 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(connectionManager.getConnection()); - targetVessel = connectionManager.getSpaceCenter().getTargetVessel(); - control = getActiveVessel().getControl(); - vesselRefFrame = getActiveVessel().getReferenceFrame(); - orbitalRefVessel = getActiveVessel().getOrbitalReferenceFrame(); + drawing = Drawing.newInstance(vessel.getConnectionManager().getConnection()); + targetVessel = vessel.getConnectionManager().getSpaceCenter().getTargetVessel(); + control = vessel.getActiveVessel().getControl(); + vesselRefFrame = vessel.getActiveVessel().getReferenceFrame(); + orbitalRefVessel = vessel.getActiveVessel().getOrbitalReferenceFrame(); - myDockingPort = getActiveVessel().getParts().getDockingPorts().get(0); + myDockingPort = vessel.getActiveVessel().getParts().getDockingPorts().get(0); targetDockingPort = targetVessel.getParts().getDockingPorts().get(0); dockingStep = DOCKING_STEPS.STOP_RELATIVE_SPEED; @@ -163,7 +158,7 @@ private void updateDockingState() throws RPCException, StreamException, Interrup targetPosition = new Vector(targetVessel.position(vesselRefFrame)); if (targetPosition.magnitude() > SAFE_DISTANCE) { Vector targetDirection = - new Vector(getActiveVessel().position(orbitalRefVessel)) + new Vector(vessel.getActiveVessel().position(orbitalRefVessel)) .subtract(new Vector(targetVessel.position(orbitalRefVessel))) .multiply(-1); pointToTarget(targetDirection); @@ -222,11 +217,11 @@ private void updateDockingState() throws RPCException, StreamException, Interrup private void pointToTarget(Vector targetDirection) throws RPCException, InterruptedException, StreamException { - getActiveVessel().getAutoPilot().setReferenceFrame(orbitalRefVessel); - getActiveVessel().getAutoPilot().setTargetDirection(targetDirection.toTriplet()); - getActiveVessel().getAutoPilot().setTargetRoll(90); - getActiveVessel().getAutoPilot().engage(); - errorStream = connection.addStream(ap, "getError"); + 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) -> { @@ -236,7 +231,7 @@ private void pointToTarget(Vector targetDirection) } }); errorStream.start(); - getActiveVessel().getAutoPilot().disengage(); + vessel.getActiveVessel().getAutoPilot().disengage(); control.setSAS(true); control.setSASMode(SASMode.STABILITY_ASSIST); } @@ -403,8 +398,8 @@ private void cleanup() { distLineXAxis.remove(); distLineYAxis.remove(); distLineZAxis.remove(); - ap.disengage(); - throttle(0); + 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 faab03c..de94552 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -1,7 +1,6 @@ package com.pesterenan.controllers; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; @@ -40,6 +39,7 @@ private enum MODE { private boolean hoveringMode, hoverAfterApproximation, landingMode; private MODE currentMode; private float maxTWR; + private final Map commands; private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling, wasAirborne = false; private int isOrientedCallbackTag, @@ -49,13 +49,10 @@ private enum MODE { private Stream apErrorStream; private Stream utStream; - public LandingController( - ConnectionManager connectionManager, - VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); + public LandingController(ActiveVessel vessel, Map commands) { + super(vessel); this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); this.initializeParameters(); } @@ -88,16 +85,16 @@ public void run() { private void initializeParameters() { maxTWR = Float.parseFloat(commands.get(Module.MAX_TWR.get())); hoverAltitude = Double.parseDouble(commands.get(Module.HOVER_ALTITUDE.get())); - altitudeCtrl = new ControlePID(spaceCenter, sleepTime); - velocityCtrl = new ControlePID(spaceCenter, sleepTime); + altitudeCtrl = new ControlePID(vessel.spaceCenter, sleepTime); + velocityCtrl = new ControlePID(vessel.spaceCenter, sleepTime); altitudeCtrl.setOutput(0, 1); velocityCtrl.setOutput(0, 1); } private void hoverArea() throws RPCException, StreamException, InterruptedException { hoveringMode = true; - ap.engage(); - tuneAutoPilot(); + vessel.ap.engage(); + vessel.tuneAutoPilot(); setupCallbacks(); } @@ -109,18 +106,19 @@ private void autoLanding() throws RPCException, StreamException, InterruptedExce } altitudeCtrl.reset(); velocityCtrl.reset(); - setCurrentStatus(Bundle.getString("status_starting_landing_at") + " " + currentBody.getName()); + setCurrentStatus( + Bundle.getString("status_starting_landing_at") + " " + vessel.currentBody.getName()); currentMode = MODE.DEORBITING; - ap.engage(); - tuneAutoPilot(); - getActiveVessel().getControl().setBrakes(true); + vessel.ap.engage(); + vessel.tuneAutoPilot(); + vessel.getActiveVessel().getControl().setBrakes(true); setCurrentStatus(Bundle.getString("status_starting_landing")); setupCallbacks(); } private void setupCallbacks() throws RPCException, StreamException { // Callback for ship orientation - apErrorStream = connection.addStream(ap, "getError"); + apErrorStream = vessel.connection.addStream(vessel.ap, "getError"); isOrientedCallbackTag = apErrorStream.addCallback( (error) -> { @@ -132,7 +130,7 @@ private void setupCallbacks() throws RPCException, StreamException { // Callback for de-orbit burn completion isDeorbitBurnDoneCallbackTag = - periapsis.addCallback( + vessel.periapsis.addCallback( p -> { try { if (isDeorbitBurnDone) return; // Stop if flag is already set @@ -141,10 +139,10 @@ private void setupCallbacks() throws RPCException, StreamException { // During de-orbit, actively control throttle if (p <= targetPeriapsis) { isDeorbitBurnDone = true; - throttle(0.0f); - periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); + vessel.throttle(0.0f); + vessel.periapsis.removeCallback(isDeorbitBurnDoneCallbackTag); } else { - throttle(altitudeCtrl.calculate(targetPeriapsis, p)); + vessel.throttle(altitudeCtrl.calculate(targetPeriapsis, p)); } } } catch (RPCException e) { @@ -154,15 +152,15 @@ private void setupCallbacks() throws RPCException, StreamException { // Callback for when the ship starts falling back to the ground isFallingCallbackTag = - verticalVelocity.addCallback( + vessel.verticalVelocity.addCallback( (vv) -> { if (vv <= -1) { isFalling = true; - verticalVelocity.removeCallback(isFallingCallbackTag); + vessel.verticalVelocity.removeCallback(isFallingCallbackTag); } }); - utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = utStream.addCallback( (ut) -> { @@ -170,7 +168,8 @@ private void setupCallbacks() throws RPCException, StreamException { if (landingMode) { executeLandingStep(); } else if (hoveringMode) { - altitudeErrorPercentage = surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; + altitudeErrorPercentage = + vessel.surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; if (altitudeErrorPercentage > HUNDRED_PERCENT * 1.05) { currentMode = MODE.GOING_DOWN; } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.95) { @@ -189,9 +188,9 @@ private void setupCallbacks() throws RPCException, StreamException { // Start all streams apErrorStream.start(); - apoapsis.start(); - periapsis.start(); - verticalVelocity.start(); + vessel.apoapsis.start(); + vessel.periapsis.start(); + vessel.verticalVelocity.start(); utStream.start(); } @@ -200,14 +199,16 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup // Change vessel behavior depending on which mode is active switch (currentMode) { case DEORBITING: - if (getActiveVessel().getSituation().equals(VesselSituation.ORBITING) - || getActiveVessel().getSituation().equals(VesselSituation.SUB_ORBITAL)) { + if (vessel.getActiveVessel().getSituation().equals(VesselSituation.ORBITING) + || vessel.getActiveVessel().getSituation().equals(VesselSituation.SUB_ORBITAL)) { setCurrentStatus(Bundle.getString("status_going_suborbital")); - getActiveVessel().getControl().setRCS(true); - throttle(0.0f); - targetPeriapsis = currentBody.getAtmosphereDepth(); + vessel.getActiveVessel().getControl().setRCS(true); + vessel.throttle(0.0f); + targetPeriapsis = vessel.currentBody.getAtmosphereDepth(); targetPeriapsis = - targetPeriapsis > 0 ? targetPeriapsis / 2 : -currentBody.getEquatorialRadius() / 2; + targetPeriapsis > 0 + ? targetPeriapsis / 2 + : -vessel.currentBody.getEquatorialRadius() / 2; currentMode = MODE.ORIENTING_DEORBIT; } else { currentMode = MODE.APPROACHING; @@ -223,8 +224,8 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup case EXECUTING_DEORBIT_BURN: setCurrentStatus(Bundle.getString("status_lowering_periapsis")); if (isDeorbitBurnDone) { - getActiveVessel().getControl().setRCS(false); - throttle(0.0f); + vessel.getActiveVessel().getControl().setRCS(false); + vessel.throttle(0.0f); currentMode = MODE.WAITING; } break; @@ -233,7 +234,7 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup currentMode = MODE.APPROACHING; } else { setCurrentStatus(Bundle.getString("status_waiting_for_landing")); - throttle(0.0f); + vessel.throttle(0.0f); } break; case APPROACHING: @@ -241,7 +242,7 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup velocityCtrl.setOutput(0, 1); double currentVelocity = calculateCurrentVelocityMagnitude(); double zeroVelocity = calculateZeroVelocityMagnitude(); - double landingDistanceThreshold = Math.max(hoverAltitude, getMaxAcel(maxTWR) * 5); + double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 5); double threshold = Utilities.clamp( ((currentVelocity + zeroVelocity) - landingDistanceThreshold) @@ -251,12 +252,13 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup double altPID = altitudeCtrl.calculate(currentVelocity, zeroVelocity); double velPID = velocityCtrl.calculate( - verticalVelocity.get(), (-Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20))); - throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); + vessel.verticalVelocity.get(), + (-Utilities.clamp(vessel.surfaceAltitude.get() * 0.1, 3, 20))); + vessel.throttle(Utilities.linearInterpolation(velPID, altPID, threshold)); navigation.aimForLanding(); - if (threshold < 0.15 || surfaceAltitude.get() < landingDistanceThreshold) { + if (threshold < 0.15 || vessel.surfaceAltitude.get() < landingDistanceThreshold) { hoverAltitude = landingDistanceThreshold; - getActiveVessel().getControl().setGear(true); + vessel.getActiveVessel().getControl().setGear(true); if (hoverAfterApproximation) { landingMode = false; hoverArea(); @@ -269,9 +271,9 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup case LANDING: if (hasTheVesselLanded()) break; controlThrottleByMatchingVerticalVelocity( - horizontalVelocity.get() > 4 + vessel.horizontalVelocity.get() > 4 ? 0 - : -Utilities.clamp(surfaceAltitude.get() * 0.1, 3, 20)); + : -Utilities.clamp(vessel.surfaceAltitude.get() * 0.1, 3, 20)); navigation.aimForLanding(); setCurrentStatus("Pousando..."); break; @@ -290,9 +292,9 @@ private void executeHoverStep() throws RPCException, StreamException, Interrupte case GOING_UP: altitudeCtrl.setOutput(-0.5, 0.5); velocityCtrl.setOutput(-0.5, 0.5); - throttle( + vessel.throttle( altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) - + velocityCtrl.calculate(verticalVelocity.get(), MAX_VELOCITY)); + + velocityCtrl.calculate(vessel.verticalVelocity.get(), MAX_VELOCITY)); navigation.aimAtRadialOut(); setCurrentStatus("Subindo altitude..."); break; @@ -304,9 +306,9 @@ private void executeHoverStep() throws RPCException, StreamException, Interrupte case HOVERING: altitudeCtrl.setOutput(-0.5, 0.5); velocityCtrl.setOutput(-0.5, 0.5); - throttle( + vessel.throttle( altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) - + velocityCtrl.calculate(verticalVelocity.get(), 0)); + + velocityCtrl.calculate(vessel.verticalVelocity.get(), 0)); navigation.aimAtRadialOut(); setCurrentStatus("Sobrevoando area..."); break; @@ -316,15 +318,17 @@ private void executeHoverStep() throws RPCException, StreamException, Interrupte } private void adjustLandingPID() throws RPCException, StreamException { - double maxAccel = getMaxAcel(maxTWR); - double currentTWR = Math.min(getTWR(), maxTWR); + 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 (usando o método que já temos) double trajectoryLength = calculateCurrentVelocityMagnitude(); // 2. Calcula a velocidade total (vetorial) double totalVelocity = - Math.sqrt(Math.pow(verticalVelocity.get(), 2) + Math.pow(horizontalVelocity.get(), 2)); + Math.sqrt( + Math.pow(vessel.verticalVelocity.get(), 2) + + Math.pow(vessel.horizontalVelocity.get(), 2)); // 3. Calcula o tempo de impacto baseado na TRAJETÓRIA double timeToImpact = 10.0; // Default seguro @@ -347,7 +351,7 @@ private void adjustLandingPID() throws RPCException, StreamException { } private void adjustHoverPID() throws RPCException, StreamException { - double currentTWR = Math.min(getTWR(), maxTWR); + double currentTWR = Math.min(vessel.getTWR(), maxTWR); altitudeCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); velocityCtrl.setPIDValues(currentTWR * velP, currentTWR * velI, currentTWR * velD); } @@ -355,23 +359,24 @@ private void adjustHoverPID() throws RPCException, StreamException { private void controlThrottleByMatchingVerticalVelocity(double velocityToMatch) throws RPCException, StreamException { velocityCtrl.setOutput(0, 1); - throttle( - velocityCtrl.calculate(verticalVelocity.get(), velocityToMatch + horizontalVelocity.get())); + vessel.throttle( + velocityCtrl.calculate( + vessel.verticalVelocity.get(), velocityToMatch + vessel.horizontalVelocity.get())); } private boolean hasTheVesselLanded() throws RPCException { - VesselSituation situation = getActiveVessel().getSituation(); + VesselSituation situation = vessel.getActiveVessel().getSituation(); if (wasAirborne && (situation.equals(VesselSituation.LANDED) || situation.equals(VesselSituation.SPLASHED))) { setCurrentStatus(Bundle.getString("status_landed")); hoveringMode = false; landingMode = false; - throttle(0.0f); - getActiveVessel().getControl().setSAS(true); - getActiveVessel().getControl().setRCS(true); - getActiveVessel().getControl().setBrakes(false); - ap.disengage(); + 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 @@ -384,15 +389,15 @@ private boolean hasTheVesselLanded() throws RPCException { } private double calculateCurrentVelocityMagnitude() throws RPCException, StreamException { - double timeToGround = surfaceAltitude.get() / verticalVelocity.get(); - double horizontalDistance = horizontalVelocity.get() * timeToGround; - return calculateEllipticTrajectory(horizontalDistance, surfaceAltitude.get()); + double timeToGround = vessel.surfaceAltitude.get() / vessel.verticalVelocity.get(); + double horizontalDistance = vessel.horizontalVelocity.get() * timeToGround; + return calculateEllipticTrajectory(horizontalDistance, vessel.surfaceAltitude.get()); } private double calculateZeroVelocityMagnitude() throws RPCException, StreamException { double zeroVelocityDistance = - calculateEllipticTrajectory(horizontalVelocity.get(), verticalVelocity.get()); - double zeroVelocityBurnTime = zeroVelocityDistance / getMaxAcel(maxTWR); + calculateEllipticTrajectory(vessel.horizontalVelocity.get(), vessel.verticalVelocity.get()); + double zeroVelocityBurnTime = zeroVelocityDistance / vessel.getMaxAcel(maxTWR); return zeroVelocityDistance * zeroVelocityBurnTime; } @@ -406,8 +411,8 @@ private void cleanup() { try { landingMode = false; hoveringMode = false; - if (ap != null) { - ap.disengage(); + if (vessel.ap != null) { + vessel.ap.disengage(); } if (apErrorStream != null) { apErrorStream.remove(); @@ -415,7 +420,7 @@ private void cleanup() { if (utStream != null) { utStream.remove(); } - throttle(0); + 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 a2d181b..fc65387 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -1,7 +1,6 @@ package com.pesterenan.controllers; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; @@ -39,14 +38,12 @@ private enum LIFTOFF_MODE { private Navigation navigation; private LIFTOFF_MODE liftoffMode; private double startCurveAlt; + private final Map commands; - public LiftoffController( - ConnectionManager connectionManager, - VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); + public LiftoffController(ActiveVessel vessel, Map commands) { + super(vessel); this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); initializeParameters(); } @@ -56,28 +53,28 @@ public void run() { isLiftoffRunning = true; // Part 1: Blocking Countdown and Launch - if (getActiveVessel().getSituation().equals(VesselSituation.PRE_LAUNCH)) { - throttleUp(getMaxThrottleForTWR(1.4), 1); + 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 (Thread.interrupted()) throw new InterruptedException(); setCurrentStatus(String.format(Bundle.getString("status_launching_in"), count)); Thread.sleep(100); } setCurrentStatus(Bundle.getString("status_liftoff")); - getActiveVessel().getControl().activateNextStage(); + vessel.getActiveVessel().getControl().activateNextStage(); } else { - throttle(1.0f); + vessel.throttle(1.0f); } // Part 2: Async Gravity Turn liftoffMode = LIFTOFF_MODE.GRAVITY_TURN; // Set initial state for async phase setupCallbacks(); // This starts the UT stream - tuneAutoPilot(); - startCurveAlt = altitude.get(); - ap.setReferenceFrame(surfaceReferenceFrame); - ap.targetPitchAndHeading(currentPitch, heading); - ap.setTargetRoll(this.roll); - ap.engage(); + vessel.tuneAutoPilot(); + startCurveAlt = vessel.altitude.get(); + vessel.ap.setReferenceFrame(vessel.surfaceReferenceFrame); + vessel.ap.targetPitchAndHeading(currentPitch, heading); + vessel.ap.setTargetRoll(this.roll); + vessel.ap.engage(); // Loop to keep thread alive while async part runs while (isLiftoffRunning) { @@ -119,21 +116,21 @@ private void initializeParameters() { 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(getConnectionManager().getSpaceCenter(), 25); + thrControl = new ControlePID(vessel.getConnectionManager().getSpaceCenter(), 25); thrControl.setOutput(0.0, 1.0); } private void setupCallbacks() throws RPCException, StreamException { apoapsisCallbackTag = - apoapsis.addCallback( + vessel.apoapsis.addCallback( (apo) -> { if (apo >= finalApoapsisAlt) { targetApoapsisReached = true; } }); - apoapsis.start(); + vessel.apoapsis.start(); - pressureStream = connection.addStream(flightParameters, "getDynamicPressure"); + pressureStream = vessel.connection.addStream(vessel.flightParameters, "getDynamicPressure"); pressureCallbackTag = pressureStream.addCallback( (pressure) -> { @@ -143,7 +140,7 @@ private void setupCallbacks() throws RPCException, StreamException { }); pressureStream.start(); - utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = utStream.addCallback( (ut) -> { @@ -178,23 +175,23 @@ private void handleLiftoff() throws RPCException, StreamException, InterruptedEx private void gravityTurn() throws RPCException, StreamException, InterruptedException { if (currentPitch > 1 && !targetApoapsisReached) { double altitudeProgress = - Utilities.remap(startCurveAlt, finalApoapsisAlt, 1, 0.01, altitude.get(), false); + Utilities.remap(startCurveAlt, finalApoapsisAlt, 1, 0.01, vessel.altitude.get(), false); currentPitch = (float) (calculateCurrentPitch(altitudeProgress)); double currentMaxTWR = calculateTWRBasedOnPressure(currentPitch); - ap.setTargetPitch(currentPitch); + vessel.ap.setTargetPitch(currentPitch); double throttleValue = Math.min( - thrControl.calculate(apoapsis.get() / finalApoapsisAlt * 1000, 1000), - getMaxThrottleForTWR(currentMaxTWR)); - throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); + thrControl.calculate(vessel.apoapsis.get() / finalApoapsisAlt * 1000, 1000), + vessel.getMaxThrottleForTWR(currentMaxTWR)); + vessel.throttle(Utilities.clamp(throttleValue, 0.05, 1.0)); if (willDecoupleStages && isCurrentStageWithoutFuel()) { - decoupleStage(); + vessel.decoupleStage(); } setCurrentStatus( String.format(Bundle.getString("status_liftoff_inclination") + " %.1f", currentPitch)); } else { - throttle(0); + vessel.throttle(0); liftoffMode = LIFTOFF_MODE.FINALIZE_ORBIT; } } @@ -202,18 +199,18 @@ private void gravityTurn() throws RPCException, StreamException, InterruptedExce private void finalizeOrbit() throws RPCException, StreamException, InterruptedException { if (!dynamicPressureLowEnough) { setCurrentStatus(Bundle.getString("status_maintaining_until_orbit")); - getActiveVessel().getControl().setRCS(true); + vessel.getActiveVessel().getControl().setRCS(true); navigation.aimAtPrograde(); - throttle(thrControl.calculate(apoapsis.get() / finalApoapsisAlt * 1000, 1000)); + vessel.throttle(thrControl.calculate(vessel.apoapsis.get() / finalApoapsisAlt * 1000, 1000)); } else { - throttle(0.0f); + vessel.throttle(0.0f); if (willDecoupleStages) { jettisonFairings(); } if (willOpenPanelsAndAntenna) { openPanelsAndAntenna(); } - apoapsis.removeCallback(apoapsisCallbackTag); + vessel.apoapsis.removeCallback(apoapsisCallbackTag); pressureStream.removeCallback(pressureCallbackTag); liftoffMode = LIFTOFF_MODE.CIRCULARIZE; } @@ -222,20 +219,20 @@ private void finalizeOrbit() throws RPCException, StreamException, InterruptedEx private void cleanup() { try { isLiftoffRunning = false; - apoapsis.removeCallback(apoapsisCallbackTag); + vessel.apoapsis.removeCallback(apoapsisCallbackTag); pressureStream.removeCallback(pressureCallbackTag); utStream.removeCallback(utCallbackTag); pressureStream.remove(); utStream.remove(); - ap.disengage(); - throttle(0); + vessel.ap.disengage(); + vessel.throttle(0); } catch (RPCException | NullPointerException e) { // ignore } } private double calculateTWRBasedOnPressure(float currentPitch) throws RPCException { - float currentPressure = flightParameters.getDynamicPressure(); + float currentPressure = vessel.flightParameters.getDynamicPressure(); if (currentPressure <= 10) { return Utilities.remap(90.0, 0.0, maxTWR, 5.0, currentPitch, true); } @@ -248,11 +245,11 @@ private void circularizeOrbitOnApoapsis() { 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)); - getVesselManager().startModule(commands); + vessel.getVesselManager().startModule(commands); } private void jettisonFairings() throws RPCException, InterruptedException { - List fairings = getActiveVessel().getParts().getFairings(); + List fairings = vessel.getActiveVessel().getParts().getFairings(); if (fairings.size() > 0) { setCurrentStatus(Bundle.getString("status_jettisoning_shields")); for (Fairing f : fairings) { @@ -266,9 +263,9 @@ private void jettisonFairings() throws RPCException, InterruptedException { } private void openPanelsAndAntenna() throws RPCException, InterruptedException { - getActiveVessel().getControl().setSolarPanels(true); - getActiveVessel().getControl().setRadiators(true); - getActiveVessel().getControl().setAntennas(true); + vessel.getActiveVessel().getControl().setSolarPanels(true); + vessel.getActiveVessel().getControl().setRadiators(true); + vessel.getActiveVessel().getControl().setAntennas(true); } private double calculateCurrentPitch(double currentAltitude) { @@ -288,8 +285,8 @@ private double calculateCurrentPitch(double currentAltitude) { } private boolean isCurrentStageWithoutFuel() throws RPCException { - for (Engine engine : getActiveVessel().getParts().getEngines()) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() + for (Engine engine : vessel.getActiveVessel().getParts().getEngines()) { + if (engine.getPart().getStage() == vessel.getActiveVessel().getControl().getCurrentStage() && !engine.getHasFuel()) { return true; } diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 237ee56..25b74d4 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -1,7 +1,6 @@ package com.pesterenan.controllers; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.Attributes; import com.pesterenan.utils.ControlePID; @@ -70,14 +69,12 @@ private static class RendezvousState { private Stream> remainingBurnStream; private double lastBurnDv = Double.MAX_VALUE; + private final Map commands; - public ManeuverController( - ConnectionManager connectionManager, - VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); + public ManeuverController(ActiveVessel vessel, Map commands) { + super(vessel); this.commands = commands; - this.navigation = new Navigation(connectionManager, getActiveVessel()); + this.navigation = new Navigation(vessel.getConnectionManager(), vessel.getActiveVessel()); initializeParameters(); } @@ -98,12 +95,12 @@ public void run() { public void calculateManeuver() { try { - tuneAutoPilot(); + vessel.tuneAutoPilot(); if (commands.get(Module.FUNCTION.get()).equals(Module.EXECUTE.get())) { return; } - if (getActiveVessel().getSituation() == VesselSituation.LANDED - || getActiveVessel().getSituation() == VesselSituation.SPLASHED) { + if (vessel.getActiveVessel().getSituation() == VesselSituation.LANDED + || vessel.getActiveVessel().getSituation() == VesselSituation.SPLASHED) { throw new InterruptedException(); } if (commands.get(Module.FUNCTION.get()).equals(Module.ADJUST.get())) { @@ -116,21 +113,21 @@ public void calculateManeuver() { } if (commands.get(Module.FUNCTION.get()).equals(Module.LOW_ORBIT.get())) { biEllipticTransferToOrbit( - lowOrbitAltitude, getActiveVessel().getOrbit().getTimeToPeriapsis()); + lowOrbitAltitude, vessel.getActiveVessel().getOrbit().getTimeToPeriapsis()); return; } - double gravParameter = currentBody.getGravitationalParameter(); + double gravParameter = vessel.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(); + startingAltitutde = vessel.getActiveVessel().getOrbit().getApoapsis(); + timeUntilAltitude = vessel.getActiveVessel().getOrbit().getTimeToApoapsis(); } if (commands.get(Module.FUNCTION.get()).equals(Module.PERIAPSIS.get())) { - startingAltitutde = getActiveVessel().getOrbit().getPeriapsis(); - timeUntilAltitude = getActiveVessel().getOrbit().getTimeToPeriapsis(); + startingAltitutde = vessel.getActiveVessel().getOrbit().getPeriapsis(); + timeUntilAltitude = vessel.getActiveVessel().getOrbit().getTimeToPeriapsis(); } - double semiMajorAxis = getActiveVessel().getOrbit().getSemiMajorAxis(); + double semiMajorAxis = vessel.getActiveVessel().getOrbit().getSemiMajorAxis(); double currentOrbitalVelocity = Math.sqrt(gravParameter * ((2.0 / startingAltitutde) - (1.0 / semiMajorAxis))); double targetOrbitalVelocity = @@ -145,7 +142,7 @@ public void calculateManeuver() { public void executeNextManeuver() throws InterruptedException { try { - Node maneuverNode = getActiveVessel().getControl().getNodes().get(0); + Node maneuverNode = vessel.getActiveVessel().getControl().getNodes().get(0); double burnTime = calculateBurnTime(maneuverNode); executeBurn(maneuverNode, burnTime); } catch (UnsupportedOperationException e) { @@ -167,18 +164,18 @@ public void executeNextManeuver() throws InterruptedException { public void orientToManeuverNode(Node maneuverNode) throws RPCException, StreamException, InterruptedException { setCurrentStatus(Bundle.getString("status_orienting_ship")); - ap.engage(); - ap.setReferenceFrame(maneuverNode.getReferenceFrame()); + vessel.ap.engage(); + vessel.ap.setReferenceFrame(maneuverNode.getReferenceFrame()); // --- STAGE 1: ORIENT TO MANEUVER (PITCH/HEADING) --- try { setCurrentStatus(Bundle.getString("status_orienting_to_maneuver")); - ap.setTargetDirection(new Triplet<>(0.0, 1.0, 0.0)); // Prograde in node's frame - ap.setTargetRoll(Float.NaN); // Disable roll control + vessel.ap.setTargetDirection(new Triplet<>(0.0, 1.0, 0.0)); // Prograde in node's frame + vessel.ap.setTargetRoll(Float.NaN); // Disable roll control final CountDownLatch directionLatch = new CountDownLatch(1); - this.headingErrorStream = connection.addStream(ap, "getHeadingError"); - this.pitchErrorStream = connection.addStream(ap, "getPitchError"); + this.headingErrorStream = vessel.connection.addStream(vessel.ap, "getHeadingError"); + this.pitchErrorStream = vessel.connection.addStream(vessel.ap, "getPitchError"); final Stream finalHeadingErrorStream = this.headingErrorStream; final Stream finalPitchErrorStream = this.pitchErrorStream; @@ -217,9 +214,9 @@ public void orientToManeuverNode(Node maneuverNode) // --- STAGE 2: STABILIZE ROLL --- try { setCurrentStatus(Bundle.getString("status_stabilizing_roll")); - ap.setTargetRoll(0.0f); + vessel.ap.setTargetRoll(0.0f); final CountDownLatch rollLatch = new CountDownLatch(1); - this.rollErrorStream = connection.addStream(ap, "getRollError"); + this.rollErrorStream = vessel.connection.addStream(vessel.ap, "getRollError"); final Stream finalRollErrorStream = this.rollErrorStream; final int[] rollCallbackTag = new int[1]; rollCallbackTag[0] = @@ -248,9 +245,9 @@ public void orientToManeuverNode(Node maneuverNode) public double calculateBurnTime(Node maneuverNode) { try { - List engines = getActiveVessel().getParts().getEngines(); + List engines = vessel.getActiveVessel().getParts().getEngines(); for (Engine engine : engines) { - if (engine.getPart().getStage() == getActiveVessel().getControl().getCurrentStage() + if (engine.getPart().getStage() == vessel.getActiveVessel().getControl().getCurrentStage() && !engine.getActive()) { engine.setActive(true); } @@ -260,9 +257,9 @@ public double calculateBurnTime(Node maneuverNode) { } double burnDuration = 0; try { - double thrust = getActiveVessel().getAvailableThrust(); - double isp = getActiveVessel().getSpecificImpulse() * CONST_GRAV; - double totalMass = getActiveVessel().getMass(); + 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; @@ -281,7 +278,7 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt // 2. WARP AND COUNTDOWN // Wait until it's time to burn final CountDownLatch timeToBurnLatch = new CountDownLatch(1); - timeToNodeStream = connection.addStream(maneuverNode, "getTimeTo"); + timeToNodeStream = vessel.connection.addStream(maneuverNode, "getTimeTo"); timeToNodeStream.addCallback( (time) -> { // Countdown for the last 5 seconds of warp @@ -307,9 +304,13 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt double burnStartTime = maneuverNode.getTimeTo() - (burnDuration / 2.0) - 30; if (burnStartTime > 0) { setCurrentStatus(Bundle.getString("status_maneuver_warp")); - getConnectionManager() + vessel + .getConnectionManager() .getSpaceCenter() - .warpTo((getConnectionManager().getSpaceCenter().getUT() + burnStartTime), 100000, 4); + .warpTo( + (vessel.getConnectionManager().getSpaceCenter().getUT() + burnStartTime), + 100000, + 4); } // 2. ORIENT (AFTER WARP) @@ -324,7 +325,7 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt // 4. EXECUTE THE BURN final CountDownLatch burnCompleteLatch = new CountDownLatch(1); remainingBurnStream = - connection.addStream( + vessel.connection.addStream( maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); remainingBurnStream.addCallback( (burn) -> { @@ -334,7 +335,7 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt // 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."); - throttle(0); + vessel.throttle(0); burnCompleteLatch.countDown(); return; } @@ -345,7 +346,7 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt return; } navigation.aimAtManeuverNode(maneuverNode); - throttle(ctrlManeuver.calculate(0, burnDvLeft / 100.0)); + vessel.throttle(ctrlManeuver.calculate(0, burnDvLeft / 100.0)); } catch (Exception e) { e.printStackTrace(); burnCompleteLatch.countDown(); @@ -355,17 +356,17 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt setCurrentStatus(Bundle.getString("status_maneuver_executing")); burnCompleteLatch.await(); - throttle(0.0f); + vessel.throttle(0.0f); remainingBurnStream.remove(); if (fineAdjustment) { adjustManeuverWithRCS(maneuverNode); } - ap.setReferenceFrame(surfaceReferenceFrame); - ap.disengage(); - getActiveVessel().getControl().setSAS(true); - getActiveVessel().getControl().setRCS(false); + 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) { @@ -377,14 +378,14 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt } private void initializeParameters() { - ctrlRCS = new ControlePID(getConnectionManager().getSpaceCenter(), 25); - ctrlManeuver = new ControlePID(getConnectionManager().getSpaceCenter(), 25); + ctrlRCS = new ControlePID(vessel.getConnectionManager().getSpaceCenter(), 25); + ctrlManeuver = new ControlePID(vessel.getConnectionManager().getSpaceCenter(), 25); 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(currentBody.getName()); + lowOrbitAltitude = new Attributes().getLowOrbitAltitude(vessel.currentBody.getName()); } catch (RPCException e) { } } @@ -392,16 +393,16 @@ private void initializeParameters() { private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart) { double[] totalDv = {0, 0, 0}; try { - Orbit currentOrbit = getActiveVessel().getOrbit(); + Orbit currentOrbit = vessel.getActiveVessel().getOrbit(); double startingRadius = currentOrbit.getApoapsis(); - double gravParameter = currentBody.getGravitationalParameter(); + double gravParameter = vessel.currentBody.getGravitationalParameter(); double deltaV1 = Math.sqrt(2 * gravParameter / startingRadius) - Math.sqrt(gravParameter / startingRadius); - double intermediateRadius = currentBody.getEquatorialRadius() + targetAltitude; + double intermediateRadius = vessel.currentBody.getEquatorialRadius() + targetAltitude; double deltaV2 = Math.sqrt(gravParameter / intermediateRadius) - Math.sqrt(2 * gravParameter / intermediateRadius); - double targetRadius = currentBody.getEquatorialRadius() + targetAltitude; + double targetRadius = vessel.currentBody.getEquatorialRadius() + targetAltitude; double deltaV3 = Math.sqrt(2 * gravParameter / intermediateRadius) - Math.sqrt(gravParameter / intermediateRadius); @@ -417,14 +418,14 @@ private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { Stream utStream = null; try { - Vessel vessel = getActiveVessel(); - Orbit vesselOrbit = getActiveVessel().getOrbit(); + Vessel vesselObj = this.vessel.getActiveVessel(); + Orbit vesselOrbit = this.vessel.getActiveVessel().getOrbit(); Orbit targetVesselOrbit = - getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); - if (vessel.getControl().getNodes().isEmpty()) { + this.vessel.getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + if (vesselObj.getControl().getNodes().isEmpty()) { MainGui.newInstance().getCreateManeuverPanel().createManeuver(); } - java.util.List currentManeuvers = vessel.getControl().getNodes(); + java.util.List currentManeuvers = vesselObj.getControl().getNodes(); Node currentManeuver = currentManeuvers.get(0); double[] incNodesUt = { vesselOrbit.uTAtTrueAnomaly(vesselOrbit.trueAnomalyAtAN(targetVesselOrbit)), @@ -438,7 +439,7 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep ctrlManeuver.setTimeSample(25); final CountDownLatch latch = new CountDownLatch(1); - utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); final Stream finalUtStream = utStream; final int[] utCallbackTag = new int[1]; utCallbackTag[0] = @@ -478,22 +479,22 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { Stream utStream = null; try { - List currentManeuvers = getActiveVessel().getControl().getNodes(); + List currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); Node lastManeuverNode; if (currentManeuvers.isEmpty()) { MainGui.newInstance().getCreateManeuverPanel().createManeuver(); - currentManeuvers = getActiveVessel().getControl().getNodes(); + currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); } else { double lastManeuverNodeUT = 60 + currentManeuvers.get(currentManeuvers.size() - 1).getUT(); MainGui.newInstance().getCreateManeuverPanel().createManeuver(lastManeuverNodeUT); - currentManeuvers = getActiveVessel().getControl().getNodes(); + currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); } lastManeuverNode = currentManeuvers.get(currentManeuvers.size() - 1); final CountDownLatch latch = new CountDownLatch(1); final RendezvousState state = new RendezvousState(lastManeuverNode); - utStream = connection.addStream(spaceCenter.getClass(), "getUT"); + utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); final Stream finalUtStream = utStream; final int[] utCallbackTag = new int[1]; utCallbackTag[0] = @@ -524,9 +525,10 @@ private void rendezvousWithTargetVessel() throws InterruptedException, RPCExcept private void updateRendezvousState( RendezvousState state, CountDownLatch latch, Stream stream, int callbackTag) throws RPCException, IOException { - Orbit targetVesselOrbit = getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); + Orbit targetVesselOrbit = + vessel.getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); ReferenceFrame currentBodyRefFrame = - getActiveVessel().getOrbit().getBody().getNonRotatingReferenceFrame(); + vessel.getActiveVessel().getOrbit().getBody().getNonRotatingReferenceFrame(); switch (state.phase) { case SETUP: @@ -604,14 +606,15 @@ private void updateRendezvousState( private Node createManeuver(double laterTime, double[] deltaV) { Node maneuverNode = null; try { - getActiveVessel() + vessel + .getActiveVessel() .getControl() .addNode( - getConnectionManager().getSpaceCenter().getUT() + laterTime, + vessel.getConnectionManager().getSpaceCenter().getUT() + laterTime, (float) deltaV[0], (float) deltaV[1], (float) deltaV[2]); - List currentNodes = getActiveVessel().getControl().getNodes(); + List currentNodes = vessel.getActiveVessel().getControl().getNodes(); maneuverNode = currentNodes.get(currentNodes.size() - 1); } catch (UnsupportedOperationException | RPCException e) { setCurrentStatus(Bundle.getString("status_maneuver_not_possible")); @@ -626,8 +629,8 @@ private void cleanup() { if (rollErrorStream != null) rollErrorStream.remove(); if (timeToNodeStream != null) timeToNodeStream.remove(); if (remainingBurnStream != null) remainingBurnStream.remove(); - if (ap != null) ap.disengage(); - throttle(0); + if (vessel.ap != null) vessel.ap.disengage(); + vessel.throttle(0); } catch (RPCException | NullPointerException e) { // ignore } @@ -636,13 +639,13 @@ private void cleanup() { private void adjustManeuverWithRCS(Node maneuverNode) throws RPCException, StreamException, InterruptedException { setCurrentStatus("Fine tuning with RCS..."); - getActiveVessel().getControl().setRCS(true); + vessel.getActiveVessel().getControl().setRCS(true); final CountDownLatch rcsLatch = new CountDownLatch(1); Stream> rcsStream = null; try { rcsStream = - connection.addStream( + vessel.connection.addStream( maneuverNode, "remainingBurnVector", maneuverNode.getReferenceFrame()); final Stream> finalRcsStream = rcsStream; final int[] rcsCallbackTag = new int[1]; @@ -658,7 +661,8 @@ private void adjustManeuverWithRCS(Node maneuverNode) return; } // Use the RCS PID controller to gently burn off remaining dV - getActiveVessel() + vessel + .getActiveVessel() .getControl() .setForward((float) ctrlRCS.calculate(-progradeDv, 0)); } catch (Exception e) { @@ -673,7 +677,7 @@ private void adjustManeuverWithRCS(Node maneuverNode) System.err.println("Timeout during RCS fine tuning."); } } finally { - getActiveVessel().getControl().setForward(0); + vessel.getActiveVessel().getControl().setForward(0); if (rcsStream != null) { rcsStream.remove(); } @@ -683,7 +687,7 @@ private void adjustManeuverWithRCS(Node maneuverNode) private boolean canFineAdjust(String string) { if ("true".equals(string)) { try { - List rcsEngines = getActiveVessel().getParts().getRCS(); + List rcsEngines = vessel.getActiveVessel().getParts().getRCS(); if (rcsEngines.size() > 0) { for (RCS rcs : rcsEngines) { if (rcs.getHasFuel()) { @@ -714,7 +718,7 @@ private double calculatePhaseAngle( double targetOrbit = endPosition.magnitude(); - double activeVesselSMA = getActiveVessel().getOrbit().getSemiMajorAxis(); + double activeVesselSMA = vessel.getActiveVessel().getOrbit().getSemiMajorAxis(); angularDifference = targetPhaseAngle + Math.PI diff --git a/src/main/java/com/pesterenan/controllers/RoverController.java b/src/main/java/com/pesterenan/controllers/RoverController.java index f07ac55..374a8fd 100644 --- a/src/main/java/com/pesterenan/controllers/RoverController.java +++ b/src/main/java/com/pesterenan/controllers/RoverController.java @@ -1,7 +1,6 @@ package com.pesterenan.controllers; -import com.pesterenan.model.ConnectionManager; -import com.pesterenan.model.VesselManager; +import com.pesterenan.model.ActiveVessel; import com.pesterenan.resources.Bundle; import com.pesterenan.utils.ControlePID; import com.pesterenan.utils.Module; @@ -32,7 +31,7 @@ public class RoverController extends Controller { private Vector targetPoint = new Vector(); private Vector roverDirection = new Vector(); private MODE currentMode; - private ConnectionManager connectionManager; + private final Map commands; private volatile double currentDistanceToTarget = 0; private volatile float currentChargePercentage = 100; @@ -40,12 +39,8 @@ public class RoverController extends Controller { private Stream> positionStream; private Stream chargeAmountStream; - public RoverController( - ConnectionManager connectionManager, - VesselManager vesselManager, - Map commands) { - super(connectionManager, vesselManager); - this.connectionManager = connectionManager; + public RoverController(ActiveVessel vessel, Map commands) { + super(vessel); this.commands = commands; initializeParameters(); } @@ -53,9 +48,9 @@ public RoverController( 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(getConnectionManager(), getVesselManager()); + 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); isAutoRoverRunning = true; @@ -80,7 +75,9 @@ public void run() { } private void setupCallbacks() throws RPCException, IOException, StreamException { - positionStream = connection.addStream(getActiveVessel(), "position", orbitalReferenceFrame); + positionStream = + vessel.connection.addStream( + vessel.getActiveVessel(), "position", vessel.orbitalReferenceFrame); distanceCallbackTag = positionStream.addCallback( (pos) -> { @@ -89,12 +86,13 @@ private void setupCallbacks() throws RPCException, IOException, StreamException positionStream.start(); chargeAmountStream = - connection.addStream(getActiveVessel().getResources(), "amount", "ElectricCharge"); + vessel.connection.addStream( + vessel.getActiveVessel().getResources(), "amount", "ElectricCharge"); chargeCallbackTag = chargeAmountStream.addCallback( (amount) -> { try { - float totalCharge = getActiveVessel().getResources().max("ElectricCharge"); + float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); currentChargePercentage = (float) Math.ceil(amount * 100 / totalCharge); } catch (RPCException e) { } @@ -112,10 +110,11 @@ private void setTarget() { if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.TARGET_VESSEL.get())) { Vector targetVesselPosition = new Vector( - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() .getTargetVessel() - .position(orbitalReferenceFrame)); + .position(vessel.orbitalReferenceFrame)); setCurrentStatus("Calculando rota até o alvo..."); pathFinding.buildPathToTarget(targetVesselPosition); } @@ -163,7 +162,7 @@ private void driveRoverToTarget() { private void cleanup() { try { - getActiveVessel().getControl().setBrakes(true); + vessel.getActiveVessel().getControl().setBrakes(true); pathFinding.removeDrawnPath(); isAutoRoverRunning = false; if (positionStream != null) { @@ -181,7 +180,7 @@ private void cleanup() { private void setNextPointInPath() throws RPCException, IOException, InterruptedException { pathFinding.removePathsCurrentPoint(); - getActiveVessel().getControl().setBrakes(true); + vessel.getActiveVessel().getControl().setBrakes(true); if (pathFinding.isPathToTargetEmpty()) { if (commands.get(Module.ROVER_TARGET_TYPE.get()).equals(Module.MAP_MARKER.get())) { pathFinding.removeWaypointFromList(); @@ -207,19 +206,19 @@ private boolean needToChargeBatteries() { private void rechargeRover() throws RPCException, StreamException, InterruptedException { - float totalCharge = getActiveVessel().getResources().max("ElectricCharge"); - float currentCharge = getActiveVessel().getResources().amount("ElectricCharge"); + float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); + float currentCharge = vessel.getActiveVessel().getResources().amount("ElectricCharge"); setRoverThrottle(0); - getActiveVessel().getControl().setLights(false); - getActiveVessel().getControl().setBrakes(true); + vessel.getActiveVessel().getControl().setLights(false); + vessel.getActiveVessel().getControl().setBrakes(true); - if (horizontalVelocity.get() < 1 && getActiveVessel().getControl().getBrakes()) { + if (vessel.horizontalVelocity.get() < 1 && vessel.getActiveVessel().getControl().getBrakes()) { Thread.sleep(1000); double chargeTime; double totalEnergyFlow = 0; List solarPanels = - getActiveVessel().getParts().getSolarPanels().stream() + vessel.getActiveVessel().getParts().getSolarPanels().stream() .filter(this::isSolarPanelNotBroken) .collect(Collectors.toList()); @@ -231,10 +230,11 @@ private void rechargeRover() throws RPCException, StreamException, InterruptedEx if (chargeTime < 1 || chargeTime > 21600) { chargeTime = 3600; } - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() - .warpTo((connectionManager.getSpaceCenter().getUT() + chargeTime), 10000, 4); - getActiveVessel().getControl().setLights(true); + .warpTo((vessel.getConnectionManager().getSpaceCenter().getUT() + chargeTime), 10000, 4); + vessel.getActiveVessel().getControl().setLights(true); } } @@ -242,26 +242,29 @@ private void driveRover() throws RPCException, IOException, StreamException { Vector targetDirection = posSurfToRover(posOrbToSurf(targetPoint)).normalize(); Vector radarSourcePosition = posRoverToSurf( - new Vector(getActiveVessel().position(roverReferenceFrame)) + 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), surfaceReferenceFrame, 30); + 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(getActiveVessel().boundingBox(roverReferenceFrame))) + .sum(directionFromRadar(vessel.getActiveVessel().boundingBox(roverReferenceFrame))) .normalize()) .heading(); double deltaAngle = Math.abs(targetAndRadarAngle - roverAngle); - getActiveVessel().getControl().setSAS(deltaAngle < 1); + vessel.getActiveVessel().getControl().setSAS(deltaAngle < 1); // Control Rover Throttle - setRoverThrottle(acelCtrl.calculate(horizontalVelocity.get() / maxSpeed * 50, 50)); + setRoverThrottle(acelCtrl.calculate(vessel.horizontalVelocity.get() / maxSpeed * 50, 50)); // Control Rover Steering if (deltaAngle > 1) { setRoverSteering(sterringCtrl.calculate(roverAngle / (targetAndRadarAngle) * 100, 100)); @@ -340,49 +343,60 @@ private Vector calculateRaycastDirection(Vector point, Vector direction, double throws RPCException { double raycast = pathFinding.raycastDistance( - posRoverToSurf(point), transformDirection(direction), surfaceReferenceFrame, distance); + posRoverToSurf(point), + transformDirection(direction), + vessel.surfaceReferenceFrame, + distance); return direction.multiply(raycast); } private Vector transformDirection(Vector vector) throws RPCException { return new Vector( - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() - .transformDirection(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); + .transformDirection( + vector.toTriplet(), roverReferenceFrame, vessel.surfaceReferenceFrame)); } private Vector posSurfToRover(Vector vector) throws RPCException { return new Vector( - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() - .transformPosition(vector.toTriplet(), surfaceReferenceFrame, roverReferenceFrame)); + .transformPosition( + vector.toTriplet(), vessel.surfaceReferenceFrame, roverReferenceFrame)); } private Vector posRoverToSurf(Vector vector) throws RPCException { return new Vector( - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() - .transformPosition(vector.toTriplet(), roverReferenceFrame, surfaceReferenceFrame)); + .transformPosition( + vector.toTriplet(), roverReferenceFrame, vessel.surfaceReferenceFrame)); } private Vector posOrbToSurf(Vector vector) throws RPCException { return new Vector( - connectionManager + vessel + .getConnectionManager() .getSpaceCenter() - .transformPosition(vector.toTriplet(), orbitalReferenceFrame, surfaceReferenceFrame)); + .transformPosition( + vector.toTriplet(), vessel.orbitalReferenceFrame, vessel.surfaceReferenceFrame)); } private void setRoverThrottle(double throttle) throws RPCException, StreamException { - if (horizontalVelocity.get() < (maxSpeed * 1.01)) { - getActiveVessel().getControl().setBrakes(false); - getActiveVessel().getControl().setWheelThrottle((float) throttle); + if (vessel.horizontalVelocity.get() < (maxSpeed * 1.01)) { + vessel.getActiveVessel().getControl().setBrakes(false); + vessel.getActiveVessel().getControl().setWheelThrottle((float) throttle); } else { - getActiveVessel().getControl().setBrakes(true); + vessel.getActiveVessel().getControl().setBrakes(true); } } private void setRoverSteering(double steering) throws RPCException { - getActiveVessel().getControl().setWheelSteering((float) steering); + vessel.getActiveVessel().getControl().setWheelSteering((float) steering); } private enum MODE { diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 8f92649..3d721af 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -26,9 +26,9 @@ public class ActiveVessel { - protected Vessel activeVessel; - protected SpaceCenter spaceCenter; - protected Connection connection; + public Vessel activeVessel; + public SpaceCenter spaceCenter; + public Connection connection; private final Map telemetryData = new ConcurrentHashMap<>(); public AutoPilot ap; public Flight flightParameters; @@ -46,10 +46,10 @@ public class ActiveVessel { public Stream missionTime; public Stream horizontalVelocity; public Map commands; - protected int currentVesselId = 0; - protected Thread controllerThread = null; - protected Controller controller; - protected long timer = 0; + 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; @@ -60,7 +60,7 @@ public ActiveVessel(ConnectionManager connectionManager, VesselManager vesselMan this.vesselManager = vesselManager; this.connection = connectionManager.getConnection(); this.spaceCenter = connectionManager.getSpaceCenter(); - initializeParameters(); + reinitialize(); } public ConnectionManager getConnectionManager() { @@ -151,24 +151,24 @@ public void startModule(Map commands) { runningModule = false; } if (currentFunction.equals(Module.LIFTOFF.get())) { - controller = new LiftoffController(this.connectionManager, this.vesselManager, commands); + controller = new LiftoffController(this, commands); runningModule = true; } if (currentFunction.equals(Module.HOVERING.get()) || currentFunction.equals(Module.LANDING.get())) { - controller = new LandingController(this.connectionManager, this.vesselManager, commands); + controller = new LandingController(this, commands); runningModule = true; } if (currentFunction.equals(Module.MANEUVER.get())) { - controller = new ManeuverController(this.connectionManager, this.vesselManager, commands); + controller = new ManeuverController(this, commands); runningModule = true; } if (currentFunction.equals(Module.ROVER.get())) { - controller = new RoverController(this.connectionManager, this.vesselManager, commands); + controller = new RoverController(this, commands); runningModule = true; } if (currentFunction.equals(Module.DOCKING.get())) { - controller = new DockingController(this.connectionManager, this.vesselManager, commands); + controller = new DockingController(this, commands); runningModule = true; } String controllerThreadName = "MP_CTRL_" + currentFunction + "_" + currentVesselId; @@ -188,20 +188,54 @@ public void cancelControl() { } public void removeStreams() { + // Interrupt any running controller thread + if (controllerThread != null && controllerThread.isAlive()) { + controllerThread.interrupt(); + runningModule = false; + } + // Close all streams safely 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(); - if (controllerThread != null && controllerThread.isAlive()) { - controllerThread.interrupt(); - } - } catch (RPCException e) { - System.err.println("ERRO: Ao remover streams da nave atual. " + e.getMessage()); + } catch (Exception e) { + /* ignore */ + } + try { + if (missionTime != null) missionTime.remove(); + } catch (Exception e) { + /* ignore */ } + // Clear the telemetry data map + telemetryData.clear(); } public Map getTelemetryData() { @@ -212,7 +246,7 @@ public boolean hasModuleRunning() { return runningModule; } - protected void decoupleStage() throws RPCException, InterruptedException { + public void decoupleStage() throws RPCException, InterruptedException { setCurrentStatus(Bundle.getString("status_separating_stage")); spaceCenter.setActiveVessel(getActiveVessel()); double currentThrottle = getActiveVessel().getControl().getThrottle(); @@ -222,7 +256,7 @@ protected void decoupleStage() throws RPCException, InterruptedException { throttleUp(currentThrottle, 1); } - protected void throttleUp(double throttleAmount, double seconds) + public void throttleUp(double throttleAmount, double seconds) throws RPCException, InterruptedException { double secondsElapsed = 0; while (secondsElapsed < seconds) { @@ -232,8 +266,11 @@ protected void throttleUp(double throttleAmount, double seconds) } } - private void initializeParameters() { - System.out.println("DEBUG: Initializing ActiveVessel parameters..."); + 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(); @@ -252,10 +289,12 @@ private void initializeParameters() { 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)); + 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)); @@ -267,10 +306,11 @@ private void initializeParameters() { 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 initializing parameters for active vessel: " + "DEBUG: CRITICAL ERROR while re-initializing parameters for active vessel: " + e.getMessage()); } } diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index 6f2ecf4..edf40ce 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -166,27 +166,22 @@ public void checkAndUpdateActiveVessel() { try { int activeVesselId = getSpaceCenter().getActiveVessel().hashCode(); if (currentVesselId != activeVesselId) { - System.out.println("DEBUG: Vessel ID mismatch. Re-initializing..."); + System.out.println("DEBUG: Active vessel changed. Re-initializing..."); if (currentVessel != null) { - Thread oldThread = currentVessel.getControllerThread(); - currentVessel.removeStreams(); // This will set flags and interrupt - if (oldThread != null) { - try { - oldThread.join(1000); // Wait for the old thread to die, with a timeout - } catch (InterruptedException e) { - System.err.println("Interrupted while waiting for old controller thread to die."); - } - } + currentVessel.reinitialize(); + } else { + currentVessel = new ActiveVessel(connectionManager, this); } - currentVessel = new ActiveVessel(connectionManager, this); currentVesselId = currentVessel.getCurrentVesselId(); MainGui.getInstance().setVesselManager(this); } } catch (RPCException e) { - System.err.println( - "ERRO: Atualização de nave interrompida. Não foi possível buscar o ID da nave atual." - + e.getMessage()); - e.printStackTrace(); + if (currentVessel != null) { + System.out.println("DEBUG: Could not get active vessel. Cleaning up."); + currentVessel.removeStreams(); + currentVessel = null; + currentVesselId = -1; + } } } diff --git a/src/main/java/com/pesterenan/utils/PathFinding.java b/src/main/java/com/pesterenan/utils/PathFinding.java index bed5c53..700125d 100644 --- a/src/main/java/com/pesterenan/utils/PathFinding.java +++ b/src/main/java/com/pesterenan/utils/PathFinding.java @@ -1,6 +1,5 @@ package com.pesterenan.utils; -import com.pesterenan.controllers.Controller; import com.pesterenan.model.ConnectionManager; import com.pesterenan.model.VesselManager; import java.io.IOException; @@ -16,7 +15,7 @@ import krpc.client.services.SpaceCenter.WaypointManager; import org.javatuples.Triplet; -public class PathFinding extends Controller { +public class PathFinding { private WaypointManager waypointManager; private String waypointName; @@ -24,18 +23,21 @@ public class PathFinding extends Controller { private List pathToTarget; private Drawing drawing; private Drawing.Polygon polygonPath; + private final ConnectionManager connectionManager; + private final VesselManager vesselManager; public PathFinding(ConnectionManager connectionManager, VesselManager vesselManager) { - super(connectionManager, vesselManager); + this.connectionManager = connectionManager; + this.vesselManager = vesselManager; initializeParameters(); } private void initializeParameters() { try { - waypointManager = getConnectionManager().getSpaceCenter().getWaypointManager(); + waypointManager = connectionManager.getSpaceCenter().getWaypointManager(); waypointsToReach = new ArrayList<>(); pathToTarget = new ArrayList<>(); - drawing = Drawing.newInstance(getConnectionManager().getConnection()); + drawing = Drawing.newInstance(connectionManager.getConnection()); } catch (RPCException ignored) { } } @@ -51,7 +53,7 @@ public void addWaypointsOnSameBody(String waypointName) throws RPCException { private boolean hasSameName(Waypoint waypoint) { try { return waypoint.getName().toLowerCase().contains(waypointName.toLowerCase()) - && waypoint.getBody().equals(currentBody); + && waypoint.getBody().equals(vesselManager.getCurrentVessel().currentBody); } catch (RPCException e) { return false; } @@ -67,11 +69,21 @@ public Vector findNearestWaypoint() throws RPCException, IOException, Interrupte try { w1Distance = Vector.distance( - new Vector(getActiveVessel().position(orbitalReferenceFrame)), + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position( + vesselManager.getCurrentVessel().orbitalReferenceFrame)), waypointPosOnSurface(w1)); w2Distance = Vector.distance( - new Vector(getActiveVessel().position(orbitalReferenceFrame)), + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position( + vesselManager.getCurrentVessel().orbitalReferenceFrame)), waypointPosOnSurface(w2)); } catch (RPCException e) { } @@ -80,23 +92,18 @@ public Vector findNearestWaypoint() throws RPCException, IOException, Interrupte .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( - currentBody.surfacePosition( - waypoint.getLatitude(), waypoint.getLongitude(), orbitalReferenceFrame)); + vesselManager + .getCurrentVessel() + .currentBody + .surfacePosition( + waypoint.getLatitude(), + waypoint.getLongitude(), + vesselManager.getCurrentVessel().orbitalReferenceFrame)); } public boolean isPathToTargetEmpty() { @@ -135,27 +142,19 @@ public void removeWaypointFromList() throws RPCException { waypointsToReach.remove(0); } - /** - * 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: + new Vector( + vesselManager + .getCurrentVessel() + .getActiveVessel() + .position(vesselManager.getCurrentVessel().surfaceReferenceFrame)) + .sum(roverHeight)); 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()) { @@ -183,7 +182,9 @@ private void drawPathToTarget(List path) throws RPCException { drawablePath.offerLast(new Triplet<>(0.0, 0.0, 0.0)); polygonPath = drawing.addPolygon( - drawablePath.stream().collect(Collectors.toList()), orbitalReferenceFrame, true); + drawablePath.stream().collect(Collectors.toList()), + vesselManager.getCurrentVessel().orbitalReferenceFrame, + true); polygonPath.setThickness(0.5f); polygonPath.setColor(new Triplet<>(1.0, 0.5, 0.0)); } @@ -197,9 +198,7 @@ public void removeDrawnPath() { 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)))); @@ -213,7 +212,7 @@ public double raycastDistance( double searchDistance) throws RPCException { return Math.min( - getConnectionManager() + connectionManager .getSpaceCenter() .raycastDistance(currentPoint.toTriplet(), targetDirection.toTriplet(), reference), searchDistance); @@ -221,23 +220,40 @@ public double raycastDistance( private Vector getPosOnSurface(Vector vector) throws RPCException { return new Vector( - currentBody.surfacePosition( - currentBody.latitudeAtPosition(vector.toTriplet(), orbitalReferenceFrame), - currentBody.longitudeAtPosition(vector.toTriplet(), orbitalReferenceFrame), - orbitalReferenceFrame)); + 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( - getConnectionManager() + connectionManager .getSpaceCenter() - .transformPosition(vector.toTriplet(), surfaceReferenceFrame, orbitalReferenceFrame)); + .transformPosition( + vector.toTriplet(), + vesselManager.getCurrentVessel().surfaceReferenceFrame, + vesselManager.getCurrentVessel().orbitalReferenceFrame)); } private Vector transformOrbToSurf(Vector vector) throws IOException, RPCException { return new Vector( - getConnectionManager() + connectionManager .getSpaceCenter() - .transformPosition(vector.toTriplet(), orbitalReferenceFrame, surfaceReferenceFrame)); + .transformPosition( + vector.toTriplet(), + vesselManager.getCurrentVessel().orbitalReferenceFrame, + vesselManager.getCurrentVessel().surfaceReferenceFrame)); } } From 3b991983ee6815bc96c521bf50dc2fb457f26a00 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Sun, 12 Oct 2025 14:29:03 -0300 Subject: [PATCH 16/25] [MP-8]: Using MissionTime Stream instead of utStream --- .../controllers/DockingController.java | 10 +--- .../controllers/LandingController.java | 42 +++++++-------- .../controllers/LiftoffController.java | 10 ++-- .../controllers/ManeuverController.java | 30 +++-------- .../com/pesterenan/model/ActiveVessel.java | 5 -- .../pesterenan/model/ConnectionManager.java | 2 +- .../com/pesterenan/model/VesselManager.java | 51 +++++++++++-------- 7 files changed, 66 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/DockingController.java b/src/main/java/com/pesterenan/controllers/DockingController.java index 894eccf..dc2c324 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -68,7 +68,6 @@ private enum DOCKING_STEPS { private boolean isDocking, isOriented = false; private final Map commands; - private Stream utStream; private Stream errorStream; private int utCallbackTag, errorCallbackTag; @@ -104,22 +103,20 @@ public void startDocking() throws RPCException, StreamException { krpc = KRPC.newInstance(vessel.getConnectionManager().getConnection()); currentPhase = DockingPhase.SETUP; - utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = - utStream.addCallback( + vessel.missionTime.addCallback( (ut) -> { try { if (isDocking) { updateDockingState(); } else { - utStream.removeCallback(utCallbackTag); + vessel.missionTime.removeCallback(utCallbackTag); } } catch (Exception e) { setCurrentStatus("Docking failed: " + e.getMessage()); cleanup(); } }); - utStream.start(); } private void initializeParameters() { @@ -388,9 +385,6 @@ private void updateLines(Vector start, Vector end) { private void cleanup() { try { isDocking = false; - if (utStream != null) { - utStream.remove(); - } if (errorStream != null) { errorStream.remove(); } diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index de94552..77a7de3 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -10,6 +10,7 @@ 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 { @@ -27,9 +28,8 @@ private enum MODE { } 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 velI = 0.000001; private static final double velD = 0.001; private ControlePID altitudeCtrl; private ControlePID velocityCtrl; @@ -47,7 +47,6 @@ private enum MODE { isFallingCallbackTag, utCallbackTag; private Stream apErrorStream; - private Stream utStream; public LandingController(ActiveVessel vessel, Map commands) { super(vessel); @@ -74,6 +73,7 @@ public void run() { } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Restore interrupted status System.err.println("Controle de Pouso finalizado via interrupção."); + setCurrentStatus(Bundle.getString("status_ready")); } catch (RPCException | StreamException e) { System.err.println("Erro no run do landingController:" + e.getMessage()); setCurrentStatus(Bundle.getString("status_data_unavailable")); @@ -85,8 +85,8 @@ public void run() { 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, sleepTime); - velocityCtrl = new ControlePID(vessel.spaceCenter, sleepTime); + altitudeCtrl = new ControlePID(vessel.spaceCenter); + velocityCtrl = new ControlePID(vessel.spaceCenter); altitudeCtrl.setOutput(0, 1); velocityCtrl.setOutput(0, 1); } @@ -154,15 +154,14 @@ private void setupCallbacks() throws RPCException, StreamException { isFallingCallbackTag = vessel.verticalVelocity.addCallback( (vv) -> { - if (vv <= -1) { + if (vv <= 0) { isFalling = true; vessel.verticalVelocity.removeCallback(isFallingCallbackTag); } }); - utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = - utStream.addCallback( + vessel.missionTime.addCallback( (ut) -> { try { if (landingMode) { @@ -179,7 +178,7 @@ private void setupCallbacks() throws RPCException, StreamException { } executeHoverStep(); } else { - utStream.removeCallback(utCallbackTag); + vessel.missionTime.removeCallback(utCallbackTag); } } catch (Exception e) { System.err.println("UT Callback error: " + e.getMessage()); @@ -191,7 +190,6 @@ private void setupCallbacks() throws RPCException, StreamException { vessel.apoapsis.start(); vessel.periapsis.start(); vessel.verticalVelocity.start(); - utStream.start(); } private void executeLandingStep() throws RPCException, StreamException, InterruptedException { @@ -238,11 +236,17 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup } 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); double currentVelocity = calculateCurrentVelocityMagnitude(); double zeroVelocity = calculateZeroVelocityMagnitude(); - double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 5); + double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 3); double threshold = Utilities.clamp( ((currentVelocity + zeroVelocity) - landingDistanceThreshold) @@ -268,10 +272,11 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup } setCurrentStatus("Se aproximando do momento do pouso..."); break; + case LANDING: if (hasTheVesselLanded()) break; controlThrottleByMatchingVerticalVelocity( - vessel.horizontalVelocity.get() > 4 + vessel.horizontalVelocity.get() > 5 ? 0 : -Utilities.clamp(vessel.surfaceAltitude.get() * 0.1, 3, 20)); navigation.aimForLanding(); @@ -288,10 +293,10 @@ private void executeHoverStep() throws RPCException, StreamException, Interrupte return; } adjustHoverPID(); + altitudeCtrl.setOutput(-0.5, 0.5); + velocityCtrl.setOutput(-0.5, 0.5); switch (currentMode) { case GOING_UP: - altitudeCtrl.setOutput(-0.5, 0.5); - velocityCtrl.setOutput(-0.5, 0.5); vessel.throttle( altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) + velocityCtrl.calculate(vessel.verticalVelocity.get(), MAX_VELOCITY)); @@ -304,11 +309,9 @@ private void executeHoverStep() throws RPCException, StreamException, Interrupte setCurrentStatus("Baixando altitude..."); break; case HOVERING: - altitudeCtrl.setOutput(-0.5, 0.5); - velocityCtrl.setOutput(-0.5, 0.5); vessel.throttle( altitudeCtrl.calculate(altitudeErrorPercentage, HUNDRED_PERCENT) - + velocityCtrl.calculate(vessel.verticalVelocity.get(), 0)); + + velocityCtrl.calculate(0, -vessel.verticalVelocity.get())); navigation.aimAtRadialOut(); setCurrentStatus("Sobrevoando area..."); break; @@ -339,7 +342,7 @@ private void adjustLandingPID() throws RPCException, StreamException { // 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.0; + double Ki = 0.0001; altitudeCtrl.setPIDValues(Kp, Ki, Kd); } else { // Para outros modos, usa um PID mais simples e estável @@ -417,9 +420,6 @@ private void cleanup() { if (apErrorStream != null) { apErrorStream.remove(); } - if (utStream != null) { - utStream.remove(); - } vessel.throttle(0); setCurrentStatus(Bundle.getString("status_ready")); } catch (RPCException | NullPointerException e) { diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index fc65387..4819f41 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -32,7 +32,6 @@ private enum LIFTOFF_MODE { isLiftoffRunning = false; private int apoapsisCallbackTag, pressureCallbackTag, utCallbackTag; private Stream pressureStream; - private Stream utStream; private boolean willDecoupleStages, willOpenPanelsAndAntenna; private String gravityCurveModel = Module.CIRCULAR.get(); private Navigation navigation; @@ -140,13 +139,12 @@ private void setupCallbacks() throws RPCException, StreamException { }); pressureStream.start(); - utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); utCallbackTag = - utStream.addCallback( + vessel.missionTime.addCallback( (ut) -> { try { if (!isLiftoffRunning) { - utStream.removeCallback(utCallbackTag); + vessel.missionTime.removeCallback(utCallbackTag); return; } handleLiftoff(); @@ -154,7 +152,6 @@ private void setupCallbacks() throws RPCException, StreamException { System.err.println("Liftoff UT Callback error: " + e.getMessage()); } }); - utStream.start(); } private void handleLiftoff() throws RPCException, StreamException, InterruptedException { @@ -221,9 +218,8 @@ private void cleanup() { isLiftoffRunning = false; vessel.apoapsis.removeCallback(apoapsisCallbackTag); pressureStream.removeCallback(pressureCallbackTag); - utStream.removeCallback(utCallbackTag); + vessel.missionTime.removeCallback(utCallbackTag); pressureStream.remove(); - utStream.remove(); vessel.ap.disengage(); vessel.throttle(0); } catch (RPCException | NullPointerException e) { diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 25b74d4..40657cf 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -416,7 +416,7 @@ private Node biEllipticTransferToOrbit(double targetAltitude, double timeToStart } private void alignPlanesWithTargetVessel() throws InterruptedException, RPCException { - Stream utStream = null; + final int[] utCallbackTag = new int[1]; try { Vessel vesselObj = this.vessel.getActiveVessel(); Orbit vesselOrbit = this.vessel.getActiveVessel().getOrbit(); @@ -439,11 +439,8 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep ctrlManeuver.setTimeSample(25); final CountDownLatch latch = new CountDownLatch(1); - utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); - final Stream finalUtStream = utStream; - final int[] utCallbackTag = new int[1]; utCallbackTag[0] = - finalUtStream.addCallback( + vessel.missionTime.addCallback( (ut) -> { try { double currentInclination = @@ -451,7 +448,6 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep currentManeuver.getOrbit().relativeInclination(targetVesselOrbit)); if (currentInclination <= 0.05) { latch.countDown(); - finalUtStream.removeCallback(utCallbackTag[0]); return; } double ctrlOutput = ctrlManeuver.calculate(currentInclination * 100, 0); @@ -462,7 +458,6 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep latch.countDown(); } }); - utStream.start(); latch.await(); // Wait until the alignment is done } catch (Exception err) { if (err instanceof InterruptedException) { @@ -470,14 +465,12 @@ private void alignPlanesWithTargetVessel() throws InterruptedException, RPCExcep } System.err.println("Error aligning planes: " + err); } finally { - if (utStream != null) { - utStream.remove(); - } + vessel.missionTime.removeCallback(utCallbackTag[0]); } } private void rendezvousWithTargetVessel() throws InterruptedException, RPCException { - Stream utStream = null; + final int[] utCallbackTag = new int[1]; try { List currentManeuvers = vessel.getActiveVessel().getControl().getNodes(); Node lastManeuverNode; @@ -494,21 +487,17 @@ private void rendezvousWithTargetVessel() throws InterruptedException, RPCExcept final CountDownLatch latch = new CountDownLatch(1); final RendezvousState state = new RendezvousState(lastManeuverNode); - utStream = vessel.connection.addStream(vessel.spaceCenter.getClass(), "getUT"); - final Stream finalUtStream = utStream; - final int[] utCallbackTag = new int[1]; utCallbackTag[0] = - finalUtStream.addCallback( + vessel.missionTime.addCallback( ut -> { try { - updateRendezvousState(state, latch, finalUtStream, utCallbackTag[0]); + updateRendezvousState(state, latch, utCallbackTag[0]); } catch (Exception e) { e.printStackTrace(); System.err.println("Error in rendezvous update: " + e); latch.countDown(); } }); - utStream.start(); latch.await(); } catch (Exception err) { if (err instanceof InterruptedException) { @@ -516,14 +505,12 @@ private void rendezvousWithTargetVessel() throws InterruptedException, RPCExcept } System.err.println("Error during rendezvous: " + err); } finally { - if (utStream != null) { - utStream.remove(); - } + vessel.missionTime.removeCallback(utCallbackTag[0]); } } private void updateRendezvousState( - RendezvousState state, CountDownLatch latch, Stream stream, int callbackTag) + RendezvousState state, CountDownLatch latch, int callbackTag) throws RPCException, IOException { Orbit targetVesselOrbit = vessel.getConnectionManager().getSpaceCenter().getTargetVessel().getOrbit(); @@ -598,7 +585,6 @@ private void updateRendezvousState( case DONE: latch.countDown(); - stream.removeCallback(callbackTag); break; } } diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index 3d721af..eaab29a 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -229,11 +229,6 @@ public void removeStreams() { } catch (Exception e) { /* ignore */ } - try { - if (missionTime != null) missionTime.remove(); - } catch (Exception e) { - /* ignore */ - } // Clear the telemetry data map telemetryData.clear(); } diff --git a/src/main/java/com/pesterenan/model/ConnectionManager.java b/src/main/java/com/pesterenan/model/ConnectionManager.java index 4c4140f..4708736 100644 --- a/src/main/java/com/pesterenan/model/ConnectionManager.java +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -90,7 +90,7 @@ private void attemptConnection() { public boolean isConnectionAlive() { try { - if (krpc == null || connection == null) return false; + if (krpc == null || connection == null) return false; krpc.getStatus(); return true; } catch (RPCException e) { diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index edf40ce..9c9491d 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -3,6 +3,7 @@ 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; @@ -27,6 +28,7 @@ public class VesselManager { private FunctionsAndTelemetryJPanel telemetryPanel; private StatusDisplay statusDisplay; private int currentVesselId = -1; + private ScheduledExecutorService telemetryMonitor; public VesselManager( final ConnectionManager connectionManager, @@ -54,9 +56,10 @@ public int getCurrentVesselId() { } public void startTelemetryLoop() { - ScheduledExecutorService scheduler = + if (telemetryMonitor != null && !telemetryMonitor.isShutdown()) return; + telemetryMonitor = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "MP_TELEMETRY_UPDATER")); - scheduler.scheduleAtFixedRate( + telemetryMonitor.scheduleAtFixedRate( () -> { if (connectionManager.isConnectionAlive()) { checkAndUpdateActiveVessel(); @@ -92,7 +95,7 @@ public ListModel getCurrentManeuvers() { } }); } catch (RPCException | NullPointerException | UnsupportedOperationException e) { - System.err.println("ERRO: Não foi possivel atualizar as manobras atuais." + e.getMessage()); + System.err.println("ERRO: Não foi possivel atualizar as manobras atuais. " + e.getClass().getSimpleName()); } return list; } @@ -162,7 +165,7 @@ public void cancelControl(ActionEvent e) { } } - public void checkAndUpdateActiveVessel() { + private void checkAndUpdateActiveVessel() { try { int activeVesselId = getSpaceCenter().getActiveVessel().hashCode(); if (currentVesselId != activeVesselId) { @@ -175,28 +178,36 @@ public void checkAndUpdateActiveVessel() { currentVesselId = currentVessel.getCurrentVesselId(); MainGui.getInstance().setVesselManager(this); } + } catch (IllegalArgumentException e) { + System.out.println("DEBUG: SpaceCenter not ready yet, skipping..."); } catch (RPCException e) { - if (currentVessel != null) { - System.out.println("DEBUG: Could not get active vessel. Cleaning up."); - currentVessel.removeStreams(); - currentVessel = null; - currentVesselId = -1; - } + 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()); - MainGui.getInstance().getCreateManeuverPanel().updatePanel(getCurrentManeuvers()); - if (currentVessel.hasModuleRunning()) { - statusDisplay.setStatusMessage(currentVessel.getCurrentStatus()); - } - } - }); - } + () -> { + 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)) { From 9e5dbe6713fa1102cef9e993aa77d514c0c701fb Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 19:43:10 -0300 Subject: [PATCH 17/25] [MP-8]: Cleaning up PID controller use --- src/main/java/com/pesterenan/Main.java | 246 ------------------ .../controllers/LiftoffController.java | 2 +- .../controllers/ManeuverController.java | 4 +- .../com/pesterenan/model/ActiveVessel.java | 123 +++++---- .../com/pesterenan/utils/ControlePID.java | 6 +- .../pesterenan/views/RunManeuverJPanel.java | 2 - 6 files changed, 71 insertions(+), 312 deletions(-) delete mode 100644 src/main/java/com/pesterenan/Main.java diff --git a/src/main/java/com/pesterenan/Main.java b/src/main/java/com/pesterenan/Main.java deleted file mode 100644 index 1a6d8bb..0000000 --- a/src/main/java/com/pesterenan/Main.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.pesterenan; - -import com.pesterenan.utils.Utilities; -import com.pesterenan.utils.Vector; -import java.io.IOException; -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/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index 4819f41..e7ce22d 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -115,7 +115,7 @@ private void initializeParameters() { 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(), 25); + thrControl = new ControlePID(vessel.getConnectionManager().getSpaceCenter()); thrControl.setOutput(0.0, 1.0); } diff --git a/src/main/java/com/pesterenan/controllers/ManeuverController.java b/src/main/java/com/pesterenan/controllers/ManeuverController.java index 40657cf..8ce85aa 100644 --- a/src/main/java/com/pesterenan/controllers/ManeuverController.java +++ b/src/main/java/com/pesterenan/controllers/ManeuverController.java @@ -378,8 +378,8 @@ public void executeBurn(Node maneuverNode, double burnDuration) throws Interrupt } private void initializeParameters() { - ctrlRCS = new ControlePID(vessel.getConnectionManager().getSpaceCenter(), 25); - ctrlManeuver = new ControlePID(vessel.getConnectionManager().getSpaceCenter(), 25); + 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); diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index eaab29a..b73f779 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -149,6 +149,11 @@ public void startModule(Map commands) { if (controllerThread != null) { controllerThread.interrupt(); runningModule = false; + try { + controllerThread.join(); // Wait for the previous thread to finish + } catch (InterruptedException e) { + e.printStackTrace(); + } } if (currentFunction.equals(Module.LIFTOFF.get())) { controller = new LiftoffController(this, commands); @@ -194,40 +199,42 @@ public void removeStreams() { runningModule = false; } // Close all streams safely - 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 */ + 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(); @@ -277,31 +284,33 @@ public void reinitialize() { flightParameters = getActiveVessel().flight(orbitalReferenceFrame); System.out.println("DEBUG: Basic parameters set. Creating streams..."); - 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"); + 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)); + 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)); - altitude.start(); - apoapsis.start(); - horizontalVelocity.start(); - periapsis.start(); - surfaceAltitude.start(); - totalMass.start(); - verticalVelocity.start(); - missionTime.start(); + 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( diff --git a/src/main/java/com/pesterenan/utils/ControlePID.java b/src/main/java/com/pesterenan/utils/ControlePID.java index 5aeb86d..5534712 100644 --- a/src/main/java/com/pesterenan/utils/ControlePID.java +++ b/src/main/java/com/pesterenan/utils/ControlePID.java @@ -11,7 +11,7 @@ public class ControlePID { private double ki = 0.001; private double kd = 0.01; private double integralTerm = 0.0; - private double previousError, previousMeasurement, lastTime = 0.0; + private double previousError, lastTime = 0.0; private double timeSample = 0.025; // 25 millisegundos private double proportionalTerm; @@ -19,9 +19,8 @@ public class ControlePID { public ControlePID() {} - public ControlePID(SpaceCenter spaceCenter, double timeSample) { + public ControlePID(SpaceCenter spaceCenter) { this.spaceCenter = spaceCenter; - setTimeSample(timeSample); } public ControlePID(double kp, double ki, double kd, double outputMin, double outputMax) { @@ -38,7 +37,6 @@ public double getTimeSample() { public void reset() { this.previousError = 0; - this.previousMeasurement = 0; this.proportionalTerm = 0; this.integralTerm = 0; this.derivativeTerm = 0; diff --git a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java index 2fb884d..0e4e1fa 100644 --- a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java @@ -7,7 +7,6 @@ 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; @@ -42,7 +41,6 @@ public class RunManeuverJPanel extends JPanel implements ActionListener, UIMetho btnAlignPlanes, btnRendezvous; private JCheckBox chkFineAdjusment; - private final ControlePID ctrlManeuver = new ControlePID(); private VesselManager vesselManager; public RunManeuverJPanel(StatusDisplay statusDisplay) { From 4631b6abf35167d0bc8e1d60eae46dc19e1c0e77 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 20:15:53 -0300 Subject: [PATCH 18/25] [MP-8]: Removing log --- .../controllers/LandingController.java | 73 ++++++++++++------- .../com/pesterenan/model/VesselManager.java | 1 - 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index 77a7de3..19e914e 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -42,6 +42,7 @@ private enum MODE { private final Map commands; private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling, wasAirborne = false; + private volatile boolean isCleaningUp = false; private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag, @@ -163,15 +164,18 @@ private void setupCallbacks() throws RPCException, StreamException { utCallbackTag = vessel.missionTime.addCallback( (ut) -> { + if (isCleaningUp) { + return; + } try { if (landingMode) { executeLandingStep(); } else if (hoveringMode) { altitudeErrorPercentage = vessel.surfaceAltitude.get() / hoverAltitude * HUNDRED_PERCENT; - if (altitudeErrorPercentage > HUNDRED_PERCENT * 1.05) { + if (altitudeErrorPercentage > HUNDRED_PERCENT * 1.1) { currentMode = MODE.GOING_DOWN; - } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.95) { + } else if (altitudeErrorPercentage < HUNDRED_PERCENT * 0.9) { currentMode = MODE.GOING_UP; } else { currentMode = MODE.HOVERING; @@ -244,24 +248,27 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup } altitudeCtrl.setOutput(0, 1); velocityCtrl.setOutput(0, 1); - double currentVelocity = calculateCurrentVelocityMagnitude(); - double zeroVelocity = calculateZeroVelocityMagnitude(); - double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 3); + + 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 = - Utilities.clamp( - ((currentVelocity + zeroVelocity) - landingDistanceThreshold) - / landingDistanceThreshold, - 0, - 1); - double altPID = altitudeCtrl.calculate(currentVelocity, zeroVelocity); + (brakingDistance > 1) + ? Utilities.clamp(surfaceAltitude / brakingDistance - 1, 0, 1) + : 0; + + 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(); - if (threshold < 0.15 || vessel.surfaceAltitude.get() < landingDistanceThreshold) { - hoverAltitude = landingDistanceThreshold; + + double landingDistanceThreshold = Math.max(hoverAltitude, vessel.getMaxAcel(maxTWR) * 3); + if (surfaceAltitude < landingDistanceThreshold) { vessel.getActiveVessel().getControl().setGear(true); if (hoverAfterApproximation) { landingMode = false; @@ -325,8 +332,20 @@ private void adjustLandingPID() throws RPCException, StreamException { double currentTWR = Math.min(vessel.getTWR(), maxTWR); if (currentMode == MODE.APPROACHING) { - // 1. Calcula a distância de trajetória restante (usando o método que já temos) - double trajectoryLength = calculateCurrentVelocityMagnitude(); + // 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( @@ -391,17 +410,18 @@ private boolean hasTheVesselLanded() throws RPCException { return false; } - private double calculateCurrentVelocityMagnitude() throws RPCException, StreamException { - double timeToGround = vessel.surfaceAltitude.get() / vessel.verticalVelocity.get(); - double horizontalDistance = vessel.horizontalVelocity.get() * timeToGround; - return calculateEllipticTrajectory(horizontalDistance, vessel.surfaceAltitude.get()); - } - - private double calculateZeroVelocityMagnitude() throws RPCException, StreamException { - double zeroVelocityDistance = - calculateEllipticTrajectory(vessel.horizontalVelocity.get(), vessel.verticalVelocity.get()); - double zeroVelocityBurnTime = zeroVelocityDistance / vessel.getMaxAcel(maxTWR); - return zeroVelocityDistance * zeroVelocityBurnTime; + 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) { @@ -411,6 +431,7 @@ private double calculateEllipticTrajectory(double a, double b) { } private void cleanup() { + isCleaningUp = true; try { landingMode = false; hoveringMode = false; diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index 9c9491d..7de8f09 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -179,7 +179,6 @@ private void checkAndUpdateActiveVessel() { MainGui.getInstance().setVesselManager(this); } } catch (IllegalArgumentException e) { - System.out.println("DEBUG: SpaceCenter not ready yet, skipping..."); } catch (RPCException e) { System.out.println("DEBUG: Could not get active vessel. Cleaning up."); clearVessel(); From 39a0d83a76abae9109c7d5c46e974486ce678cca Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 20:41:05 -0300 Subject: [PATCH 19/25] [MP-8]: Update About modal with channel info --- .../com/pesterenan/views/AboutJFrame.java | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/pesterenan/views/AboutJFrame.java b/src/main/java/com/pesterenan/views/AboutJFrame.java index 1466bf2..8c55628 100644 --- a/src/main/java/com/pesterenan/views/AboutJFrame.java +++ b/src/main/java/com/pesterenan/views/AboutJFrame.java @@ -5,18 +5,25 @@ 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 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 JLabel lblMechpeste; + private JEditorPane editorPane; private JButton btnOk; public AboutJFrame() { @@ -29,13 +36,7 @@ public AboutJFrame() { 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"); + editorPane = new JEditorPane(); // Buttons: btnOk = new JButton("OK"); @@ -44,7 +45,7 @@ public void initComponents() { @Override public void setupComponents() { // Main Panel setup: - setTitle("MechPeste - por Pesterenan"); + setTitle("Sobre"); setBounds(centerDialogOnScreen()); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setResizable(false); @@ -54,7 +55,34 @@ public void setupComponents() { // 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 -> { @@ -73,12 +101,13 @@ public void layoutComponents() { pnlMain.add(createMarginComponent(10, 10)); pnlMain.add(lblMechpeste); pnlMain.add(createMarginComponent(10, 10)); - pnlMain.add(lblAboutInfo); + pnlMain.add(editorPane); pnlMain.add(Box.createVerticalGlue()); pnlMain.add(btnOk); pnlMain.add(createMarginComponent(10, 10)); getContentPane().add(pnlMain); + pack(); setVisible(true); } } From 62f21015d76a9694d700ffee87c65adabcdc7f0e Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 20:44:15 -0300 Subject: [PATCH 20/25] [MP-8]: Bumping version to 0.9.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ed1c25..cc95adc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.pesterenan MechPeste - 0.8.1 + 0.9.0 From 4dc52da28a3625484a083746b52604628b4e7158 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 21:26:44 -0300 Subject: [PATCH 21/25] [MP-8]: Implement connection listener to fix connection bug --- .../pesterenan/model/ConnectionListener.java | 8 ++++++ .../pesterenan/model/ConnectionManager.java | 27 +++++++++++++++++++ .../com/pesterenan/model/VesselManager.java | 13 +++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/pesterenan/model/ConnectionListener.java 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 index 4708736..e961598 100644 --- a/src/main/java/com/pesterenan/model/ConnectionManager.java +++ b/src/main/java/com/pesterenan/model/ConnectionManager.java @@ -3,6 +3,8 @@ 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; @@ -20,6 +22,7 @@ public class ConnectionManager { 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; @@ -27,6 +30,16 @@ public ConnectionManager(final String connectionName, final StatusDisplay status 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; } @@ -43,6 +56,19 @@ 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 = @@ -71,6 +97,7 @@ private void attemptConnection() { 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"))); diff --git a/src/main/java/com/pesterenan/model/VesselManager.java b/src/main/java/com/pesterenan/model/VesselManager.java index 7de8f09..08b8390 100644 --- a/src/main/java/com/pesterenan/model/VesselManager.java +++ b/src/main/java/com/pesterenan/model/VesselManager.java @@ -22,11 +22,12 @@ import krpc.client.services.SpaceCenter.Node; import krpc.client.services.SpaceCenter.Vessel; -public class VesselManager { +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; @@ -37,6 +38,14 @@ public VesselManager( 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() { @@ -44,7 +53,7 @@ public Connection getConnection() { } public SpaceCenter getSpaceCenter() { - return connectionManager.getSpaceCenter(); + return spaceCenter; } public ActiveVessel getCurrentVessel() { From 889d342624626ea196aaa64f65b74da76c27b45e Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 21:27:04 -0300 Subject: [PATCH 22/25] [MP-8]: Refactor RoverController busy-waiting --- .../controllers/RoverController.java | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/RoverController.java b/src/main/java/com/pesterenan/controllers/RoverController.java index 374a8fd..719d6d5 100644 --- a/src/main/java/com/pesterenan/controllers/RoverController.java +++ b/src/main/java/com/pesterenan/controllers/RoverController.java @@ -10,6 +10,9 @@ 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; @@ -26,7 +29,6 @@ public class RoverController extends Controller { 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(); @@ -35,9 +37,11 @@ public class RoverController extends Controller { 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); @@ -53,7 +57,6 @@ private void initializeParameters() { pathFinding = new PathFinding(vessel.getConnectionManager(), vessel.getVesselManager()); acelCtrl.setOutput(0, 1); sterringCtrl.setOutput(-1, 1); - isAutoRoverRunning = true; } catch (RPCException ignored) { } } @@ -70,7 +73,32 @@ private boolean isSolarPanelNotBroken(SolarPanel sp) { public void run() { if (commands.get(Module.MODULO.get()).equals(Module.ROVER.get())) { setTarget(); - driveRoverToTarget(); + 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 roverStateMachine() { + try { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + changeControlMode(); + if (isFarFromTarget()) { + currentMode = needToChargeBatteries() ? MODE.CHARGING : MODE.DRIVE; + } else { // Rover arrived at destiny + currentMode = MODE.NEXT_POINT; + } + } catch (InterruptedException e) { + cleanup(); + } catch (RPCException | IOException | StreamException e) { + cleanup(); } } @@ -92,6 +120,7 @@ private void setupCallbacks() throws RPCException, IOException, StreamException chargeAmountStream.addCallback( (amount) -> { try { + currentChargeAmount = amount; float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); currentChargePercentage = (float) Math.ceil(amount * 100 / totalCharge); } catch (RPCException e) { @@ -137,34 +166,13 @@ private void changeControlMode() } } - private void driveRoverToTarget() { - currentMode = MODE.NEXT_POINT; - try { - setupCallbacks(); - 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 e) { - cleanup(); - } catch (RPCException | IOException | StreamException e) { - cleanup(); - } - } - private void cleanup() { try { + if (roverExecutor != null && !roverExecutor.isShutdown()) { + roverExecutor.shutdownNow(); + } vessel.getActiveVessel().getControl().setBrakes(true); pathFinding.removeDrawnPath(); - isAutoRoverRunning = false; if (positionStream != null) { positionStream.removeCallback(distanceCallbackTag); positionStream.remove(); @@ -207,7 +215,6 @@ private boolean needToChargeBatteries() { private void rechargeRover() throws RPCException, StreamException, InterruptedException { float totalCharge = vessel.getActiveVessel().getResources().max("ElectricCharge"); - float currentCharge = vessel.getActiveVessel().getResources().amount("ElectricCharge"); setRoverThrottle(0); vessel.getActiveVessel().getControl().setLights(false); @@ -225,7 +232,7 @@ private void rechargeRover() throws RPCException, StreamException, InterruptedEx for (SolarPanel sp : solarPanels) { totalEnergyFlow += sp.getEnergyFlow(); } - chargeTime = ((totalCharge - currentCharge) / totalEnergyFlow); + chargeTime = ((totalCharge - currentChargeAmount) / totalEnergyFlow); setCurrentStatus("Segundos de Carga: " + chargeTime); if (chargeTime < 1 || chargeTime > 21600) { chargeTime = 3600; From 1ed987d2ed5d8a3ccd3ffce88f362fb3d6f44a1a Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 21:28:21 -0300 Subject: [PATCH 23/25] [MP-8]: Show status ready after cancelling a module --- .../pesterenan/views/FunctionsAndTelemetryJPanel.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java index 01f4fa4..38f82b4 100644 --- a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java +++ b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java @@ -11,6 +11,9 @@ import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.Map; +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; @@ -112,6 +115,14 @@ public void setupComponents() { 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 From 81cd0b44696d99cb92b291a084f76e8489c327d1 Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 22:29:38 -0300 Subject: [PATCH 24/25] [MP-8]: Update controllers to use new pattern --- .../pesterenan/controllers/Controller.java | 9 + .../controllers/DockingController.java | 171 +++++++++--------- .../controllers/LandingController.java | 98 +++++----- .../controllers/LiftoffController.java | 76 ++++---- .../controllers/RoverController.java | 20 +- .../com/pesterenan/model/ActiveVessel.java | 4 +- 6 files changed, 183 insertions(+), 195 deletions(-) diff --git a/src/main/java/com/pesterenan/controllers/Controller.java b/src/main/java/com/pesterenan/controllers/Controller.java index 6f0f98c..910f770 100644 --- a/src/main/java/com/pesterenan/controllers/Controller.java +++ b/src/main/java/com/pesterenan/controllers/Controller.java @@ -4,6 +4,7 @@ public class Controller implements Runnable { protected final ActiveVessel vessel; + private volatile boolean isRunning = true; private String currentStatus = ""; public Controller(ActiveVessel vessel) { @@ -14,6 +15,14 @@ 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; } diff --git a/src/main/java/com/pesterenan/controllers/DockingController.java b/src/main/java/com/pesterenan/controllers/DockingController.java index dc2c324..e6f1704 100644 --- a/src/main/java/com/pesterenan/controllers/DockingController.java +++ b/src/main/java/com/pesterenan/controllers/DockingController.java @@ -7,6 +7,9 @@ import com.pesterenan.utils.Vector; import com.pesterenan.views.DockingJPanel; 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; @@ -65,14 +68,15 @@ private enum DOCKING_STEPS { private long sleepTime = 25; private DOCKING_STEPS dockingStep; - private boolean isDocking, isOriented = false; + private boolean isOriented = false; private final Map commands; private Stream errorStream; - private int utCallbackTag, errorCallbackTag; + private int errorCallbackTag; private DockingPhase currentPhase; + private ScheduledExecutorService dockingExecutor; public DockingController(ActiveVessel vessel, Map commands) { super(vessel); @@ -85,38 +89,18 @@ public void run() { if (commands.get(Module.MODULO.get()).equals(Module.DOCKING.get())) { try { startDocking(); - while (isDocking) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Thread.sleep(250); - } - } catch (RPCException | InterruptedException | StreamException | IllegalArgumentException e) { + } catch (RPCException | StreamException e) { cleanup(); - setCurrentStatus("Docking interrupted: " + e.getMessage()); + setCurrentStatus("Docking failed: " + e.getMessage()); } } } public void startDocking() throws RPCException, StreamException { - isDocking = true; krpc = KRPC.newInstance(vessel.getConnectionManager().getConnection()); currentPhase = DockingPhase.SETUP; - - utCallbackTag = - vessel.missionTime.addCallback( - (ut) -> { - try { - if (isDocking) { - updateDockingState(); - } else { - vessel.missionTime.removeCallback(utCallbackTag); - } - } catch (Exception e) { - setCurrentStatus("Docking failed: " + e.getMessage()); - cleanup(); - } - }); + dockingExecutor = Executors.newSingleThreadScheduledExecutor(); + dockingExecutor.scheduleAtFixedRate(this::updateDockingState, 0, 100, TimeUnit.MILLISECONDS); } private void initializeParameters() { @@ -139,68 +123,77 @@ private void initializeParameters() { } } - private void updateDockingState() throws RPCException, StreamException, InterruptedException { - 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); - 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: + private void updateDockingState() { + try { + if (!isRunning()) { cleanup(); - break; + 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); + 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(); } } @@ -383,8 +376,10 @@ private void updateLines(Vector start, Vector end) { } private void cleanup() { + if (dockingExecutor != null && !dockingExecutor.isShutdown()) { + dockingExecutor.shutdownNow(); + } try { - isDocking = false; if (errorStream != null) { errorStream.remove(); } diff --git a/src/main/java/com/pesterenan/controllers/LandingController.java b/src/main/java/com/pesterenan/controllers/LandingController.java index 19e914e..12bf23b 100644 --- a/src/main/java/com/pesterenan/controllers/LandingController.java +++ b/src/main/java/com/pesterenan/controllers/LandingController.java @@ -7,6 +7,9 @@ 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; @@ -36,18 +39,15 @@ private enum MODE { private Navigation navigation; private final int HUNDRED_PERCENT = 100; private double altitudeErrorPercentage, hoverAltitude, targetPeriapsis; - private boolean hoveringMode, hoverAfterApproximation, landingMode; + private boolean hoverAfterApproximation, landingMode; private MODE currentMode; private float maxTWR; private final Map commands; private volatile boolean isDeorbitBurnDone, isOrientedForDeorbit, isFalling, wasAirborne = false; - private volatile boolean isCleaningUp = false; - private int isOrientedCallbackTag, - isDeorbitBurnDoneCallbackTag, - isFallingCallbackTag, - utCallbackTag; + private int isOrientedCallbackTag, isDeorbitBurnDoneCallbackTag, isFallingCallbackTag; private Stream apErrorStream; + private ScheduledExecutorService landingExecutor; public LandingController(ActiveVessel vessel, Map commands) { super(vessel); @@ -61,24 +61,39 @@ public void run() { try { if (commands.get(Module.MODULO.get()).equals(Module.HOVERING.get())) { hoverArea(); - } - if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { + } else if (commands.get(Module.MODULO.get()).equals(Module.LANDING.get())) { autoLanding(); } - while (landingMode || hoveringMode) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Thread.sleep(100); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // Restore interrupted status - System.err.println("Controle de Pouso finalizado via interrupção."); - setCurrentStatus(Bundle.getString("status_ready")); + 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")); - } finally { + cleanup(); + } + } + + 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(); } } @@ -92,14 +107,14 @@ private void initializeParameters() { velocityCtrl.setOutput(0, 1); } - private void hoverArea() throws RPCException, StreamException, InterruptedException { - hoveringMode = true; + private void hoverArea() throws RPCException, StreamException { + landingMode = false; vessel.ap.engage(); vessel.tuneAutoPilot(); setupCallbacks(); } - private void autoLanding() throws RPCException, StreamException, InterruptedException { + private void autoLanding() throws RPCException, StreamException { landingMode = true; hoverAfterApproximation = Boolean.parseBoolean(commands.get(Module.HOVER_AFTER_LANDING.get())); if (!hoverAfterApproximation) { @@ -161,34 +176,6 @@ private void setupCallbacks() throws RPCException, StreamException { } }); - utCallbackTag = - vessel.missionTime.addCallback( - (ut) -> { - if (isCleaningUp) { - return; - } - try { - if (landingMode) { - executeLandingStep(); - } else if (hoveringMode) { - 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(); - } else { - vessel.missionTime.removeCallback(utCallbackTag); - } - } catch (Exception e) { - System.err.println("UT Callback error: " + e.getMessage()); - } - }); - // Start all streams apErrorStream.start(); vessel.apoapsis.start(); @@ -296,7 +283,7 @@ private void executeLandingStep() throws RPCException, StreamException, Interrup private void executeHoverStep() throws RPCException, StreamException, InterruptedException { if (hasTheVesselLanded()) { - hoveringMode = false; + stop(); return; } adjustHoverPID(); @@ -392,8 +379,7 @@ private boolean hasTheVesselLanded() throws RPCException { && (situation.equals(VesselSituation.LANDED) || situation.equals(VesselSituation.SPLASHED))) { setCurrentStatus(Bundle.getString("status_landed")); - hoveringMode = false; - landingMode = false; + stop(); vessel.throttle(0.0f); vessel.getActiveVessel().getControl().setSAS(true); vessel.getActiveVessel().getControl().setRCS(true); @@ -431,10 +417,10 @@ private double calculateEllipticTrajectory(double a, double b) { } private void cleanup() { - isCleaningUp = true; + if (landingExecutor != null && !landingExecutor.isShutdown()) { + landingExecutor.shutdownNow(); + } try { - landingMode = false; - hoveringMode = false; if (vessel.ap != null) { vessel.ap.disengage(); } diff --git a/src/main/java/com/pesterenan/controllers/LiftoffController.java b/src/main/java/com/pesterenan/controllers/LiftoffController.java index e7ce22d..2c9139d 100644 --- a/src/main/java/com/pesterenan/controllers/LiftoffController.java +++ b/src/main/java/com/pesterenan/controllers/LiftoffController.java @@ -9,6 +9,9 @@ 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; @@ -27,10 +30,8 @@ private enum LIFTOFF_MODE { private static final float PITCH_UP = 90; private ControlePID thrControl; private float currentPitch, finalApoapsisAlt, heading, roll, maxTWR; - private volatile boolean targetApoapsisReached, - dynamicPressureLowEnough, - isLiftoffRunning = false; - private int apoapsisCallbackTag, pressureCallbackTag, utCallbackTag; + private volatile boolean targetApoapsisReached, dynamicPressureLowEnough; + private int apoapsisCallbackTag, pressureCallbackTag; private Stream pressureStream; private boolean willDecoupleStages, willOpenPanelsAndAntenna; private String gravityCurveModel = Module.CIRCULAR.get(); @@ -38,6 +39,7 @@ private enum LIFTOFF_MODE { private LIFTOFF_MODE liftoffMode; private double startCurveAlt; private final Map commands; + private ScheduledExecutorService liftoffExecutor; public LiftoffController(ActiveVessel vessel, Map commands) { super(vessel); @@ -49,13 +51,11 @@ public LiftoffController(ActiveVessel vessel, Map commands) { @Override public void run() { try { - isLiftoffRunning = true; - // 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 (Thread.interrupted()) throw new InterruptedException(); + if (!isRunning()) return; setCurrentStatus(String.format(Bundle.getString("status_launching_in"), count)); Thread.sleep(100); } @@ -67,7 +67,7 @@ public void run() { // Part 2: Async Gravity Turn liftoffMode = LIFTOFF_MODE.GRAVITY_TURN; // Set initial state for async phase - setupCallbacks(); // This starts the UT stream + setupCallbacks(); vessel.tuneAutoPilot(); startCurveAlt = vessel.altitude.get(); vessel.ap.setReferenceFrame(vessel.surfaceReferenceFrame); @@ -75,11 +75,9 @@ public void run() { vessel.ap.setTargetRoll(this.roll); vessel.ap.engage(); - // Loop to keep thread alive while async part runs - while (isLiftoffRunning) { - if (Thread.interrupted()) throw new InterruptedException(); - Thread.sleep(250); - } + liftoffExecutor = Executors.newSingleThreadScheduledExecutor(); + liftoffExecutor.scheduleAtFixedRate(this::handleLiftoff, 0, 100, TimeUnit.MILLISECONDS); + } catch (RPCException | InterruptedException | StreamException e) { cleanup(); setCurrentStatus(Bundle.getString("status_ready")); @@ -138,34 +136,29 @@ private void setupCallbacks() throws RPCException, StreamException { } }); pressureStream.start(); - - utCallbackTag = - vessel.missionTime.addCallback( - (ut) -> { - try { - if (!isLiftoffRunning) { - vessel.missionTime.removeCallback(utCallbackTag); - return; - } - handleLiftoff(); - } catch (Exception e) { - System.err.println("Liftoff UT Callback error: " + e.getMessage()); - } - }); } - private void handleLiftoff() throws RPCException, StreamException, InterruptedException { - switch (liftoffMode) { - case GRAVITY_TURN: - gravityTurn(); - break; - case FINALIZE_ORBIT: - finalizeOrbit(); - break; - case CIRCULARIZE: - circularizeOrbitOnApoapsis(); - isLiftoffRunning = false; - break; + 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(); } } @@ -215,10 +208,11 @@ private void finalizeOrbit() throws RPCException, StreamException, InterruptedEx private void cleanup() { try { - isLiftoffRunning = false; + if (liftoffExecutor != null && !liftoffExecutor.isShutdown()) { + liftoffExecutor.shutdownNow(); + } vessel.apoapsis.removeCallback(apoapsisCallbackTag); pressureStream.removeCallback(pressureCallbackTag); - vessel.missionTime.removeCallback(utCallbackTag); pressureStream.remove(); vessel.ap.disengage(); vessel.throttle(0); diff --git a/src/main/java/com/pesterenan/controllers/RoverController.java b/src/main/java/com/pesterenan/controllers/RoverController.java index 719d6d5..4fb2e9c 100644 --- a/src/main/java/com/pesterenan/controllers/RoverController.java +++ b/src/main/java/com/pesterenan/controllers/RoverController.java @@ -86,8 +86,9 @@ public void run() { private void roverStateMachine() { try { - if (Thread.interrupted()) { - throw new InterruptedException(); + if (!isRunning()) { + cleanup(); + return; } changeControlMode(); if (isFarFromTarget()) { @@ -95,9 +96,7 @@ private void roverStateMachine() { } else { // Rover arrived at destiny currentMode = MODE.NEXT_POINT; } - } catch (InterruptedException e) { - cleanup(); - } catch (RPCException | IOException | StreamException e) { + } catch (RPCException | IOException | StreamException | InterruptedException e) { cleanup(); } } @@ -186,16 +185,21 @@ private void cleanup() { } } - private void setNextPointInPath() throws RPCException, IOException, InterruptedException { + 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()) { - throw new InterruptedException(); + stop(); + return; + } + try { + pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); + } catch (InterruptedException e) { + stop(); } - pathFinding.buildPathToTarget(pathFinding.findNearestWaypoint()); } } else { diff --git a/src/main/java/com/pesterenan/model/ActiveVessel.java b/src/main/java/com/pesterenan/model/ActiveVessel.java index b73f779..9a65eee 100644 --- a/src/main/java/com/pesterenan/model/ActiveVessel.java +++ b/src/main/java/com/pesterenan/model/ActiveVessel.java @@ -186,8 +186,8 @@ public Thread getControllerThread() { } public void cancelControl() { - if (controllerThread != null) { - controllerThread.interrupt(); + if (controller != null) { + controller.stop(); runningModule = false; } } From 17b22c757ea723e093ee85df9c946eb8273624ca Mon Sep 17 00:00:00 2001 From: Pesterenan Date: Tue, 4 Nov 2025 22:32:56 -0300 Subject: [PATCH 25/25] [MP-8]: Disabled not yet ready functions --- .../java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java | 1 + src/main/java/com/pesterenan/views/RunManeuverJPanel.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java index 38f82b4..b603bfc 100644 --- a/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java +++ b/src/main/java/com/pesterenan/views/FunctionsAndTelemetryJPanel.java @@ -110,6 +110,7 @@ public void setupComponents() { btnDocking.setActionCommand(Module.DOCKING.get()); btnDocking.setMaximumSize(btnFuncDimension); btnDocking.setPreferredSize(btnFuncDimension); + btnDocking.setEnabled(false); // FIX: Not yet ready } private void cancelCurrentAction(ActionEvent e) { diff --git a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java index 0e4e1fa..3e463e9 100644 --- a/src/main/java/com/pesterenan/views/RunManeuverJPanel.java +++ b/src/main/java/com/pesterenan/views/RunManeuverJPanel.java @@ -132,10 +132,12 @@ public void setupComponents() { 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);