Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edge: Implement Janitza UMG104 #2970

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.openems.edge.meter.janitza.umg104;

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

import io.openems.common.types.MeterType;

@ObjectClassDefinition(//
name = "Meter Janitza UMG 104", //
description = "Implements the Janitza UMG 104 power analyser.")
@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 = "Meter-Type", description = "What is measured by this Meter?")
MeterType type() default MeterType.PRODUCTION;

@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. Defaults to '1' for Modbus/TCP.")
int modbusUnitId() default 1;

@AttributeDefinition(name = "Invert Power", description = "Inverts all Power values, inverts current values, swaps production and consumptioon energy, i.e. Power is multiplied with -1.")
boolean invert() default false;

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

String webconsole_configurationFactory_nameHint() default "Meter Janitza UMG 104 [{id}]";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.openems.edge.meter.janitza.umg104;

import io.openems.common.channel.Unit;
import io.openems.common.types.OpenemsType;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.modbusslave.ModbusSlave;
import io.openems.edge.meter.api.ElectricityMeter;

public interface MeterJanitzaUmg104 extends ElectricityMeter, OpenemsComponent, ModbusSlave {

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
ROTATION_FIELD(Doc.of(OpenemsType.INTEGER)),

INTERNAL_TEMPERATURE(Doc.of(OpenemsType.FLOAT) //
.unit(Unit.DEGREE_CELSIUS)), //
;

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,175 @@
package io.openems.edge.meter.janitza.umg104;

import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.INVERT_IF_TRUE;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_3;

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.common.types.MeterType;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.bridge.modbus.api.ModbusProtocol;
import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement;
import io.openems.edge.bridge.modbus.api.element.FloatDoublewordElement;
import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.modbusslave.ModbusSlave;
import io.openems.edge.common.modbusslave.ModbusSlaveTable;
import io.openems.edge.common.taskmanager.Priority;
import io.openems.edge.meter.api.ElectricityMeter;

/**
* Implements the Janitza UMG 104 power analyzer.
*
* <p>
* https://www.janitza.de/umg-104-pro.html
*/
@Designate(ocd = Config.class, factory = true)
@Component(//
name = "Meter.Janitza.UMG104", //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
public class MeterJanitzaUmg104Impl extends AbstractOpenemsModbusComponent
implements MeterJanitzaUmg104, ElectricityMeter, ModbusComponent, OpenemsComponent, ModbusSlave {

@Reference
private ConfigurationAdmin cm;

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

private MeterType meterType = MeterType.PRODUCTION;
/** Invert power values. */
private boolean invert = false;

public MeterJanitzaUmg104Impl() {
super(//
OpenemsComponent.ChannelId.values(), //
ModbusComponent.ChannelId.values(), //
ElectricityMeter.ChannelId.values(), //
MeterJanitzaUmg104.ChannelId.values() //
);

// Automatically calculate sum values from L1/L2/L3
ElectricityMeter.calculateSumCurrentFromPhases(this);
ElectricityMeter.calculateAverageVoltageFromPhases(this);
}

@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
this.meterType = config.type();
this.invert = config.invert();

if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
"Modbus", config.modbus_id())) {
return;

Check warning on line 86 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L86

Added line #L86 was not covered by tests
}
}

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

Check warning on line 94 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L93-L94

Added lines #L93 - L94 were not covered by tests

@Override
public MeterType getMeterType() {
return this.meterType;

Check warning on line 98 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L98

Added line #L98 was not covered by tests
}

