-
Notifications
You must be signed in to change notification settings - Fork 331
/
BlockManager.java
342 lines (309 loc) · 11.4 KB
/
BlockManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
package jmri;
import java.beans.PropertyChangeEvent;
import java.text.DecimalFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import jmri.implementation.SignalSpeedMap;
import jmri.jmrit.roster.RosterEntry;
import jmri.jmrix.internal.InternalSystemConnectionMemo;
import jmri.managers.AbstractManager;
/**
* Basic Implementation of a BlockManager.
* <p>
* Note that this does not enforce any particular system naming convention.
* <p>
* Note this is a concrete class, unlike the interface/implementation pairs of
* most Managers, because there are currently only one implementation for
* Blocks.
* <hr>
* This file is part of JMRI.
* <p>
* JMRI is free software; you can redistribute it and/or modify it under the
* terms of version 2 of the GNU General Public License as published by the Free
* Software Foundation. See the "COPYING" file for a copy of this license.
* <p>
* JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* @author Bob Jacobsen Copyright (C) 2006
*/
public class BlockManager extends AbstractManager<Block> implements ProvidingManager<Block>, InstanceManagerAutoDefault {
private final String powerManagerChangeName;
public BlockManager() {
super(InstanceManager.getDefault(InternalSystemConnectionMemo.class));
InstanceManager.getDefault(SensorManager.class).addVetoableChangeListener(this);
InstanceManager.getDefault(ReporterManager.class).addVetoableChangeListener(this);
InstanceManager.getList(PowerManager.class).forEach((pm) -> {
pm.addPropertyChangeListener(this);
});
powerManagerChangeName = InstanceManager.getListPropertyName(PowerManager.class);
InstanceManager.addPropertyChangeListener(this);
}
@Override
@CheckReturnValue
public int getXMLOrder() {
return Manager.BLOCKS;
}
@Override
@CheckReturnValue
public char typeLetter() {
return 'B';
}
private boolean saveBlockPath = true;
@CheckReturnValue
public boolean isSavedPathInfo() {
return saveBlockPath;
}
public void setSavedPathInfo(boolean save) {
saveBlockPath = save;
}
/**
* Method to create a new Block only if it does not exist
*
* @param systemName the system name
* @param userName the user name
* @return null if a Block with the same systemName or userName already
* exists, or if there is trouble creating a new Block
*/
@CheckForNull
public Block createNewBlock(@Nonnull String systemName, @CheckForNull String userName) {
// Check that Block does not already exist
Block r;
if (userName != null && !userName.equals("")) {
r = getByUserName(userName);
if (r != null) {
return null;
}
}
r = getBySystemName(systemName);
if (r != null) {
return null;
}
// Block does not exist, create a new Block
r = new Block(systemName, userName);
// save in the maps
register(r);
/*The following keeps track of the last created auto system name.
currently we do not reuse numbers, although there is nothing to stop the
user from manually recreating them*/
if (systemName.startsWith("IB:AUTO:")) {
try {
int autoNumber = Integer.parseInt(systemName.substring(8));
if (autoNumber > lastAutoBlockRef) {
lastAutoBlockRef = autoNumber;
}
} catch (NumberFormatException e) {
log.warn("Auto generated SystemName {} is not in the correct format", systemName);
}
}
try {
r.setBlockSpeed("Global"); // NOI18N
} catch (JmriException ex) {
log.error("{}", ex.getMessage());
}
return r;
}
/**
* Method to create a new Block using an automatically incrementing system
* name.
*
* @param userName the user name for the new block
* @return null if a Block with the same systemName or userName already
* exists, or if there is trouble creating a new Block.
*/
@CheckForNull
public Block createNewBlock(@Nonnull String userName) {
int nextAutoBlockRef = lastAutoBlockRef + 1;
StringBuilder b = new StringBuilder("IB:AUTO:");
String nextNumber = paddedNumber.format(nextAutoBlockRef);
b.append(nextNumber);
return createNewBlock(b.toString(), userName);
}
/**
* If the Block exists, return it, otherwise create a new one and return it.
* If the argument starts with the system prefix and type letter, usually
* "IB", then the argument is considered a system name, otherwise it's
* considered a user name and a system name is automatically created.
*
* @param name the system name or the user name for the block
* @return a new or existing Block
* @throws IllegalArgumentException if cannot create block or no name supplied; never returns null
*/
@Nonnull
public Block provideBlock(@Nonnull String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Could not create block, no name supplied");
}
Block b = getBlock(name);
if (b != null) {
return b;
}
if (name.startsWith(getSystemPrefix() + typeLetter())) {
b = createNewBlock(name, null);
} else {
b = createNewBlock(name);
}
if (b == null) {
throw new IllegalArgumentException("Could not create block \"" + name + "\"");
}
return b;
}
DecimalFormat paddedNumber = new DecimalFormat("0000");
int lastAutoBlockRef = 0;
/**
* Method to get an existing Block. First looks up assuming that name is a
* User Name. If this fails looks up assuming that name is a System Name. If
* both fail, returns null.
*
* @param name the name of an existing block
* @return a Block or null if none found
*/
@CheckReturnValue
@CheckForNull
public Block getBlock(@Nonnull String name) {
Block r = getByUserName(name);
if (r != null) {
return r;
}
return getBySystemName(name);
}
@CheckReturnValue
@CheckForNull
public Block getBySystemName(@Nonnull String key) {
return _tsys.get(key);
}
@CheckReturnValue
@CheckForNull
public Block getByUserName(@Nonnull String key) {
return _tuser.get(key);
}
@CheckReturnValue
@CheckForNull
public Block getByDisplayName(@Nonnull String key) {
// First try to find it in the user list.
// If that fails, look it up in the system list
Block retv = this.getByUserName(key);
if (retv == null) {
retv = this.getBySystemName(key);
}
// If it's not in the system list, go ahead and return null
return (retv);
}
String defaultSpeed = "Normal";
/**
* @param speed the speed
* @throws IllegalArgumentException if provided speed is invalid
*/
public void setDefaultSpeed(@Nonnull String speed) {
if (defaultSpeed.equals(speed)) {
return;
}
try {
Float.parseFloat(speed);
} catch (NumberFormatException nx) {
try {
InstanceManager.getDefault(SignalSpeedMap.class).getSpeed(speed);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException("Value of requested default block speed \"" + speed + "\" is not valid", ex);
}
}
String oldSpeed = defaultSpeed;
defaultSpeed = speed;
firePropertyChange("DefaultBlockSpeedChange", oldSpeed, speed);
}
@CheckReturnValue
@Nonnull
public String getDefaultSpeed() {
return defaultSpeed;
}
@Override
@CheckReturnValue
@Nonnull
public String getBeanTypeHandled(boolean plural) {
return Bundle.getMessage(plural ? "BeanNameBlocks" : "BeanNameBlock");
}
/**
* Returns a list of blocks which the supplied roster entry appears to be
* occupying. A block is assumed to contain this roster entry if its value
* is the RosterEntry itself, or a string with the entry's id or dcc
* address.
*
* @param re the roster entry
* @return list of block system names
*/
@CheckReturnValue
@Nonnull
public List<Block> getBlocksOccupiedByRosterEntry(@Nonnull RosterEntry re) {
List<Block> blockList = new ArrayList<>();
getNamedBeanSet().stream().forEach((b) -> {
Object obj;
if (b!= null && (obj = b.getValue()) != null) {
if (obj instanceof RosterEntry && obj == re) {
blockList.add(b);
} else if (obj.toString().equals(re.getId()) || obj.toString().equals(re.getDccAddress())) {
blockList.add(b);
}
}
});
return blockList;
}
private Instant lastTimeLayoutPowerOn; // the most recent time any power manager had a power ON event
/**
* Listen for changes to the power state from any power managers
* in use in order to track how long it's been since power was applied
* to the layout. This information is used in {@link Block#goingActive()}
* when deciding whether to restore a block's last value.
*
* Also listen for additions/removals or PowerManagers
*
* @param e the change event
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
if (jmri.PowerManager.POWER.equals(e.getPropertyName())) {
try {
PowerManager pm = (PowerManager) e.getSource();
if (pm.getPower() == jmri.PowerManager.ON) {
lastTimeLayoutPowerOn = Instant.now();
}
} catch (JmriException | NoSuchMethodError xe) {
// do nothing
}
}
if (powerManagerChangeName.equals(e.getPropertyName())) {
if (e.getNewValue() == null) {
// powermanager has been removed
PowerManager pm = (PowerManager) e.getOldValue();
pm.removePropertyChangeListener(this);
} else {
// a powermanager has been added
PowerManager pm = (PowerManager) e.getNewValue();
pm.addPropertyChangeListener(this);
}
}
}
/**
* Returns the amount of time since the layout was last powered up,
* in milliseconds. If the layout has not been powered up as far as
* JMRI knows it returns a very long time indeed.
*
* @return long int
*/
public long timeSinceLastLayoutPowerOn() {
if (lastTimeLayoutPowerOn == null) {
return Long.MAX_VALUE;
}
return Instant.now().toEpochMilli() - lastTimeLayoutPowerOn.toEpochMilli();
}
@Override
public Block provide(String name) throws IllegalArgumentException {
return provideBlock(name);
}
private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BlockManager.class);
}