Skip to content

Commit b4e9263

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

File tree

9 files changed

+118
-20
lines changed

9 files changed

+118
-20
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

+15-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ abstract public class ToolkitRepresentation<TWP extends Object, TW> implements E
7474
private final RepresentationUpdateThrottle throttle = new RepresentationUpdateThrottle(this);
7575

7676
/** Listener list */
77-
private final List<ToolkitListener> listeners = new CopyOnWriteArrayList<>();
77+
protected final List<ToolkitListener> listeners = new CopyOnWriteArrayList<>();
7878

7979
/** Add/remove representations for child elements as model changes */
8080
private final WidgetPropertyListener<List<Widget>> container_children_listener = (children, removed, added) ->
@@ -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-1
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()
@@ -452,7 +454,6 @@ void trackCurrentModel(final DisplayModel model)
452454
// setInput sets the tab name based on the input via runLater.
453455
// Update to the display name via another runLater.
454456
Platform.runLater(() -> dock_item.setLabel(info.getName()));
455-
456457
navigation.setCurrentDisplay(info);
457458
active_model = model;
458459
}

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/ActiveTab.java

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public ActiveTab(DockItemWithInput tab){
4343
toolkitListener = new UXAToolkitListener();
4444
((UXAToolkitListener)toolkitListener).setTabWrapper(this);
4545
((DisplayRuntimeInstance)tab.getProperties().get("application")).addListener(toolkitListener);
46+
((DisplayRuntimeInstance)tab.getProperties().get("application")).addListener(toolkitListener);
4647
jfxNode = tab.getContent();
4748
mouseMonitor = new UXAMouseMonitor(this);
4849
jfxNode.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseMonitor);

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

+24-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import javafx.stage.Stage;
66
import javafx.stage.Window;
77

8+
import net.dongliu.commons.Sys;
89
import org.csstudio.display.builder.runtime.app.DisplayRuntimeInstance;
910
import org.phoebus.ui.docking.DockItemWithInput;
1011
import org.phoebus.ui.docking.DockPane;
@@ -34,20 +35,21 @@ public void onChanged(Change<? extends Tab> change) {
3435
Window window = change.getList().get(0).getTabPane().getScene().getWindow();
3536
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){
3637

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.
4038
try {
39+
//Creating the wrapper object first (in the application thread) attaches a listener ASAP
40+
//we want to catch what caused the display to open
41+
ActiveTab tabWrapper = new ActiveTab((DockItemWithInput) tab);
42+
DisplayRuntimeInstance instance = (DisplayRuntimeInstance) tabWrapper.getParentTab().getProperties().get("application");
43+
//When @DockItem s are initialized, their models aren't ready yet.
44+
//On startup, the DockItemWithInput will show up but its DisplayModel will be null.
45+
//block until the model is ready, in an individual background thread.
4146
new Thread(() -> {
4247
try {
43-
DisplayRuntimeInstance instance = ((DisplayRuntimeInstance) tab.getProperties().get("application"));
4448
//block until the model is ready
4549
instance.getRepresentation_init().get();
4650
lock.lock();
4751
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
48-
49-
DockItemWithInput diwi = (DockItemWithInput)tab;
50-
activeWindowsAndTabs.get(windowID).add(diwi);
52+
activeWindowsAndTabs.get(windowID).add(tabWrapper);
5153
lock.unlock();
5254
} catch (Exception e) {
5355
e.printStackTrace();
@@ -59,6 +61,16 @@ public void onChanged(Change<? extends Tab> change) {
5961
}
6062
}
6163
}
64+
else if(change.wasRemoved()){
65+
for(Tab tab: change.getRemoved()){
66+
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){
67+
lock.lock();
68+
String windowID = (String) tab.getTabPane().getScene().getWindow().getProperties().get(DockStage.KEY_ID);
69+
activeWindowsAndTabs.get(windowID).remove((DockItemWithInput) tab);
70+
lock.unlock();
71+
}
72+
}
73+
}
6274
}
6375
}
6476
};
@@ -72,27 +84,23 @@ public void onChanged(Change<? extends Window> change) {
7284
for (javafx.stage.Window window : change.getAddedSubList()) {
7385
if(window.getProperties().containsKey(DockStage.KEY_ID)){
7486
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
87+
lock.lock();
7588
activeWindowsAndTabs.putIfAbsent(windowID, new ActiveTabsOfWindow(window));
7689
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-
}
90+
item.getTabs().removeListener(UXATabChangeListener);
8691
item.getTabs().addListener(UXATabChangeListener);
8792
}
93+
lock.unlock();
8894
}
8995
}
9096
}
9197
else if(change.wasRemoved()){
9298
for(Window window: change.getRemoved()){
9399
if(window.getProperties().containsKey(DockStage.KEY_ID)){
94100
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
101+
lock.lock();
95102
activeWindowsAndTabs.remove(windowID);
103+
lock.unlock();
96104
}
97105
}
98106
}
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)