1
1
package uk .ac .stfc .isis .ibex .e4 .ui .perspectiveswitcher ;
2
2
3
+ import java .lang .reflect .Type ;
4
+ import java .util .HashMap ;
5
+ import java .util .List ;
6
+ import java .util .Map ;
7
+
8
+ import org .apache .logging .log4j .Logger ;
3
9
import org .eclipse .e4 .core .di .annotations .Execute ;
4
10
import org .eclipse .e4 .core .services .events .IEventBroker ;
5
11
import org .eclipse .e4 .ui .model .application .MApplication ;
11
17
import org .eclipse .e4 .ui .workbench .UIEvents .EventTags ;
12
18
import org .eclipse .e4 .ui .workbench .modeling .EModelService ;
13
19
import org .eclipse .e4 .ui .workbench .modeling .EPartService ;
20
+ import org .eclipse .swt .widgets .Display ;
14
21
import org .osgi .service .event .Event ;
15
22
import org .osgi .service .event .EventHandler ;
16
23
24
+ import com .google .gson .Gson ;
25
+ import com .google .gson .reflect .TypeToken ;
26
+
17
27
import uk .ac .stfc .isis .ibex .e4 .ui .perspectiveswitcher .controls .ResetLayoutButtonModel ;
18
- import uk .ac .stfc .isis .ibex .preferences .PreferenceSupplier ;
28
+ import uk .ac .stfc .isis .ibex .epics .observing .BaseObserver ;
29
+ import uk .ac .stfc .isis .ibex .epics .switching .ObservableFactory ;
30
+ import uk .ac .stfc .isis .ibex .epics .switching .OnInstrumentSwitch ;
31
+ import uk .ac .stfc .isis .ibex .epics .switching .SwitchableObservable ;
32
+ import uk .ac .stfc .isis .ibex .instrument .InstrumentUtils ;
33
+ import uk .ac .stfc .isis .ibex .instrument .channels .CompressedCharWaveformChannel ;
34
+ import uk .ac .stfc .isis .ibex .logger .IsisLog ;
19
35
20
36
/**
21
37
* Copies all snippet perspectives to perspective stack called
25
41
*/
26
42
public class CopyPerspectiveSnippetProcessor {
27
43
44
+ private static final Logger LOG = IsisLog .getLogger (CopyPerspectiveSnippetProcessor .class );
45
+
28
46
private PerspectivesProvider perspectivesProvider ;
29
47
private MPerspectiveStack perspectiveStack ;
48
+ private SwitchableObservable <String > perspectiveSettings ;
49
+
50
+ private final PerspectivePreferenceSupplier preferenceSupplier = new PerspectivePreferenceSupplier ();
51
+
52
+ private static final Gson GSON = new Gson ();
53
+ private static final Type SERVER_IOC_DATA_FORMAT = new TypeToken <Map <String , Boolean >>() { }.getType ();
54
+
55
+ private ObservableFactory switchingObsFactory = new ObservableFactory (OnInstrumentSwitch .SWITCH );
30
56
57
+ private static final String PERSPECTIVE_CONFIG_PV = "CS:PERSP:SETTINGS" ;
58
+ private static final String PERSPECTIVE_CONFIG_PV_DISCONNECTED_MSG = "Remote perspective PV disconnected, using last known good value." ;
59
+ private static final String PERSPECTIVE_CONFIG_PV_ERROR_MSG = "Remote perspective PV in error, using last known good value." ;
60
+
61
+ /* We are expecting the PV to take a while to connect after starting the client.
62
+ * This is to prevent excessive logging on the client startup.
63
+ */
64
+ private final long connectionCheckTimeout = 10000 ;
65
+ private boolean connectionCheck = false ;
66
+
31
67
/**
32
- * Clone each snippet that is a perspective and add the cloned perspective
33
- * to the main PerspectiveStack.
68
+ * Clone each snippet that is a perspective and add the cloned perspective to
69
+ * the main PerspectiveStack.
34
70
*
35
- * @param app
36
- * The MApplication used.
37
- * @param partService
38
- * The EPartService used.
39
- * @param modelService
40
- * The EModelService used.
41
- * @param broker
42
- * The IEventBroker used.
71
+ * @param app The MApplication used.
72
+ * @param partService The EPartService used.
73
+ * @param modelService The EModelService used.
74
+ * @param broker The IEventBroker used.
43
75
*/
44
76
@ Execute
45
- public void execute (MApplication app , EPartService partService , EModelService modelService , IEventBroker broker ) {
77
+ public void execute (MApplication app , EModelService modelService , EPartService partService , IEventBroker broker ) {
46
78
perspectivesProvider = new PerspectivesProvider (app , partService , modelService );
47
79
perspectiveStack = perspectivesProvider .getTopLevelStack ();
48
80
81
+ perspectiveSettings = switchingObsFactory .getSwitchableObservable (new CompressedCharWaveformChannel (),
82
+ InstrumentUtils .addPrefix (PERSPECTIVE_CONFIG_PV ));
83
+
49
84
// Only do this when no other children, or the restored workspace state
50
85
// will be overwritten.
51
86
if (!perspectiveStack .getChildren ().isEmpty ()) {
@@ -56,36 +91,110 @@ public void execute(MApplication app, EPartService partService, EModelService mo
56
91
// perspective into the main PerspectiveStack
57
92
boolean isFirst = true ;
58
93
for (MPerspective perspective : perspectivesProvider .getInitialPerspectives ()) {
59
- if (!new PreferenceSupplier ().perspectivesToHide ().contains (perspective .getElementId ())) {
60
- perspectiveStack .getChildren ().add (perspective );
61
- if (isFirst ) {
62
- perspectiveStack .setSelectedElement (perspective );
63
- isFirst = false ;
64
- }
94
+
95
+ perspectiveStack .getChildren ().add (perspective );
96
+ if (isFirst ) {
97
+ perspectiveStack .setSelectedElement (perspective );
98
+ isFirst = false ;
65
99
}
66
100
subscribeChangedElement (broker , perspective );
67
101
subscribeSelectedPerspective (broker , perspective );
68
102
}
103
+
104
+ perspectiveSettings .subscribe (new BaseObserver <String >() {
105
+ @ Override
106
+ public void onValue (String value ) {
107
+ if (!preferenceSupplier .getUseLocalPerspectives ()) {
108
+ Map <String , Boolean > visiblePerspectives = GSON .fromJson (value , SERVER_IOC_DATA_FORMAT );
109
+ setVisiblePerspectivesAsync (visiblePerspectives );
110
+ }
111
+ }
112
+
113
+ @ Override
114
+ public void onError (Exception e ) {
115
+ LOG .error (PERSPECTIVE_CONFIG_PV_DISCONNECTED_MSG );
116
+ }
117
+
118
+ @ Override
119
+ public void onConnectionStatus (boolean isConnected ) {
120
+ if (!isConnected && connectionCheck ) {
121
+ LOG .error (PERSPECTIVE_CONFIG_PV_ERROR_MSG );
122
+ }
123
+ }
124
+ });
125
+ setupConnectionCheckTimeout (connectionCheckTimeout );
126
+
127
+ preferenceSupplier .addUseLocalPerspectivesListener (useLocal -> {
128
+ if (useLocal ) {
129
+ setVisiblePerspectives (preferenceSupplier .perspectivesToHide ());
130
+ } else {
131
+ Map <String , Boolean > visiblePerspectives = GSON .fromJson (perspectiveSettings .getValue (), SERVER_IOC_DATA_FORMAT );
132
+ setVisiblePerspectivesAsync (visiblePerspectives );
133
+ }
134
+ });
135
+
136
+ preferenceSupplier .addHiddenPerspectivesListener (hiddenPerspectives -> {
137
+ if (preferenceSupplier .getUseLocalPerspectives ()) {
138
+ setVisiblePerspectives (hiddenPerspectives );
139
+ }
140
+ });
141
+
69
142
ResetLayoutButtonModel .getInstance ().reset (perspectiveStack .getSelectedElement ());
70
143
}
71
144
145
+ /**
146
+ * Set which perspectives are visible based on a list of the ones that are hidden.
147
+ * @param hiddenPerspectives The list of hidden perspectives
148
+ */
149
+ private void setVisiblePerspectives (List <String > hiddenPerspectives ) {
150
+ Map <String , Boolean > visibilityMap = new HashMap <String , Boolean >();
151
+ for (MPerspective perspective : perspectivesProvider .getInitialPerspectives ()) {
152
+ visibilityMap .put (perspective .getElementId (), !hiddenPerspectives .contains (perspective .getElementId ()));
153
+ }
154
+ setVisiblePerspectivesAsync (visibilityMap );
155
+ }
156
+
157
+ /**
158
+ * Set which perspectives are visible based on a map of the perspective ID against and true if visible, false if hidden.
159
+ * @param visiblePerspectiveMap A map of perspective ID vs true if visible, false if hidden.
160
+ */
161
+ private void setVisiblePerspectivesAsync (Map <String , Boolean > visiblePerspectiveMap ) {
162
+ Display .getDefault ().asyncExec (new Runnable () {
163
+ @ Override
164
+ public void run () {
165
+ List <MPerspective > perspectives = perspectiveStack .getChildren ();
166
+ if (perspectives .size () > 0 ) {
167
+ perspectives .forEach (c -> c .setVisible (true ));
168
+ perspectives .forEach (c -> c .setVisible (visiblePerspectiveMap .getOrDefault (c .getElementId (), true )));
169
+ if (!perspectiveStack .getSelectedElement ().isVisible ()) {
170
+ // If selected perspective is no longer visible then
171
+ // find first visible perspective and set it as current
172
+ perspectiveStack .setSelectedElement (perspectives .stream ()
173
+ .filter (c -> c .isVisible ()).findFirst ().orElse (perspectives .get (0 )));
174
+ }
175
+ } else {
176
+ LOG .error ("No perspectives available to show." );
177
+ }
178
+ }
179
+ });
180
+ }
181
+
72
182
/**
73
183
* Listen to perspective changes and set the new perspective as current
74
184
* perspective in ResetLayoutButtonModel.
75
185
*
76
- * @param broker
77
- * IEventBroker
78
- * @param perspective
79
- * The new current perspective
186
+ * @param broker IEventBroker
187
+ * @param perspective The new current perspective
80
188
*/
81
- public void subscribeSelectedPerspective (IEventBroker broker , MPerspective perspective ) {
189
+ private void subscribeSelectedPerspective (IEventBroker broker , MPerspective perspective ) {
82
190
83
191
EventHandler handler = new EventHandler () {
84
192
@ Override
85
193
public void handleEvent (Event event ) {
86
194
MUIElement element = (MUIElement ) event .getProperty (EventTags .NEW_VALUE );
87
195
88
- if (!perspectiveStack .getSelectedElement ().equals (perspective )) {
196
+ if (perspectiveStack .getSelectedElement () != null
197
+ && !perspectiveStack .getSelectedElement ().equals (perspective )) {
89
198
return ;
90
199
}
91
200
@@ -105,12 +214,10 @@ public void handleEvent(Event event) {
105
214
* Listen to perspective content changes set the current perspective in
106
215
* ResetLayoutButtonModel to changed.
107
216
*
108
- * @param broker
109
- * IEventBroker
110
- * @param perspective
111
- * The new current perspective
217
+ * @param broker IEventBroker
218
+ * @param perspective The new current perspective
112
219
*/
113
- public void subscribeChangedElement (IEventBroker broker , MPerspective perspective ) {
220
+ private void subscribeChangedElement (IEventBroker broker , MPerspective perspective ) {
114
221
115
222
EventHandler handler = new EventHandler () {
116
223
boolean alreadyCalled = false ;
@@ -140,4 +247,26 @@ public void handleEvent(Event event) {
140
247
141
248
broker .subscribe (UIEvents .UIElement .TOPIC_CONTAINERDATA , handler );
142
249
}
250
+
251
+ /**
252
+ * Enables checking the connection of perspective PV after a variable time. After that, performs
253
+ * an additional connection check.
254
+ * @param time Time in milliseconds of how long to wait before enabling PV connection check.
255
+ */
256
+ private void setupConnectionCheckTimeout (long time ) {
257
+ Thread connectionTimeoutThread = new Thread () {
258
+ public void run () {
259
+ try {
260
+ Thread .sleep (time );
261
+ connectionCheck = true ;
262
+ if (!perspectiveSettings .isConnected ()) {
263
+ LOG .error (PERSPECTIVE_CONFIG_PV_DISCONNECTED_MSG );
264
+ }
265
+ } catch (InterruptedException e ) {
266
+ LOG .error ("Connection timeout interrupted." );
267
+ }
268
+ }
269
+ };
270
+ connectionTimeoutThread .start ();
271
+ }
143
272
}
0 commit comments