Compare commits

..

No commits in common. 'master' and 'lookup-tables' have entirely different histories.

  1. 2
      pom.xml
  2. 6
      resources/css/style.css
  3. 82
      resources/js/plan.js
  4. 4
      resources/logback.xml
  5. 39
      resources/translations/Application.de.translation
  6. 1
      src/main/java/de/srsoftware/web4rail/BaseClass.java
  7. 1
      src/main/java/de/srsoftware/web4rail/Constants.java
  8. 138
      src/main/java/de/srsoftware/web4rail/Destination.java
  9. 5
      src/main/java/de/srsoftware/web4rail/LookupTable.java
  10. 2
      src/main/java/de/srsoftware/web4rail/Plan.java
  11. 8
      src/main/java/de/srsoftware/web4rail/Route.java
  12. 40
      src/main/java/de/srsoftware/web4rail/Store.java
  13. 16
      src/main/java/de/srsoftware/web4rail/actions/AbortActions.java
  14. 2
      src/main/java/de/srsoftware/web4rail/actions/Action.java
  15. 8
      src/main/java/de/srsoftware/web4rail/actions/ActionList.java
  16. 86
      src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java
  17. 2
      src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java
  18. 2
      src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java
  19. 6
      src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java
  20. 2
      src/main/java/de/srsoftware/web4rail/actions/DisableEnableTile.java
  21. 2
      src/main/java/de/srsoftware/web4rail/actions/LookupValue.java
  22. 2
      src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java
  23. 35
      src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java
  24. 2
      src/main/java/de/srsoftware/web4rail/actions/SetPower.java
  25. 76
      src/main/java/de/srsoftware/web4rail/actions/SetValue.java
  26. 40
      src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java
  27. 6
      src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java
  28. 15
      src/main/java/de/srsoftware/web4rail/conditions/Condition.java
  29. 74
      src/main/java/de/srsoftware/web4rail/conditions/StoreHasValue.java
  30. 2
      src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java
  31. 22
      src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java
  32. 174
      src/main/java/de/srsoftware/web4rail/moving/Train.java
  33. 8
      src/main/java/de/srsoftware/web4rail/tags/Window.java
  34. 2
      src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java
  35. 32
      src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java
  36. 12
      src/main/java/de/srsoftware/web4rail/tiles/Block.java
  37. 13
      src/main/java/de/srsoftware/web4rail/tiles/BlockH.java
  38. 13
      src/main/java/de/srsoftware/web4rail/tiles/BlockV.java
  39. 6
      src/main/java/de/srsoftware/web4rail/tiles/Switch.java
  40. 36
      src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java
  41. 18
      src/main/java/de/srsoftware/web4rail/tiles/Tile.java

2
pom.xml

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.srsoftware</groupId>
<artifactId>web4rail</artifactId>
<version>1.5.39</version>
<version>1.5.23</version>
<name>Web4Rail</name>
<packaging>jar</packaging>
<description>Java Model Railway Control</description>

6
resources/css/style.css

