Skip to content

Commit

Permalink
Plugin now knows who opened a given display
Browse files Browse the repository at this point in the history
  • Loading branch information
daykin committed Jun 19, 2024
1 parent b129053 commit 06ad320
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ public interface ToolkitListener
* @param value The value
*/
default public void handleWrite(Widget widget, Object value) {};

default public void handleMethodCalled(Object... user_args) {};

}
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,20 @@ public void fireWrite(final Widget widget, final Object value)
}
}

public void fireMethodCall(Object... user_args) {
for (final ToolkitListener listener : listeners)
{
try
{
listener.handleMethodCalled(user_args);
}
catch (final Throwable ex)
{
logger.log(Level.WARNING, "Failure when firing method-call event for " + Thread.currentThread().getStackTrace()[1].getMethodName(), ex);
}
}
};

/** Close the toolkit's "window" that displays a model
* @param model Model that has been represented in this toolkit
* @throws Exception on error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ public void loadDisplayFile(final DisplayInfo info)
// another instance
dock_item.setInput(info.toURI());

representation.fireMethodCall((Object)display_info);

// Now that old model is no longer represented,
// show info.
// Showing this info before disposeModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.csstudio.display.builder.model.DisplayModel;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.representation.ToolkitListener;
import org.csstudio.display.builder.representation.ToolkitRepresentation;
import org.csstudio.display.builder.representation.javafx.JFXRepresentation;
import org.phoebus.framework.workbench.ApplicationService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public ActiveTabsOfWindow(Window window){
activeWindowsService = ActiveWindowsService.getInstance();
}

public void add(ActiveTab tab) throws Exception {
this.remove(tab);
activeTabs.putIfAbsent(tab.toString(), tab);
}

public void add(DockItemWithInput tab) throws Exception {
this.remove(tab);
activeTabs.putIfAbsent(tab.toString(), new ActiveTab(tab));
Expand All @@ -29,6 +34,13 @@ public void remove(DockItemWithInput tab){
}
}

public void remove(ActiveTab tab){
if(activeTabs.containsKey(tab.toString())){
activeTabs.get(tab.toString()).close();
activeTabs.remove(tab.toString());
}
}

public boolean contains(DockItemWithInput tab){
return activeTabs.containsKey(tab.toString());
}
Expand All @@ -37,8 +49,6 @@ public synchronized void addWidget(DockItemWithInput tab, Widget widget){
activeTabs.get(tab.toString()).add(widget);
}



public ConcurrentHashMap<String, ActiveTab> getActiveTabs() {
return activeTabs;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,21 @@ public void onChanged(Change<? extends Tab> change) {
Window window = change.getList().get(0).getTabPane().getScene().getWindow();
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){

//When @DockItem s are initialized, their models aren't ready yet.
//On startup, the DockItemWithInput will show up but its DisplayModel will be null.
//block until the model is ready, in an individual background thread.
try {
//Creating the wrapper object first (in the application thread) attaches a listener ASAP
//we want to catch what caused the display to open
ActiveTab tabWrapper = new ActiveTab((DockItemWithInput) tab);
DisplayRuntimeInstance instance = (DisplayRuntimeInstance) tabWrapper.getParentTab().getProperties().get("application");
//When @DockItem s are initialized, their models aren't ready yet.
//On startup, the DockItemWithInput will show up but its DisplayModel will be null.
//block until the model is ready, in an individual background thread.
new Thread(() -> {
try {
DisplayRuntimeInstance instance = ((DisplayRuntimeInstance) tab.getProperties().get("application"));
//block until the model is ready
instance.getRepresentation_init().get();
lock.lock();
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);

DockItemWithInput diwi = (DockItemWithInput)tab;
activeWindowsAndTabs.get(windowID).add(diwi);
activeWindowsAndTabs.get(windowID).add(tabWrapper);
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
Expand All @@ -59,6 +60,16 @@ public void onChanged(Change<? extends Tab> change) {
}
}
}
else if(change.wasRemoved()){
for(Tab tab: change.getRemoved()){
if(tab.getProperties().get("application") instanceof DisplayRuntimeInstance && tab instanceof DockItemWithInput){
lock.lock();
String windowID = (String) tab.getTabPane().getScene().getWindow().getProperties().get(DockStage.KEY_ID);
activeWindowsAndTabs.get(windowID).remove((DockItemWithInput) tab);
lock.unlock();
}
}
}
}
}
};
Expand All @@ -72,27 +83,23 @@ public void onChanged(Change<? extends Window> change) {
for (javafx.stage.Window window : change.getAddedSubList()) {
if(window.getProperties().containsKey(DockStage.KEY_ID)){
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
lock.lock();
activeWindowsAndTabs.putIfAbsent(windowID, new ActiveTabsOfWindow(window));
for(DockPane item: DockStage.getDockPanes((Stage)window)){
for (Tab tab: item.getTabs()){
if(tab instanceof DockItemWithInput){
try {
activeWindowsAndTabs.get(windowID).add((DockItemWithInput)tab);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
item.getTabs().removeListener(UXATabChangeListener);
item.getTabs().addListener(UXATabChangeListener);
}
lock.unlock();
}
}
}
else if(change.wasRemoved()){
for(Window window: change.getRemoved()){
if(window.getProperties().containsKey(DockStage.KEY_ID)){
String windowID = (String) window.getProperties().get(DockStage.KEY_ID);
lock.lock();
activeWindowsAndTabs.remove(windowID);
lock.unlock();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.phoebus.applications.uxanalytics.monitor;

public enum ResourceOpenSources {
UNKNOWN,
ACTION_BUTTON,
FILE_BROWSER,
NAVIGATION_BUTTON,
TOP_RESOURCES,
RESTORED
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@
import org.csstudio.display.builder.model.properties.ActionInfo;
import org.csstudio.display.builder.representation.ToolkitListener;

import java.util.HashMap;
import java.util.Map;


public class UXAToolkitListener implements ToolkitListener {

public static final HashMap<String, ResourceOpenSources> openSources = new HashMap<>(
Map.of(
"org.csstudio.display.builder.runtime.ActionUtil.openDisplay", ResourceOpenSources.ACTION_BUTTON,
"org.phoebus.ui.application.PhoebusApplication.fileOpen", ResourceOpenSources.FILE_BROWSER,
"org.csstudio.display.builder.runtime.app.NavigationAction.navigate", ResourceOpenSources.NAVIGATION_BUTTON,
"org.phoebus.ui.internal.MementoHelper.restoreDockItem", ResourceOpenSources.RESTORED,
"TOP_RESOURCES", ResourceOpenSources.TOP_RESOURCES
)
);

private ActiveTab tabWrapper;
private final UXAMonitor monitor = UXAMonitor.getInstance();
void setTabWrapper(ActiveTab tabWrapper){
Expand All @@ -32,4 +45,41 @@ public void handleWrite(Widget widget, Object value) {
public void handleClick(Widget widget, boolean with_control) {
//nothing for now
}

//Traverse down the call stack to find out what caused the display to open
public static ResourceOpenSources getSourceOfOpen(StackTraceElement[] stackTrace){
for(StackTraceElement e: stackTrace){
String methodName = e.getMethodName();
if( methodName.contains("lambda$")){
methodName = unmangleLambda(methodName);
}
String fullName = e.getClassName()+"."+methodName;
if(openSources.containsKey(fullName)){
return openSources.get(fullName);
}
}
return ResourceOpenSources.UNKNOWN;
}

private static String unmangleLambda(String expression){
//find index of first '$' after 'lambda$'
int start = expression.indexOf("lambda$") + 7;
int end = expression.indexOf("$", start);
String unmangled = expression.substring(start,end);
return unmangled;
}

@Override
public void handleMethodCalled(Object... user_args) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
//zeroth element is getStackTrace itself, first is this method,
//second is fireMethodCalled, third is the one we care about
String methodName = stackTrace[3].getMethodName();
if(methodName.equals("loadDisplayFile")){
ResourceOpenSources source = getSourceOfOpen(stackTrace);
System.out.println("Method call: "+methodName);
System.out.println("Source: "+getSourceOfOpen(stackTrace));
}
}

}

0 comments on commit 06ad320

Please sign in to comment.