Skip to content

Commit 8384f5a

Browse files
authored
Backport FEMS UI (OpenEMS#2117)
This PR backports multiple improvements from FEMS UI to OpenEMS UI: - Add pagination to Overview page (this requires changes als in the Backend/Edge side and new JsonRpc-Requests - Add error log from UI to Backend. This allows tracing UI errors in the Backend log - Add empty Changelog -> can be used by companies to provide custom version Changelogs to their customers - Draft for ServiceWorker/App-Update-Service - Error message when History queries fail - Time-Of-Use: prepare for Charge-From-Grid-Controller - Improvements to App-Center (Sorry for this big Backport PR again, but this way we will enable better collaboration on OpenEMS UI with the Community in future)
1 parent 2a46b59 commit 8384f5a

File tree

129 files changed

+5085
-1310
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+5085
-1310
lines changed

io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/SendLogMessageRequest.java

-79
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package io.openems.backend.common.jsonrpc.request;
2+
3+
import java.util.Set;
4+
import java.util.stream.Collectors;
5+
6+
import com.google.gson.JsonObject;
7+
import com.google.gson.JsonPrimitive;
8+
9+
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
10+
import io.openems.common.jsonrpc.base.JsonrpcRequest;
11+
import io.openems.common.utils.JsonUtils;
12+
13+
/**
14+
* Represents a JSON-RPC Request to subscribe to Edges.
15+
*
16+
* <p>
17+
* This is used by UI to get regular updates on specific channels.
18+
*
19+
* <pre>
20+
* {
21+
* "jsonrpc": "2.0",
22+
* "id": "UUID",
23+
* "method": "subscribeEdges",
24+
* "params": {
25+
* "edges": string[]
26+
* }
27+
* }
28+
* </pre>
29+
*/
30+
public class SubscribeEdgesRequest extends JsonrpcRequest {
31+
32+
public static final String METHOD = "subscribeEdges";
33+
34+
/**
35+
* Create {@link SubscribeEdgesRequest} from a template {@link JsonrpcRequest}.
36+
*
37+
* @param r the template {@link JsonrpcRequest}
38+
* @return the {@link SubscribeEdgesRequest}
39+
* @throws OpenemsNamedException on parse error
40+
*/
41+
public static SubscribeEdgesRequest from(JsonrpcRequest r) throws OpenemsNamedException {
42+
var p = r.getParams();
43+
return new SubscribeEdgesRequest(r, JsonUtils.stream(JsonUtils.getAsJsonArray(p, "edges"))
44+
.map(t -> t.getAsString()).collect(Collectors.toSet()));
45+
}
46+
47+
private final Set<String> edges;
48+
49+
private SubscribeEdgesRequest(JsonrpcRequest request, Set<String> edges) {
50+
super(request, SubscribeEdgesRequest.METHOD);
51+
this.edges = edges;
52+
}
53+
54+
/**
55+
* Gets the set of Edges.
56+
*
57+
* @return the Edges
58+
*/
59+
public Set<String> getEdges() {
60+
return this.edges;
61+
}
62+
63+
@Override
64+
public JsonObject getParams() {
65+
return JsonUtils.buildJsonObject() //
66+
.add("edges", this.edges.stream() //
67+
.map(t -> new JsonPrimitive(t)) //
68+
.collect(JsonUtils.toJsonArray())) //
69+
.build();
70+
}
71+
}

io.openems.backend.common/src/io/openems/backend/common/metadata/Metadata.java

+22
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import io.openems.common.exceptions.OpenemsError;
2121
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
2222
import io.openems.common.exceptions.OpenemsException;
23+
import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions;
2324
import io.openems.common.session.Language;
25+
import io.openems.common.session.Role;
2426
import io.openems.common.types.ChannelAddress;
2527
import io.openems.common.types.EdgeConfig;
2628
import io.openems.common.types.EdgeConfig.Component.Channel;
@@ -342,4 +344,24 @@ public static final class Events {
342344
*/
343345
public Optional<String> getSerialNumberForEdge(Edge edge);
344346

347+
/**
348+
* Gets a map of Edge-IDs with the role of the given user.
349+
*
350+
* @param user {@link User} the current user
351+
* @param paginationOptions the options of the requesting page
352+
* @return the role to the Edge-IDs
353+
* @throws OpenemsNamedException on error
354+
*/
355+
public Map<String, Role> getPageDevice(User user, PaginationOptions paginationOptions) throws OpenemsNamedException;
356+
357+
/**
358+
* Gets the Role for a edge of the current user.
359+
*
360+
* @param user {@link User} the current user
361+
* @param edgeId the Edge-ID
362+
* @return the role to the edge
363+
* @throws OpenemsNamedException on error
364+
*/
365+
public Role getRoleForEdge(User user, String edgeId) throws OpenemsNamedException;
366+
345367
}

io.openems.backend.common/src/io/openems/backend/common/metadata/User.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import java.util.List;
55
import java.util.Map.Entry;
66
import java.util.NavigableMap;
7+
import java.util.TreeMap;
78

89
import io.openems.common.exceptions.OpenemsError;
910
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
10-
import io.openems.common.jsonrpc.response.AuthenticateResponse.EdgeMetadata;
11+
import io.openems.common.jsonrpc.response.GetEdgesResponse.EdgeMetadata;
1112
import io.openems.common.session.AbstractUser;
1213
import io.openems.common.session.Language;
1314
import io.openems.common.session.Role;
@@ -22,6 +23,10 @@ public class User extends AbstractUser {
2223
*/
2324
private final String token;
2425

26+
public User(String id, String name, String token, Language language, Role globalRole) {
27+
this(id, name, token, language, globalRole, new TreeMap<>());
28+
}
29+
2530
public User(String id, String name, String token, Language language, Role globalRole,
2631
NavigableMap<String, Role> roles) {
2732
super(id, name, language, globalRole, roles);
@@ -87,4 +92,5 @@ public static List<EdgeMetadata> generateEdgeMetadatas(User user, Metadata metad
8792
}
8893
return metadatas;
8994
}
95+
9096
}

io.openems.backend.common/src/io/openems/backend/common/test/DummyMetadata.java

+13
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import io.openems.common.OpenemsOEM;
1818
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
1919
import io.openems.common.exceptions.OpenemsException;
20+
import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions;
2021
import io.openems.common.session.Language;
22+
import io.openems.common.session.Role;
2123

2224
public class DummyMetadata implements Metadata {
2325
@Override
@@ -134,4 +136,15 @@ public EdgeHandler edge() {
134136
public Optional<String> getSerialNumberForEdge(Edge edge) {
135137
throw new UnsupportedOperationException("Unsupported by Dummy Class");
136138
}
139+
140+
@Override
141+
public Map<String, Role> getPageDevice(User user, PaginationOptions paginationOptions) throws OpenemsNamedException {
142+
throw new UnsupportedOperationException("Unsupported by Dummy Class");
143+
}
144+
145+
@Override
146+
public Role getRoleForEdge(User user, String edgeId) throws OpenemsNamedException {
147+
throw new UnsupportedOperationException("Unsupported by Dummy Class");
148+
}
149+
137150
}

io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/DummyMetadata.java

+31-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.List;
66
import java.util.Map;
77
import java.util.Optional;
8-
import java.util.TreeMap;
98
import java.util.UUID;
109
import java.util.concurrent.Executors;
1110
import java.util.concurrent.ScheduledExecutorService;
@@ -41,8 +40,10 @@
4140
import io.openems.common.exceptions.OpenemsError;
4241
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
4342
import io.openems.common.exceptions.OpenemsException;
43+
import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions;
4444
import io.openems.common.session.Language;
4545
import io.openems.common.session.Role;
46+
import io.openems.common.utils.StringUtils;
4647
import io.openems.common.utils.ThreadPoolUtils;
4748

4849
@Designate(ocd = Config.class, factory = false)
@@ -93,11 +94,7 @@ private void deactivate() {
9394
public User authenticate(String username, String password) throws OpenemsNamedException {
9495
var name = "User #" + this.nextUserId.incrementAndGet();
9596
var token = UUID.randomUUID().toString();
96-
var roles = new TreeMap<String, Role>();
97-
for (String edgeId : this.edges.keySet()) {
98-
roles.put(edgeId, Role.ADMIN);
99-
}
100-
var user = new User(username, name, token, this.defaultLanguage, Role.ADMIN, roles);
97+
var user = new User(username, name, token, this.defaultLanguage, Role.ADMIN);
10198
this.users.put(user.getId(), user);
10299
return user;
103100
}
@@ -171,7 +168,7 @@ public Optional<User> getUser(String userId) {
171168

172169
@Override
173170
public Collection<Edge> getAllOfflineEdges() {
174-
return this.edges.values().stream().filter(Edge::isOffline).collect(Collectors.toUnmodifiableList());
171+
return this.edges.values().stream().filter(Edge::isOffline).collect(Collectors.toUnmodifiableList());
175172
}
176173

177174
private static Optional<Integer> parseNumberFromName(String name) {
@@ -267,4 +264,31 @@ public AlertingSetting getUserAlertingSettings(String edgeId, String userId) thr
267264
public void setUserAlertingSettings(User user, String edgeId, List<AlertingSetting> users) {
268265
throw new UnsupportedOperationException("DummyMetadata.setUserAlertingSettings() is not implemented");
269266
}
267+
268+
@Override
269+
public Map<String, Role> getPageDevice(User user, PaginationOptions paginationOptions)
270+
throws OpenemsNamedException {
271+
var pagesStream = this.edges.values().stream();
272+
final var query = paginationOptions.getQuery();
273+
if (query != null) {
274+
pagesStream = pagesStream.filter(//
275+
edge -> StringUtils.containsWithNullCheck(edge.getId(), query) //
276+
|| StringUtils.containsWithNullCheck(edge.getComment(), query) //
277+
|| StringUtils.containsWithNullCheck(edge.getProducttype(), query) //
278+
);
279+
}
280+
return pagesStream //
281+
.sorted((s1, s2) -> s1.getId().compareTo(s2.getId())) //
282+
.skip(paginationOptions.getPage() * paginationOptions.getLimit()) //
283+
.limit(paginationOptions.getLimit()) //
284+
.peek(t -> user.setRole(t.getId(), Role.ADMIN)) //
285+
.collect(Collectors.toMap(t -> t.getId(), t -> Role.ADMIN)); //
286+
}
287+
288+
@Override
289+
public Role getRoleForEdge(User user, String edgeId) throws OpenemsNamedException {
290+
user.setRole(edgeId, Role.ADMIN);
291+
return Role.ADMIN;
292+
}
293+
270294
}

io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/FileMetadata.java

+31-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.Map;
1111
import java.util.Map.Entry;
1212
import java.util.Optional;
13-
import java.util.TreeMap;
1413
import java.util.UUID;
1514
import java.util.concurrent.CompletableFuture;
1615
import java.util.stream.Collectors;
@@ -43,9 +42,11 @@
4342
import io.openems.common.exceptions.OpenemsError;
4443
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
4544
import io.openems.common.exceptions.OpenemsException;
45+
import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions;
4646
import io.openems.common.session.Language;
4747
import io.openems.common.session.Role;
4848
import io.openems.common.utils.JsonUtils;
49+
import io.openems.common.utils.StringUtils;
4950

5051
/**
5152
* This implementation of MetadataService reads Edges configuration from a file.
@@ -70,7 +71,8 @@
7071
@Designate(ocd = Config.class, factory = false)
7172
@Component(//
7273
name = "Metadata.File", //
73-
configurationPolicy = ConfigurationPolicy.REQUIRE //
74+
configurationPolicy = ConfigurationPolicy.REQUIRE, //
75+
immediate = true //
7476
)
7577
@EventTopics({ //
7678
Edge.Events.ON_SET_CONFIG //
@@ -215,15 +217,14 @@ private synchronized void refreshData() {
215217
// Add Edges and configure User permissions
216218
for (MyEdge edge : edges) {
217219
this.edges.put(edge.getId(), edge);
218-
this.user.setRole(edge.getId(), Role.ADMIN);
219220
}
220221
}
221222
this.setInitialized();
222223
}
223224

224225
private static User generateUser() {
225226
return new User(FileMetadata.USER_ID, FileMetadata.USER_NAME, UUID.randomUUID().toString(),
226-
FileMetadata.LANGUAGE, FileMetadata.USER_GLOBAL_ROLE, new TreeMap<>());
227+
FileMetadata.LANGUAGE, FileMetadata.USER_GLOBAL_ROLE);
227228
}
228229

229230
@Override
@@ -307,4 +308,30 @@ public void setUserAlertingSettings(User user, String edgeId, List<AlertingSetti
307308
throw new UnsupportedOperationException("FileMetadata.setUserAlertingSettings() is not implemented");
308309
}
309310

311+
@Override
312+
public Map<String, Role> getPageDevice(User user, PaginationOptions paginationOptions)
313+
throws OpenemsNamedException {
314+
var pagesStream = this.edges.values().stream();
315+
final var query = paginationOptions.getQuery();
316+
if (query != null) {
317+
pagesStream = pagesStream.filter(//
318+
edge -> StringUtils.containsWithNullCheck(edge.getId(), query) //
319+
|| StringUtils.containsWithNullCheck(edge.getComment(), query) //
320+
|| StringUtils.containsWithNullCheck(edge.getProducttype(), query) //
321+
);
322+
}
323+
return pagesStream //
324+
.sorted((s1, s2) -> s1.getId().compareTo(s2.getId())) //
325+
.skip(paginationOptions.getPage() * paginationOptions.getLimit()) //
326+
.limit(paginationOptions.getLimit()) //
327+
.peek(t -> user.setRole(t.getId(), Role.ADMIN)) //
328+
.collect(Collectors.toMap(t -> t.getId(), t -> Role.ADMIN)); //
329+
}
330+
331+
@Override
332+
public Role getRoleForEdge(User user, String edgeId) throws OpenemsNamedException {
333+
user.setRole(edgeId, Role.ADMIN);
334+
return Role.ADMIN;
335+
}
336+
310337
}

0 commit comments

Comments
 (0)