added a run command to the cli; closes #1246

This commit is contained in:
hneemann 2024-01-12 11:31:01 +01:00
parent b1650bd007
commit 61cdd9db3c
7 changed files with 121 additions and 5 deletions

View File

@ -1,6 +1,7 @@
Release Notes
HEAD, planned as v0.31
- Added a run command to the cli to run circuit headless
- Main open dialog is able to open FSM and Truth Tables
- FSM editor highlights the current transition
- Adds drivers with inverted output

View File

@ -20,6 +20,7 @@ public class Main extends Muxer {
addCommand(new CommandLineTester.TestCommand());
addCommand(new SVGExport());
addCommand(new StatsExport());
addCommand(new Runner());
}
/**

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.cli;
import de.neemann.digital.FileLocator;
import de.neemann.digital.cli.cli.Argument;
import de.neemann.digital.cli.cli.BasicCommand;
import de.neemann.digital.cli.cli.CLIException;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.ModelEventType;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.wiring.Clock;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.draw.model.RealTimeClock;
import de.neemann.digital.gui.ProgramMemoryLoader;
import de.neemann.digital.lang.Lang;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
* Is used to run a circuit headless
*/
public class Runner extends BasicCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(Runner.class);
private final Argument<String> digFile;
/**
* Creates the run command
*/
public Runner() {
super("run");
digFile = addArgument(new Argument<>("dig", "", false));
}
@Override
protected void execute() throws CLIException {
try {
final CircuitLoader circuitLoader = new CircuitLoader(digFile.get(), false);
final Circuit circuit = circuitLoader.getCircuit();
long time = System.currentTimeMillis();
ModelCreator modelCreator = new ModelCreator(circuit, circuitLoader.getLibrary());
Model model = modelCreator.createModel(true);
time = System.currentTimeMillis() - time;
LOGGER.debug("model creation: " + time + " ms, " + model.getNodes().size() + " nodes");
ArrayList<Clock> clocks = model.getClocks();
if (clocks.size() == 0)
throw new CLIException(Lang.get("cli_run_noClock"), null);
ScheduledThreadPoolExecutor timerExecutor = new ScheduledThreadPoolExecutor(1);
int threadRunnerCount = 0;
boolean realTimeClockRunning = false;
for (Clock c : clocks) {
int frequency = c.getFrequency();
if (frequency > 0) {
final RealTimeClock realTimeClock = new RealTimeClock(model, c, timerExecutor, null);
if (realTimeClock.isThreadRunner()) threadRunnerCount++;
realTimeClockRunning = true;
}
}
if (threadRunnerCount > 1)
throw new CLIException(Lang.get("err_moreThanOneFastClock"), null);
if (!realTimeClockRunning) {
throw new CLIException(Lang.get("cli_run_noClock"), null);
}
ElementAttributes settings = circuit.getAttributes();
if (settings.get(Keys.PRELOAD_PROGRAM)) {
File romHex = new FileLocator(settings.get(Keys.PROGRAM_TO_PRELOAD))
.setBaseFile(new File(digFile.get()))
.locate();
new ProgramMemoryLoader(romHex, settings.get(Keys.BIG_ENDIAN_SETTING))
.preInit(model);
}
model.addObserver(event -> {
if (event.getType() == ModelEventType.POSTCLOSED) {
timerExecutor.shutdownNow();
}
}, ModelEventType.POSTCLOSED);
model.init();
} catch (Exception e) {
throw new CLIException(Lang.get("cli_errorRunningCircuit"), e);
}
}
}

View File

@ -92,7 +92,7 @@ public class RealTimeClock implements ModelStateObserverTyped {
RealTimeRunner(int delay) {
FrequencyCalculator frequencyCalculator;
if (frequency > 2000)
if (frequency > 2000 && status != null)
frequencyCalculator = new FrequencyCalculator(status, frequency);
else
frequencyCalculator = null;
@ -120,10 +120,13 @@ public class RealTimeClock implements ModelStateObserverTyped {
ThreadRunner() {
thread = new Thread(() -> {
LOGGER.debug("thread start");
FrequencyCalculator frequencyCalculator = new FrequencyCalculator(status, frequency);
FrequencyCalculator frequencyCalculator = null;
if (status != null)
frequencyCalculator = new FrequencyCalculator(status, frequency);
while (!Thread.interrupted()) {
model.modify(() -> output.setValue(1 - output.getValue()));
frequencyCalculator.calc();
if (frequencyCalculator != null)
frequencyCalculator.calc();
}
});
thread.setDaemon(true);

View File

@ -31,9 +31,10 @@ public class ProgramMemoryLoader implements ModelModifier {
/**
* Creates a new rom modifier
*
* @param romHex the file to load
* @param romHex the file to load
* @param bigEndian reads the file in big endian mode
*/
ProgramMemoryLoader(File romHex, boolean bigEndian) {
public ProgramMemoryLoader(File romHex, boolean bigEndian) {
this.romHex = romHex;
this.bigEndian = bigEndian;
}

View File

@ -1813,6 +1813,11 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
</string>
<string name="cli_errorCreatingStats">Fehler bei der Erzeugung der CSV Datei!</string>
<string name="cli_run_noClock">Kein aktivierter Takt in der Schaltung gefunden!</string>
<string name="cli_errorRunningCircuit">Fehler beim Starten der Schaltung!</string>
<string name="cli_help_run">Start eine Schaltung headless.</string>
<string name="cli_help_run_dig">Der Dateiname der Schaltung.</string>
<string name="menu_window">Fenster</string>
<string name="menu_about">Über Digital</string>
<string name="menu_analyse">Analyse</string>

View File

@ -1793,6 +1793,11 @@
</string>
<string name="cli_errorCreatingStats">Error while creating the stats file!</string>
<string name="cli_run_noClock">No running clock found in circuit!</string>
<string name="cli_errorRunningCircuit">Error running the circuit!</string>
<string name="cli_help_run">Runs a circuit headless.</string>
<string name="cli_help_run_dig">File name of the circuit.</string>
<string name="menu_window">Windows</string>
<string name="menu_about">About</string>
<string name="menu_analyse">Analysis</string>