Skip to content

Commit 0b62d06

Browse files
committed
mostly-working end-to-end neo4j connection
1 parent 09bdb52 commit 0b62d06

File tree

5 files changed

+92
-57
lines changed

5 files changed

+92
-57
lines changed

app/ux-analytics/monitor/src/main/java/org/phoebus/applications/uxanalytics/monitor/BackendConnection.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.csstudio.display.builder.model.Widget;
44
import org.csstudio.display.builder.model.properties.ActionInfo;
55
import org.csstudio.display.builder.runtime.app.DisplayInfo;
6+
import org.epics.vtype.VType;
67

78
@FunctionalInterface
89
public interface BackendConnection {
@@ -15,7 +16,7 @@ public interface BackendConnection {
1516
public default void handleClick(ActiveTab who, Widget widget, Integer x, Integer y){}
1617
public default void handleClick(ActiveTab who, Integer x, Integer y){this.handleClick(who, null, x, y);}
1718
public default void handleAction(ActiveTab who, Widget widget, ActionInfo info){}
18-
public default void handlePVWrite(ActiveTab who, Widget widget, String PVName, Object value){}
1919
public default void handleDisplayOpen(ActiveTab who, Widget widget, DisplayInfo targetDisplayInfo){};
20+
public default void handlePVWrite(ActiveTab who, Widget widget, String PVName, String value){};
2021
public default void handleDisplayOpen(DisplayInfo target, DisplayInfo src, ResourceOpenSources how){};
2122
}

app/ux-analytics/monitor/src/main/java/org/phoebus/applications/uxanalytics/monitor/FileUtils.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ public class FileUtils {
1515
public static final String GIT_METADATA_DIR = ".git";
1616
public static final String SVN_METADATA_DIR = ".svn";
1717
public static final String HG_METADATA_DIR = ".hg";
18-
public static final String WEB_CONTENT_ROOT_SETTING_NAME = "PHOEBUS_WEB_CONTENT_ROOT";
19-
18+
public static final String WEB_CONTENT_ROOT_ENV_VAR = "PHOEBUS_WEB_CONTENT_ROOT";
19+
public static final String WEB_CONTENT_ROOT_SETTING_NAME = "web_content_root";
2020
public static String getWebContentRoot(){
2121
//check if PHOEBUS_WEB_CONTENT_ROOT is set
2222
String webContentRoot;
23-
webContentRoot = System.getenv(WEB_CONTENT_ROOT_SETTING_NAME);
23+
webContentRoot = System.getenv(WEB_CONTENT_ROOT_ENV_VAR);
2424
if(webContentRoot != null){
2525
return webContentRoot;
2626
}
@@ -138,17 +138,18 @@ public static String getSHA256Suffix(String path){
138138
}
139139
}
140140

141-
142141
public static String getAnalyticsPathFor(String path){
143-
String pathWithoutRoot = getPathWithoutSourceRoot(path);
144-
if(pathWithoutRoot == null){
145-
return null;
146-
}
142+
String pathWithoutRoot = ModelResourceUtil.normalize(getPathWithoutSourceRoot(path));
147143
String first8OfSHA256 = getSHA256Suffix(path);
148144
if(first8OfSHA256 == null){
149145
return null;
150146
}
151147
return pathWithoutRoot + "_" + first8OfSHA256;
152148
}
153149

150+
public static String analyticsPathForTab(ActiveTab tab){
151+
String path = tab.getDisplayInfo().getPath();
152+
return getAnalyticsPathFor(path);
153+
}
154+
154155
}

app/ux-analytics/monitor/src/main/java/org/phoebus/applications/uxanalytics/monitor/Neo4JConnection.java

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.csstudio.display.builder.model.properties.WritePVActionInfo;
88
import org.csstudio.display.builder.model.util.ModelResourceUtil;
99
import org.csstudio.display.builder.runtime.app.DisplayInfo;
10+
import org.epics.vtype.VType;
1011
import org.neo4j.driver.*;
1112

1213
import java.time.Instant;
@@ -32,6 +33,7 @@ public class Neo4JConnection implements BackendConnection{
3233
//names for action types (Opened a display, wrote to a PV)
3334
public static final String ACTION_WROTE = "wrote_to";
3435
public static final String ACTION_OPENED = "opened";
36+
public static final String ACTION_NAVIGATED = "navigation_button";
3537

3638
public static final String PROTOCOL = "neo4j://";
3739

@@ -45,7 +47,7 @@ public Boolean connect(String host, Integer port, String username, String passwo
4547
logger.log(Level.INFO, "Connected to " + host + " on port " + port + " as " + username);
4648
session = driver.session(
4749
SessionConfig.builder()
48-
.withDatabase("phoebus-analytics")
50+
.withDatabase("neo4j")
4951
.build());
5052
return true;
5153
} catch (Exception ex) {
@@ -81,17 +83,74 @@ public void handleClick(ActiveTab who, Widget widget, Integer x, Integer y) {
8183
return;
8284
}
8385

86+
@Override
87+
public void handlePVWrite(ActiveTab who, Widget widget, String PVName, String value) {
88+
String display = FileUtils.analyticsPathForTab(who);
89+
String widgetID = widget.getName();
90+
if(session!=null && session.isOpen()){
91+
Platform.runLater(()->{
92+
String query = String.format("MERGE(src:%s {name:$srcName}) " +
93+
"MERGE(dst:%s {name:$dstName}) " +
94+
"MERGE(src)-[connection:%s]->(dst) " +
95+
"ON CREATE SET connection.timestamps = [$timestamp]," +
96+
"connection.via = [$widgetId]"+
97+
"ON MATCH SET connection.timestamps=connection.timestamps+$timestamp,"+
98+
"connection.via = connection.via+[$widgetId]",
99+
TYPE_DISPLAY,TYPE_PV,ACTION_WROTE);
100+
session.executeWriteWithoutResult(tx->tx.run(query, Map.of("srcName",display,"dstName",PVName,
101+
"widgetId", widgetID, "timestamp", Instant.now().getEpochSecond())));
102+
});
103+
}
104+
}
105+
106+
@Override
107+
public void handleDisplayOpen(DisplayInfo target, DisplayInfo src, ResourceOpenSources how) {
108+
String sourcePath="UNKNOWN";
109+
String targetPath = FileUtils.getAnalyticsPathFor(target.getPath());
110+
String sourceType = null;
111+
String action = ACTION_OPENED;
112+
switch(how) {
113+
case RELOAD:
114+
case NAVIGATION_BUTTON:
115+
if(src!=null) {
116+
sourcePath = FileUtils.getAnalyticsPathFor(src.getPath());
117+
sourceType = TYPE_DISPLAY;
118+
action = ACTION_NAVIGATED;
119+
}
120+
break;
121+
case FILE_BROWSER:
122+
sourcePath=SRC_FILE_BROWSER;
123+
sourceType=TYPE_ORIGIN;
124+
break;
125+
case TOP_RESOURCES:
126+
sourcePath=SRC_TOP_RESOURCES;
127+
sourceType=TYPE_ORIGIN;
128+
break;
129+
case RESTORED:
130+
sourcePath=SRC_RESTORATION;
131+
sourceType=TYPE_ORIGIN;
132+
break;
133+
default:
134+
sourcePath=SRC_UNKNOWN;
135+
sourceType=TYPE_ORIGIN;
136+
}
137+
fileOpenConnection(targetPath,sourcePath,sourceType, action);
138+
}
139+
84140
//this operation involves calculating a hash and posting an update to the Neo4J database
85141
//so it should not be done on the JavaFX thread
142+
//This is separate from the rest because it requires additional validation to ensure the target is real.
86143
public void handleDisplayOpenViaActionButton(ActiveTab who, Widget widget, ActionInfo actionInfo) {
87144
Platform.runLater(() -> {
88145
DisplayInfo currentDisplayInfo = who.getDisplayInfo();
89-
String sourcePath = ModelResourceUtil.normalize(currentDisplayInfo.getPath());
90-
String targetPath = ModelResourceUtil.normalize(
91-
ModelResourceUtil.resolveResource(sourcePath,
92-
((OpenDisplayActionInfo) actionInfo).getFile()));
146+
String sourcePath = FileUtils.getAnalyticsPathFor(currentDisplayInfo.getPath());
147+
String targetPath = FileUtils.getAnalyticsPathFor(
148+
ModelResourceUtil.resolveResource(
149+
currentDisplayInfo.getPath(),
150+
((OpenDisplayActionInfo)actionInfo).getFile())
151+
);
93152
try {
94-
logger.info("Neo4J would have handled file open action: " + FileUtils.getAnalyticsPathFor(sourcePath) + "->" + FileUtils.getAnalyticsPathFor(targetPath));
153+
fileOpenConnection(targetPath,sourcePath,TYPE_DISPLAY,ACTION_OPENED);
95154
}
96155
catch(Exception e){
97156
logger.warning("Problem opening " + targetPath + " from " + sourcePath + ", not logging in analytics.");
@@ -115,46 +174,22 @@ public void handleAction(ActiveTab who, Widget widget, ActionInfo info) {
115174
}
116175
}
117176

118-
@Override
119-
public void handlePVWrite(ActiveTab who, Widget widget, String PVName, Object value) {
120-
logger.log(Level.INFO, "Neo4J Connection would have handled PV Write of " + value + " to " + PVName+ " from " + who + "on" + widget);
121-
}
122-
123-
private void createNodeIfNotExists(String name, String type){
124-
Platform.runLater(()->session.executeWriteWithoutResult(tx->tx.run("MERGE ($name:$type {name: $name})", Map.of("name",name,"type",type))));
125-
}
126-
127-
private void fileOpenConnection(String srcName, String srcType, String dstName){
177+
private void fileOpenConnection( String dstName, String srcName, String srcType, String action){
128178
Platform.runLater(()->{
129-
session.executeWriteWithoutResult(tx -> tx.run(
130-
"MERGE(src:$srcType {name:'$srcName'}"+
131-
"MERGE(dst:$dstType {name:'$dstName'}"+
132-
"MERGE(src)-[connection:$connectionType]->(dst)"+
133-
"ON CREATE SET connection.timestamps = [$timestamp]"+
134-
"ON MATCH SET connection.timestamps=connection.timestamps+$timestamp",
135-
Map.of("srcName","srcType",
136-
"dstName",TYPE_DISPLAY,
137-
"connectionType",ACTION_OPENED,
138-
"timestamp", Instant.now().getEpochSecond())
139-
));
179+
String query = String.format("MERGE(src:%s {name:$srcName}) " +
180+
"MERGE(dst:%s {name:$dstName}) " +
181+
"MERGE(src)-[connection:%s]->(dst) " +
182+
"ON CREATE SET connection.timestamps = [$timestamp]" +
183+
"ON MATCH SET connection.timestamps=connection.timestamps+$timestamp",
184+
srcType, TYPE_DISPLAY, action);
185+
if(dstName != null && srcName != null && srcType != null && session!=null && session.isOpen()) {
186+
session.executeWriteWithoutResult(tx -> tx.run(query,
187+
Map.of("srcName", srcName,
188+
"dstName", dstName,
189+
"timestamp", Instant.now().getEpochSecond())
190+
));
191+
}
140192
});
141193
}
142194

143-
@Override
144-
public void handleDisplayOpen(DisplayInfo target, DisplayInfo src, ResourceOpenSources how) {
145-
146-
switch(how) {
147-
case RELOAD:
148-
case NAVIGATION_BUTTON:
149-
//create a connection with source and destination
150-
case FILE_BROWSER:
151-
//create a connection from SRC_FILE_BROWSER to destination
152-
case TOP_RESOURCES:
153-
//create a connection from SRC_TOP_RESOURCES to destination
154-
case RESTORED:
155-
//create a connection from SRC_RESTORATION to destination
156-
case UNKNOWN:
157-
//create a connection from SRC_UNKNOWN to destination
158-
}
159-
}
160195
}

app/ux-analytics/monitor/src/main/java/org/phoebus/applications/uxanalytics/monitor/NoopBackendConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public void handleAction(ActiveTab who, Widget widget, ActionInfo info) {
2626
}
2727

2828
@Override
29-
public void handlePVWrite(ActiveTab who, Widget widget, String PVName, Object value) {
29+
public void handlePVWrite(ActiveTab who, Widget widget, String PVName, String value) {
3030
logger.log(Level.INFO, "Backend Connection would've handled PV Write" + value + "from" + who + "on" + widget);
3131
}
3232
}

app/ux-analytics/monitor/src/main/java/org/phoebus/applications/uxanalytics/monitor/UXAToolkitListener.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ public void handleAction(Widget widget, ActionInfo action) {
4747

4848
@Override
4949
public void handleWrite(Widget widget, Object value) {
50-
System.out.println("wrote from "+ widget+" from thread "+Thread.currentThread().getName());
51-
System.out.println("Telling backend that this PV was written from " + tabWrapper.getParentTab());
52-
monitor.getPhoebusConnection().handlePVWrite(tabWrapper, widget, widget.getPropertyValue("pv_name"), value);
50+
monitor.getPhoebusConnection().handlePVWrite(tabWrapper, widget, widget.getPropertyValue("pv_name"), value.toString());
5351
}
5452

5553
@Override

0 commit comments

Comments
 (0)