77import org .csstudio .display .builder .model .properties .WritePVActionInfo ;
88import org .csstudio .display .builder .model .util .ModelResourceUtil ;
99import org .csstudio .display .builder .runtime .app .DisplayInfo ;
10+ import org .epics .vtype .VType ;
1011import org .neo4j .driver .*;
1112
1213import 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}
0 commit comments