From 00a79f76deb3097664de9614d78dc6dfca7fe11e Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 4 May 2021 09:30:46 -0700 Subject: [PATCH] 0.7.0 - Eliminate intermediate nodes named for the nav id. - Fix set and getHistory actions when node tree doesn't exist. - Add table metadata to history tables. - Fix an infinite loop when clearing the node tree. --- build.gradle | 2 +- dslink.json | 2 +- .../java/org/dsa/iot/haystack/Haystack.java | 2 +- src/main/java/org/dsa/iot/haystack/Main.java | 63 +++++++++++--- src/main/java/org/dsa/iot/haystack/Utils.java | 6 +- .../org/dsa/iot/haystack/actions/Actions.java | 87 +++++-------------- .../dsa/iot/haystack/actions/GetHistory.java | 36 ++++++-- .../iot/haystack/actions/ServerActions.java | 3 +- .../iot/haystack/handlers/ClosedHandler.java | 4 +- .../dsa/iot/haystack/helpers/NavHelper.java | 80 +++++++++-------- 10 files changed, 154 insertions(+), 131 deletions(-) diff --git a/build.gradle b/build.gradle index 7b7fde8..5266d6f 100755 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'java-library' mainClassName = 'org.dsa.iot.haystack.Main' sourceCompatibility = 1.7 targetCompatibility = 1.7 -version = '0.6.0' +version = '0.7.0' repositories { mavenLocal() diff --git a/dslink.json b/dslink.json index 67d8e45..2e98bfa 100644 --- a/dslink.json +++ b/dslink.json @@ -1,6 +1,6 @@ { "name": "dslink-java-haystack", - "version": "0.6.0", + "version": "0.7.0", "description": "An implementation dslink of a haystack protocol consumer", "license": "Apache", "author": { diff --git a/src/main/java/org/dsa/iot/haystack/Haystack.java b/src/main/java/org/dsa/iot/haystack/Haystack.java index 60ba2e2..737de17 100755 --- a/src/main/java/org/dsa/iot/haystack/Haystack.java +++ b/src/main/java/org/dsa/iot/haystack/Haystack.java @@ -178,7 +178,7 @@ public void editConnection(String url, } Action a = ServerActions.getEditAction(node); - node.getChild("editServer").setAction(a); + node.getChild("editServer", false).setAction(a); } diff --git a/src/main/java/org/dsa/iot/haystack/Main.java b/src/main/java/org/dsa/iot/haystack/Main.java index 0ff161a..3664bfc 100755 --- a/src/main/java/org/dsa/iot/haystack/Main.java +++ b/src/main/java/org/dsa/iot/haystack/Main.java @@ -9,10 +9,10 @@ import org.dsa.iot.dslink.DSLinkFactory; import org.dsa.iot.dslink.DSLinkHandler; import org.dsa.iot.dslink.node.Node; -import org.dsa.iot.dslink.node.NodeBuilder; import org.dsa.iot.dslink.node.NodeManager; import org.dsa.iot.dslink.util.StringUtils; import org.dsa.iot.haystack.actions.Actions; +import org.dsa.iot.haystack.actions.GetHistory; import org.dsa.iot.haystack.actions.InvokeActions; import org.dsa.iot.haystack.helpers.StateHandler; import org.projecthaystack.HDict; @@ -20,6 +20,7 @@ import org.projecthaystack.HRef; import org.projecthaystack.HRow; import org.projecthaystack.HStr; +import org.projecthaystack.HTimeZone; import org.projecthaystack.HVal; import org.projecthaystack.client.HClient; import org.projecthaystack.io.HZincReader; @@ -84,10 +85,10 @@ public Node onSubscriptionFail(String path) { while (i < split.length) { Node next = n.getChild(split[i], false); int tries = 0; - while (next == null && tries < 6) { + while (next == null && tries < 10) { tries++; try { - subFailLock.wait(200); + subFailLock.wait(250); } catch (InterruptedException ignore) { } next = n.getChild(split[i], false); @@ -116,12 +117,56 @@ public Node onInvocationFail(final String path) { final NodeManager manager = link.getNodeManager(); final Node superRoot = manager.getSuperRoot(); - final Haystack haystack = superRoot.getChild(split[0]).getMetaData(); + final Haystack haystack = superRoot.getChild(split[0], true).getMetaData(); final String actName = StringUtils.decodeName(split[split.length - 1]); final CountDownLatch latch = new CountDownLatch(1); final Container container = new Container(); - if ("pointWrite".equals(actName)) { + if ("getHistory".equals(actName)) { + haystack.getConnHelper().getClient(new StateHandler() { + @Override + public void handle(HClient event) { + final HRef id; + { + String sID = split[split.length - 3]; + sID = StringUtils.decodeName(sID); + id = HRef.make(sID); + } + + HDict dict = event.readById(id); + HVal tz = dict.get("tz", false); + HTimeZone htz = null; + if (tz != null) { + htz = HTimeZone.make(tz.toString(), false); + } + + String[] pSplit = Arrays.copyOf(split, split.length - 1); + String parent = StringUtils.join(pSplit, "/"); + Node node = manager.getNode(parent, true).getNode(); + container.node = new GetHistory(node, haystack, HRef.make(id.toString()), htz) + .getActionNode(); + latch.countDown(); + } + }); + } else if ("set".equals(actName)) { + haystack.getConnHelper().getClient(new StateHandler() { + @Override + public void handle(HClient event) { + HDict dict = event.readById(id); + HVal hKind = dict.get("kind", false); + String kind = null; + if (hKind != null) { + kind = hKind.toString(); + } + + String[] pSplit = Arrays.copyOf(split, split.length - 1); + String parent = StringUtils.join(pSplit, "/"); + Node node = manager.getNode(parent, true).getNode(); + container.node = Actions.getSetAction(haystack, node, id, kind); + latch.countDown(); + } + }); + } else if ("pointWrite".equals(actName)) { haystack.getConnHelper().getClient(new StateHandler() { @Override public void handle(HClient event) { @@ -135,11 +180,7 @@ public void handle(HClient event) { String[] pSplit = Arrays.copyOf(split, split.length - 1); String parent = StringUtils.join(pSplit, "/"); Node node = manager.getNode(parent, true).getNode(); - NodeBuilder b = Utils.getBuilder(node, "pointWrite"); - b.setDisplayName("Point Write"); - b.setSerializable(false); - b.setAction(Actions.getPointWriteAction(haystack, id, kind)); - container.node = b.build(); + container.node = Actions.getPointWriteAction(haystack, node, id, kind); latch.countDown(); } }); @@ -170,7 +211,7 @@ public void handle(HClient event) { String name = split[split.length - 1]; name = StringUtils.encodeName(name); - container.node = node.getChild(name); + container.node = node.getChild(name, false); break; } if (doThrow) { diff --git a/src/main/java/org/dsa/iot/haystack/Utils.java b/src/main/java/org/dsa/iot/haystack/Utils.java index 235a322..9469ebc 100644 --- a/src/main/java/org/dsa/iot/haystack/Utils.java +++ b/src/main/java/org/dsa/iot/haystack/Utils.java @@ -151,11 +151,7 @@ public static void initCommon(Haystack haystack, Node node) { subNode.setSerializable(false); subNode.build(); - NodeBuilder writeNode = getBuilder(node, "pointWrite"); - writeNode.setDisplayName("Point Write"); - writeNode.setAction(Actions.getPointWriteAction(haystack)); - writeNode.setSerializable(false); - writeNode.build(); + Actions.getPointWriteAction(haystack, node); NodeBuilder invokeNode = getBuilder(node, "invoke"); invokeNode.setDisplayName("Invoke"); diff --git a/src/main/java/org/dsa/iot/haystack/actions/Actions.java b/src/main/java/org/dsa/iot/haystack/actions/Actions.java index 3583c83..586b62b 100644 --- a/src/main/java/org/dsa/iot/haystack/actions/Actions.java +++ b/src/main/java/org/dsa/iot/haystack/actions/Actions.java @@ -4,6 +4,8 @@ import java.util.Locale; import java.util.Map; import org.dsa.iot.dslink.methods.StreamState; +import org.dsa.iot.dslink.node.Node; +import org.dsa.iot.dslink.node.NodeBuilder; import org.dsa.iot.dslink.node.Permission; import org.dsa.iot.dslink.node.actions.Action; import org.dsa.iot.dslink.node.actions.ActionResult; @@ -79,13 +81,14 @@ public void handle(Void event) { return a; } - public static Action getPointWriteAction(Haystack haystack) { - return getPointWriteAction(haystack, null, null); + public static Node getPointWriteAction(Haystack haystack, Node parent) { + return getPointWriteAction(haystack, parent, null, null); } - public static Action getPointWriteAction(final Haystack haystack, - final HRef treeId, - final String kind) { + public static Node getPointWriteAction(final Haystack haystack, + final Node parent, + final HRef treeId, + final String kind) { Action a = new Action(Permission.READ, new Handler() { @Override public void handle(final ActionResult event) { @@ -236,12 +239,17 @@ public void handle(HClient client) { p.setDescription("Duration unit."); a.addParameter(p); } - return a; + NodeBuilder writeNode = Utils.getBuilder(parent, "pointWrite"); + writeNode.setDisplayName("Point Write"); + writeNode.setSerializable(false); + writeNode.setAction(a); + return writeNode.build(); } - public static Action getSetAction(final Haystack haystack, - final HRef treeId, - final String kind) { + public static Node getSetAction(final Haystack haystack, + final Node parent, + final HRef treeId, + final String kind) { final String type = kind.toLowerCase(Locale.ROOT); final ValueType valueType; switch (type) { @@ -293,7 +301,10 @@ public void handle(HClient client) { }); Parameter p = new Parameter("Value", valueType); a.addParameter(p); - return a; + return parent.createChild("set", false) + .setAction(a) + .setDisplayName("Set") + .build(); } public static Action getReadAction(final Haystack haystack) { @@ -392,62 +403,6 @@ public void handle(HGrid grid) { return a; } - /* todo Save for awhile - public static Action getHistoryAction(final Haystack haystack, - final HRef treeId, - final TimeZone tz) { - Action a = new Action(Permission.READ, new Handler() { - @Override - public void handle(final ActionResult event) { - if (!haystack.isEnabled()) { - throw new IllegalStateException("Disabled"); - } - Value vRange = event.getParameter("Timerange", ValueType.STRING); - String range = vRange.getString(); - String[] split = range.split("/"); - Calendar cal = TimeUtils.decode(split[0], null); - if (tz != null) { - cal.setTimeZone(tz); - } - StringBuilder buf = new StringBuilder(); - TimeUtils.encode(cal, true, buf); - buf.append(','); - TimeUtils.decode(split[1], cal); - if (tz != null) { - cal.setTimeZone(tz); - } - TimeUtils.encode(cal, true, buf); - HGridBuilder builder = new HGridBuilder(); - builder.addCol("id"); - builder.addCol("range"); - builder.addRow(new HVal[]{ - HRef.make(treeId.toString()), - HStr.make(buf.toString()) - }); - - haystack.call("hisRead", builder.toGrid(), new Handler() { - @Override - public void handle(HGrid grid) { - if (grid != null) { - buildTable(grid, event, true); - } - } - }); - - } - }); - //parameters - Parameter param = new Parameter("Timerange", ValueType.STRING); - param.setEditorType(EditorType.DATE_RANGE); - a.addParameter(param); - //results - a.addResult(new Parameter("timestamp", ValueType.TIME)); - a.addResult(new Parameter("value", ValueType.DYNAMIC)); - a.setResultType(ResultType.TABLE); - return a; - } - */ - public static void buildTable(HGrid in, ActionResult out, boolean historyColNames) { Table t = out.getTable(); diff --git a/src/main/java/org/dsa/iot/haystack/actions/GetHistory.java b/src/main/java/org/dsa/iot/haystack/actions/GetHistory.java index 35206c8..71f4397 100644 --- a/src/main/java/org/dsa/iot/haystack/actions/GetHistory.java +++ b/src/main/java/org/dsa/iot/haystack/actions/GetHistory.java @@ -6,6 +6,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.dsa.iot.dslink.methods.StreamState; import org.dsa.iot.dslink.node.Node; import org.dsa.iot.dslink.node.NodeBuilder; @@ -24,6 +25,7 @@ import org.dsa.iot.dslink.util.TimeUtils; import org.dsa.iot.dslink.util.handler.CompleteHandler; import org.dsa.iot.dslink.util.handler.Handler; +import org.dsa.iot.dslink.util.json.JsonObject; import org.dsa.iot.haystack.Haystack; import org.dsa.iot.haystack.Utils; import org.dsa.iot.historian.stats.interval.IntervalParser; @@ -32,6 +34,7 @@ import org.dsa.iot.historian.utils.QueryData; import org.projecthaystack.HCol; import org.projecthaystack.HDateTime; +import org.projecthaystack.HDict; import org.projecthaystack.HGrid; import org.projecthaystack.HGridBuilder; import org.projecthaystack.HRef; @@ -55,6 +58,7 @@ public class GetHistory implements Handler { private static final Parameter TIMESTAMP = new Parameter("timestamp", ValueType.TIME); private static final Parameter VALUE = new Parameter("value", ValueType.DYNAMIC); + private Node actionNode; private final Haystack haystack; private final HRef id; private final HTimeZone tz; @@ -93,6 +97,10 @@ public GetHistory(Node node, initAction(node); } + public Node getActionNode() { + return actionNode; + } + @Override public void handle(final ActionResult event) { long from; @@ -137,7 +145,7 @@ protected void process(final ActionResult event, final StringBuilder buffer = new StringBuilder(); final Calendar calendar = TimeUtils.reuseCalendar(); calendar.setTimeZone(tz.java); - query(from, to, new CompleteHandler() { + query(table, from, to, new CompleteHandler() { private List updates = new LinkedList<>(); @@ -170,7 +178,7 @@ public void complete() { }); } - protected void query(long from, long to, final CompleteHandler handler) { + protected void query(final Table table, long from, long to, final CompleteHandler handler) { StringBuilder buf = new StringBuilder(); buf.append(HDateTime.make(from, tz)); buf.append(','); @@ -186,7 +194,7 @@ protected void query(long from, long to, final CompleteHandler handle haystack.call("hisRead", builder.toGrid(), new Handler() { @Override public void handle(HGrid grid) { - LoopProvider.getProvider().schedule(new QueryProcessor(grid, handler)); + LoopProvider.getProvider().schedule(new QueryProcessor(table, grid, handler)); } }); } @@ -246,24 +254,42 @@ private void initAction(Node node) { a.setResultType(ResultType.STREAM); NodeBuilder b = node.createChild("getHistory", false); + b.setProfile("getHistory_"); b.setDisplayName("Get History"); b.setSerializable(false); b.setAction(a); - b.build(); + actionNode = b.build(); } private static class QueryProcessor implements Runnable { private final HGrid grid; private final CompleteHandler handler; + private final Table table; - QueryProcessor(HGrid grid, CompleteHandler handler) { + QueryProcessor(Table table, HGrid grid, CompleteHandler handler) { this.grid = grid; this.handler = handler; + this.table = table; } public void run() { try { + HDict meta = grid.meta(); + if (meta != null && !meta.isEmpty()) { + Iterator it = meta.iterator(); + JsonObject metaObj = new JsonObject(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String name = (String) entry.getKey(); + if (name != null) { + HVal val = (HVal) entry.getValue(); + Value value = Utils.hvalToVal(val); + metaObj.put(name, value); + } + } + table.setTableMeta(metaObj); + } HCol ts = grid.col("ts"); HCol val = grid.col("val"); Iterator it = grid.iterator(); diff --git a/src/main/java/org/dsa/iot/haystack/actions/ServerActions.java b/src/main/java/org/dsa/iot/haystack/actions/ServerActions.java index 4852b07..b9242f8 100644 --- a/src/main/java/org/dsa/iot/haystack/actions/ServerActions.java +++ b/src/main/java/org/dsa/iot/haystack/actions/ServerActions.java @@ -9,6 +9,7 @@ import org.dsa.iot.dslink.node.actions.Parameter; import org.dsa.iot.dslink.node.value.Value; import org.dsa.iot.dslink.node.value.ValueType; +import org.dsa.iot.dslink.util.StringUtils; import org.dsa.iot.dslink.util.handler.Handler; import org.dsa.iot.haystack.Haystack; import org.dsa.iot.haystack.Utils; @@ -39,7 +40,7 @@ public void handle(ActionResult event) { String name = vName.getString(); String url = vUrl.getString(); - NodeBuilder builder = Utils.getBuilder(parent, name); + NodeBuilder builder = Utils.getBuilder(parent, StringUtils.encodeName(name)); builder.setConfig("url", new Value(url)); String user = null; if (vUser != null) { diff --git a/src/main/java/org/dsa/iot/haystack/handlers/ClosedHandler.java b/src/main/java/org/dsa/iot/haystack/handlers/ClosedHandler.java index 2bd8ec0..f9b1bc5 100644 --- a/src/main/java/org/dsa/iot/haystack/handlers/ClosedHandler.java +++ b/src/main/java/org/dsa/iot/haystack/handlers/ClosedHandler.java @@ -59,13 +59,13 @@ private void removeNodes(Node node) { if (children != null) { for (Node n : children.values()) { if (n != null && (n.getValue() == null)) { - removeNodes(node); + removeNodes(n); } } } if (!man.hasValueSub(node)) { LOGGER.debug("Removed: {}", node.getPath()); - node.getParent().removeChild(node); + node.getParent().removeChild(node, false); } } diff --git a/src/main/java/org/dsa/iot/haystack/helpers/NavHelper.java b/src/main/java/org/dsa/iot/haystack/helpers/NavHelper.java index f8a67e6..137db89 100644 --- a/src/main/java/org/dsa/iot/haystack/helpers/NavHelper.java +++ b/src/main/java/org/dsa/iot/haystack/helpers/NavHelper.java @@ -9,7 +9,6 @@ import org.dsa.iot.dslink.node.Node; import org.dsa.iot.dslink.node.NodeBuilder; import org.dsa.iot.dslink.node.NodeListener; -import org.dsa.iot.dslink.node.actions.Action; import org.dsa.iot.dslink.node.value.Value; import org.dsa.iot.dslink.util.Objects; import org.dsa.iot.dslink.util.StringUtils; @@ -100,15 +99,9 @@ public void iterateNavChildren(final HGrid nav, final HVal writable = row.get("writable", false); if (writable instanceof HMarker) { HRef id = row.id(); - NodeBuilder b = Utils.getBuilder(child, "pointWrite"); - b.setDisplayName("Point Write"); - b.setSerializable(false); - HVal hKind = row.get("kind", false); String kind = hKind.toString(); - - b.setAction(Actions.getPointWriteAction(haystack, id, kind)); - b.build(); + Actions.getPointWriteAction(haystack, child, id, kind); } // Handle actions @@ -164,11 +157,13 @@ public void handle(HGrid event) { HVal val = row.get("equipRef"); String ref = ((HRef) val).val; String encoded = StringUtils.encodeName(ref); - Node n = node.getParent().getChild(encoded); + Node n = node.getParent().getChild(encoded, false); if (n == null) { - n = node.createChild(encoded) //double encoded have to leave - .setDisplayName(ref) - .build(); + if (encoded.equals(node.getName())) { + n = node; + } else { + n = node.createChild(encoded, false).build(); + } } encoded = StringUtils.encodeName(name); @@ -195,15 +190,9 @@ public void handle(HGrid event) { final HVal writable = row.get("writable", false); if (writable instanceof HMarker) { HRef id = row.id(); - NodeBuilder b = Utils.getBuilder(child, "pointWrite"); - b.setDisplayName("Point Write"); - b.setSerializable(false); - HVal hKind = row.get("kind", false); String kind = hKind.toString(); - - b.setAction(Actions.getPointWriteAction(haystack, id, kind)); - b.build(); + Actions.getPointWriteAction(haystack, child, id, kind); } // Handle actions @@ -254,10 +243,10 @@ private void iterateRow(Node node, HRow row) { SubscriptionController subController = getSubController(node, row); Iterator it = row.iterator(); Node curVal = null; + Node his = null; String id = null; String kind = null; String tz = null; - boolean his = false; boolean writable = false; while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); @@ -269,16 +258,23 @@ private void iterateRow(Node node, HRow row) { continue; } - Node child = node.getChild(name); + Node child = node.getChild(name, true); if (child == null) { - child = node.createChild(name).build(); + child = node.createChild(name, true).build(); } + child.setValueType(value.getType()); + child.setValue(value); + boolean hasAction = false; switch (name) { case "curVal": curVal = child; + hasAction = true; break; case "his": - his = val instanceof HMarker; + if (val instanceof HMarker) { + his = child; + hasAction = true; + } break; case "id": id = val.toString(); @@ -292,9 +288,12 @@ private void iterateRow(Node node, HRow row) { case "writable": writable = val instanceof HMarker; break; + default: + child.setHasChildren(false); + } + if (!hasAction) { + child.setHasChildren(false); } - child.setValueType(value.getType()); - child.setValue(value); NodeListener listener = child.getListener(); listener.setOnSubscribeHandler(subController.getSubHandler()); @@ -303,22 +302,19 @@ private void iterateRow(Node node, HRow row) { subController.childSubscribed(child); } } - if ((curVal != null) && (id != null)) { - Action a; - if (writable && (kind != null)) { - a = Actions.getSetAction(haystack, HRef.make(id), kind); - curVal.createChild("set", false) - .setAction(a) - .setDisplayName("Set") - .build(); - } - if (his) { + if (id != null) { //add getHistory and set + HRef hid = HRef.make(id); + if (his != null) { HTimeZone htz = null; if (tz != null) { htz = HTimeZone.make(tz, false); } - new GetHistory(curVal, haystack, HRef.make(id), htz); - //new GetHistory(node, haystack, HRef.make(id), htz); + new GetHistory(his, haystack, hid, htz); + //add to parent, but sometimes parent shows in metrics rather than nav tree + //new GetHistory(node, haystack, hid, htz); + } + if ((curVal != null) && writable && (kind != null)) { + Actions.getSetAction(haystack, curVal, hid, kind); } } } @@ -338,9 +334,17 @@ private SubscriptionController getSubController(Node node, HRow row) { return subController; } + private String getDisplayName(HRow row) { + HVal val = row.get("navName", false); + if (val != null) { + return val.toString(); + } + return getName(row); + } + private String getName(HRow row) { HRef id = (HRef) row.get("id", false); - String name; + String name = null; if (id != null) { name = id.val; } else {