@ -99,11 +99,6 @@ svg.preview rect{ @@ -99,11 +99,6 @@ svg.preview rect{
fill:cyan;
}
polygon.direction {
fill: rgba(0,0,0,0.4) !important;
stroke: none;
}
svg rect.sig_a,
svg rect.sig_b{
fill: inherit;
@ -178,7 +173,6 @@ svg circle{ @@ -178,7 +173,6 @@ svg circle{
border-radius: 5px;
background: yellow;
z-index: 100;
font-size: 13px;
}
h2{

82
resources/js/plan.js

@ -9,7 +9,6 @@ const POST = 'POST'; @@ -9,7 +9,6 @@ const POST = 'POST';
const PROPS = 'props';
const SQUARE = 30;
const SVG = 'svg';
const MSG_TIME = 5000;
var selected = null;
var mode = null;
var messageTimer = null;
@ -17,24 +16,6 @@ var messageOpacity = 0; @@ -17,24 +16,6 @@ var messageOpacity = 0;
var pendingAssignment = null;
var clickHistory = [];
var messages = [];
var android = false;
function addBlockDirections(){
$('.BlockH').each(function (){
let block = $(this);
let width = block.attr('viewBox').split(" ")[2];
block.append('<polygon points="0,0 100,50 0,100" class="direction"></polygon>');
block.append('<polygon points="'+width+',0 '+(width-100)+',50 '+width+',100" class="direction"></polygon>');
block.html(block.html());
});
$('.BlockV').each(function (){
let block = $(this);
let height = block.attr('viewBox').split(" ")[3];
block.append('<polygon points="0,0 50,100 100,0" class="direction"></polygon>');
block.append('<polygon points="0,'+height+' 50,'+(height-100)+' 100,'+height+'" class="direction"></polygon>');
block.html(block.html());
});
}
function addClass(data){
parts = data.split(" ");
@ -42,18 +23,10 @@ function addClass(data){ @@ -42,18 +23,10 @@ function addClass(data){
}
function addMessage(txt){
let delay = MSG_TIME;
if (txt.includes('⌛')){
let parts = txt.split('⌛');
delay = parseInt(parts[0]);
txt = parts[1];
}
let mid = 'm'+Date.now();
$('#messages').append($('<div/>').attr('id',mid).html(txt));
setTimeout(function(){
$('#'+mid).remove();
},delay);
if (delay != MSG_TIME) countDown(mid,delay/1000,txt);
messages.unshift(txt);
if (messages.length>5) messages.pop();
updateMessages();
setTimeout(fadeMessage,60000);
}
function addTile(x,y){
@ -89,14 +62,15 @@ function assign(context){ @@ -89,14 +62,15 @@ function assign(context){
pendingAssignment = context;
closeWindows();
$(PLAN).css('cursor','help');
if (context.assign == 'destination') addBlockDirections();
return false;
}
function changeSpeed(inputId){
console.log(inputId);
let parts = inputId.split('_');
let val = $('#'+inputId).val();
let data = { realm : parts[0], id : parts[1], action : "setSpeed", speed : val};
console.log(data);
let caption = $('#'+inputId+'_caption');
caption.text(caption.text().replace(/\d+/,val));
request(data);
@ -108,7 +82,7 @@ function clickLegend(ev){ @@ -108,7 +82,7 @@ function clickLegend(ev){
$(ev.target).addClass('front');
$('.window > fieldset').hide();
$('#'+lastTab).show();
if (!android) $('#'+lastTab+" input:not([type=hidden])").first().focus().select();
$('#'+lastTab+" input:not([type=hidden])").first().focus().select();
if (!('no-update' in ev)) remember(lastTab);
}
@ -120,11 +94,12 @@ function clickTile(x,y,shift){ @@ -120,11 +94,12 @@ function clickTile(x,y,shift){
if (pendingAssignment != null) {
var key = pendingAssignment.assign;
delete pendingAssignment.assign;
console.log("assigning key:",key);
pendingAssignment[key] = id;
console.log("pA:",pendingAssignment);
request(pendingAssignment);
pendingAssignment = null;
$(PLAN).css('cursor','');
$('.direction').remove();
return false;
}
var json = {realm:'plan',action:'click',id:id};
@ -159,15 +134,6 @@ function copyCv(ev){ @@ -159,15 +134,6 @@ function copyCv(ev){
$('input[name=val]').val(td.innerText);
}
function countDown(mid,time,msg){
time = parseInt(time);
$('#'+mid).html(msg.replace('%secs%',time));
time = time -1;
if (time>0) setTimeout(function(){
countDown(mid,time,msg);
},1000);
}
function dropClass(data){
var parts = data.split(" ");
for (var i=1; i<parts.length; i++) $('#'+parts[0]).removeClass(parts[i]);
@ -203,6 +169,24 @@ function enableMove(ev){ @@ -203,6 +169,24 @@ function enableMove(ev){
return false; // otherwise body.click would also be triggered
}
function fadeMessage(){
if (messages.length > 1) {
messages.pop();
updateMessages();
} else {
messageOpacity -= 5;
if (messageOpacity < 1) {
messages.pop();
updateMessages();
return;
}
messageTimer = setTimeout(fadeMessage,100);
var o = messageOpacity;
if (o>OPAC) o=OPAC;
$('#messages').css('opacity',o/OPAC);
}
}
function getCookie(key) {
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
return keyValue ? keyValue[2] : null;
@ -248,7 +232,7 @@ function place(data){ @@ -248,7 +232,7 @@ function place(data){
}
function planClick(ev){
//console.log('planClick:',ev);
console.log('planClick:',ev);
var plan=$('#scroll').get(0);
var x = Math.floor((plan.scrollLeft+ev.clientX)/SQUARE);
var y = Math.floor((plan.scrollTop+ev.clientY)/SQUARE);
@ -266,7 +250,7 @@ function planClick(ev){ @@ -266,7 +250,7 @@ function planClick(ev){
}
function remember(lastClickedId){
//console.log("lastClickedId: "+lastClickedId)
console.log("lastClickedId: "+lastClickedId)
var index = clickHistory.indexOf(lastClickedId);
if (index > -1) clickHistory.splice(index, 1);
clickHistory.unshift(lastClickedId);
@ -278,7 +262,7 @@ function remove(id){ @@ -278,7 +262,7 @@ function remove(id){
}
function request(data){
//console.log("request:",data);
console.log("request:",data);
$.ajax({
url : 'plan',
method : POST,
@ -325,7 +309,7 @@ function runAction(ev){ @@ -325,7 +309,7 @@ function runAction(ev){
function stream(ev){
var data = ev.data;
data = data.replace(/%newline%/g,"\n");
console.log("received: ",data);
if (data.startsWith('<svg')) return place(data);
if (data.startsWith("heartbeat")) return heartbeat(data);
if (data.startsWith("place ")) return place(data.substring(6));
@ -344,7 +328,7 @@ function stream(ev){ @@ -344,7 +328,7 @@ function stream(ev){
}
function submitForm(formId){
//console.log("submitForm("+formId+")");
console.log("submitForm("+formId+")");
return request($('#'+formId).serialize());
}
@ -377,7 +361,7 @@ function updateMessages() { @@ -377,7 +361,7 @@ function updateMessages() {
}
window.onload = function () {
android = navigator.userAgent.toLowerCase().includes('android')
var isDragging = false;
$('.menu > div').click(closeMenu);
$('.menu .addtile .list svg').click(enableAdding);
$('.menu .move .list div').click(enableMove);

4
resources/logback.xml

@ -8,8 +8,8 @@ @@ -8,8 +8,8 @@
</pattern>
</encoder>
<filter class="de.srsoftware.web4rail.ThreadFilter">
<level>WARN</level>
<keywords>BaseClass, ControlUnit, RoutePrepper, Tile</keywords>
<level>DEBUG</level>
<keywords>Brake, Contact, Feed, Route, e, u</keywords>
</filter>
</appender>

39
resources/translations/Application.de.translation

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
abort : abbrechen
AbortActions : Aktions-Ausführung abbrechen
Aborting route allocation... : Routen-Reservierung wird abgebrochen...
Accessory : Zubehör
Action : Aktion
@ -50,7 +49,6 @@ AlterDirection : Fahrtrichtung ändern @@ -50,7 +49,6 @@ AlterDirection : Fahrtrichtung ändern
AndCondition : Und-Bedingung
Application will load "{}" on next launch and will now quit! : Anwendung wird beim nächsten Start „{}“ laden und wird jetzt beendet.
Apply : Übernehmen
Are you sure you want to delete {}? : Sind Sie sicher, dass Sie {} löschen möchten?
Aspect : Signalbild
Aspects : Signalbilder
Auto pilot : Autopilot
@ -87,7 +85,6 @@ Cars : Fahrzeuge @@ -87,7 +85,6 @@ Cars : Fahrzeuge
&nbsp;cars : &nbsp;Fahrzeugen teilen
Clear destinations : Ziele löschen
Clear destinations of train : Ziele des Zugs löschen
Clear trigger : Trigger löschen
Clicked tile is not a {}! : Angeklickte Kachel ist kein {}!
Click here to add conditions : Hier klicken, um Bedingungen hinzuzufügen
Click here to select block! : Hier klicken, um Block auszuwählen!
@ -95,12 +92,10 @@ Click here to select car! : Hier klicken, um Fahrzeug auszuwählen! @@ -95,12 +92,10 @@ Click here to select car! : Hier klicken, um Fahrzeug auszuwählen!
Click here to select display! : Hier klicken, um Anzeige auszuwählen!
Click here to select switch! : Hier klicken, um Schalter auszuwählen!
Click here to select train! : Hier klicken, um Zug auszuwählen!
Click here to setup assignment : Hier klicken, um Zuweisung zu bearbeiten!
Click here to setup contact/switch : Hier klicken, um Kontakt/Schalter auszuwählen
Click here to setup contact : Hier klicken, um Kontakt auszuwählen
Click here to setup decoupler : Hier klicken, um Entkuppler einzurichten
click here to setup relay or switch : Hier klicken, um Relais oder Schalter einzurichten
click here to setup signal : Hier klicken, um Signal einzurichten
Click setup store lookup : Hier klicken, um Speichervergleich einzurichten
Click here to setup tag : Hier klicken, um Markierung anzugeben
click here to setup turnout : Hier klicken, um Weiche einzurichten
Click on a name to edit the entry. : Klicke auf einen Namen, um einen Eintrag zu bearbeiten.
@ -149,7 +144,6 @@ DetermineCarAtPosition : Fahrzeug im Zug bestimmen @@ -149,7 +144,6 @@ DetermineCarAtPosition : Fahrzeug im Zug bestimmen
DetermineTrainInBlock : Zug im Block bestimmen
Determine, which car is at position {} of the current train : Bestimmen, welches Fahrzeug sich an Position {} des Zuges befindet
Determine, which train is in {} : Bestimmen, welcher Zug sich in {} befindet
Determine, which train is parked in {} : Bestimmen, welcher Zug in {} abgestellt ist
Direction : Richtung
Direction\: heading {} : Richtung: nach {}
directional : fahrtrichtungs-abhängig
@ -177,8 +171,8 @@ Effect : Effekt @@ -177,8 +171,8 @@ Effect : Effekt
else\: : falls nicht:
Emergency : Notfall
empty train : leerer Zug
enable : entsperren
enable {} : {} entsperren
enable : aktivieren
enable {} : {} aktivieren
Engage {} : {} aktivieren
EngageDecoupler : Entkuppler aktivieren
Enter new name for plan : Neuen Namen für den Plan eingeben
@ -197,7 +191,6 @@ Found {} routes. : {} Routen gefunden. @@ -197,7 +191,6 @@ Found {} routes. : {} Routen gefunden.
free : frei machen
FreeStartBlock : Start-Block freigeben
Free tiles behind train : Kacheln hinter dem Zug freigeben
{} from {} : {} von {}
Fullscreen : Vollbild
Function : Funktion
Function {} : Funktion {}
@ -217,7 +210,6 @@ internal contacts : interne Kontakte @@ -217,7 +210,6 @@ internal contacts : interne Kontakte
Interval : Intervall
inverted : invertiert
Inverts the direction {} is heading to. : Kehrt die Richtung, in welche {} fährt, um.
{} is neither a contact nor a switch! : {} ist weder ein Kontakt, noch ein Schalter!
{} is not a block! : {} ist kein Block!
{} is off : {} ist Aus
{} is on : {} ist Ein
@ -245,7 +237,6 @@ Locomotives and cars : Lokomotiven und Waggons @@ -245,7 +237,6 @@ Locomotives and cars : Lokomotiven und Waggons
lookup tables : Wertetabellen
Lookup table : Wertetabelle
LookupTable : Wertetabelle
LookupValue : Wert aus Tabelle laden
Loop : Wiederholung
Lower speed limit : Minimale Geschwindigkeit
Maintenance : Wartung
@ -356,7 +347,7 @@ Search new routes, update existing : Neue Routen suchen, bestehende Aktualisiere @@ -356,7 +347,7 @@ Search new routes, update existing : Neue Routen suchen, bestehende Aktualisiere
Seek in last : Durchsuche letzte
Select block : Block auswählen
Select car : Fahrzeug auswählen
Select contact or switch : Kontakt oder Schalter auswählen
Select contact : Kotakt auswählen
Select destination : Ziel wählen
Select display : Anzeige auswählen
Select from plan : Auf Plan auswählen
@ -364,7 +355,6 @@ Select function : Funktion wählen @@ -364,7 +355,6 @@ Select function : Funktion wählen
Select object : Objekt auswählen
Select relay : Relais auswählen
Select state : Status auswählen
Select store to read from\: : Speicher für Vergleich auswählen:
Select switch : Schalter auswählen
Select train : Zug auswählen
Select turnout : Weiche wählen
@ -379,19 +369,15 @@ SetRelayOrSwitch : Relais oder Schalter schalten @@ -379,19 +369,15 @@ SetRelayOrSwitch : Relais oder Schalter schalten
SetSignal : Signal stellen
SetSignalsToStop : Signale auf Halt stellen
SetSpeed : Geschwindigkeit ändern
Set speed to {} {} : Geschwindigkeit auf {} {} setzen
Set {} to {} {} : {} auf {} {} gesetzt
Set speed to {} {} : Geschwindigkeit auf {} {} setzen
Set {} to {} : {} auf {} setzen
Set "{}" to "{}" : „{}“ auf „{}“ setzen
Set "{}" to value from {} : Setze „{}“ auf Wert von {}
setting : Einstellung
Set train of {} as context : Setze Zug von {} als Kontext
SetPower : Strom schalten
Set speed to : Geschwindigkeit setzen
SetTurnout : Weiche stellen
Setup actions : Vorbereitung-Aktionen
SetValue : Wert zuweisen
show : zeigen
ShowText : Text anzeigen
Shunting : Rangieren
shunting destination : Rangier-Ziel
@ -412,9 +398,6 @@ Start actions : Start-Aktionen @@ -412,9 +398,6 @@ Start actions : Start-Aktionen
Stock ID : Inventarnummer
Stop settings : Halte-Einstellungen
Store : Speicher
Store "{}" does not have value "{}" : Wert des Speichers „{}“ ist nicht „{}”
Store "{}" has value "{}" : Wert des Speichers „{}“ ist gleich „{}“
StoreHasValue : Wert eines Speichers prüfen
Start autopilot : Autopilot starten
Start delay : Start-Verzögerung
Started {} : {} gestartet
@ -434,7 +417,7 @@ Swap order : Reihenfolge umkehren @@ -434,7 +417,7 @@ Swap order : Reihenfolge umkehren
Swap order of trains : Reihenfolge der Züge tauschen
Switch : Schalter
SwitchFunction : Funktion schalten
SwitchIsOn : Schalter ist an
SwitchIsOn : Schalter is an
Switch power off : Strom ausschalten
Switch power on : Strom anschalten
SYSTEM : Betriebssystem
@ -463,10 +446,8 @@ train has tag "{}" : Zug hat Markierung „{}“ @@ -463,10 +446,8 @@ train has tag "{}" : Zug hat Markierung „{}“
TrainHasTag : Zug hat Markierung
train is a push-pull train : Zug ist ein Wendezug
train is faster than {} {} : Zug ist schneller als {} {}
Train is in "{}" : Zug ist in „{}“
train is longer than {} {} : Zug ist länger als {} {}
train is not a push-pull train : Zug ist kein Wendezug
Train is not in "{}" : Zug ist nicht in „{}“
train is not shunting : Zug rangiert nicht
train is shorter than {} {} : Zug ist kürzer als {} {}
TrainIsShunting : Rangierfahrt
@ -481,10 +462,8 @@ Train speed : Zug-Geschwindigkeit @@ -481,10 +462,8 @@ Train speed : Zug-Geschwindigkeit
TrainWasInBlock : Zug war im Block
Trigger {} : {} betätigen
Trigger a feedback sensor to assign it with this contact! : Rückmeldekontakt auslösen, um ihn diesem Kontakt zuzuweisen!
TriggerContact : Kontakt oder Schalter auslösen
TriggerContact : Kontakt auslösen
Trigger contact to learn new contact : Kontakt auslösen, um neuen Kontakt zu lernen
Trigger Contact/Switch at destination : Kontakt/Schalter am Ziel auslösen
Triggers {} when reaching destination : Löst bei Erreichen des Ziels {} aus
Turn : Richtung wechseln
turn train : Richtung des Zuges Wechseln
Turn allowed : Wenden erlaubt
@ -506,17 +485,15 @@ Turns the train, as if it went through a loop. : Dreht den Zug, als wenn er eine @@ -506,17 +485,15 @@ Turns the train, as if it went through a loop. : Dreht den Zug, als wenn er eine
Type : Typ
Unknown action\: {} : Unbekannte Aktion: {}
Unknown decoder type : Unbekannter Decoder-Typ
Updating {}\: {} → {} : Aktualisiere {}: {} → {}
Use negative number to count from end. : Nutze negative Nummern, um von Ende zu zählen.
unset : ungesetzt
update : aktualisieren
value : Wert
Value : Wert
Values : Werte
WaitForContact : Auf Kontakt warten
Wait for {}, then : auf {} warten, dann
Wait {} ms, then : {} ms warten, dann
{} waiting %secs% secs. : {} wartet %secs% Sekunden.
{} waiting {} secs. : {} wartet {} Sekunden.
Wait times : Wartezeiten
Was not able to assign {} to {}! : Konnte {} nicht an {} zuweisen!
Was not able to lock {} : Konnte {} nicht reservieren

1
src/main/java/de/srsoftware/web4rail/BaseClass.java

@ -377,7 +377,6 @@ public abstract class BaseClass implements Constants{ @@ -377,7 +377,6 @@ public abstract class BaseClass implements Constants{
@SuppressWarnings("unchecked")
public static <T extends BaseClass> T get(Id id) {
if (isNull(id)) return null;
return (T) registry.get(id);
}

1
src/main/java/de/srsoftware/web4rail/Constants.java

@ -81,7 +81,6 @@ public interface Constants { @@ -81,7 +81,6 @@ public interface Constants {
public static final String ROUTE = "route";
public static final String SPEED = "speed";
public static final String STATE = "state";
public static final String SWITCH = "switch";
public static final String TIME = "time";
public static final String TURNOUT = "turnout";
public static final String TYPE = "type";

138
src/main/java/de/srsoftware/web4rail/Destination.java

@ -1,138 +0,0 @@ @@ -1,138 +0,0 @@
package de.srsoftware.web4rail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.srsoftware.web4rail.BaseClass.Id;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tiles.Block;
public class Destination {
private static final Logger LOG = LoggerFactory.getLogger(Destination.class);
private Direction enterDirection;
public Block block;
private boolean shunting = false;
private boolean turn = false;
public Destination(Block block, Direction enterFrom) {
if (block == null) throw new NullPointerException();
this.block = block;
this.enterDirection = enterFrom;
}
public Destination(Block block) {
this(block,null);
}
@SuppressWarnings("unused")
private Destination() {}
boolean accepts(Direction enterDirection) {
boolean result = this.enterDirection == null || enterDirection == null || this.enterDirection == enterDirection;
LOG.debug(BaseClass.t(result ? "{} accepts train from {}" : "{} does not accept train from {}",this,enterDirection.inverse()));
return result;
}
public String block() {
return block.id().toString();
};
public static String dropFirstFrom(String tag) {
if (BaseClass.isNull(tag)) return null;
String[] parts = tag.split(Train.DESTINATION_PREFIX,3);
if (parts.length<3) return null;
return Train.DESTINATION_PREFIX+parts[2];
}
public Destination enterFrom(Direction enterFrom) {
this.enterDirection = enterFrom;
return this;
}
public Direction enterFrom() {
return enterDirection;
}
public static Destination from(String tag) {
if (BaseClass.isNull(tag)) return null;
LOG.debug("→ processing \"{}\"...",tag);
String[] parts = tag.split(Train.DESTINATION_PREFIX,3);
if (parts.length<2) return null;
String firstTag = parts[1];
LOG.debug("processing first tag: {}",firstTag);
if (firstTag.length()<1) return null;
int pos = firstTag.indexOf(Train.FLAG_SEPARATOR);
String blockId = pos<0 ? firstTag : firstTag.substring(0,pos);
Block block = Block.get(new Id(blockId));
if (block == null) return null;
Destination destination = new Destination(block);
if (pos>0) {
String modifiers = firstTag.substring(pos+1);
for (int i=0; i<modifiers.length(); i++) {
switch (modifiers.charAt(i)) {
case Train.SHUNTING_FLAG: destination.shunting(true); break;
case Train.TURN_FLAG: destination.turn(true); break;
case '→': destination.enterFrom(Direction.WEST); break;
case '←': destination.enterFrom(Direction.EAST); break;
case '↓': destination.enterFrom(Direction.NORTH); break;
case '↑': destination.enterFrom(Direction.SOUTH); break;
}
}
}
return destination;
}
public Destination shunting(boolean enable) {
this.shunting = enable;
return this;
}
public boolean shunting() {
return shunting;
}
public String tag() {
StringBuilder flags = new StringBuilder();
if (turn) flags.append(Train.TURN_FLAG);
if (shunting) flags.append(Train.SHUNTING_FLAG);
if (enterDirection == Direction.EAST) flags.append('←');
if (enterDirection == Direction.WEST) flags.append('→');
if (enterDirection == Direction.NORTH)flags.append('↓');
if (enterDirection == Direction.SOUTH) flags.append('↑');
StringBuilder sb = new StringBuilder();
sb.append('@').append(block());
if (flags.length()>0) sb.append('+').append(flags);
return sb.toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (enterDirection == null) {
sb.append(block);
} else {
sb.append(BaseClass.t("{} from {}",block,enterDirection));
}
return sb.toString();
}
public void turn(boolean enable) {
this.turn = enable;
}
public boolean turn() {
return turn;
}
}

5
src/main/java/de/srsoftware/web4rail/LookupTable.java

@ -294,10 +294,7 @@ public class LookupTable extends BaseClass{ @@ -294,10 +294,7 @@ public class LookupTable extends BaseClass{
Vector<Object> head = new Vector<Object>();
head.add("");
if (LENGTH.equals(colType)) {
for (Object col : cols) {
if ("".equals(col)) continue;
head.add("&lt; "+col);
}
for (Object col : cols) head.add("&lt; "+col);
} else head.addAll(cols);
table.addHead(head.toArray());

2
src/main/java/de/srsoftware/web4rail/Plan.java

@ -976,7 +976,7 @@ public class Plan extends BaseClass{ @@ -976,7 +976,7 @@ public class Plan extends BaseClass{
* @param data
*/
public synchronized void stream(String data) {
String fixedData = data.replaceAll("\n", "%newline%").replaceAll("\r", "");
String fixedData = data.replaceAll("\n", "").replaceAll("\r", "");
new Thread("Plan") {
@Override
public void run() {

8
src/main/java/de/srsoftware/web4rail/Route.java

@ -390,14 +390,6 @@ public class Route extends BaseClass { @@ -390,14 +390,6 @@ public class Route extends BaseClass {
return endBlock;
}
public Direction endDirection() {
return endDirection;
}
public boolean endsAt(Destination destination) {
return isSet(destination) && endBlock == destination.block && destination.accepts(endDirection.inverse());
}
public void finish(Train train) {
LOG.debug("{}.finish()",this);
if (isSet(context)) {

40
src/main/java/de/srsoftware/web4rail/Store.java

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
package de.srsoftware.web4rail;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
@ -11,49 +9,27 @@ import de.srsoftware.web4rail.tags.Select; @@ -11,49 +9,27 @@ import de.srsoftware.web4rail.tags.Select;
public class Store {
public interface Listener{
public void storeUpdated();
}
private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(Store.class);
private static HashMap<String,Store> stores = new HashMap<>();
private HashSet<Listener> listeners = new HashSet<>();
private String name;
private String value = null;
private Integer intValue = null;
private Integer value = null;
public Store(String name) {
this.name = name;
stores.put(name, this);
}
public Store addListener(Listener listener) {
listeners.add(listener);
return this;
}
public static Store get(String name) {
Store store = stores.get(name);
if (BaseClass.isNull(store)) store = new Store(name);
return store;
}
public Integer intValue() {
return intValue;
}
public String name() {
return name;
}
public static Set<String> names() {
return stores.keySet();
}
public static void removeListener(Listener listener) {
stores.values().forEach(store -> store.listeners.remove(listener));
}
public static Select selector(Store store) {
Select selector = new Select(Store.class.getSimpleName());
@ -69,19 +45,9 @@ public class Store { @@ -69,19 +45,9 @@ public class Store {
return BaseClass.t(txt, fills);
}
public void setValue(String newVal) {
public void setValue(int newVal) {
LOG.debug("Updating {}: {} → {}",name,value,newVal);
value = newVal;
intValue = null;
for (char c : value.toCharArray()) {
if (!Character.isDigit(c)) {
if (BaseClass.isSet(intValue)) break;
} else {
int add = ((byte)c-48);
intValue = BaseClass.isNull(intValue) ? add : 10*intValue + add;
}
}
listeners.forEach(listener -> listener.storeUpdated());
}
@Override
@ -89,7 +55,7 @@ public class Store { @@ -89,7 +55,7 @@ public class Store {
return name+"&nbsp;("+(BaseClass.isNull(value) ? t("no value") : value)+")";
}
public String value() {
public Integer value() {
return value;
}
}

16
src/main/java/de/srsoftware/web4rail/actions/AbortActions.java

@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
package de.srsoftware.web4rail.actions;
import de.srsoftware.web4rail.BaseClass;
public class AbortActions extends Action{
public AbortActions(BaseClass parent) {
super(parent);
}
@Override
public boolean fire(Context context) {
context.invalidate();
return false;
}
}

2
src/main/java/de/srsoftware/web4rail/actions/Action.java

@ -41,7 +41,6 @@ public abstract class Action extends BaseClass { @@ -41,7 +41,6 @@ public abstract class Action extends BaseClass {
public static List<Class<? extends Action>> classes() {
return List.of(
AbortActions.class,
AddRemoveDestination.class,
AddRemoveTag.class,
AlterDirection.class,
@ -67,7 +66,6 @@ public abstract class Action extends BaseClass { @@ -67,7 +66,6 @@ public abstract class Action extends BaseClass {
SetSignal.class,
SetSpeed.class,
SetTurnout.class,
SetValue.class,
ShowText.class,
SplitTrain.class,
StartStopAuto.class,

8
src/main/java/de/srsoftware/web4rail/actions/ActionList.java

@ -54,7 +54,7 @@ public class ActionList extends Action implements Iterable<Action>{ @@ -54,7 +54,7 @@ public class ActionList extends Action implements Iterable<Action>{
Action action = Action.create(type,this);
if (action instanceof Action) {
add(action);
return action.properties();
return context().properties();
}
return new Tag("span").content(t("Unknown action type: {}",type)).addTo(actionTypeForm());
}
@ -130,7 +130,7 @@ public class ActionList extends Action implements Iterable<Action>{ @@ -130,7 +130,7 @@ public class ActionList extends Action implements Iterable<Action>{
if (!isEmpty()) {
Tag list = new Tag("ol");
for (Action action : actions) {
Tag item = action.link("span",action, action.highlightId()).id(action.id().toString()).addTo(new Tag("li")).content(NBSP);
Tag item = action.link("span",action, action.highlightId()).addTo(new Tag("li")).content(NBSP);
action.button("↑", Map.of(ACTION,ACTION_MOVE)).title(t("move up")).addTo(item);
action.button("-", Map.of(ACTION,ACTION_DROP)).title(t("delete")).addTo(item);
if (action instanceof ActionList) ((ActionList) action).listAt(item);
@ -245,9 +245,7 @@ public class ActionList extends Action implements Iterable<Action>{ @@ -245,9 +245,7 @@ public class ActionList extends Action implements Iterable<Action>{
case ACTION_START:
return start(action);
case ACTION_UPDATE:
Object res = action.update(params);
if (res instanceof Window) ((Window) res).highlight(action);
return res;
return action.update(params);
}
return t("Unknown action: {}",command);
}

86
src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java

@ -8,27 +8,22 @@ import org.json.JSONObject; @@ -8,27 +8,22 @@ import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Destination;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Checkbox;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Switch;
import de.srsoftware.web4rail.tiles.Tile;
public class AddRemoveDestination extends Action {
private static final String TRIGGER = "destination_trigger";
private static final String TURN = "turn";
private static final String SHUNTING = "shunting";
private static final String FROM = "from";
private Destination destination;
private Tile destinationTrigger = null;
private Block destination;
private boolean turnAtDestination;
private boolean shunting;
public AddRemoveDestination(BaseClass parent) {
super(parent);
@ -46,7 +41,10 @@ public class AddRemoveDestination extends Action { @@ -46,7 +41,10 @@ public class AddRemoveDestination extends Action {
}
return true;
}
String dest = destination.tag();
String flags = "+";
if (turnAtDestination) flags += Train.TURN_FLAG;
if (shunting) flags += Train.SHUNTING_FLAG;
String dest = Train.DESTINATION_PREFIX+destination.id() + (flags.length()>1 ? flags : "");
for (String tag: train.tags()) {
if (tag.startsWith(Train.DESTINATION_PREFIX)) {
train.removeTag(tag);
@ -55,55 +53,35 @@ public class AddRemoveDestination extends Action { @@ -55,55 +53,35 @@ public class AddRemoveDestination extends Action {
}
}
train.addTag(dest);
train.setDestinationTrigger(destinationTrigger);
return true;
}
@Override
protected String highlightId() {
return isSet(destination) ? destination.block() : null;
return isSet(destination) ? destination.id().toString() : null;
}
@Override
public JSONObject json() {
JSONObject json = super.json();
if (isSet(destination)) {
json.put(Train.DESTINATION,destination.block());
if (destination.turn()) json.put(TURN,true);
if (destination.shunting()) json.put(SHUNTING, true);
if (isSet(destination.enterFrom())) json.put(FROM, destination.enterFrom());
}
if (isSet(destinationTrigger)) json.put(TRIGGER, destinationTrigger.id());
if (isSet(destination)) json.put(Train.DESTINATION,destination.id().toString());
if (turnAtDestination) json.put(TURN,true);
if (shunting) json.put(SHUNTING, true);
return json;
}
@Override
public Action load(JSONObject json) {
if (json.has(TURN)) turnAtDestination = json.getBoolean(TURN);
if (json.has(SHUNTING)) shunting = json.getBoolean(SHUNTING);
if (json.has(Train.DESTINATION)) new LoadCallback() {
@Override
public void afterLoad() {
Id id = Id.from(json, Train.DESTINATION);
Block block = Block.get(id);
if (isNull(block)) {
LOG.warn("Unknown block id \"{}\" encountered during AddRemoveDestination.load(json)",id);
return;
}
destination = new Destination(block);
if (json.has(TURN)) destination.turn(json.getBoolean(TURN));
if (json.has(SHUNTING)) destination.shunting(json.getBoolean(SHUNTING));
if (json.has(FROM)) destination.enterFrom(Direction.valueOf(json.getString(FROM)));
}
};
if (json.has(TRIGGER)) new LoadCallback() {
@Override
public void afterLoad() {
destinationTrigger = Tile.get(Id.from(json, TRIGGER));
destination = BaseClass.get(Id.from(json, Train.DESTINATION));
}
};
return super.load(json);
}
@ -113,23 +91,16 @@ public class AddRemoveDestination extends Action { @@ -113,23 +91,16 @@ public class AddRemoveDestination extends Action {
button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,Train.DESTINATION)).addTo(span);
button(t("Clear destinations"),Map.of(ACTION,ACTION_UPDATE,Train.DESTINATION,"0")).addTo(span);
formInputs.add(t("Destination")+": "+(isNull(destination) ? t("Clear destinations") : destination),span);
if (isSet(destination)) {
formInputs.add(t("Turn at destination"),new Checkbox(TURN, t("Turn"), destination.turn()));
formInputs.add(t("Shunting"),new Checkbox(SHUNTING, t("Shunting"), destination.shunting()));
}
span = new Tag("span");
button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,CONTACT)).addTo(span);
button(t("Clear trigger"),Map.of(ACTION,ACTION_UPDATE,CONTACT,"0")).addTo(span);
formInputs.add(t("Trigger Contact/Switch at destination")+": "+(isNull(destinationTrigger) ? t("unset") : destinationTrigger),span);
formInputs.add(t("Turn at destination"),new Checkbox(TURN, t("Turn"), turnAtDestination));
formInputs.add(t("Shunting"),new Checkbox(SHUNTING, t("Shunting"), shunting));
return super.properties(preForm, formInputs, postForm,errors);
}
@Override
public String toString() {
if (isNull(destination)) return t("Clear destinations of train");
String suffix = destination.turn() ? t("Turn") : null;
if (destination.shunting()) suffix = (isSet(suffix) ? suffix+" + " : "")+t("Shunting");
if (isSet(destinationTrigger)) suffix = (isSet(suffix) ? suffix+" + " : "")+t("Trigger {}",destinationTrigger);
String suffix = turnAtDestination ? t("Turn") : null;
if (shunting) suffix = (isSet(suffix) ? suffix+" + " : "")+t("Shunting");
return t("Add {} to destinations of train",destination)+(isSet(suffix) ? " ("+suffix+")" : "");
}
@ -142,27 +113,14 @@ public class AddRemoveDestination extends Action { @@ -142,27 +113,14 @@ public class AddRemoveDestination extends Action {
} else {
Tile tile = plan.get(new Id(destId), true);
if (tile instanceof Block) {
Block block = (Block) tile;
destination = new Destination(block,block.enterDirection(destId));
destination = (Block) tile;
} else {
return t("Clicked tile is not a {}!",t("block"));
}
}
}
if (params.containsKey(CONTACT)) {
Id id = Id.from(params,CONTACT);
if (id.equals(0)) {
destinationTrigger = null;
return properties();
}
Tile tile = Tile.get(id);
if (tile instanceof Contact || tile instanceof Switch) destinationTrigger = tile;
return properties();
}
if (isSet(destination)) {
destination.turn("on".equals(params.getString(TURN)));
destination.shunting("on".equals(params.getString(SHUNTING)));
}
turnAtDestination = "on".equals(params.getString(TURN));
shunting = "on".equals(params.getString(SHUNTING));
return context().properties();
}
}

2
src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java

@ -62,7 +62,7 @@ public class ConditionalAction extends ActionList { @@ -62,7 +62,7 @@ public class ConditionalAction extends ActionList {
public <T extends Tag> T listAt(T parent) {
T tag = super.listAt(parent);
if (!elseActions.isEmpty()) {
Tag div = new Tag("div").id(elseActions.id().toString()).clazz("else");
Tag div = new Tag("div").clazz("else");
new Tag("span").content(t("else:")+NBSP).addTo(div);
elseActions.listAt(div);
div.addTo(tag);

2
src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java

@ -34,7 +34,7 @@ public class DelayedAction extends ActionList { @@ -34,7 +34,7 @@ public class DelayedAction extends ActionList {
@Override
public boolean fire(Context context) {
try {
int delay = isSet(store) ? store.intValue() : min_delay + (min_delay < max_delay ? random.nextInt(max_delay - min_delay) : 0);
int delay = isSet(store) ? store.value() : min_delay + (min_delay < max_delay ? random.nextInt(max_delay - min_delay) : 0);
new DelayedExecution(delay,this) {

6
src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java

@ -76,21 +76,19 @@ public class DetermineTrainInBlock extends Action { @@ -76,21 +76,19 @@ public class DetermineTrainInBlock extends Action {
}
public String toString() {
if (isSet(block)) return parked ? t("Determine, which train is parked in {}",block) : t("Determine, which train is in {}",block);
return "["+t("Click here to select block!")+"]";
return isSet(block) ? t("Determine, which train is in {}",block) : "["+t("Click here to select block!")+"]";
};
@Override
protected Object update(Params params) {
LOG.debug("update: {}",params);
parked = ("on".equals(params.get(PARKED_TRAIN)));
if (params.containsKey(BLOCK)) {
Tile tile = plan.get(new Id(params.getString(BLOCK)), true);
if (tile instanceof Block) {
block = (Block) tile;
return properties();
} else return t("Clicked tile is not a {}!",t("block"));
}
parked = ("on".equals(params.get(PARKED_TRAIN)));
return context().properties();
}
}

2
src/main/java/de/srsoftware/web4rail/actions/DisableEnableTile.java

@ -89,6 +89,6 @@ public class DisableEnableTile extends Action { @@ -89,6 +89,6 @@ public class DisableEnableTile extends Action {
if (newTile instanceof Shadow) newTile = ((Shadow)newTile).overlay();
if (isSet(newTile)) tile = newTile;
disable = !"enable".equals(params.get(STATE));
return super.update(params);
return properties();
}
}

2
src/main/java/de/srsoftware/web4rail/actions/LookupValue.java

@ -30,7 +30,7 @@ public class LookupValue extends Action { @@ -30,7 +30,7 @@ public class LookupValue extends Action {
try {
if (!isSet(store,lookupTable)) return false;
int value = lookupTable.getValue(context);
store.setValue(""+value);
store.setValue(value);
} catch (NullPointerException npe) {
return false;
}

2
src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java

@ -23,7 +23,7 @@ public class PreserveRoute extends Action { @@ -23,7 +23,7 @@ public class PreserveRoute extends Action {
// These are NOT errors:
if (!train.usesAutopilot()) return true; // do not reserve routes, when not in auto-mode
Block endBlock = route.endBlock();
if (route.endsAt(train.destination())) return true; // do not reserve routes, when destination has been reached
if (train.destination() == endBlock) return true; // do not reserve routes, when destination has been reached
Integer waitTime = context.waitTime();
if (isSet(waitTime) && waitTime > 0) {

35
src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java

@ -7,7 +7,6 @@ import org.json.JSONObject; @@ -7,7 +7,6 @@ import org.json.JSONObject;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.moving.Car;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Window;
@ -15,7 +14,6 @@ import de.srsoftware.web4rail.tags.Window; @@ -15,7 +14,6 @@ import de.srsoftware.web4rail.tags.Window;
public class SetContextTrain extends Action {
private Train train = null;
private Car car = null;
public SetContextTrain(BaseClass parent) {
super(parent);
@ -23,9 +21,7 @@ public class SetContextTrain extends Action { @@ -23,9 +21,7 @@ public class SetContextTrain extends Action {
@Override
public boolean fire(Context context) {
Train t = isSet(train) ? train : car.train();
if (isNull(t)) return false;
context.train(t);
context.train(train);
return true;
}
@ -33,7 +29,6 @@ public class SetContextTrain extends Action { @@ -33,7 +29,6 @@ public class SetContextTrain extends Action {
public JSONObject json() {
JSONObject json = super.json();
if (isSet(train)) json.put(REALM_TRAIN, train.id());
if (isSet(car)) json.put(REALM_CAR, car.id());
return json;
}
@ -45,19 +40,12 @@ public class SetContextTrain extends Action { @@ -45,19 +40,12 @@ public class SetContextTrain extends Action {
train = Train.get(Id.from(json,REALM_TRAIN));
}
};
if (json.has(REALM_CAR)) new LoadCallback() {
@Override
public void afterLoad() {
car = Car.get(Id.from(json,REALM_CAR));
}
};
return super.load(json);
}
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Select train"),Train.selector(train, null));
formInputs.add(t("Select car"),Car.selector(car, null));
return super.properties(preForm, formInputs, postForm,errors);
}
@ -68,31 +56,14 @@ public class SetContextTrain extends Action { @@ -68,31 +56,14 @@ public class SetContextTrain extends Action {
}
public String toString() {
if (isSet(train)) return t("Set {} as context",train);
if (isSet(car)) return t("Set train of {} as context",car);
return "["+t("Click here to select train!")+"]";
return isSet(train) ? t("Set {} as context",train) : "["+t("Click here to select train!")+"]";
};
@Override
protected Object update(Params params) {
LOG.debug("update: {}",params);
Id trainId = Id.from(params,Train.class.getSimpleName());
if (isSet(trainId)) {
Train newTrain = Train.get(trainId);
if (newTrain != train) {
train = newTrain;
car = null;
params.remove(Car.class.getSimpleName());
}
}
Id carId = Id.from(params,Car.class.getSimpleName());
if (isSet(carId) && !carId.equals(0)) {
car = Car.get(carId);
train = null;
}
if (isSet(trainId)) train = Train.get(trainId);
return super.update(params);
}

2
src/main/java/de/srsoftware/web4rail/actions/SetPower.java

@ -85,6 +85,6 @@ public class SetPower extends Action{ @@ -85,6 +85,6 @@ public class SetPower extends Action{
LOG.debug("update: {}",params);
String newState = params.getString(STATE);
if (isSet(newState)) pc = POWERCHANGE.valueOf(newState);
return context().properties();
return parent().properties();
}
}

76
src/main/java/de/srsoftware/web4rail/actions/SetValue.java

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
package de.srsoftware.web4rail.actions;
import java.util.List;
import org.json.JSONObject;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Store;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Window;
public class SetValue extends Action {
private static final String VALUE = "value";
private static final String STORE = "store";
private String value = null;
private Store store = null;
public SetValue(BaseClass parent) {
super(parent);
}
@Override
public boolean fire(Context context) {
try {
if (!isSet(store,value)) return false;
store.setValue(value);
} catch (NullPointerException npe) {
return false;
}
return true;
}
@Override
public JSONObject json() {
JSONObject json = super.json();
if (isSet(value)) json.put(VALUE, value);
if (isSet(store)) json.put(STORE, store.name());
return json;
}
@Override
public Action load(JSONObject json) {
super.load(json);
if (json.has(VALUE)) value = json.getString(VALUE);
if (json.has(STORE)) store = Store.get(json.getString(STORE));
return this;
}
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Store"), new Input(STORE,isSet(store)?store.name():""));
formInputs.add(t("Value"), new Input(VALUE,isSet(value)?value:0));
return super.properties(preForm, formInputs, postForm,errors);
}
@Override
public String toString() {
if (isSet(store,value)) return t("Set \"{}\" to \"{}\"",store.name(),value);
return "["+t("Click here to setup assignment")+"]";
}
@Override
protected Object update(Params params) {
LOG.debug("update: {}",params);
String newValue = params.getString(VALUE);
if (isSet(newValue)) value = newValue;
String storeName = params.getString(STORE);
if (isSet(storeName) && !storeName.isEmpty()) store = Store.get(storeName);
return super.update(params);
}
}

40
src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java

@ -11,8 +11,6 @@ import de.srsoftware.web4rail.Params; @@ -11,8 +11,6 @@ import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Switch;
import de.srsoftware.web4rail.tiles.Tile;
public class TriggerContact extends Action {
@ -20,78 +18,64 @@ public class TriggerContact extends Action { @@ -20,78 +18,64 @@ public class TriggerContact extends Action {
super(parent);
}
private Tile contactOrSwitch = null;
private Contact contact = null;
@Override
public boolean fire(Context context) {
if (contactOrSwitch instanceof Contact) return ((Contact)contactOrSwitch).trigger(200);
if (contactOrSwitch instanceof Switch) return ((Switch)contactOrSwitch).trigger(context);
if (isSet(contact)) return contact.trigger(200);
return false;
}
@Override
protected String highlightId() {
return isSet(contactOrSwitch) ? contactOrSwitch.id().toString() : null;
return isSet(contact) ? contact.id().toString() : null;
}
@Override
public JSONObject json() {
JSONObject json = super.json();
if (contactOrSwitch instanceof Contact) json.put(CONTACT, contactOrSwitch.id());
if (contactOrSwitch instanceof Switch) json.put(SWITCH, contactOrSwitch.id());
if (isSet(contact)) json.put(CONTACT, contact.id());
return json;
}
@Override
public Action load(JSONObject json) {
super.load(json);
Id contactId = Id.from(json,CONTACT);
if (isSet(contactId)) new LoadCallback() {
if (json.has(CONTACT)) new LoadCallback() {
@Override
public void afterLoad() {
contactOrSwitch = Contact.get(Id.from(json,CONTACT));
contact = Contact.get(contactId);
}
};
if (json.has(SWITCH)) new LoadCallback() {
@Override
public void afterLoad() {
contactOrSwitch = Switch.get(Id.from(json,SWITCH));
}
};
return this;
}
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Select contact or switch")+": "+(isNull(contactOrSwitch) ? t("unset") : contactOrSwitch),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,CONTACT)));
formInputs.add(t("Select contact")+": "+(isNull(contact) ? t("unset") : contact),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,CONTACT)));
return super.properties(preForm, formInputs, postForm,errors);
}
@Override
protected void removeChild(BaseClass child) {
if (child == contactOrSwitch) contactOrSwitch = null;
if (child == contact) contact = null;
super.removeChild(child);
}
public String toString() {
return isSet(contactOrSwitch) ? t("Trigger {}",contactOrSwitch) : "["+t("Click here to setup contact/switch")+"]";
return isSet(contact) ? t("Trigger {}",contact) : "["+t("Click here to setup contact")+"]";
};
@Override
protected Object update(Params params) {
LOG.debug("update: {}",params);
Id contactId = Id.from(params,CONTACT);
String error = null;
if (isSet(contactId)) {
Tile tile = BaseClass.get(contactId);
if (tile instanceof Contact || tile instanceof Switch) {
contactOrSwitch = tile;
} else error = t("{} is neither a contact nor a switch!",tile);
}
return context().properties(error);
if (isSet(contactId)) contact = Contact.get(contactId);
return properties();
}
}

6
src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java

@ -25,10 +25,7 @@ public class BlockFree extends Condition { @@ -25,10 +25,7 @@ public class BlockFree extends Condition {
@Override
public boolean fulfilledBy(Context context) {
if (isNull(context)) return false;
if (isSet(context.train())) context = context.clone().train(null); // block.isFree would return true, if context.train == block.train. That has to be avoided!
if (!inverted) return block.isFreeFor(context);
return !block.isFreeFor(new Context(block)); // block.isFreeFor würde true liefern, wenn der Zug im Kontext gleich dem Zug im Block wäre. Da wir aber nur wissen wollen, ob der Block belegt ist, brauchen wir einen Context ohne Zug.
return block.isFreeFor(context) != inverted;
}
@Override
@ -72,7 +69,6 @@ public class BlockFree extends Condition { @@ -72,7 +69,6 @@ public class BlockFree extends Condition {
if (tile instanceof Block) {
block = (Block) tile;
} else return t("Clicked tile is not a {}!",t("block"));
return context().properties();
}
return super.update(params);

15
src/main/java/de/srsoftware/web4rail/conditions/Condition.java

@ -16,7 +16,6 @@ import de.srsoftware.web4rail.BaseClass; @@ -16,7 +16,6 @@ import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Plan;
import de.srsoftware.web4rail.actions.Action;
import de.srsoftware.web4rail.actions.ActionList;
import de.srsoftware.web4rail.tags.Checkbox;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Select;
@ -54,18 +53,7 @@ public abstract class Condition extends BaseClass { @@ -54,18 +53,7 @@ public abstract class Condition extends BaseClass {
case ACTION_PROPS:
return condition.properties();
case ACTION_UPDATE:
Object res = condition.update(params);
if (res instanceof Window) {
BaseClass parent = condition.parent();
while (isSet(parent)) {
if (parent instanceof ActionList) {
((Window) res).highlight(parent);
break;
}
parent = parent.parent();
}
}
return res;
return condition.update(params);
}
return t("Unknown action: {}",action);
}
@ -139,7 +127,6 @@ public abstract class Condition extends BaseClass { @@ -139,7 +127,6 @@ public abstract class Condition extends BaseClass {
OrCondition.class,
PushPullTrain.class,
RouteEndBlock.class,
StoreHasValue.class,
SwitchIsOn.class,
TrainHasTag.class,
TrainIsShunting.class,

74
src/main/java/de/srsoftware/web4rail/conditions/StoreHasValue.java

@ -1,74 +0,0 @@ @@ -1,74 +0,0 @@
package de.srsoftware.web4rail.conditions;
import java.util.List;
import org.json.JSONObject;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Store;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Window;
public class StoreHasValue extends Condition {
private static final String VALUE = "value";
private static final String STORE = "store";
private String value = null;
private Integer intValue = null;
private Store store = null;
private boolean evaluate() {
if (!isSet(store,value)) return false;
if (isSet(intValue)) return store.intValue() == intValue;
return value.equals(store.value());
}
@Override
public boolean fulfilledBy(Context context) {
return evaluate() != inverted;
}
@Override
public JSONObject json() {
JSONObject json = super.json();
if (isSet(value)) json.put(VALUE, value);
if (isSet(store)) json.put(STORE, store.name());
return json;
}
public Condition load(JSONObject json) {
super.load(json);
if (json.has(STORE)) store = Store.get(json.getString(STORE));
if (json.has(VALUE)) {
value = json.getString(VALUE);
try {
intValue = Integer.parseInt(value);
} catch (NumberFormatException nfe) {}
}
return this;
}
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Select store to read from:"),Store.selector(store));
formInputs.add(t("Value"),new Input(VALUE, isSet(value) ? value : ""));
return super.properties(preForm, formInputs, postForm,errors);
}
@Override
public String toString() {
if (isNull(store)) return "["+t("Click setup store lookup")+"]";
return t(inverted ? "Store \"{}\" does not have value \"{}\"" : "Store \"{}\" has value \"{}\"",store.name(),value) ;
}
@Override
protected Object update(Params params) {
String storeName = params.getString(Store.class.getSimpleName());
if (isSet(storeName)) store = Store.get(storeName);
String newVal = params.getString(VALUE);
if (isSet(newVal)) value = newVal;
return super.update(params);
}
}

2
src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java

@ -47,7 +47,7 @@ public class TrainHasTag extends Condition { @@ -47,7 +47,7 @@ public class TrainHasTag extends Condition {
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Tag"),new Input(TAG, isNull(tag) ? "" : tag));
formInputs.add(t("Tag"),new Input(TAG, tag == null ? "" : tag));
return super.properties(preForm, formInputs, postForm,errors);
}

22
src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java

@ -7,7 +7,6 @@ import org.json.JSONObject; @@ -7,7 +7,6 @@ import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset;
@ -32,8 +31,7 @@ public class TrainWasInBlock extends Condition { @@ -32,8 +31,7 @@ public class TrainWasInBlock extends Condition {
@Override
public boolean fulfilledBy(Context context) {
Train train = context.train();
if (isNull(train)) return false;
if (count == 0) return (train.currentBlock() == block) != inverted;
if (isNull(train)) return false;
return train.lastBlocks(count).contains(block) != inverted;
}
@ -44,14 +42,8 @@ public class TrainWasInBlock extends Condition { @@ -44,14 +42,8 @@ public class TrainWasInBlock extends Condition {
public Condition load(JSONObject json) {
super.load(json);
if (json.has(BLOCK)) block(Block.get(new Id(json.getString(BLOCK))));
if (json.has(COUNT)) count = json.getInt(COUNT);
new LoadCallback() {
@Override
public void afterLoad() {
if (json.has(BLOCK)) block(Block.get(Id.from(json, BLOCK)));
}
};
return this;
}
@ -71,22 +63,18 @@ public class TrainWasInBlock extends Condition { @@ -71,22 +63,18 @@ public class TrainWasInBlock extends Condition {
@Override
public String toString() {
if (block == null) return "["+t("Click here to select block!")+"]";
if (count == 0) return t(inverted ? "Train is not in \"{}\"" : "Train is in \"{}\"", block);
return t(inverted ? "{} not within last {} blocks of train":"{} within last {} blocks of train",block,count);
}
@Override
protected Object update(Params params) {
Id bid = Id.from(params, BLOCK);
if (!params.containsKey(BLOCK)) return t("No block id passed to TrainWasInBlock.update()!");
Id bid = new Id(params.getString(BLOCK));
Tile tile = BaseClass.get(bid);
if (tile instanceof Shadow) tile = ((Shadow)tile).overlay();
if (tile instanceof Block) block = (Block) tile;
if (params.containsKey(COUNT)) count=params.getInt(COUNT);
if (tile instanceof Block) {
block = (Block) tile;
super.update(params);
return properties();
}
return super.update(params);
}
}

174
src/main/java/de/srsoftware/web4rail/moving/Train.java

@ -9,7 +9,6 @@ import java.util.Collection; @@ -9,7 +9,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
@ -24,7 +23,6 @@ import org.slf4j.LoggerFactory; @@ -24,7 +23,6 @@ import org.slf4j.LoggerFactory;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Destination;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Plan;
@ -45,7 +43,6 @@ import de.srsoftware.web4rail.threads.DelayedExecution; @@ -45,7 +43,6 @@ import de.srsoftware.web4rail.threads.DelayedExecution;
import de.srsoftware.web4rail.threads.RoutePrepper;
import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Switch;
import de.srsoftware.web4rail.tiles.Tile;
/**
@ -87,8 +84,7 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -87,8 +84,7 @@ public class Train extends BaseClass implements Comparable<Train> {
private HashSet<String> tags = new HashSet<String>();
private Block currentBlock;
Destination destination = null;
private Block currentBlock,destination = null;
HashSet<Tile> trace = new HashSet<Tile>();
private Vector<Block> lastBlocks = new Vector<Block>();
@ -105,8 +101,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -105,8 +101,6 @@ public class Train extends BaseClass implements Comparable<Train> {
private BrakeProcess brake;
private Tile destinationTrigger;
public static Object action(Params params, Plan plan) throws IOException {
String action = params.getString(ACTION);
if (isNull(action)) return t("No action passed to Train.action!");
@ -175,10 +169,7 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -175,10 +169,7 @@ public class Train extends BaseClass implements Comparable<Train> {
}
public void addTag(String tag) {
if (isSet(tag)) {
tags.add(tag);
LOG.debug("added tag \"{}\" to {}",tag,this);
}
tags.add(tag);
}
private Object addCar(Params params) {
@ -328,7 +319,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -328,7 +319,6 @@ public class Train extends BaseClass implements Comparable<Train> {
public void coupleWith(Train parkingTrain,boolean swap) {
if (isSet(direction) && isSet(parkingTrain.direction) && parkingTrain.direction != direction) parkingTrain.turn();
boolean locoOnly = !cars.stream().anyMatch(car -> !(car instanceof Locomotive));
if (swap) {
Vector<Car> dummy = new Vector<Car>();
for (Car car : parkingTrain.cars) dummy.add(car.train(this));
@ -339,9 +329,9 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -339,9 +329,9 @@ public class Train extends BaseClass implements Comparable<Train> {
cars.add(car.train(this));
}
}
if (locoOnly && isSet(parkingTrain.name)) name = parkingTrain.name;
parkingTrain.remove();
updateEnds();
updateEnds();
if (isSet(currentBlock)) currentBlock.setTrain(this);
}
@ -369,12 +359,29 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -369,12 +359,29 @@ public class Train extends BaseClass implements Comparable<Train> {
return properties();
}
public Destination destination(){
public Block destination(){
//LOG.debug("{}.destination()",this);
if (isNull(destination)) {
destination = Destination.from(destinationTag());
LOG.debug("{}.destination() → {}",this,destination);
if (isSet(destination)) shunting |= destination.shunting();
}
String destTag = destinationTag();
LOG.debug("→ processing \"{}\"...",destTag);
if (isSet(destTag)) {
destTag = destTag.split(DESTINATION_PREFIX)[1];
LOG.debug("....processing \"{}\"…",destTag);
for (int i=destTag.length()-1; i>0; i--) {
switch (destTag.charAt(i)) {
case FLAG_SEPARATOR:
destTag = destTag.substring(0,i);
i=0;
break;
case SHUNTING_FLAG:
LOG.debug("....enabled shunting option");
shunting = true;
break;
}
}
destination = BaseClass.get(new Id(destTag));
}
}// else LOG.debug("→ heading towards {}",destination);
return destination;
}
@ -384,7 +391,7 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -384,7 +391,7 @@ public class Train extends BaseClass implements Comparable<Train> {
}
return null;
}
public String directedName() {
String result = name();
if (needsMainenance()) result+="⚠";
@ -404,10 +411,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -404,10 +411,6 @@ public class Train extends BaseClass implements Comparable<Train> {
public Direction direction() {
return direction;
}
public void disableShunting() {
shunting = false;
}
private Object dropCar(Params params) {
String carId = params.getString(CAR_ID);
@ -454,25 +457,48 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -454,25 +457,48 @@ public class Train extends BaseClass implements Comparable<Train> {
direction = endedRoute.endDirection; // muss vor der Auswertung des Destination-Tags stehen!
Block endBlock = endedRoute.endBlock();
Block startBlock = endedRoute.startBlock();
boolean resetDest = endedRoute.endsAt(destination);
LinkedList<Tile> triggerRef = new LinkedList<>();
if (resetDest){
if (endBlock == destination) {
destination = null;
String destTag = destinationTag();
Destination destinationFromTag = Destination.from(destTag);
if (endedRoute.endsAt(destinationFromTag)) {
if (destinationFromTag.turn()) turn();
removeTag(destTag);
destTag = Destination.dropFirstFrom(destTag);
addTag(destTag);
String destTag = destinationTag();
if (isSet(destTag)) {
LOG.debug("destination list: {}",destTag);
String[] parts = destTag.split(Train.DESTINATION_PREFIX);
for (int i=0; i<parts.length;i++) LOG.debug(" part {}: {}",i+1,parts[i]);
String destId = parts[1];
LOG.debug("destination tag: {}",destId);
boolean turn = false;
for (int i=destId.length()-1; i>0; i--) {
switch (destId.charAt(i)) {
case Train.FLAG_SEPARATOR:
destId = destId.substring(0,i);
i=0;
break;
case Train.TURN_FLAG:
turn = true;
LOG.debug("Turn flag is set!");
break;
}
}
if (destId.equals(endBlock.id().toString())) {
if (turn) turn();
// update destination tag: remove and add altered tag:
removeTag(destTag);
destTag = destTag.substring(parts[1].length()+1);
if (destTag.isEmpty()) { // no further destinations
destTag = null;
} else addTag(destTag);
}
}
if (isNull(destTag)) {
triggerRef.add(destinationTrigger); // quitAutopilot drops destinationTrigger
quitAutopilot();
quitAutopilot();
plan.stream(t("{} reached it`s destination!",this));
}
}
if (isSet(brake)) brake.updateTime();
Integer waitTime = route.waitTime();
@ -481,25 +507,14 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -481,25 +507,14 @@ public class Train extends BaseClass implements Comparable<Train> {
if ((!autopilot) || isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0);
route = null;
endBlock.setTrain(this);
shunting = false; // is used in endBlock.setTrain(…), this it must be set thereafter
if (resetDest) destination = null; // destination is called during endBlock.setTrain(…)
shunting = false; // wird in setTrain verwendet, muss also danach stehen
currentBlock = endBlock;
trace.add(endBlock);
if (!trace.contains(startBlock)) startBlock.dropTrain(this);
stuckTrace = null;
if (!triggerRef.isEmpty()) new DelayedExecution(1000,this) {
@Override
public void execute() {
Tile trigger = triggerRef.getFirst();
if (trigger instanceof Contact) ((Contact)trigger).trigger(200);
if (trigger instanceof Switch) ((Switch)trigger).trigger(new Context(Train.this));
}
};
if (autopilot) {
if (isNull(waitTime)) waitTime = 0;
if (waitTime>0) plan.stream(waitTime+"⌛"+t("{} waiting %secs% secs.",this));
if (waitTime>0) plan.stream(t("{} waiting {} secs",this,(int)(waitTime/1000)));
new DelayedExecution(waitTime,this) {
@Override
@ -709,27 +724,13 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -709,27 +724,13 @@ public class Train extends BaseClass implements Comparable<Train> {
public String name() {
if (isSet(name) && !name.isEmpty()) return name;
if (isSet(name)) return name;
if (cars.isEmpty()) return t("emtpy train");
StringBuffer sb = new StringBuffer();
String lastName = null;
int count = 0;
for (Car car : cars) {
String carName = car.name();
if (isNull(carName)) continue;
if (carName.equals(lastName)) {
count++;
} else {
if (count>1) sb.append("x").append(count);
count = 1;
lastName = carName;
sb.append(", ").append(carName);
}
String name = car.name();
if (isSet(name)) return name;
}
if (count>1) sb.append("x").append(count);
if (sb.length()>2) sb.delete(0, 2);
return sb.toString();
//return t("empty train");
return t("empty train");
}
private Train name(String newName) {
@ -761,13 +762,12 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -761,13 +762,12 @@ public class Train extends BaseClass implements Comparable<Train> {
Tag dest = new Tag("li").content(t("Destination")+COL);
if (isSet(destination)) {
link("span",destination,Map.of(REALM,REALM_PLAN,ID,destination.block(),ACTION,ACTION_CLICK),null).addTo(dest);
link("span",destination,Map.of(REALM,REALM_PLAN,ID,destination.id().toString(),ACTION,ACTION_CLICK),null).addTo(dest);
new Button(t("Drop"),Map.of(REALM,REALM_TRAIN,ID,id,ACTION,ACTION_MOVE,DESTINATION,"")).addTo(dest);
}
button(t("Select from plan"),Map.of(ACTION,ACTION_MOVE,ASSIGN,DESTINATION)).addTo(dest);
dest.addTo(propList);
if (isSet(destinationTrigger)) new Tag("li").content(t("Triggers {} when reaching destination",destinationTrigger)).addTo(propList);
if (isSet(route)) route.link("li", route).addTo(propList);
int ms = maxSpeed();
if (ms < Integer.MAX_VALUE) new Tag("li").content(t("Maximum Speed")+COL+maxSpeed()+NBSP+speedUnit).addTo(propList);
@ -787,7 +787,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -787,7 +787,6 @@ public class Train extends BaseClass implements Comparable<Train> {
ul.addTo(li).addTo(propList);
}
carList().addTo(propList);
formInputs.add(t("Name"), new Input(NAME,name()));
@ -805,7 +804,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -805,7 +804,6 @@ public class Train extends BaseClass implements Comparable<Train> {
}
public String quitAutopilot() {
destinationTrigger = null;
if (isSet(routePrepper)) {
routePrepper.stop();
routePrepper = null;
@ -837,7 +835,7 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -837,7 +835,7 @@ public class Train extends BaseClass implements Comparable<Train> {
if (child == route) route = null;
//if (child == nextRoute) nextRoute = null; // TODO
if (child == currentBlock) currentBlock = null;
if (isSet(destination) && child == destination.block) destination = null;
if (child == destination) destination = null;
if (child == routePrepper) routePrepper.stop();
cars.remove(child);
trace.remove(child);
@ -845,7 +843,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -845,7 +843,6 @@ public class Train extends BaseClass implements Comparable<Train> {
}
public Iterator<String> removeTag(String tag) {
LOG.debug("Removing tag \"{}\" from {}",tag,this);
tags.remove(tag);
return tags().iterator();
}
@ -916,29 +913,21 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -916,29 +913,21 @@ public class Train extends BaseClass implements Comparable<Train> {
if (dest.isEmpty()) {
destination = null;
return properties();
}
}
Tile tile = plan.get(new Id(dest), true);
if (isNull(tile)) return properties(t("Tile {} not known!",dest));
if (tile instanceof Block) {
Block block = (Block) tile;
Direction enterDirection = null;
if (shunting) {
boolean connection = currentBlock.routes().stream().anyMatch(route -> route.startBlock() == currentBlock && route.endBlock() == tile);
if (!connection) return t("No direct route from {} to {}",currentBlock,tile);
} else enterDirection = block.enterDirection(dest);
}
destination = new Destination(block,enterDirection);
destination = (Block) tile;
start(true);
return t("{} now heading for {}",this,destination);
}
return properties(t("{} is not a block!",tile));
}
public void setDestinationTrigger(Tile destinationTrigger) {
this.destinationTrigger = destinationTrigger;
}
public Object setFunction(int num, boolean active) {
// TODO
@ -966,13 +955,17 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -966,13 +955,17 @@ public class Train extends BaseClass implements Comparable<Train> {
public boolean splitAfter(int position) {
if (isNull(currentBlock)) return false; // can only split within blocks!
Train remaining = new Train();
remaining.name = name;
int len = cars.size();
for (int i=0; i<len; i++) {
if (i>=position) {
Car car = cars.remove(position);
LOG.debug("Moving {} from {} to {}",car,this,remaining);
remaining.add(car);
if (isNull(remaining.name)) {
remaining.name = car.name();
} else if (remaining.name.length()+car.name().length()<30){
remaining.name += ", "+car.name();
}
} else LOG.debug("Skipping {}",cars.get(i));
}
if (remaining.cars.isEmpty()) return false;
@ -1073,7 +1066,6 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -1073,7 +1066,6 @@ public class Train extends BaseClass implements Comparable<Train> {
public SortedSet<String> tags() {
TreeSet<String> list = new TreeSet<String>(tags);
for (Car car:cars) list.addAll(car.tags());
LOG.debug("{}.tags() → {}",this,list);
return list;
}
@ -1167,23 +1159,23 @@ public class Train extends BaseClass implements Comparable<Train> { @@ -1167,23 +1159,23 @@ public class Train extends BaseClass implements Comparable<Train> {
for (Tile tile : reversedPath) {
if (isNull(remainingLength) && onTrace(tile)) remainingLength = length();
if (remainingLength == null) { // ahead of train
//LOG.debug("{} is ahead of train and will not be touched.",tile);
LOG.debug("{} is ahead of train and will not be touched.",tile);
trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace
} else if (remainingLength > 0) { // within train
//LOG.debug("{} is occupied by train and will be marked as \"occupied\"",tile);
LOG.debug("{} is occupied by train and will be marked as \"occupied\"",tile);
remainingLength -= tile.length();
newTrace.add(tile);
trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace
tile.setTrain(this);
//LOG.debug("remaining length: {}",remainingLength);
LOG.debug("remaining length: {}",remainingLength);
} else { // behind train
if (Route.freeBehindTrain) {
//LOG.debug("{} is behind train and will be freed in the next step",tile);
LOG.debug("{} is behind train and will be freed in the next step",tile);
if (tile != route.endBlock()) { // if train is shorter than contact after endblock, the endblock would be cleared…
trace.add(tile); // old trace will be cleared afterwards
}
} else {
//LOG.debug("{} is behind train and will be reset to \"locked\" state",tile);
LOG.debug("{} is behind train and will be reset to \"locked\" state",tile);
tile.lockFor(context,true);
trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace
}

8
src/main/java/de/srsoftware/web4rail/tags/Window.java

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
package de.srsoftware.web4rail.tags;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
/**
* @author Stephan Richter, SRSoftware 2020-2021 *
@ -25,11 +24,4 @@ public class Window extends Tag{ @@ -25,11 +24,4 @@ public class Window extends Tag{
.attr("onclick", "return swapTiling();")
.content("◧").addTo(this);
}
public void highlight(BaseClass element) {
BaseClass scrollTarget = element.parent();
if (scrollTarget == null) scrollTarget = element;
children().add(new Tag("script").content("document.getElementById('"+scrollTarget.id()+"').scrollIntoView({ behavior: \"smooth\" }); document.getElementById('"+element.id()+"').classList.add('highlight');"));
}
}

2
src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java

@ -101,7 +101,7 @@ public class BrakeProcess extends BaseClass implements Runnable{ @@ -101,7 +101,7 @@ public class BrakeProcess extends BaseClass implements Runnable{
route.brakeTime(brakeId,newTimeStep);
calculated = calcDistance(newTimeStep);
LOG.debug("Corrected brake timestep from {} to {} ms for {} @ {}.",timeStep,newTimeStep,train,route);
LOG.debug("Difference from estimated distance: {} ({}%)",distance-calculated,100*(distance-calculated)/(float)distance);
LOG.debug("Differemce from estimated distance: {} ({}%)",distance-calculated,100*(distance-calculated)/(float)distance);
}
}
}

32
src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java

@ -9,7 +9,6 @@ import java.util.Vector; @@ -9,7 +9,6 @@ import java.util.Vector;
import de.srsoftware.web4rail.Application;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Destination;
import de.srsoftware.web4rail.EventListener;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.Route;
@ -45,8 +44,8 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -45,8 +44,8 @@ public class RoutePrepper extends BaseClass implements Runnable{
return derived;
}
public boolean endsAt(Destination destination) {
return lastElement().endsAt(destination);
public boolean endsAt(Block block) {
return lastElement().endBlock() == block;
}
private Trail prepend(Trail trail) {
@ -96,23 +95,23 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -96,23 +95,23 @@ public class RoutePrepper extends BaseClass implements Runnable{
context = c;
}
private static PriorityQueue<Trail> availableRoutes(Context context){
private static PriorityQueue<Trail> availableRoutes(Context c){
boolean error = false;
Block startBlock = context.block();
Block startBlock = c.block();
if (isNull(startBlock) && (error=true)) LOG.warn("RoutePrepper.availableRoutes(…) called without a startBlock!");
Train train = context.train();
Train train = c.train();
if (isNull(train) && (error=true)) LOG.warn("RoutePrepper.availableRoutes(…) called without a startBlock!");
if (error) return new PriorityQueue<>();
Destination destination = train.destination();
Block destination = train.destination();
Direction startDirection = context.direction();
Direction startDirection = c.direction();
LOG.debug("RoutePrepper.availableRoutes({},{},{}), dest = {}",startBlock,startDirection,train,destination);
PriorityQueue<Trail> candidates = routesFrom(context); // map: route → score
PriorityQueue<Trail> candidates = routesFrom(c); // map: route → score
if (isNull(destination) || train.isShunting()) {
LOG.debug("{} has no destination, returning {}",train,candidates);
@ -121,14 +120,14 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -121,14 +120,14 @@ public class RoutePrepper extends BaseClass implements Runnable{
LOG.debug("{} is heading for {}, starting breadth-first search…",train,destination);
for (int depth=1; depth<33; depth++) {
for (int depth=1; depth<99; depth++) {
if (candidates.stream().filter(trail -> trail.endsAt(destination)).count() > 0) return candidates; // return connectingTrails + other routes, in case all connecting trails are occupied
PriorityQueue<Trail> nextLevelCandidates = new PriorityQueue<Trail>();
for (Trail trail : candidates) {
Route lastRoute = trail.lastElement();
context.block(lastRoute.endBlock()).direction(lastRoute.endDirection);
PriorityQueue<Trail> ongoing = routesFrom(context);
c.block(lastRoute.endBlock()).direction(lastRoute.endDirection);
PriorityQueue<Trail> ongoing = routesFrom(c);
if (!ongoing.isEmpty()) nextLevelCandidates.addAll(trail.derive(ongoing));
}
candidates = nextLevelCandidates;
@ -233,7 +232,7 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -233,7 +232,7 @@ public class RoutePrepper extends BaseClass implements Runnable{
if (error) return null;
Destination destination = train.destination();
Block destination = train.destination();
Direction startDirection = context.direction();
@ -242,8 +241,7 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -242,8 +241,7 @@ public class RoutePrepper extends BaseClass implements Runnable{
PriorityQueue<Trail> trails = new PriorityQueue<>();
for (Route route : startBlock.leavingRoutes()) {
boolean reachesDest = route.endsAt(destination);
int score = reachesDest ? 100_000 : 0;
int score = (route.endBlock() == destination) ? 100_000 : 0;
if (isSet(startDirection) && route.startDirection != startDirection) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges
if (!train.pushPull) continue; // Zug kann nicht wenden
@ -255,13 +253,13 @@ public class RoutePrepper extends BaseClass implements Runnable{ @@ -255,13 +253,13 @@ public class RoutePrepper extends BaseClass implements Runnable{
if (!route.allowed(new Context(train).block(startBlock).direction(startDirection))) {
LOG.debug(" - {} not allowed for {}", route, train);
if (!reachesDest) continue;
if (route.endBlock() != destination) continue;
LOG.debug(" …overridden by destination of train!", route, train);
}
trails.add(new Trail().add(route,score));
}
return trails;
}

12
src/main/java/de/srsoftware/web4rail/tiles/Block.java

@ -198,11 +198,6 @@ public abstract class Block extends StretchableTile{ @@ -198,11 +198,6 @@ public abstract class Block extends StretchableTile{
return t("Trigger contact to learn new contact");
}
public List<Route> arrivingRoutes() {
return routes().stream().filter(route -> route.endBlock() == Block.this).collect(Collectors.toList());
}
@Override
protected HashSet<String> classes() {
HashSet<String> classes = super.classes();
@ -248,8 +243,6 @@ public abstract class Block extends StretchableTile{ @@ -248,8 +243,6 @@ public abstract class Block extends StretchableTile{
return internalContacts;
}
public abstract Direction enterDirection(String dest);
public abstract Direction directionA();
public abstract Direction directionB();
@ -487,11 +480,6 @@ public abstract class Block extends StretchableTile{ @@ -487,11 +480,6 @@ public abstract class Block extends StretchableTile{
@Override
public String title() {
StringBuilder sb = new StringBuilder(name);
sb.append(" @ (");
sb.append(x);
sb.append(", ");
sb.append(y);
sb.append(")");
Train occupyingTrain = occupyingTrain();
if (isSet(occupyingTrain)) sb.append(title(occupyingTrain));
if (isSet(parkedTrains)) for (Train parked : parkedTrains.trains) sb.append(title(parked));

13
src/main/java/de/srsoftware/web4rail/tiles/BlockH.java

@ -3,12 +3,9 @@ package de.srsoftware.web4rail.tiles; @@ -3,12 +3,9 @@ package de.srsoftware.web4rail.tiles;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import de.srsoftware.web4rail.Connector;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.tiles.Turnout.State;
public class BlockH extends Block{
@ -27,16 +24,6 @@ public class BlockH extends Block{ @@ -27,16 +24,6 @@ public class BlockH extends Block{
}
}
@Override
public Direction enterDirection(String id) {
Set<Direction> endDirections = arrivingRoutes().stream().map(Route::endDirection).collect(Collectors.toSet());
if (endDirections.size()<2) return endDirections.stream().findAny().get().inverse();
if (stretch()<2) return null;
if (id().equals(id)) return directionA();
if (((x+stretch()-1)+"-"+y).equals(id)) return directionB();
return null;
}
@Override
public Direction directionA() {
return Direction.WEST;

13
src/main/java/de/srsoftware/web4rail/tiles/BlockV.java

@ -3,11 +3,8 @@ package de.srsoftware.web4rail.tiles; @@ -3,11 +3,8 @@ package de.srsoftware.web4rail.tiles;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import de.srsoftware.web4rail.Connector;
import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.tiles.Turnout.State;
@ -27,16 +24,6 @@ public class BlockV extends Block{ @@ -27,16 +24,6 @@ public class BlockV extends Block{
}
}
@Override
public Direction enterDirection(String id) {
Set<Direction> endDirections = arrivingRoutes().stream().map(Route::endDirection).collect(Collectors.toSet());
if (endDirections.size()<2) return endDirections.stream().findAny().get().inverse();
if (stretch()<2) return null;
if (id().equals(id)) return directionA();
if ((x+"-"+(y+stretch()-1)).equals(id)) return directionB();
return null;
}
@Override
public Direction directionA() {
return Direction.NORTH;

6
src/main/java/de/srsoftware/web4rail/tiles/Switch.java

@ -161,12 +161,6 @@ public class Switch extends Tile{ @@ -161,12 +161,6 @@ public class Switch extends Tile{
return state;
}
public boolean trigger(Context context) {
return state ?
actionsOn.fire(context) :
actionsOff.fire(context);
}
public void state(boolean newState) {
state = newState;

36
src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java

@ -3,7 +3,6 @@ package de.srsoftware.web4rail.tiles; @@ -3,7 +3,6 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@ -14,27 +13,14 @@ import org.json.JSONObject; @@ -14,27 +13,14 @@ import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Store;
import de.srsoftware.web4rail.Store.Listener;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Select;
import de.srsoftware.web4rail.tags.Window;
public class TextDisplay extends StretchableTile implements Listener {
public class TextDisplay extends StretchableTile {
private static final String TEXT = "text";
private String text = "Hello, world!";
private HashSet<Store> stores = new HashSet<>();
private String fillPlaceholders() {
String result = text;
for (Store store : stores) {
String val = store.value();
if (isNull(val)) continue;
result = result.replace("{"+store.name()+"}", val);
}
return result;
}
@Override
public JSONObject json() {
@ -43,7 +29,7 @@ public class TextDisplay extends StretchableTile implements Listener { @@ -43,7 +29,7 @@ public class TextDisplay extends StretchableTile implements Listener {
@Override
public Tile load(JSONObject json) {
if (json.has(TEXT)) text(json.getString(TEXT));
if (json.has(TEXT)) text = json.getString(TEXT);
return super.load(json);
}
@ -64,12 +50,7 @@ public class TextDisplay extends StretchableTile implements Listener { @@ -64,12 +50,7 @@ public class TextDisplay extends StretchableTile implements Listener {
}
return select;
}
@Override
public void storeUpdated() {
plan.place(this);
}
@Override
protected String stretchType() {
return t("Width");
@ -79,22 +60,13 @@ public class TextDisplay extends StretchableTile implements Listener { @@ -79,22 +60,13 @@ public class TextDisplay extends StretchableTile implements Listener {
@Override
public Tag tag(Map<String, Object> replacements) throws IOException {
if (isNull(replacements)) replacements = new HashMap<String, Object>();
replacements.put("%text%",fillPlaceholders());
replacements.put("%text%",text);
Tag tag = super.tag(replacements);
return tag.clazz(tag.get("class")+" fill");
}
public TextDisplay text(String tx) {
text = tx;
stores.clear();
int pos = text.indexOf("{");
while (pos > -1) {
int end = text.indexOf("}",pos);
if (end < 0) break;
stores.add(Store.get(text.substring(pos+1, end)).addListener(this));
pos = text.indexOf("{",end);
}
return this;
}

18
src/main/java/de/srsoftware/web4rail/tiles/Tile.java

@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Connector;
import de.srsoftware.web4rail.Destination;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Plan;
@ -469,7 +468,7 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> { @@ -469,7 +468,7 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
if (isSet(reservingTrain) && newTrain != reservingTrain) return false;
if (isSet(lockingTrain) && newTrain != lockingTrain) return false;
if (isSet(occupyingTrain) && (newTrain != occupyingTrain) && !newTrain.isShunting()) return false;
lockingTrain = reservingTrain = null;
reservingTrain = lockingTrain = null;
if (occupyingTrain == newTrain) return true;
occupyingTrain = newTrain;
plan.place(this);
@ -540,8 +539,12 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> { @@ -540,8 +539,12 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
sb.append(", ");
sb.append(y);
sb.append(")");
if (isSet(occupyingTrain)) sb.append(title(occupyingTrain));
if (isSet(occupyingTrain)) {
sb.append("\n");
sb.append(occupyingTrain);
sb.append(":\n");
occupyingTrain.cars().forEach(car -> sb.append("\t"+car+"\n"));
}
return sb.toString();
}
@ -552,11 +555,8 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> { @@ -552,11 +555,8 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
sb.append(train.length());
sb.append(" ");
sb.append(t(lengthUnit));
sb.append(")");
Destination dest = train.destination();
if (isSet(dest)) sb.append(" → ").append(dest);
sb.append(":\n");
train.cars().forEach(car -> sb.append("\t- ").append(car).append("\n"));
sb.append("):\n");
train.cars().forEach(car -> sb.append("\t"+car+"\n"));
return sb.toString();
}

Loading…
Cancel
Save