Added simulation of tumble recovery. Has pretty major FIXME which needs
addressing.
This commit is contained in:
parent
fab85c2918
commit
b99a704eb6
File diff suppressed because it is too large
Load Diff
@ -1368,6 +1368,7 @@ FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = Recovery device deployment
|
|||||||
FlightEvent.Type.GROUND_HIT = Ground hit
|
FlightEvent.Type.GROUND_HIT = Ground hit
|
||||||
FlightEvent.Type.SIMULATION_END = Simulation end
|
FlightEvent.Type.SIMULATION_END = Simulation end
|
||||||
FlightEvent.Type.ALTITUDE = Altitude change
|
FlightEvent.Type.ALTITUDE = Altitude change
|
||||||
|
FlightEvent.Type.TUMBLE = Tumbling
|
||||||
|
|
||||||
! ThrustCurveMotorColumns
|
! ThrustCurveMotorColumns
|
||||||
TCurveMotorCol.MANUFACTURER = Manufacturer
|
TCurveMotorCol.MANUFACTURER = Manufacturer
|
||||||
|
@ -37,6 +37,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Total motion vs. time
|
//// Total motion vs. time
|
||||||
@ -50,6 +51,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Flight side profile
|
//// Flight side profile
|
||||||
@ -61,6 +63,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Stability vs. time
|
//// Stability vs. time
|
||||||
@ -74,6 +77,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Drag coefficients vs. Mach number
|
//// Drag coefficients vs. Mach number
|
||||||
@ -98,6 +102,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Angle of attack and orientation vs. time
|
//// Angle of attack and orientation vs. time
|
||||||
@ -111,6 +116,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
//// Simulation time step and computation time
|
//// Simulation time step and computation time
|
||||||
@ -123,6 +129,7 @@ public class PlotConfiguration implements Cloneable {
|
|||||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||||
|
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||||
configs.add(config);
|
configs.add(config);
|
||||||
|
|
||||||
DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]);
|
DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]);
|
||||||
|
@ -37,6 +37,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
// TODO: MEDIUM: Allow selecting steppers
|
// TODO: MEDIUM: Allow selecting steppers
|
||||||
private SimulationStepper flightStepper = new RK4SimulationStepper();
|
private SimulationStepper flightStepper = new RK4SimulationStepper();
|
||||||
private SimulationStepper landingStepper = new BasicLandingStepper();
|
private SimulationStepper landingStepper = new BasicLandingStepper();
|
||||||
|
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
|
||||||
|
|
||||||
private SimulationStepper currentStepper;
|
private SimulationStepper currentStepper;
|
||||||
|
|
||||||
@ -191,6 +192,20 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
(RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId));
|
(RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for Tumbling
|
||||||
|
// FIXME - need to test things like no longer stable.
|
||||||
|
|
||||||
|
if (status.isApogeeReached() && !status.isTumbling() ) {
|
||||||
|
int last_data_index = status.getFlightData().getLength() -1;
|
||||||
|
double cp = status.getFlightData().get(FlightDataType.TYPE_CP_LOCATION).get(last_data_index);
|
||||||
|
double cg = status.getFlightData().get(FlightDataType.TYPE_CG_LOCATION).get(last_data_index);
|
||||||
|
if( cg > cp ) {
|
||||||
|
addEvent( new FlightEvent(FlightEvent.Type.TUMBLE,status.getSimulationTime()));
|
||||||
|
}
|
||||||
|
status.setTumbling(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,6 +494,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
|
|
||||||
case ALTITUDE:
|
case ALTITUDE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TUMBLE:
|
||||||
|
this.currentStepper = this.tumbleStepper;
|
||||||
|
this.status = currentStepper.initialize(status);
|
||||||
|
status.getFlightData().addEvent(event);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
42
core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java
Normal file
42
core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package net.sf.openrocket.simulation;
|
||||||
|
|
||||||
|
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||||
|
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||||
|
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||||
|
import net.sf.openrocket.motor.MotorInstanceConfiguration;
|
||||||
|
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||||
|
|
||||||
|
public class BasicTumbleStatus extends SimulationStatus {
|
||||||
|
|
||||||
|
private double tumbleCd;
|
||||||
|
|
||||||
|
public BasicTumbleStatus(Configuration configuration,
|
||||||
|
MotorInstanceConfiguration motorConfiguration,
|
||||||
|
SimulationConditions simulationConditions) {
|
||||||
|
super(configuration, motorConfiguration, simulationConditions);
|
||||||
|
computeTumbleCd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicTumbleStatus(SimulationStatus orig) {
|
||||||
|
super(orig);
|
||||||
|
if ( orig instanceof BasicTumbleStatus ) {
|
||||||
|
this.tumbleCd = ((BasicTumbleStatus) orig).tumbleCd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTumbleCd( ) {
|
||||||
|
return tumbleCd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void computeTumbleCd() {
|
||||||
|
// FIXME - probably want to compute the overall CD more accurately. Perhaps average
|
||||||
|
// CD over three AoA: 0, 90, 180. In any case, using barrowman to compute this Cd is
|
||||||
|
// completely wrong.
|
||||||
|
WarningSet warnings = new WarningSet();
|
||||||
|
FlightConditions cond = new FlightConditions(this.getConfiguration());
|
||||||
|
cond.setAOA(Math.PI);
|
||||||
|
AerodynamicForces forces = this.getSimulationConditions().getAerodynamicCalculator().getAerodynamicForces(this.getConfiguration(), cond, warnings);
|
||||||
|
tumbleCd = forces.getCD();
|
||||||
|
}
|
||||||
|
}
|
138
core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java
Normal file
138
core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package net.sf.openrocket.simulation;
|
||||||
|
|
||||||
|
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||||
|
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||||
|
import net.sf.openrocket.util.Coordinate;
|
||||||
|
import net.sf.openrocket.util.GeodeticComputationStrategy;
|
||||||
|
import net.sf.openrocket.util.MathUtil;
|
||||||
|
import net.sf.openrocket.util.WorldCoordinate;
|
||||||
|
|
||||||
|
public class BasicTumbleStepper extends AbstractSimulationStepper {
|
||||||
|
|
||||||
|
private static final double RECOVERY_TIME_STEP = 0.5;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
|
||||||
|
return new BasicTumbleStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
|
||||||
|
double refArea = status.getConfiguration().getReferenceArea();
|
||||||
|
|
||||||
|
// Get the atmospheric conditions
|
||||||
|
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
|
||||||
|
|
||||||
|
//// Local wind speed and direction
|
||||||
|
Coordinate windSpeed = modelWindVelocity(status);
|
||||||
|
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
|
||||||
|
|
||||||
|
// Get total CD
|
||||||
|
double mach = airSpeed.length() / atmosphere.getMachSpeed();
|
||||||
|
|
||||||
|
double totalCD = ((BasicTumbleStatus)status).getTumbleCd();
|
||||||
|
|
||||||
|
// Compute drag force
|
||||||
|
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
|
||||||
|
double dragForce = totalCD * dynP * refArea;
|
||||||
|
MassData massData = calculateMassData(status);
|
||||||
|
double mass = massData.getCG().weight;
|
||||||
|
|
||||||
|
|
||||||
|
// Compute drag acceleration
|
||||||
|
Coordinate linearAcceleration;
|
||||||
|
if (airSpeed.length() > 0.001) {
|
||||||
|
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
|
||||||
|
} else {
|
||||||
|
linearAcceleration = Coordinate.NUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add effect of gravity
|
||||||
|
double gravity = modelGravity(status);
|
||||||
|
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
|
||||||
|
|
||||||
|
|
||||||
|
// Add coriolis acceleration
|
||||||
|
Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
|
||||||
|
status.getRocketWorldPosition(), status.getRocketVelocity());
|
||||||
|
linearAcceleration = linearAcceleration.add(coriolisAcceleration);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Select time step
|
||||||
|
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
|
||||||
|
|
||||||
|
// Perform Euler integration
|
||||||
|
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
||||||
|
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
|
||||||
|
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
|
||||||
|
status.setSimulationTime(status.getSimulationTime() + timeStep);
|
||||||
|
|
||||||
|
|
||||||
|
// Update the world coordinate
|
||||||
|
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
|
||||||
|
w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
|
||||||
|
status.setRocketWorldPosition(w);
|
||||||
|
|
||||||
|
|
||||||
|
// Store data
|
||||||
|
FlightDataBranch data = status.getFlightData();
|
||||||
|
boolean extra = status.getSimulationConditions().isCalculateExtras();
|
||||||
|
data.addPoint();
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
|
||||||
|
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
|
||||||
|
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
|
||||||
|
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
|
||||||
|
if (extra) {
|
||||||
|
data.setValue(FlightDataType.TYPE_POSITION_XY,
|
||||||
|
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
|
||||||
|
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
|
||||||
|
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
|
||||||
|
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
|
||||||
|
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
|
||||||
|
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
|
||||||
|
|
||||||
|
double Re = airSpeed.length() *
|
||||||
|
status.getConfiguration().getLength() /
|
||||||
|
atmosphere.getKinematicViscosity();
|
||||||
|
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
|
||||||
|
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
|
||||||
|
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
|
||||||
|
|
||||||
|
if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
|
||||||
|
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
|
||||||
|
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
|
||||||
|
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_MASS, mass);
|
||||||
|
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, 0.0); // Is this a reasonable assumption? Probably.
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
|
||||||
|
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
|
||||||
|
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
|
||||||
|
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
|
||||||
|
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
|
||||||
|
|
||||||
|
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
|
||||||
|
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
|
||||||
|
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -72,7 +72,12 @@ public class FlightEvent implements Comparable<FlightEvent> {
|
|||||||
* A change in altitude has occurred. Data is a <code>Pair<Double,Double></code>
|
* A change in altitude has occurred. Data is a <code>Pair<Double,Double></code>
|
||||||
* which contains the old and new altitudes.
|
* which contains the old and new altitudes.
|
||||||
*/
|
*/
|
||||||
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE"));
|
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rocket begins to tumble.
|
||||||
|
*/
|
||||||
|
TUMBLE(trans.get("FlightEvent.Type.TUMBLE"));
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
@ -69,6 +69,9 @@ public class SimulationStatus implements Monitorable {
|
|||||||
/** Set to true when apogee has been detected. */
|
/** Set to true when apogee has been detected. */
|
||||||
private boolean apogeeReached = false;
|
private boolean apogeeReached = false;
|
||||||
|
|
||||||
|
/** Set to true to indicate the rocket is tumbling. */
|
||||||
|
private boolean tumbling = false;
|
||||||
|
|
||||||
/** Contains a list of deployed recovery devices. */
|
/** Contains a list of deployed recovery devices. */
|
||||||
private MonitorableSet<RecoveryDevice> deployedRecoveryDevices = new MonitorableSet<RecoveryDevice>();
|
private MonitorableSet<RecoveryDevice> deployedRecoveryDevices = new MonitorableSet<RecoveryDevice>();
|
||||||
|
|
||||||
@ -179,6 +182,7 @@ public class SimulationStatus implements Monitorable {
|
|||||||
this.liftoff = orig.liftoff;
|
this.liftoff = orig.liftoff;
|
||||||
this.launchRodCleared = orig.launchRodCleared;
|
this.launchRodCleared = orig.launchRodCleared;
|
||||||
this.apogeeReached = orig.apogeeReached;
|
this.apogeeReached = orig.apogeeReached;
|
||||||
|
this.tumbling = orig.tumbling;
|
||||||
this.motorBurntOut = orig.motorBurntOut;
|
this.motorBurntOut = orig.motorBurntOut;
|
||||||
|
|
||||||
this.deployedRecoveryDevices.clear();
|
this.deployedRecoveryDevices.clear();
|
||||||
@ -380,6 +384,15 @@ public class SimulationStatus implements Monitorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setTumbling( boolean tumbling ) {
|
||||||
|
this.tumbling = tumbling;
|
||||||
|
this.modID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTumbling() {
|
||||||
|
return tumbling;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
||||||
return deployedRecoveryDevices;
|
return deployedRecoveryDevices;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user