Skip to content

Commit 06ad320

Browse files
committed
Plugin now knows who opened a given display
1 parent b129053 commit 06ad320

File tree

8 files changed

+115
-18
lines changed

8 files changed

+115
-18
lines changed

app/display/representation/src/main/java/org/csstudio/display/builder/representation/ToolkitListener.java

+3
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@ public interface ToolkitListener
4444
* @param value The value
4545
*/
4646
default public void handleWrite(Widget widget, Object value) {};
47+
48+
default public void handleMethodCalled(Object... user_args) {};
49+
4750
}

app/display/representation/src/main/java/org/csstudio/display/builder/representation/ToolkitRepresentation.java

+14
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,20 @@ public void fireWrite(final Widget widget, final Object value)
689689
}
690690
}
691691

692+
public void fireMethodCall(Object... user_args) {
693+
for (final ToolkitListener listener : listeners)
694+
{
695+
try
696+
{
697+
listener.handleMethodCalled(user_args);
698+
}
699+
catch (final Throwable ex)
700+
{
701+
logger.log(Level.WARNING, "Failure when firing method-call event for " + Thread.currentThread().getStackTrace()[1].getMethodName(), ex);
702+
}
703+
}
704+
};
705+
692706
/** Close the toolkit's "window" that displays a model
693707
* @param model Model that has been represented in this toolkit
694708
* @throws Exception on error

app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/DisplayRuntimeInstance.java

+2
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ public void loadDisplayFile(final DisplayInfo info)
294294
// another instance
295295
dock_item.setInput(info.toURI());
296296

297+
representation.fireMethodCall((Object)display_info);
298+
297299
// Now that old model is no longer represented,
298300
// show info.
299301
// Showing this info before disposeModel()

app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/DockItemRepresentation.java

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import org.csstudio.display.builder.model.DisplayModel;
1616
import org.csstudio.display.builder.model.Widget;
17+
import org.csstudio.display.builder.representation.ToolkitListener;
1718
import org.csstudio.display.builder.representation.ToolkitRepresentation;
1819
import org.csstudio.display.builder.representation.javafx.JFXRepresentation;
1920
import org.phoebus.framework.workbench.ApplicationService;

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ public ActiveTabsOfWindow(Window window){
1717
activeWindowsService = ActiveWindowsService.getInstance();
1818
}
1919

20+
public void add(ActiveTab tab) throws Exception {
21+
this.remove(tab);
22+
activeTabs.putIfAbsent(tab.toString(), tab);
23+
}
24+
2025
public void add(DockItemWithInput tab) throws Exception {
2126
this.remove(tab);
2227
activeTabs.putIfAbsent(tab.toString(), new ActiveTab(tab));
@@ -29,6 +34,13 @@ public void remove(DockItemWithInput tab){
2934
}
3035
}
3136

37+
public void remove(ActiveTab tab){
38+
if(activeTabs.containsKey(tab.toString())){
39+
activeTabs.get(tab.toString()).close();
40+
activeTabs.remove(tab.toString());
41+
}
42+
}
43+
3244
public boolean contains(DockItemWithInput tab){
3345
return activeTabs.containsKey(tab.toString());
3446
}
@@ -37,8 +49,6 @@ public synchronized void addWidget(DockItemWithInput tab, Widget widget){
3749
activeTabs.get(tab.toString()).add(widget);
3850
}
3951

40-
41-
4252
public ConcurrentHashMap<String, ActiveTab> getActiveTabs() {
4353
return activeTabs;
4454
}

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

+23-16
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,21 @@ public void onChanged(Change<? extends Tab> change) {
3434
Window window = change.getList().get(0).getTabPane().getScene().getWindow();
3535
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){
3636

37-
//When @DockItem s are initialized, their models aren't ready yet.
38-
//On startup, the DockItemWithInput will show up but its DisplayModel will be null.
39-
//block until the model is ready, in an individual background thread.
4037
try {
38+
//Creating the wrapper object first (in the application thread) attaches a listener ASAP
39+
//we want to catch what caused the display to open
40+
ActiveTab tabWrapper = new ActiveTab((DockItemWithInput) tab);
41+
DisplayRuntimeInstance instance = (DisplayRuntimeInstance) tabWrapper.getParentTab().getProperties().get("application");
42+
//When @DockItem s are initialized, their models aren't ready yet.
43+
//On startup, the DockItemWithInput will show up but its DisplayModel will be null.
44+
//block until the model is ready, in an individual background thread.
4145
new Thread(() -> {
4246
try {
43-
DisplayRuntimeInstance instance = ((DisplayRuntimeInstance) tab.getProperties().get("application"));
4447
//block until the model is ready
4548
instance.getRepresentation_init().get();
4649
lock.lock();
4750
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
48-
49-
DockItemWithInput diwi = (DockItemWithInput)tab;
50-
activeWindowsAndTabs.get(windowID).add(diwi);
51+
activeWindowsAndTabs.get(windowID).add(tabWrapper);
5152
lock.unlock();
5253
} catch (Exception e) {
5354
e.printStackTrace();
@@ -59,6 +60,16 @@ public void onChanged(Change<? extends Tab> change) {
5960
}
6061
}
6162
}
63+
else if(change.wasRemoved()){
64+
for(Tab tab: change.getRemoved()){
65+
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){
66+
lock.lock();
67+
String windowID = (String) tab.getTabPane().getScene().getWindow().getProperties().get(DockStage.KEY_ID);
68+
activeWindowsAndTabs.get(windowID).remove((DockItemWithInput) tab);
69+
lock.unlock();
70+
}
71+
}
72+
}
6273
}
6374
}
6475
};
@@ -72,27 +83,23 @@ public void onChanged(Change<? extends Window> change) {
7283
for (javafx.stage.Window window : change.getAddedSubList()) {
7384
if(window.getProperties().containsKey(DockStage.KEY_ID)){
7485
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
86+
lock.lock();
7587
activeWindowsAndTabs.putIfAbsent(windowID, new ActiveTabsOfWindow(window));
7688
for(DockPane item: DockStage.getDockPanes((Stage)window)){
77-
for (Tab tab: item.getTabs()){
78-
if(tab instanceof DockItemWithInput){
79-
try {
80-
activeWindowsAndTabs.get(windowID).add((DockItemWithInput)tab);
81-
} catch (Exception e) {
82-
throw new RuntimeException(e);
83-
}
84-
}
85-
}
89+
item.getTabs().removeListener(UXATabChangeListener);
8690
item.getTabs().addListener(UXATabChangeListener);
8791
}
92+
lock.unlock();
8893
}
8994
}
9095
}
9196
else if(change.wasRemoved()){
9297
for(Window window: change.getRemoved()){
9398
if(window.getProperties().containsKey(DockStage.KEY_ID)){
9499
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
100+
lock.lock();
95101
activeWindowsAndTabs.remove(windowID);
102+
lock.unlock();
96103
}
97104
}
98105
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.phoebus.applications.uxanalytics.monitor;
2+
3+
public enum ResourceOpenSources {
4+
UNKNOWN,
5+
ACTION_BUTTON,
6+
FILE_BROWSER,
7+
NAVIGATION_BUTTON,
8+
TOP_RESOURCES,
9+
RESTORED
10+
}

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

+50
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,22 @@
44
import org.csstudio.display.builder.model.properties.ActionInfo;
55
import org.csstudio.display.builder.representation.ToolkitListener;
66

7+
import java.util.HashMap;
8+
import java.util.Map;
9+
710

811
public class UXAToolkitListener implements ToolkitListener {
912

13+
public static final HashMap<String, ResourceOpenSources> openSources = new HashMap<>(
14+
Map.of(
15+
"org.csstudio.display.builder.runtime.ActionUtil.openDisplay", ResourceOpenSources.ACTION_BUTTON,
16+
"org.phoebus.ui.application.PhoebusApplication.fileOpen", ResourceOpenSources.FILE_BROWSER,
17+
"org.csstudio.display.builder.runtime.app.NavigationAction.navigate", ResourceOpenSources.NAVIGATION_BUTTON,
18+
"org.phoebus.ui.internal.MementoHelper.restoreDockItem", ResourceOpenSources.RESTORED,
19+
"TOP_RESOURCES", ResourceOpenSources.TOP_RESOURCES
20+
)
21+
);
22+
1023
private ActiveTab tabWrapper;
1124
private final UXAMonitor monitor = UXAMonitor.getInstance();
1225
void setTabWrapper(ActiveTab tabWrapper){
@@ -32,4 +45,41 @@ public void handleWrite(Widget widget, Object value) {
3245
public void handleClick(Widget widget, boolean with_control) {
3346
//nothing for now
3447
}
48+
49+
//Traverse down the call stack to find out what caused the display to open
50+
public static ResourceOpenSources getSourceOfOpen(StackTraceElement[] stackTrace){
51+
for(StackTraceElement e: stackTrace){
52+
String methodName = e.getMethodName();
53+
if( methodName.contains("lambda$")){
54+
methodName = unmangleLambda(methodName);
55+
}
56+
String fullName = e.getClassName()+"."+methodName;
57+
if(openSources.containsKey(fullName)){
58+
return openSources.get(fullName);
59+
}
60+
}
61+
return ResourceOpenSources.UNKNOWN;
62+
}
63+
64+
private static String unmangleLambda(String expression){
65+
//find index of first '$' after 'lambda$'
66+
int start = expression.indexOf("lambda$") + 7;
67+
int end = expression.indexOf("$", start);
68+
String unmangled = expression.substring(start,end);
69+
return unmangled;
70+
}
71+
72+
@Override
73+
public void handleMethodCalled(Object... user_args) {
74+
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
75+
//zeroth element is getStackTrace itself, first is this method,
76+
//second is fireMethodCalled, third is the one we care about
77+
String methodName = stackTrace[3].getMethodName();
78+
if(methodName.equals("loadDisplayFile")){
79+
ResourceOpenSources source = getSourceOfOpen(stackTrace);
80+
System.out.println("Method call: "+methodName);
81+
System.out.println("Source: "+getSourceOfOpen(stackTrace));
82+
}
83+
}
84+
3585
}

0 commit comments

Comments
 (0)