implemented contact feedback
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.srsoftware</groupId>
|
||||
<artifactId>web4rail</artifactId>
|
||||
<version>0.9.1</version>
|
||||
<version>0.9.2</version>
|
||||
<name>Web4Rail</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Java Model Railway Control</description>
|
||||
|
||||
@@ -120,12 +120,6 @@ function moveTile(x,y){
|
||||
return request({realm:'plan',action:mode,direction:selected.id,id:id});
|
||||
}
|
||||
|
||||
function openRoute(id){
|
||||
console.log("openRoute("+id+")");
|
||||
request({realm:'route',action:PROPS,id:id});
|
||||
return false;
|
||||
}
|
||||
|
||||
function place(data){
|
||||
var tag = $(data);
|
||||
$('#'+tag.attr('id')).remove();
|
||||
@@ -201,6 +195,14 @@ function stream(ev){
|
||||
if (data.startsWith("remove")) return remove(data.substring(7));
|
||||
if (data.startsWith("addclass")) return addClass(data.substring(9));
|
||||
if (data.startsWith("dropclass")) return dropClass(data.substring(10));
|
||||
|
||||
if (data.startsWith("<div") && $(data).attr('class') == 'window'){
|
||||
$('.window').remove();
|
||||
$(BODY).append($(data));
|
||||
tileWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
addMessage(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.net.URLDecoder;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -31,6 +30,7 @@ import de.srsoftware.web4rail.conditions.Condition;
|
||||
import de.srsoftware.web4rail.moving.Car;
|
||||
import de.srsoftware.web4rail.moving.Locomotive;
|
||||
import de.srsoftware.web4rail.moving.Train;
|
||||
import de.srsoftware.web4rail.tiles.Contact;
|
||||
|
||||
/**
|
||||
* Entry point class for the Web4Rail application
|
||||
@@ -113,6 +113,8 @@ public class Application implements Constants{
|
||||
return ActionList.process(params,plan);
|
||||
case REALM_CAR:
|
||||
return Car.action(params);
|
||||
case REALM_CONTACT:
|
||||
return Contact.process(params);
|
||||
case REALM_CONDITION:
|
||||
return Condition.action(params,plan);
|
||||
case REALM_CU:
|
||||
@@ -169,23 +171,6 @@ public class Application implements Constants{
|
||||
if (response instanceof Page) {
|
||||
html = ((Page)response).html().toString().getBytes(UTF8);
|
||||
client.getResponseHeaders().add("content-type", "text/html");
|
||||
} else if (response instanceof CompletableFuture) {
|
||||
CompletableFuture<?> promise = (CompletableFuture<?>) response;
|
||||
promise.thenAccept(object -> {
|
||||
try {
|
||||
send(client,object);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Was not able to send {}!",object);
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
try {
|
||||
send(client,ex.getMessage());
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Was not able to send {}!",ex);
|
||||
}
|
||||
throw new RuntimeException(ex);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
html = (response == null ? "" : response.toString()).getBytes(UTF8);
|
||||
client.getResponseHeaders().add("content-type", "text/plain");
|
||||
|
||||
@@ -39,6 +39,7 @@ public interface Constants {
|
||||
public static final String REALM_ACTIONS = "actions";
|
||||
public static final String REALM_CAR = "car";
|
||||
public static final String REALM_CONDITION = "condition";
|
||||
public static final String REALM_CONTACT = "contact";
|
||||
public static final String REALM_CU = "cu";
|
||||
public static final String REALM_LOCO = "loco";
|
||||
public static final String REALM_ROUTE = "route";
|
||||
|
||||
@@ -118,6 +118,7 @@ public class Plan implements Constants{
|
||||
private HashSet<Block> blocks = new HashSet<Block>(); // the list of tiles, that are blocks
|
||||
private HashMap<Integer, Route> routes = new HashMap<Integer, Route>(); // the list of routes of the track layout
|
||||
private ControlUnit controlUnit = new ControlUnit(this); // the control unit, to which the plan is connected
|
||||
private Contact learningContact;
|
||||
|
||||
/**
|
||||
* creates a new plan, starts to send heart beats
|
||||
@@ -588,14 +589,15 @@ public class Plan implements Constants{
|
||||
/**
|
||||
* removes a route from the track layout
|
||||
* @param route
|
||||
* @return
|
||||
*/
|
||||
public void remove(Route route) {
|
||||
public String remove(Route route) {
|
||||
for (Tile tile : route.path()) tile.remove(route);
|
||||
for (Train train : Train.list()) {
|
||||
if (train.route == route) train.route = null;
|
||||
}
|
||||
routes.remove(route.id());
|
||||
stream(t("Removed {}.",route));
|
||||
return t("Removed {}.",route);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -660,7 +662,19 @@ public class Plan implements Constants{
|
||||
}
|
||||
|
||||
public void sensor(int addr, boolean active) {
|
||||
|
||||
LOG.debug("contact({},{})",addr,active);
|
||||
Contact contact = Contact.get(addr);
|
||||
LOG.debug("contact: {}",contact);
|
||||
LOG.debug("learning: {}",learningContact);
|
||||
if (contact != null) {
|
||||
contact.activate(active);
|
||||
} else {
|
||||
if (active && learningContact != null) {
|
||||
LOG.debug("learned: {} = {}",addr,learningContact);
|
||||
stream(learningContact.addr(addr).propMenu().toString());
|
||||
learningContact = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -669,13 +683,17 @@ public class Plan implements Constants{
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public Window showContext(HashMap<String, String> params) {
|
||||
public Tag showContext(HashMap<String, String> params) {
|
||||
String[] parts = params.get(CONTEXT).split(":");
|
||||
String realm = parts[0];
|
||||
String id = parts.length>1 ? parts[1] : null;
|
||||
switch (realm) {
|
||||
case REALM_ROUTE:
|
||||
return route(Integer.parseInt(id)).properties();
|
||||
return route(Integer.parseInt(id)).properties(params);
|
||||
case REALM_PLAN:
|
||||
Tile tile = get(id, false);
|
||||
return tile == null? null : tile.propMenu();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -795,4 +813,10 @@ public class Plan implements Constants{
|
||||
public void warn(Contact contact) {
|
||||
stream(t("Warning: {}",t("Ghost train @ {}",contact)));
|
||||
}
|
||||
|
||||
public void learn(Contact contact) {
|
||||
learningContact = contact;
|
||||
LOG.debug("learning contact {}",learningContact);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,21 @@ public class Route implements Constants{
|
||||
Route route = plan.route(Integer.parseInt(params.get(ID)));
|
||||
if (route == null) return t("Unknown route: {}",params.get(ID));
|
||||
switch (params.get(ACTION)) {
|
||||
case ACTION_UPDATE:
|
||||
return route.update(params);
|
||||
case ACTION_DROP:
|
||||
String message = plan.remove(route);
|
||||
String tileId = params.get(Tile.class.getSimpleName());
|
||||
if (tileId != null) {
|
||||
Tile tile = plan.get(tileId, false);
|
||||
if (tile != null) {
|
||||
plan.stream(message);
|
||||
return tile.propMenu();
|
||||
}
|
||||
}
|
||||
return message;
|
||||
case ACTION_PROPS:
|
||||
return route.properties();
|
||||
return route.properties(params);
|
||||
case ACTION_UPDATE:
|
||||
return route.update(params,plan);
|
||||
}
|
||||
return t("Unknown action: {}",params.get(ACTION));
|
||||
}
|
||||
@@ -199,12 +210,12 @@ public class Route implements Constants{
|
||||
}
|
||||
}
|
||||
|
||||
private void addFormTo(Window win) {
|
||||
private void addFormTo(Window win, HashMap<String, String> params) {
|
||||
Form form = new Form("route-"+id+"-props");
|
||||
new Input(ACTION, ACTION_UPDATE).hideIn(form);
|
||||
new Input(REALM,REALM_ROUTE).hideIn(form);
|
||||
new Input(ID,id()).hideIn(form);
|
||||
|
||||
if (params.containsKey(CONTEXT)) new Input(CONTEXT,params.get(CONTEXT)).hideIn(form);
|
||||
Tag label = new Tag("label").content(t("name:")+NBSP);
|
||||
new Tag("input").attr("type", "text").attr(NAME,"name").attr("value", name()).style("width: 80%").addTo(label);
|
||||
label.addTo(form);
|
||||
@@ -475,9 +486,9 @@ public class Route implements Constants{
|
||||
return result;
|
||||
}
|
||||
|
||||
public Window properties() {
|
||||
public Window properties(HashMap<String, String> params) {
|
||||
Window win = new Window("route-properties",t("Properties of {}",this));
|
||||
addFormTo(win);
|
||||
addFormTo(win,params);
|
||||
addBasicPropertiesTo(win);
|
||||
addTurnoutsTo(win);
|
||||
addConditionsTo(win);
|
||||
@@ -565,7 +576,7 @@ public class Route implements Constants{
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object update(HashMap<String, String> params) {
|
||||
public Object update(HashMap<String, String> params,Plan plan) {
|
||||
LOG.debug("update({})",params);
|
||||
String name = params.get(NAME);
|
||||
if (name != null) name(name);
|
||||
@@ -573,8 +584,13 @@ public class Route implements Constants{
|
||||
String condition = params.get(REALM_CONDITION);
|
||||
if (condition != null) {
|
||||
addCondition(condition);
|
||||
return properties();
|
||||
return properties(params);
|
||||
}
|
||||
return t("{} updated.",this);
|
||||
String message = t("{} updated.",this);
|
||||
if (params.containsKey(CONTEXT)) {
|
||||
plan.stream(message);
|
||||
return plan.showContext(params);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class Button extends Tag {
|
||||
this(text,"return submitForm('"+form.get("id")+"');");
|
||||
}
|
||||
|
||||
public Button(String text, Map<String, Object> props) {
|
||||
public Button(String text, Map<String, ? extends Object> props) {
|
||||
this(text,"request("+(new JSONObject(props).toString().replace("\"", "'"))+")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,118 @@
|
||||
package de.srsoftware.web4rail.tiles;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.srsoftware.tools.Tag;
|
||||
import de.srsoftware.web4rail.tags.Button;
|
||||
import de.srsoftware.web4rail.tags.Input;
|
||||
import de.srsoftware.web4rail.tags.Label;
|
||||
|
||||
public abstract class Contact extends Tile{
|
||||
|
||||
private static final String ADDRESS = "address";
|
||||
private static final HashMap<String, Contact> contactsById = new HashMap<String, Contact>();
|
||||
private static final HashMap<Integer, Contact> contactsByAddr = new HashMap<Integer, Contact>();
|
||||
private boolean active = false;
|
||||
private String trigger = null;
|
||||
private int addr = 0;
|
||||
|
||||
public void activate() throws IOException {
|
||||
active = true;
|
||||
stream();
|
||||
public void trigger(int duration) throws IOException {
|
||||
activate(true);
|
||||
new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
sleep(200);
|
||||
active=false;
|
||||
stream();
|
||||
sleep(duration);
|
||||
activate(false);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}.start();
|
||||
if (route == null) {
|
||||
plan.warn(this);
|
||||
} else {
|
||||
route.contact(this);
|
||||
}
|
||||
|
||||
public void activate(boolean active) {
|
||||
this.active = active;
|
||||
if (active) {
|
||||
if (route == null) {
|
||||
plan.warn(this);
|
||||
} else {
|
||||
route.contact(this);
|
||||
}
|
||||
}
|
||||
try {
|
||||
stream();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Contact addr(int address) {
|
||||
addr = address;
|
||||
contactsByAddr.put(addr, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object click() throws IOException {
|
||||
trigger(200);
|
||||
return super.click();
|
||||
}
|
||||
|
||||
public static Contact get(int addr) {
|
||||
return contactsByAddr.get(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject json() {
|
||||
JSONObject json = super.json();
|
||||
if (addr > 0) json.put(ADDRESS, addr);
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Tile load(JSONObject json) throws IOException {
|
||||
super.load(json);
|
||||
if (json.has(ADDRESS)) addr(json.getInt(ADDRESS));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile position(int x, int y) {
|
||||
super.position(x, y);
|
||||
contactsById.put(id(), this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Object process(HashMap<String, String> params) {
|
||||
String action = params.get(ACTION);
|
||||
String id = params.get(ID);
|
||||
if (action == null) return t("Missing ACTION on call to {}.process()",Contact.class.getSimpleName());
|
||||
Contact contact;
|
||||
switch (action) {
|
||||
case ACTION_ANALYZE:
|
||||
if (id == null) return t("Missing ID on call to {}.process()",Contact.class.getSimpleName());
|
||||
contact = contactsById.get(id);
|
||||
if (contact == null) return t("No contact with id {} found!",id);
|
||||
Tag propMenu = contact.propMenu();
|
||||
propMenu.children().insertElementAt(new Tag("div").content(t("Trigger a feedback sensor to assign it with this contact!")), 1);
|
||||
contact.plan.learn(contact);
|
||||
return propMenu;
|
||||
}
|
||||
return t("Unknown action: {}",action);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object click() throws IOException {
|
||||
activate();
|
||||
return super.click();
|
||||
public Tag propForm(String formId) {
|
||||
Tag form = super.propForm(formId);
|
||||
new Tag("h4").content(t("Hardware settings")).addTo(form);
|
||||
Tag label = new Input(ADDRESS, addr).addTo(new Label(t("Address:")+NBSP));
|
||||
Map<String, String> props = Map.of(REALM,REALM_CONTACT,ID,id(),ACTION,ACTION_ANALYZE);
|
||||
new Button(t("learn"), props).addTo(label).addTo(form);
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
public void stream() throws IOException {
|
||||
@@ -47,4 +127,11 @@ public abstract class Contact extends Tile{
|
||||
return trigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile update(HashMap<String, String> params) throws IOException {
|
||||
if (params.containsKey(ADDRESS)) addr(Integer.parseInt(params.get(ADDRESS)));
|
||||
return super.update(params);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -180,8 +180,8 @@ public abstract class Tile implements Constants{
|
||||
return new Vector<Plan.Direction>();
|
||||
}
|
||||
|
||||
public Tag propForm(String id) {
|
||||
Form form = new Form(id);
|
||||
public Tag propForm(String formId) {
|
||||
Form form = new Form(formId);
|
||||
new Input(ACTION, ACTION_UPDATE).hideIn(form);
|
||||
new Input(REALM, REALM_PLAN).hideIn(form);
|
||||
new Input(ID,id()).hideIn(form);
|
||||
@@ -216,7 +216,8 @@ public abstract class Tile implements Constants{
|
||||
new Tag("h4").content(t("Routes using this tile:")).addTo(window);
|
||||
Tag routeList = new Tag("ol");
|
||||
for (Route route : routes) {
|
||||
Tag li = new Tag("span").attr("onclick","openRoute('"+route.id()+"')").content(route.name()+NBSP).addTo(new Tag("li").clazz("link"));
|
||||
String json = new JSONObject(Map.of(REALM,ROUTE,ID,route.id(),ACTION,ACTION_PROPS,CONTEXT,REALM_PLAN+":"+id())).toString().replace("\"", "'");
|
||||
Tag li = new Tag("span").attr("onclick","return request("+json+");").content(route.name()+NBSP).addTo(new Tag("li").clazz("link"));
|
||||
Map<String, Object> params = Map.of(REALM,REALM_ROUTE,ID,route.id(),ACTION,ACTION_DROP,Tile.class.getSimpleName(),id());
|
||||
new Button(t("delete route"),params).addTo(li);
|
||||
li.addTo(routeList);
|
||||
|
||||
Reference in New Issue
Block a user