@Override
protected ModbusProtocol defineModbusProtocol() {
var modbusProtocol = new ModbusProtocol(this, //
new FC3ReadRegistersTask(1317, Priority.HIGH, //
m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(1317), //
SCALE_FACTOR_3), //
m(ElectricityMeter.ChannelId.VOLTAGE_L2, new FloatDoublewordElement(1319), //
SCALE_FACTOR_3), //
m(ElectricityMeter.ChannelId.VOLTAGE_L3, new FloatDoublewordElement(1321), //
SCALE_FACTOR_3), //
new DummyRegisterElement(1323, 1324), //
m(ElectricityMeter.ChannelId.CURRENT_L1, new FloatDoublewordElement(1325), //
SCALE_FACTOR_3), //
m(ElectricityMeter.ChannelId.CURRENT_L2, new FloatDoublewordElement(1327), //
SCALE_FACTOR_3), //
m(ElectricityMeter.ChannelId.CURRENT_L3, new FloatDoublewordElement(1329), //
SCALE_FACTOR_3), //
new DummyRegisterElement(1331, 1332), //
m(ElectricityMeter.ChannelId.ACTIVE_POWER_L1, new FloatDoublewordElement(1333), //
INVERT_IF_TRUE(this.invert)), //
m(ElectricityMeter.ChannelId.ACTIVE_POWER_L2, new FloatDoublewordElement(1335), //
INVERT_IF_TRUE(this.invert)), //
m(ElectricityMeter.ChannelId.ACTIVE_POWER_L3, new FloatDoublewordElement(1337), //
INVERT_IF_TRUE(this.invert)), //
new DummyRegisterElement(1339, 1340), //
m(ElectricityMeter.ChannelId.REACTIVE_POWER_L1, new FloatDoublewordElement(1341), //
INVERT_IF_TRUE(this.invert)), //
m(ElectricityMeter.ChannelId.REACTIVE_POWER_L2, new FloatDoublewordElement(1343), //
INVERT_IF_TRUE(this.invert)), //
m(ElectricityMeter.ChannelId.REACTIVE_POWER_L3, new FloatDoublewordElement(1345), //
INVERT_IF_TRUE(this.invert)), //
new DummyRegisterElement(1347, 1368), //
m(ElectricityMeter.ChannelId.ACTIVE_POWER, new FloatDoublewordElement(1369), //
INVERT_IF_TRUE(this.invert)), //
m(ElectricityMeter.ChannelId.REACTIVE_POWER, new FloatDoublewordElement(1371), //
INVERT_IF_TRUE(this.invert))), //
new FC3ReadRegistersTask(1439, Priority.LOW, //
m(ElectricityMeter.ChannelId.FREQUENCY, new FloatDoublewordElement(1439), //
SCALE_FACTOR_3),
new DummyRegisterElement(1441, 1448),
m(MeterJanitzaUmg104.ChannelId.ROTATION_FIELD, new FloatDoublewordElement(1449), //
SCALE_FACTOR_3),
new DummyRegisterElement(1451, 1460),
m(MeterJanitzaUmg104.ChannelId.INTERNAL_TEMPERATURE, new FloatDoublewordElement(1461), //
SCALE_FACTOR_3)
));

if (this.invert) {
modbusProtocol.addTask(new FC3ReadRegistersTask(9851, Priority.LOW, //
m(ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new FloatDoublewordElement(9851)),

Check warning on line 150 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L149-L150

Added lines #L149 - L150 were not covered by tests
new DummyRegisterElement(9853, 9862),
m(ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new FloatDoublewordElement(9863))));

Check warning on line 152 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L152

Added line #L152 was not covered by tests
} else {
modbusProtocol.addTask(new FC3ReadRegistersTask(9851, Priority.LOW, //
m(ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new FloatDoublewordElement(9851)),
new DummyRegisterElement(9853, 9862),
m(ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new FloatDoublewordElement(9863))));
}

return modbusProtocol;
}

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

Check warning on line 165 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L165

Added line #L165 was not covered by tests
}

@Override
public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
return new ModbusSlaveTable(//
OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
ElectricityMeter.getModbusSlaveNatureTable(accessMode) //

Check warning on line 172 in io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java

View check run for this annotation

Codecov / codecov/patch

io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg104/MeterJanitzaUmg104Impl.java#L170-L172

Added lines #L170 - L172 were not covered by tests
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.openems.edge.meter.janitza.umg104;

import static io.openems.common.types.MeterType.GRID;

import org.junit.Test;

import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;

public class MeterJanitzaUmg104ImplTest {

@Test
public void test() throws Exception {
new ComponentTest(new MeterJanitzaUmg104Impl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
.setId("meter0") //
.setModbusId("modbus0") //
.setType(GRID) //
.build()) //
;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.openems.edge.meter.janitza.umg104;

import io.openems.common.test.AbstractComponentConfig;
import io.openems.common.types.MeterType;
import io.openems.common.utils.ConfigUtils;

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

protected static class Builder {
private String id;
private String modbusId;
private int modbusUnitId;
private MeterType type;
private boolean invert;

private Builder() {
}

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

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

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

public Builder setInvert(boolean invert) {
this.invert = invert;
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 MeterType type() {
return this.builder.type;
}

@Override
public boolean invert() {
return this.builder.invert;
}

}
Loading