mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
enable search with different insertion strategies
This commit is contained in:
parent
c472042781
commit
7ce6a8d68a
6 changed files with 233 additions and 31 deletions
|
|
@ -494,7 +494,7 @@ public class VehicleRoutingAlgorithms {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, XMLConfiguration config,
|
private static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, XMLConfiguration config,
|
||||||
int nuOfThreads, SolutionCostCalculator solutionCostCalculator, final StateManager stateManager, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
|
int nuOfThreads, final SolutionCostCalculator solutionCostCalculator, final StateManager stateManager, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
|
||||||
// map to store constructed modules
|
// map to store constructed modules
|
||||||
TypedMap definedClasses = new TypedMap();
|
TypedMap definedClasses = new TypedMap();
|
||||||
|
|
||||||
|
|
@ -572,13 +572,22 @@ public class VehicleRoutingAlgorithms {
|
||||||
stateManager.addStateUpdater(new UpdateActivityTimes(vrp.getTransportCosts(),activityPolicy));
|
stateManager.addStateUpdater(new UpdateActivityTimes(vrp.getTransportCosts(),activityPolicy));
|
||||||
stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager, activityPolicy));
|
stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager, activityPolicy));
|
||||||
|
|
||||||
SolutionCostCalculator costCalculator;
|
final SolutionCostCalculator costCalculator;
|
||||||
if(solutionCostCalculator==null) costCalculator = getDefaultCostCalculator(stateManager);
|
if(solutionCostCalculator==null) costCalculator = getDefaultCostCalculator(stateManager);
|
||||||
else costCalculator = solutionCostCalculator;
|
else costCalculator = solutionCostCalculator;
|
||||||
|
|
||||||
//construct initial solution creator
|
//construct initial solution creator
|
||||||
AlgorithmStartsListener createInitialSolution = createInitialSolution(config,vrp,vehicleFleetManager,stateManager,algorithmListeners,definedClasses,executorService,nuOfThreads,costCalculator, constraintManager, addDefaultCostCalculators);
|
final InsertionStrategy initialInsertionStrategy = createInitialSolution(config,vrp,vehicleFleetManager,stateManager,algorithmListeners,definedClasses,executorService,nuOfThreads,costCalculator, constraintManager, addDefaultCostCalculators);
|
||||||
if(createInitialSolution != null) algorithmListeners.add(new PrioritizedVRAListener(Priority.MEDIUM, createInitialSolution));
|
if(initialInsertionStrategy != null) algorithmListeners.add(new PrioritizedVRAListener(Priority.MEDIUM, new AlgorithmStartsListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
|
||||||
|
InsertionInitialSolutionFactory insertionInitialSolutionFactory = new InsertionInitialSolutionFactory(initialInsertionStrategy, costCalculator);
|
||||||
|
VehicleRoutingProblemSolution vrpSol = insertionInitialSolutionFactory.createSolution(vrp);
|
||||||
|
solutions.add(vrpSol);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
//construct algorithm, i.e. search-strategies and its modules
|
//construct algorithm, i.e. search-strategies and its modules
|
||||||
int solutionMemory = config.getInt("strategy.memory");
|
int solutionMemory = config.getInt("strategy.memory");
|
||||||
|
|
@ -605,13 +614,6 @@ public class VehicleRoutingAlgorithms {
|
||||||
if(maxIterationsString == null) maxIterationsString = config.getString("maxIterations");
|
if(maxIterationsString == null) maxIterationsString = config.getString("maxIterations");
|
||||||
if(maxIterationsString != null) metaAlgorithm.setMaxIterations(Integer.parseInt(maxIterationsString));
|
if(maxIterationsString != null) metaAlgorithm.setMaxIterations(Integer.parseInt(maxIterationsString));
|
||||||
|
|
||||||
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(stateManager);
|
|
||||||
metaAlgorithm.getAlgorithmListeners().addListener(stateManager);
|
|
||||||
|
|
||||||
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new RemoveEmptyVehicles(vehicleFleetManager));
|
|
||||||
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new ResetAndIniFleetManager(vehicleFleetManager));
|
|
||||||
metaAlgorithm.getSearchStrategyManager().addSearchStrategyModuleListener(new VehicleSwitched(vehicleFleetManager));
|
|
||||||
|
|
||||||
//define prematureBreak
|
//define prematureBreak
|
||||||
PrematureAlgorithmTermination prematureAlgorithmTermination = getPrematureTermination(config, algorithmListeners);
|
PrematureAlgorithmTermination prematureAlgorithmTermination = getPrematureTermination(config, algorithmListeners);
|
||||||
if(prematureAlgorithmTermination != null) metaAlgorithm.setPrematureAlgorithmTermination(prematureAlgorithmTermination);
|
if(prematureAlgorithmTermination != null) metaAlgorithm.setPrematureAlgorithmTermination(prematureAlgorithmTermination);
|
||||||
|
|
@ -623,12 +625,26 @@ public class VehicleRoutingAlgorithms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveEmptyVehicles removeEmptyVehicles = new RemoveEmptyVehicles(vehicleFleetManager);
|
||||||
|
ResetAndIniFleetManager resetAndIniFleetManager = new ResetAndIniFleetManager(vehicleFleetManager);
|
||||||
|
VehicleSwitched vehicleSwitched = new VehicleSwitched(vehicleFleetManager);
|
||||||
|
|
||||||
//misc
|
metaAlgorithm.addListener(stateManager);
|
||||||
// algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, new SolutionVerifier()));
|
metaAlgorithm.addListener(removeEmptyVehicles);
|
||||||
|
metaAlgorithm.addListener(resetAndIniFleetManager);
|
||||||
|
metaAlgorithm.addListener(vehicleSwitched);
|
||||||
|
|
||||||
//register listeners
|
if(initialInsertionStrategy != null) {
|
||||||
registerListeners(metaAlgorithm,algorithmListeners);
|
if(initialInsertionStrategy.getListeners() != null){
|
||||||
|
if(!initialInsertionStrategy.getListeners().contains(stateManager)) initialInsertionStrategy.addListener(stateManager);
|
||||||
|
if(!initialInsertionStrategy.getListeners().contains(removeEmptyVehicles)) initialInsertionStrategy.addListener(removeEmptyVehicles);
|
||||||
|
if(!initialInsertionStrategy.getListeners().contains(resetAndIniFleetManager)) initialInsertionStrategy.addListener(resetAndIniFleetManager);
|
||||||
|
if(!initialInsertionStrategy.getListeners().contains(vehicleSwitched)) initialInsertionStrategy.addListener(vehicleSwitched);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//register listeners
|
||||||
|
registerListeners(metaAlgorithm, algorithmListeners);
|
||||||
registerInsertionListeners(definedClasses,insertionListeners);
|
registerInsertionListeners(definedClasses,insertionListeners);
|
||||||
return metaAlgorithm;
|
return metaAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
@ -747,7 +763,7 @@ public class VehicleRoutingAlgorithms {
|
||||||
metaAlgorithm.getAlgorithmListeners().addAll(algorithmListeners);
|
metaAlgorithm.getAlgorithmListeners().addAll(algorithmListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AlgorithmStartsListener createInitialSolution(XMLConfiguration config, final VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, final StateManager routeStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses, ExecutorService executorService, int nuOfThreads, final SolutionCostCalculator solutionCostCalculator, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
|
private static InsertionStrategy createInitialSolution(XMLConfiguration config, final VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, final StateManager routeStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses, ExecutorService executorService, int nuOfThreads, final SolutionCostCalculator solutionCostCalculator, ConstraintManager constraintManager, boolean addDefaultCostCalculators) {
|
||||||
List<HierarchicalConfiguration> modConfigs = config.configurationsAt("construction.insertion");
|
List<HierarchicalConfiguration> modConfigs = config.configurationsAt("construction.insertion");
|
||||||
if(modConfigs == null) return null;
|
if(modConfigs == null) return null;
|
||||||
if(modConfigs.isEmpty()) return null;
|
if(modConfigs.isEmpty()) return null;
|
||||||
|
|
@ -768,15 +784,7 @@ public class VehicleRoutingAlgorithms {
|
||||||
}
|
}
|
||||||
final InsertionStrategy finalInsertionStrategy = insertionStrategy;
|
final InsertionStrategy finalInsertionStrategy = insertionStrategy;
|
||||||
|
|
||||||
return new AlgorithmStartsListener() {
|
return finalInsertionStrategy;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
|
|
||||||
InsertionInitialSolutionFactory insertionInitialSolutionFactory = new InsertionInitialSolutionFactory(finalInsertionStrategy, solutionCostCalculator);
|
|
||||||
VehicleRoutingProblemSolution vrpSol = insertionInitialSolutionFactory.createSolution(vrp);
|
|
||||||
solutions.add(vrpSol);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (C) 2014 Stefan Schroeder
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 3.0 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
******************************************************************************/
|
||||||
|
package jsprit.core.algorithm;
|
||||||
|
|
||||||
|
import jsprit.core.algorithm.io.VehicleRoutingAlgorithms;
|
||||||
|
import jsprit.core.problem.VehicleRoutingProblem;
|
||||||
|
import jsprit.core.problem.io.VrpXMLReader;
|
||||||
|
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class CVRPwithDeliveriesAndDifferentInsertionStrategies_IT {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWithTwoInsertionStrategiesWhereOnleOneIsInAlgo_itShouldWork(){
|
||||||
|
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
|
||||||
|
new VrpXMLReader(vrpBuilder).read("src/test/resources/vrpnc1-jsprit-with-deliveries.xml");
|
||||||
|
VehicleRoutingProblem vrp = vrpBuilder.build();
|
||||||
|
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "src/test/resources/algorithmConfig_greedyWithRegret.xml");
|
||||||
|
vra.setMaxIterations(10);
|
||||||
|
try {
|
||||||
|
Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
|
||||||
|
Assert.assertTrue(true);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
Assert.assertTrue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWithTwoInsertionStrategiesWhereBothAreInAlgo_itShouldWork(){
|
||||||
|
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
|
||||||
|
new VrpXMLReader(vrpBuilder).read("src/test/resources/vrpnc1-jsprit-with-deliveries.xml");
|
||||||
|
VehicleRoutingProblem vrp = vrpBuilder.build();
|
||||||
|
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "src/test/resources/algorithmConfig_greedyWithRegret_v2.xml");
|
||||||
|
vra.setMaxIterations(10);
|
||||||
|
try {
|
||||||
|
Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
|
||||||
|
Assert.assertTrue(true);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
Assert.assertTrue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
66
jsprit-core/src/test/resources/algorithmConfig_greedyWithRegret.xml
Executable file
66
jsprit-core/src/test/resources/algorithmConfig_greedyWithRegret.xml
Executable file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Copyright (C) 2014 Stefan Schroeder
|
||||||
|
~
|
||||||
|
~ This library is free software; you can redistribute it and/or
|
||||||
|
~ modify it under the terms of the GNU Lesser General Public
|
||||||
|
~ License as published by the Free Software Foundation; either
|
||||||
|
~ version 3.0 of the License, or (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This library is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
~ Lesser General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Lesser General Public
|
||||||
|
~ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
|
||||||
|
|
||||||
|
<algorithm xmlns="http://www.w3schools.com"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com algorithm_schema.xsd">
|
||||||
|
|
||||||
|
<iterations>2000</iterations>
|
||||||
|
|
||||||
|
<construction>
|
||||||
|
<insertion name="regretInsertion">
|
||||||
|
|
||||||
|
</insertion>
|
||||||
|
</construction>
|
||||||
|
|
||||||
|
<strategy>
|
||||||
|
<memory>1</memory>
|
||||||
|
<searchStrategies>
|
||||||
|
|
||||||
|
<searchStrategy name="randomRuinAndRecreate">
|
||||||
|
<selector name="selectBest"/>
|
||||||
|
<acceptor name="greedyAcceptance"/>
|
||||||
|
<modules>
|
||||||
|
<module name="ruin_and_recreate">
|
||||||
|
<ruin name="randomRuin">
|
||||||
|
<share>0.5</share>
|
||||||
|
</ruin>
|
||||||
|
<insertion name="bestInsertion"/>
|
||||||
|
</module>
|
||||||
|
</modules>
|
||||||
|
<probability>0.5</probability>
|
||||||
|
</searchStrategy>
|
||||||
|
|
||||||
|
<searchStrategy name="radialRuinAndRecreate">
|
||||||
|
<selector name="selectBest"/>
|
||||||
|
<acceptor name="greedyAcceptance"/>
|
||||||
|
<modules>
|
||||||
|
<module name="ruin_and_recreate">
|
||||||
|
<ruin name="radialRuin">
|
||||||
|
<share>0.3</share>
|
||||||
|
</ruin>
|
||||||
|
<insertion name="bestInsertion"/>
|
||||||
|
</module>
|
||||||
|
</modules>
|
||||||
|
<probability>0.5</probability>
|
||||||
|
</searchStrategy>
|
||||||
|
</searchStrategies>
|
||||||
|
</strategy>
|
||||||
|
|
||||||
|
|
||||||
|
</algorithm>
|
||||||
66
jsprit-core/src/test/resources/algorithmConfig_greedyWithRegret_v2.xml
Executable file
66
jsprit-core/src/test/resources/algorithmConfig_greedyWithRegret_v2.xml
Executable file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Copyright (C) 2014 Stefan Schroeder
|
||||||
|
~
|
||||||
|
~ This library is free software; you can redistribute it and/or
|
||||||
|
~ modify it under the terms of the GNU Lesser General Public
|
||||||
|
~ License as published by the Free Software Foundation; either
|
||||||
|
~ version 3.0 of the License, or (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This library is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
~ Lesser General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Lesser General Public
|
||||||
|
~ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
|
||||||
|
|
||||||
|
<algorithm xmlns="http://www.w3schools.com"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com algorithm_schema.xsd">
|
||||||
|
|
||||||
|
<iterations>2000</iterations>
|
||||||
|
|
||||||
|
<construction>
|
||||||
|
<insertion name="regretInsertion">
|
||||||
|
|
||||||
|
</insertion>
|
||||||
|
</construction>
|
||||||
|
|
||||||
|
<strategy>
|
||||||
|
<memory>1</memory>
|
||||||
|
<searchStrategies>
|
||||||
|
|
||||||
|
<searchStrategy name="randomRuinAndRecreate">
|
||||||
|
<selector name="selectBest"/>
|
||||||
|
<acceptor name="greedyAcceptance"/>
|
||||||
|
<modules>
|
||||||
|
<module name="ruin_and_recreate">
|
||||||
|
<ruin name="randomRuin">
|
||||||
|
<share>0.5</share>
|
||||||
|
</ruin>
|
||||||
|
<insertion name="bestInsertion"/>
|
||||||
|
</module>
|
||||||
|
</modules>
|
||||||
|
<probability>0.5</probability>
|
||||||
|
</searchStrategy>
|
||||||
|
|
||||||
|
<searchStrategy name="radialRuinAndRecreate">
|
||||||
|
<selector name="selectBest"/>
|
||||||
|
<acceptor name="greedyAcceptance"/>
|
||||||
|
<modules>
|
||||||
|
<module name="ruin_and_recreate">
|
||||||
|
<ruin name="radialRuin">
|
||||||
|
<share>0.3</share>
|
||||||
|
</ruin>
|
||||||
|
<insertion name="regretInsertion"/>
|
||||||
|
</module>
|
||||||
|
</modules>
|
||||||
|
<probability>0.5</probability>
|
||||||
|
</searchStrategy>
|
||||||
|
</searchStrategies>
|
||||||
|
</strategy>
|
||||||
|
|
||||||
|
|
||||||
|
</algorithm>
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
<ruin name="randomRuin">
|
<ruin name="randomRuin">
|
||||||
<share>0.5</share>
|
<share>0.5</share>
|
||||||
</ruin>
|
</ruin>
|
||||||
<insertion name="regretInsertion"/>
|
<insertion name="bestInsertion"/>
|
||||||
</module>
|
</module>
|
||||||
</modules>
|
</modules>
|
||||||
<probability>0.5</probability>
|
<probability>0.5</probability>
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
<ruin name="radialRuin">
|
<ruin name="radialRuin">
|
||||||
<share>0.3</share>
|
<share>0.3</share>
|
||||||
</ruin>
|
</ruin>
|
||||||
<insertion name="regretInsertion"/>
|
<insertion name="bestInsertion"/>
|
||||||
</module>
|
</module>
|
||||||
</modules>
|
</modules>
|
||||||
<probability>0.5</probability>
|
<probability>0.5</probability>
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ public class SolomonWithRegretInsertionExample {
|
||||||
*
|
*
|
||||||
* The algorithm can be defined and configured in an xml-file.
|
* The algorithm can be defined and configured in an xml-file.
|
||||||
*/
|
*/
|
||||||
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, 2, "input/algorithmConfig_greedyWithRegret.xml");
|
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "input/algorithmConfig_greedyWithRegret.xml");
|
||||||
vra.setMaxIterations(50);
|
vra.setMaxIterations(2);
|
||||||
|
|
||||||
AlgorithmEventsRecorder eventsRecorder = new AlgorithmEventsRecorder(vrp,new File("output/events.dgs.gz"));
|
AlgorithmEventsRecorder eventsRecorder = new AlgorithmEventsRecorder(vrp,new File("output/events.dgs.gz"));
|
||||||
eventsRecorder.setRecordingRange(0,50);
|
eventsRecorder.setRecordingRange(0,50);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue