Skip to content

Commit

Permalink
Implement Edge2Edge Meter (#2621)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sn0w3y authored Jun 16, 2024
1 parent 54dcd4e commit ab73098
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 6 deletions.
3 changes: 2 additions & 1 deletion io.openems.edge.edge2edge/bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Bundle-Version: 1.0.0.${tstamp}
io.openems.edge.bridge.modbus,\
io.openems.edge.common,\
io.openems.edge.ess.api,\
io.openems.edge.meter.api,\

-testpath: \
${testpath}
${testpath}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.openems.edge.edge2edge.ess;

import java.util.List;
import java.util.function.Consumer;

import org.osgi.service.cm.ConfigurationAdmin;
Expand All @@ -14,8 +15,6 @@
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.Designate;

import com.google.common.collect.Lists;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.types.OpenemsType;
Expand Down Expand Up @@ -56,7 +55,7 @@ protected void setModbus(BridgeModbus modbus) {

public Edge2EdgeEssImpl() {
super(//
Lists.newArrayList(//
List.of(//
OpenemsComponent::getModbusSlaveNatureTable, //
SymmetricEss::getModbusSlaveNatureTable, //
AsymmetricEss::getModbusSlaveNatureTable, //
Expand Down Expand Up @@ -105,8 +104,8 @@ protected Consumer<Object> getOnUpdateCallback(ModbusSlaveNatureTable modbusSlav
@Override
protected io.openems.edge.common.channel.ChannelId getWriteChannelId(ModbusSlaveNatureTable modbusSlaveNatureTable,
ModbusRecord record) {
if (record instanceof ModbusRecordChannel) {
var c = ((ModbusRecordChannel) record).getChannelId();
if (record instanceof ModbusRecordChannel r) {
var c = r.getChannelId();
if (c == ManagedSymmetricEss.ChannelId.SET_ACTIVE_POWER_EQUALS) {
return Edge2EdgeEss.ChannelId.REMOTE_SET_ACTIVE_POWER_EQUALS;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.openems.edge.edge2edge.meter;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

import io.openems.edge.meter.api.MeterType;

@ObjectClassDefinition(//
name = "Edge-2-Edge Meter", //
description = "Connects an energy storage system from a slave OpenEMS Edge via Modbus")
@interface Config {

@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
String id() default "meter0";

@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";

@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
boolean enabled() default true;

@AttributeDefinition(name = "Remote Component-ID", description = "Component-ID of ESS at the slave OpenEMS Edge.")
String remoteComponentId() default "meter0";

@AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.")
String modbus_id() default "modbus0";

@AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.")
int modbusUnitId() default 1;

@AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?")
MeterType type() default MeterType.PRODUCTION;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Edge-2-Edge Meter [{id}]";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.openems.edge.edge2edge.meter;

import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.component.OpenemsComponent;

public interface Edge2EdgeMeter extends OpenemsComponent {

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
;

private final Doc doc;

private ChannelId(Doc doc) {
this.doc = doc;
}

@Override
public Doc doc() {
return this.doc;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.openems.edge.edge2edge.meter;

import java.util.List;
import java.util.function.Consumer;

import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
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.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.Designate;

import io.openems.common.channel.AccessMode;
import io.openems.common.exceptions.OpenemsException;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.modbusslave.ModbusRecord;
import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
import io.openems.edge.edge2edge.common.AbstractEdge2Edge;
import io.openems.edge.edge2edge.common.Edge2Edge;
import io.openems.edge.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.MeterType;

@Designate(ocd = Config.class, factory = true)
@Component(//
name = "Edge2Edge.Meter", //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
public class Edge2EdgeMeterImpl extends AbstractEdge2Edge
implements Edge2EdgeMeter, ElectricityMeter, Edge2Edge, ModbusComponent, OpenemsComponent {

@Reference
protected ConfigurationAdmin cm;

@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
protected void setModbus(BridgeModbus modbus) {
super.setModbus(modbus);
}

private Config config;

public Edge2EdgeMeterImpl() throws OpenemsException {
super(//
List.of(//
OpenemsComponent::getModbusSlaveNatureTable, //
ElectricityMeter::getModbusSlaveNatureTable //
), //
OpenemsComponent.ChannelId.values(), //
ModbusComponent.ChannelId.values(), //
Edge2Edge.ChannelId.values(), //
ElectricityMeter.ChannelId.values() //
);
}

@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
this.config = config;
if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
"Modbus", config.modbus_id(), config.remoteComponentId(), AccessMode.READ_ONLY)) {
return;
}
}

@Deactivate
protected void deactivate() {
super.deactivate();
}

@Override
protected Consumer<Object> getOnUpdateCallback(ModbusSlaveNatureTable modbusSlaveNatureTable, ModbusRecord record) {
return null;
}

@Override
protected io.openems.edge.common.channel.ChannelId getWriteChannelId(ModbusSlaveNatureTable modbusSlaveNatureTable,
ModbusRecord record) {
// No writable Channels
return null;
}

@Override
public String debugLog() {
return "L:" + this.getActivePower().asString();
}

@Override
public MeterType getMeterType() {
return this.config.type();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.openems.edge.edge2edge.meter;

import org.junit.Test;

import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;
import io.openems.edge.meter.api.MeterType;

public class Edge2EdgeEssMeterImplTest {

private static final String COMPONENT_ID = "meter0";
private static final String MODBUS_ID = "modbus0";

@Test
public void test() throws Exception {
new ComponentTest(new Edge2EdgeMeterImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
.activate(MyConfig.create() //
.setId(COMPONENT_ID) //
.setModbusId(MODBUS_ID) //
.setRemoteComponentId(COMPONENT_ID) //
.setMeterType(MeterType.PRODUCTION) //
.build())
.next(new TestCase());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.openems.edge.edge2edge.meter;

import io.openems.common.test.AbstractComponentConfig;
import io.openems.common.utils.ConfigUtils;
import io.openems.edge.meter.api.MeterType;

@SuppressWarnings("all")
public class MyConfig extends AbstractComponentConfig implements Config {

protected static class Builder {
private String id;
private String modbusId = null;
private int modbusUnitId;
private String remoteComponentId;
private MeterType type;

private Builder() {
}

public Builder setId(String id) {
this.id = id;
return this;
}

public Builder setModbusId(String modbusId) {
this.modbusId = modbusId;
return this;
}

public Builder setModbusUnitId(int modbusUnitId) {
this.modbusUnitId = modbusUnitId;
return this;
}

public Builder setRemoteComponentId(String remoteComponentId) {
this.remoteComponentId = remoteComponentId;
return this;
}

public Builder setMeterType(MeterType type) {
this.type = type;
return this;
}

public MyConfig build() {
return new MyConfig(this);
}
}

/**
* Create a Config builder.
*
* @return a {@link Builder}
*/
public static Builder create() {
return new Builder();
}

private final Builder builder;

private MyConfig(Builder builder) {
super(Config.class, builder.id);
this.builder = builder;
}

@Override
public String modbus_id() {
return this.builder.modbusId;
}

@Override
public String Modbus_target() {
return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id());
}

@Override
public int modbusUnitId() {
return this.builder.modbusUnitId;
}

@Override
public String remoteComponentId() {
return this.builder.remoteComponentId;
}

@Override
public MeterType type() {
return this.builder.type;
}

}

0 comments on commit ab73098

Please sign in to comment.