Skip to content

Commit 1e95d4d

Browse files
sfeilmeiermichaelgrillsebastianasenlukasrgr
authored
FEMS Backport (OpenEMS#2419)
* KACO PV-Inverter: calculate production energy manually (OpenEMS#857) Sometimes data from SunSpec was not reliable (even after fixing SunSpec scalefactors). * GoodWe 20/30 Chargers: avoid multiple HIGH Priority mobus tasks (OpenEMS#859) Set goodwe charger channels by the abstract goodwe component to avoid multiple HIGH Priority mobus tasks for Goodwe20/30 Chargers. TwoStringCharger is no longer a ModbusComponent * UI: Utils, tests and translations * fix subtractSafely of not checking for undefined * explicitely checking for undefined and null * add unittest for utils * UI: Fix chart wrong scaling when firstSetupProtocol is too close to end of month * Backport FEMS Backend - Fix compatibility with and require Odoo 16 (separate Odoo module will be updated independently) - Fix possible NPEs - Fix starting of Websocket servers (avoid event race conditions) - Add COMPONENT_IDs to backend components - Fix Websocket handshake case-insensitive - Improve general performance - AVoid excessive logs - Add GenericSystemLog to Metadata: record system execute and update - Improve InfluxDB Aggregated data handling - Allow multiple InfluxDB servers for different periods (defined by start-/enddate) - InfluxDB: only allow Channel-Addresses in standardized format * Websocket Api Controller: cleanup getEdgeRequest & JUnit tests * Cleanup getEdgeRequest to use the static method to generate the metadata for the local edge * added tests for getEdgeRequest/getEdgesRequest Edge * AppCenter: add check for peakshaving to not be compatible with home - added invert boolean for error messages - add test for peakshaving & CheckHome - update translations * AppCenter: separated AppConfiguration into tasks This change is a prerequisite for automatically adding channels to the persistence predictor for TimeOfUseTariff Apps. - separated AppConfiguration into tasks - added Builder for AppConfiguration - changed AppConfiguration to a record - moved AggregateTasks into separate folder - added simple(& automatical) order for tasks - add tests for all AggregateTasks - moved validation to AggregateTasks to validate their Task configuration - changed AppValidateWorker to a (OSGi-)Component - added OnlyIfThrowing interface * Add ENTSO-E App --------- Co-authored-by: Michael Grill <[email protected]> Co-authored-by: Sebastian Asen <[email protected]> Co-authored-by: Lukas Rieger <[email protected]> Co-authored-by: Stefan Feilmeier <[email protected]>
1 parent d629145 commit 1e95d4d

File tree

170 files changed

+5377
-2351
lines changed

Some content is hidden

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

170 files changed

+5377
-2351
lines changed

.gitpod.Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ENV TRIGGER_REBUILD 4
1212
RUN npm install -g @angular/cli
1313

1414
# Install odoo
15-
ENV ODOO_VERSION 15.0
15+
ENV ODOO_VERSION 16.0
1616
ENV ODOO_RELEASE latest
1717
RUN curl -o odoo.deb -sSL http://nightly.odoo.com/${ODOO_VERSION}/nightly/deb/odoo_${ODOO_VERSION}.${ODOO_RELEASE}_all.deb \
1818
&& sudo apt-get update \

.gitpod.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ tasks:
2626
cd /workspace/odoo
2727
mkdir -p addons-available addons-enabled
2828
cd addons-available
29-
git clone --depth=1 -b 15.0 https://github.com/OCA/partner-contact
30-
git clone --depth=1 -b 15.0 https://github.com/OCA/web.git
31-
git clone --depth=1 -b 15.0 https://github.com/OpenEMS/odoo-openems.git
29+
git clone --depth=1 -b 16.0 https://github.com/OCA/partner-contact
30+
git clone --depth=1 -b 16.0 https://github.com/OCA/web.git
31+
git clone --depth=1 -b 16.0 https://github.com/OpenEMS/odoo-openems.git
3232
cd ../addons-enabled
3333
ln -s ../addons-available/partner-contact/partner_firstname
3434
ln -s ../addons-available/web/web_m2x_options

io.openems.backend.alerting/src/io/openems/backend/alerting/handler/OfflineEdgeHandler.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ protected void tryRemoveEdge(Edge edge) {
194194
protected void tryAddEdge(Edge edge) {
195195
if (this.isValidEdge(edge)) {
196196
var msg = this.getEdgeMessage(edge);
197-
if (msg != null) {
197+
var msgScheduler = this.msgScheduler;
198+
if (msg != null && msgScheduler != null) {
198199
this.msgScheduler.schedule(msg);
199200
}
200201
}

io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/Backend2BackendWebsocket.java

+18-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import io.openems.backend.common.metadata.Metadata;
2525
import io.openems.backend.common.timedata.TimedataManager;
2626
import io.openems.common.utils.ThreadPoolUtils;
27-
import io.openems.common.websocket.AbstractWebsocketServer.DebugMode;
2827

2928
@Designate(ocd = Config.class, factory = true)
3029
@Component(//
@@ -37,6 +36,8 @@
3736
})
3837
public class Backend2BackendWebsocket extends AbstractOpenemsBackendComponent implements EventHandler {
3938

39+
private static final String COMPONENT_ID = "b2bwebsocket0";
40+
4041
public static final int DEFAULT_PORT = 8076;
4142

4243
protected final ScheduledExecutorService executor = Executors.newScheduledThreadPool(10,
@@ -64,6 +65,10 @@ public Backend2BackendWebsocket() {
6465
@Activate
6566
private void activate(Config config) {
6667
this.config = config;
68+
69+
if (this.metadata.isInitialized()) {
70+
this.startServer();
71+
}
6772
}
6873

6974
@Deactivate
@@ -74,14 +79,13 @@ private void deactivate() {
7479

7580
/**
7681
* Create and start new server.
77-
*
78-
* @param port the port
79-
* @param poolSize number of threads dedicated to handle the tasks
80-
* @param debugMode activate a regular debug log about the state of the tasks
8182
*/
82-
private synchronized void startServer(int port, int poolSize, DebugMode debugMode) {
83-
this.server = new WebsocketServer(this, this.getName(), port, poolSize, debugMode);
84-
this.server.start();
83+
private synchronized void startServer() {
84+
if (this.server == null) {
85+
this.server = new WebsocketServer(this, this.getName(), this.config.port(), this.config.poolSize(),
86+
this.config.debugMode());
87+
this.server.start();
88+
}
8589
}
8690

8791
/**
@@ -112,8 +116,13 @@ protected void logError(Logger log, String message) {
112116
public void handleEvent(Event event) {
113117
switch (event.getTopic()) {
114118
case Metadata.Events.AFTER_IS_INITIALIZED:
115-
this.startServer(this.config.port(), this.config.poolSize(), this.config.debugMode());
119+
this.startServer();
116120
break;
117121
}
118122
}
123+
124+
public String getId() {
125+
return COMPONENT_ID;
126+
}
127+
119128
}

io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnOpen.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void run(WebSocket ws, JsonObject handshake) throws OpenemsNamedException
2727
try {
2828
// Read "Authorization" header for Simple HTTP authentication. Source:
2929
// https://stackoverflow.com/questions/16000517/how-to-get-password-from-http-basic-authentication
30-
final var authorization = JsonUtils.getAsString(handshake, "Authorization");
30+
final var authorization = JsonUtils.getAsString(handshake, "authorization");
3131
if (authorization == null || !authorization.toLowerCase().startsWith("basic")) {
3232
throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception();
3333
}

io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WebsocketServer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public WebsocketServer(Backend2BackendWebsocket parent, String name, int port, i
2727
var data = TreeBasedTable.<Long, String, JsonElement>create();
2828
var now = Instant.now().toEpochMilli();
2929
ThreadPoolUtils.debugMetrics(executor).forEach((key, value) -> {
30-
data.put(now, "b2bwebsocket/" + key, new JsonPrimitive(value));
30+
data.put(now, parent.getId() + "/" + key, new JsonPrimitive(value));
3131
});
3232
parent.timedataManager.write("backend0", new TimestampedDataNotification(data));
3333
});

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

+60-70
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package io.openems.backend.common.metadata;
22

3-
import java.time.ZoneOffset;
43
import java.time.ZonedDateTime;
4+
import java.time.temporal.ChronoUnit;
55
import java.util.ArrayList;
66
import java.util.List;
7+
import java.util.concurrent.atomic.AtomicReference;
78

89
import org.slf4j.Logger;
910
import org.slf4j.LoggerFactory;
@@ -23,9 +24,9 @@ public class Edge {
2324

2425
private final String id;
2526
private String comment;
26-
private SemanticVersion version;
27-
private String producttype;
28-
private ZonedDateTime lastmessage;
27+
private final AtomicReference<SemanticVersion> version = new AtomicReference<>(SemanticVersion.ZERO);
28+
private final AtomicReference<String> producttype = new AtomicReference<>("");
29+
private final AtomicReference<ZonedDateTime> lastmessage = new AtomicReference<>(null);
2930
private boolean isOnline = false;
3031

3132
private final List<EdgeUser> user;
@@ -34,12 +35,14 @@ public Edge(Metadata parent, String id, String comment, String version, String p
3435
ZonedDateTime lastmessage) {
3536
this.id = id;
3637
this.comment = comment;
37-
this.version = SemanticVersion.fromStringOrZero(version);
38-
this.producttype = producttype;
39-
this.lastmessage = lastmessage;
4038

4139
this.parent = parent;
4240
this.user = new ArrayList<>();
41+
42+
// Avoid initial events
43+
this.setProducttype(producttype, false);
44+
this.setVersion(SemanticVersion.fromStringOrZero(version), false);
45+
this.setLastmessage(lastmessage, false);
4346
}
4447

4548
public String getId() {
@@ -66,10 +69,10 @@ public JsonObject toJsonObject() {
6669
return JsonUtils.buildJsonObject() //
6770
.addProperty("id", this.id) //
6871
.addProperty("comment", this.comment) //
69-
.addProperty("version", this.version.toString()) //
70-
.addProperty("producttype", this.producttype) //
72+
.addProperty("version", this.version.get().toString()) //
73+
.addProperty("producttype", this.producttype.get()) //
7174
.addProperty("online", this.isOnline) //
72-
.addPropertyIfNotNull("lastmessage", this.lastmessage) //
75+
.addPropertyIfNotNull("lastmessage", this.lastmessage.get()) //
7376
.build();
7477
}
7578

@@ -110,36 +113,34 @@ public synchronized void setOnline(boolean isOnline) {
110113
}
111114

112115
/**
113-
* Sets the Last-Message-Timestamp to now() and calls the
114-
* setLastmessage-Listeners.
116+
* Sets the Last-Message-Timestamp to now() (truncated to Minutes) and emits a
117+
* ON_SET_LASTMESSAGE event; but only max one event per Minute.
115118
*/
116-
public synchronized void setLastmessage() {
117-
this.setLastmessage(ZonedDateTime.now(ZoneOffset.UTC));
119+
public void setLastmessage() {
120+
this.setLastmessage(ZonedDateTime.now());
118121
}
119122

120123
/**
121-
* Sets the Last-Message-Timestamp and calls the setLastmessage-Listeners.
124+
* Sets the Last-Message-Timestamp (truncated to Minutes) and emits a
125+
* ON_SET_LASTMESSAGE event; but only max one event per Minute.
122126
*
123127
* @param timestamp the Last-Message-Timestamp
124128
*/
125-
public synchronized void setLastmessage(ZonedDateTime timestamp) {
129+
public void setLastmessage(ZonedDateTime timestamp) {
126130
this.setLastmessage(timestamp, true);
127131
}
128132

129-
/**
130-
* Sets the Last-Message-Timestamp.
131-
*
132-
* @param timestamp the Last-Message-Timestamp
133-
* @param callListeners whether to call the setLastmessage-Listeners
134-
*/
135-
public synchronized void setLastmessage(ZonedDateTime timestamp, boolean callListeners) {
136-
if (callListeners) {
133+
private void setLastmessage(ZonedDateTime timestamp, boolean emitEvent) {
134+
if (timestamp == null) {
135+
return;
136+
}
137+
timestamp = timestamp.truncatedTo(ChronoUnit.MINUTES);
138+
var previousTimestamp = this.lastmessage.getAndSet(timestamp);
139+
if (emitEvent && (previousTimestamp == null || previousTimestamp.isBefore(timestamp))) {
137140
EventBuilder.from(this.parent.getEventAdmin(), Events.ON_SET_LASTMESSAGE) //
138141
.addArg(Events.OnSetLastmessage.EDGE, this) //
139-
.send(); //
142+
.send();
140143
}
141-
var now = timestamp;
142-
this.lastmessage = now;
143144
}
144145

145146
/**
@@ -148,80 +149,69 @@ public synchronized void setLastmessage(ZonedDateTime timestamp, boolean callLis
148149
* @return Last-Message-Timestamp in UTC Timezone
149150
*/
150151
public ZonedDateTime getLastmessage() {
151-
return this.lastmessage;
152+
return this.lastmessage.get();
152153
}
153154

154155
/*
155156
* Version
156157
*/
157158
public SemanticVersion getVersion() {
158-
return this.version;
159+
return this.version.get();
159160
}
160161

161162
/**
162-
* Sets the version and calls the SetVersion-Listeners.
163+
* Sets the version and emits a ON_SET_VERSION event.
163164
*
164165
* @param version the version
165166
*/
166-
public synchronized void setVersion(SemanticVersion version) {
167+
public void setVersion(SemanticVersion version) {
167168
this.setVersion(version, true);
168169
}
169170

170-
/**
171-
* Sets the version.
172-
*
173-
* @param version the version
174-
* @param callListeners whether to call the SetVersion-Listeners
175-
*/
176-
public synchronized void setVersion(SemanticVersion version, boolean callListeners) {
177-
if (!Objects.equal(this.version, version)) { // on change
178-
if (callListeners) {
179-
this.log.info(
180-
"Edge [" + this.getId() + "]: Update version from [" + this.version + "] to [" + version + "]");
181-
182-
EventBuilder.from(this.parent.getEventAdmin(), Events.ON_SET_VERSION) //
183-
.addArg(Events.OnSetVersion.EDGE, this) //
184-
.addArg(Events.OnSetVersion.VERSION, version) //
185-
.send(); //
186-
}
187-
this.version = version;
171+
private void setVersion(SemanticVersion version, boolean emitEvent) {
172+
if (version == null) {
173+
version = SemanticVersion.ZERO;
174+
}
175+
var oldVersion = this.version.getAndSet(version);
176+
if (emitEvent && !Objects.equal(oldVersion, version)) { // on change
177+
this.log.info("Edge [" + this.getId() + "]: Update version from [" + oldVersion + "] to [" + version + "]");
178+
179+
EventBuilder.from(this.parent.getEventAdmin(), Events.ON_SET_VERSION) //
180+
.addArg(Events.OnSetVersion.EDGE, this) //
181+
.addArg(Events.OnSetVersion.VERSION, version) //
182+
.send();
188183
}
189184
}
190185

191186
/*
192187
* Producttype
193188
*/
194189
public String getProducttype() {
195-
return this.producttype;
190+
return this.producttype.get();
196191
}
197192

198193
/**
199-
* Sets the Producttype and calls the SetProducttype-Listeners.
194+
* Sets the Producttype and emits a ON_SET_PRODUCTTYPE event.
200195
*
201196
* @param producttype the Producttype
202197
*/
203-
public synchronized void setProducttype(String producttype) {
198+
public void setProducttype(String producttype) {
204199
this.setProducttype(producttype, true);
205200
}
206201

207-
/**
208-
* Sets the Producttype.
209-
*
210-
* @param producttype the Producttype
211-
* @param callListeners whether to call the SetProducttype-Listeners
212-
*/
213-
public synchronized void setProducttype(String producttype, boolean callListeners) {
214-
if (!Objects.equal(this.producttype, producttype)) { // on change
215-
if (callListeners) {
216-
this.log.info("Edge [" + this.getId() + "]: Update Product-Type to [" + producttype + "]. It was ["
217-
+ this.producttype + "]");
218-
219-
EventBuilder.from(this.parent.getEventAdmin(), Events.ON_SET_PRODUCTTYPE) //
220-
.addArg(Events.OnSetProducttype.EDGE, this) //
221-
.addArg(Events.OnSetProducttype.PRODUCTTYPE, producttype) //
222-
.send(); //
223-
}
224-
this.producttype = producttype;
202+
private void setProducttype(String producttype, boolean emitEvent) {
203+
if (producttype == null) {
204+
producttype = "";
205+
}
206+
var oldProducttype = this.producttype.getAndSet(producttype);
207+
if (emitEvent && !Objects.equal(oldProducttype, producttype)) { // on change
208+
this.log.info("Edge [" + this.getId() + "]: Update Product-Type from [" + oldProducttype + "] to ["
209+
+ producttype + "]");
210+
211+
EventBuilder.from(this.parent.getEventAdmin(), Events.ON_SET_PRODUCTTYPE) //
212+
.addArg(Events.OnSetProducttype.EDGE, this) //
213+
.addArg(Events.OnSetProducttype.PRODUCTTYPE, producttype) //
214+
.send();
225215
}
226216
}
227217

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

+38
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,42 @@ public List<EdgeMetadata> getPageDevice(User user, PaginationOptions paginationO
365365
*/
366366
public EdgeMetadata getEdgeMetadataForUser(User user, String edgeId) throws OpenemsNamedException;
367367

368+
public interface GenericSystemLog {
369+
370+
/**
371+
* Gets the edgeId of the target log.
372+
*
373+
* @return the edgeId
374+
*/
375+
public String edgeId();
376+
377+
/**
378+
* Gets the user which triggered the log.
379+
*
380+
* @return the user
381+
*/
382+
public User user();
383+
384+
/**
385+
* Gets a short string which represents the whole log.
386+
*
387+
* @return the teaser string
388+
*/
389+
public String teaser();
390+
391+
/**
392+
* Gets a map of values of the log.
393+
*
394+
* @return the map
395+
*/
396+
public Map<String, String> getValues();
397+
}
398+
399+
/**
400+
* Handles a Systemlog-Message.
401+
*
402+
* @param systemLog the log
403+
*/
404+
public void logGenericSystemLog(GenericSystemLog systemLog);
405+
368406
}

0 commit comments

Comments
 (0)