/
MetadataDummy.java
387 lines (332 loc) · 12.7 KB
/
MetadataDummy.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
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package io.openems.backend.metadata.dummy;
import static java.util.stream.Collectors.joining;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventHandler;
import org.osgi.service.event.propertytypes.EventTopics;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
import io.openems.backend.common.alerting.OfflineEdgeAlertingSetting;
import io.openems.backend.common.alerting.SumStateAlertingSetting;
import io.openems.backend.common.alerting.UserAlertingSettings;
import io.openems.backend.common.metadata.AbstractMetadata;
import io.openems.backend.common.metadata.Edge;
import io.openems.backend.common.metadata.EdgeHandler;
import io.openems.backend.common.metadata.Metadata;
import io.openems.backend.common.metadata.SimpleEdgeHandler;
import io.openems.backend.common.metadata.User;
import io.openems.common.channel.Level;
import io.openems.common.event.EventReader;
import io.openems.common.exceptions.OpenemsError;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions;
import io.openems.common.jsonrpc.response.GetEdgesResponse.EdgeMetadata;
import io.openems.common.session.Language;
import io.openems.common.session.Role;
import io.openems.common.utils.StringUtils;
import io.openems.common.utils.ThreadPoolUtils;
@Designate(ocd = Config.class, factory = false)
@Component(//
name = "Metadata.Dummy", //
configurationPolicy = ConfigurationPolicy.REQUIRE, //
immediate = true //
)
@EventTopics({ //
Edge.Events.ON_SET_CONFIG //
})
public class MetadataDummy extends AbstractMetadata implements Metadata, EventHandler {
private static final Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$");
private final Logger log = LoggerFactory.getLogger(MetadataDummy.class);
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private final EventAdmin eventAdmin;
private final AtomicInteger nextUserId = new AtomicInteger(-1);
private final AtomicInteger nextEdgeId = new AtomicInteger(-1);
private final Map<String, User> users = new HashMap<>();
private final Map<String, MyEdge> edges = new HashMap<>();
private final SimpleEdgeHandler edgeHandler = new SimpleEdgeHandler();
private Language defaultLanguage = Language.DE;
private JsonObject settings = new JsonObject();
@Activate
public MetadataDummy(@Reference EventAdmin eventadmin) {
super("Metadata.Dummy");
this.eventAdmin = eventadmin;
this.logInfo(this.log, "Activate");
// Allow the services some time to settle
this.executor.schedule(() -> {
this.setInitialized();
}, 10, TimeUnit.SECONDS);
}
@Deactivate
private void deactivate() {
ThreadPoolUtils.shutdownAndAwaitTermination(this.executor, 0);
this.logInfo(this.log, "Deactivate");
}
@Override
public User authenticate(String username, String password) throws OpenemsNamedException {
var name = "User #" + this.nextUserId.incrementAndGet();
var token = UUID.randomUUID().toString();
var user = new User(username, name, token, this.defaultLanguage, Role.ADMIN, this.hasMultipleEdges(),
this.settings);
this.users.put(user.getId(), user);
return user;
}
@Override
public User authenticate(String token) throws OpenemsNamedException {
for (var user : this.users.values()) {
if (!user.getToken().equals(token)) {
continue;
}
final var hasMultipleEdges = this.hasMultipleEdges();
final User returnUser;
if (user.hasMultipleEdges() != hasMultipleEdges //
|| !user.getSettings().equals(this.settings)) {
returnUser = this.createUser(user.getId(), user.getName(), user.getToken(), hasMultipleEdges);
this.users.put(token, returnUser);
} else {
returnUser = user;
}
return returnUser;
}
throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception();
}
private User createUser(String username, String name, String token, boolean hasMultipleEdges) {
return new User(username, name, token, this.defaultLanguage, Role.ADMIN, this.hasMultipleEdges(),
this.settings);
}
private boolean hasMultipleEdges() {
return this.edges.size() > 1;
}
@Override
public void logout(User user) {
this.users.remove(user.getId(), user);
}
@Override
public Optional<String> getEdgeIdForApikey(String apikey) {
var edgeOpt = this.edges.values().stream() //
.filter(edge -> apikey.equals(edge.getApikey())) //
.findFirst();
if (edgeOpt.isPresent()) {
return Optional.ofNullable(edgeOpt.get().getId());
}
// not found. Is apikey a valid Edge-ID?
var idOpt = MetadataDummy.parseNumberFromName(apikey);
int id;
String edgeId;
String setupPassword;
if (idOpt.isPresent()) {
edgeId = apikey;
id = idOpt.get();
} else {
// create new ID
id = this.nextEdgeId.incrementAndGet();
edgeId = "edge" + id;
}
setupPassword = edgeId;
var edge = new MyEdge(this, edgeId, apikey, setupPassword, "OpenEMS Edge #" + id, "", "");
this.edges.put(edgeId, edge);
return Optional.ofNullable(edgeId);
}
@Override
public Optional<Edge> getEdgeBySetupPassword(String setupPassword) {
var edgeOpt = this.edges.values().stream().filter(edge -> edge.getSetupPassword().equals(setupPassword))
.findFirst();
if (edgeOpt.isPresent()) {
var edge = edgeOpt.get();
return Optional.of(edge);
}
return Optional.empty();
}
@Override
public Optional<Edge> getEdge(String edgeId) {
Edge edge = this.edges.get(edgeId);
return Optional.ofNullable(edge);
}
@Override
public Optional<User> getUser(String userId) {
return Optional.ofNullable(this.users.get(userId));
}
@Override
public Collection<Edge> getAllOfflineEdges() {
return this.edges.values().stream().filter(Edge::isOffline).collect(Collectors.toUnmodifiableList());
}
private static Optional<Integer> parseNumberFromName(String name) {
try {
var matcher = MetadataDummy.NAME_NUMBER_PATTERN.matcher(name);
if (matcher.find()) {
var nameNumberString = matcher.group(1);
return Optional.ofNullable(Integer.parseInt(nameNumberString));
}
} catch (NullPointerException e) {
/* ignore */
}
return Optional.empty();
}
@Override
public void addEdgeToUser(User user, Edge edge) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.addEdgeToUser() is not implemented");
}
@Override
public Map<String, Object> getUserInformation(User user) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.getUserInformation() is not implemented");
}
@Override
public void setUserInformation(User user, JsonObject jsonObject) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.setUserInformation() is not implemented");
}
@Override
public byte[] getSetupProtocol(User user, int setupProtocolId) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.getSetupProtocol() is not implemented");
}
@Override
public JsonObject getSetupProtocolData(User user, String edgeId) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.getSetupProtocolData() is not implemented");
}
@Override
public int submitSetupProtocol(User user, JsonObject jsonObject) {
throw new UnsupportedOperationException("DummyMetadata.submitSetupProtocol() is not implemented");
}
@Override
public void registerUser(JsonObject jsonObject, String oem) throws OpenemsNamedException {
throw new UnsupportedOperationException("DummyMetadata.registerUser() is not implemented");
}
@Override
public void updateUserLanguage(User user, Language language) throws OpenemsNamedException {
this.defaultLanguage = language;
}
@Override
public EventAdmin getEventAdmin() {
return this.eventAdmin;
}
@Override
public void handleEvent(Event event) {
var reader = new EventReader(event);
switch (event.getTopic()) {
case Edge.Events.ON_SET_CONFIG:
this.edgeHandler.setEdgeConfigFromEvent(reader);
break;
}
}
@Override
public EdgeHandler edge() {
return this.edgeHandler;
}
@Override
public Optional<String> getSerialNumberForEdge(Edge edge) {
throw new UnsupportedOperationException("DummyMetadata.getSerialNumberForEdge() is not implemented");
}
@Override
public UserAlertingSettings getUserAlertingSettings(String edgeId, String userId) throws OpenemsException {
throw new UnsupportedOperationException("DummyMetadata.getUserAlertingSettings() is not implemented");
}
@Override
public List<UserAlertingSettings> getUserAlertingSettings(String edgeId) {
throw new UnsupportedOperationException("DummyMetadata.getUserAlertingSettings() is not implemented");
}
@Override
public List<OfflineEdgeAlertingSetting> getEdgeOfflineAlertingSettings(String edgeId) throws OpenemsException {
throw new UnsupportedOperationException("DummyMetadata.getEdgeOfflineAlertingSettings() is not implemented");
}
@Override
public List<SumStateAlertingSetting> getSumStateAlertingSettings(String edgeId) throws OpenemsException {
throw new UnsupportedOperationException("DummyMetadata.getSumStateAlertingSettings() is not implemented");
}
@Override
public void setUserAlertingSettings(User user, String edgeId, List<UserAlertingSettings> settings) {
throw new UnsupportedOperationException("DummyMetadata.setUserAlertingSettings() is not implemented");
}
@Override
public List<EdgeMetadata> getPageDevice(User user, PaginationOptions paginationOptions)
throws OpenemsNamedException {
var pagesStream = this.edges.values().stream();
final var query = paginationOptions.getQuery();
if (query != null) {
pagesStream = pagesStream.filter(//
edge -> StringUtils.containsWithNullCheck(edge.getId(), query) //
|| StringUtils.containsWithNullCheck(edge.getComment(), query) //
|| StringUtils.containsWithNullCheck(edge.getProducttype(), query) //
);
}
final var searchParams = paginationOptions.getSearchParams();
if (searchParams != null) {
if (searchParams.searchIsOnline()) {
pagesStream = pagesStream.filter(edge -> edge.isOnline() == searchParams.isOnline());
}
if (searchParams.productTypes() != null && !searchParams.productTypes().isEmpty()) {
pagesStream = pagesStream.filter(edge -> searchParams.productTypes().contains(edge.getProducttype()));
}
// TODO sum state filter
}
return pagesStream //
.sorted((s1, s2) -> s1.getId().compareTo(s2.getId())) //
.skip(paginationOptions.getPage() * paginationOptions.getLimit()) //
.limit(paginationOptions.getLimit()) //
.peek(t -> user.setRole(t.getId(), Role.ADMIN)) //
.map(myEdge -> {
return new EdgeMetadata(//
myEdge.getId(), //
myEdge.getComment(), //
myEdge.getProducttype(), //
myEdge.getVersion(), //
Role.ADMIN, //
myEdge.isOnline(), //
myEdge.getLastmessage(), //
null, // firstSetupProtocol
Level.OK);
}).toList();
}
@Override
public EdgeMetadata getEdgeMetadataForUser(User user, String edgeId) throws OpenemsNamedException {
final var edge = this.edges.get(edgeId);
if (edge == null) {
return null;
}
user.setRole(edgeId, Role.ADMIN);
return new EdgeMetadata(//
edge.getId(), //
edge.getComment(), //
edge.getProducttype(), //
edge.getVersion(), //
Role.ADMIN, //
edge.isOnline(), //
edge.getLastmessage(), //
null, // firstSetupProtocol
Level.OK //
);
}
@Override
public Optional<Level> getSumState(String edgeId) {
throw new UnsupportedOperationException("DummyMetadata.getSumState() is not implemented");
}
@Override
public void logGenericSystemLog(GenericSystemLog systemLog) {
this.logInfo(this.log,
"%s on %s executed %s [%s]".formatted(systemLog.user().getId(), systemLog.edgeId(), systemLog.teaser(),
systemLog.getValues().entrySet().stream() //
.map(t -> t.getKey() + "=" + t.getValue()) //
.collect(joining(", "))));
}
@Override
public void updateUserSettings(User user, JsonObject settings) {
this.settings = settings == null ? new JsonObject() : settings;
}
}